990 lines · 43.8 KB · C#
RawDownload
//******************************************************************************************************//  ProcessLauncher.cs - Gbtc////  Copyright © 2017, 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 MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://opensource.org/licenses/MIT////  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://  ----------------------------------------------------------------------------------------------------//  11/29/2017 - J. Ritchie Carroll//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Diagnostics;using System.IO;using System.Linq;using System.Text;using System.Text.RegularExpressions;using GSF;using GSF.Diagnostics;using GSF.IO;using GSF.Threading;using GSF.TimeSeries;using GSF.TimeSeries.Adapters;using GSF.TimeSeries.Statistics;using GSF.Units; // Commands are passed via configuration#pragma warning disable SG0001 // ReSharper disable AssignNullToNotNullAttributenamespace FileAdapters{    /// <summary>    /// Represents an adapter that will launch a configured executable process.    /// </summary>    /// <remarks>    /// Unless credentials are provided to create an authentication context, rights of    /// any launched executable will be limited to those available to time-series host.    /// </remarks>    [Description("Process Launcher: Launches a configured executable process on initialization")]    public class ProcessLauncher : FacileActionAdapterBase    {        #region [ Members ]         // Constants         /// <summary>        /// Default value for the <see cref="SupportsTemporalProcessing"/> property.        /// </summary>        public const bool DefaultSupportsTemporalProcessing = false;         /// <summary>        /// Default value for the <see cref="Arguments"/> property.        /// </summary>        public const string DefaultArguments = "";         /// <summary>        /// Default value for the <see cref="WorkingDirectory"/> property.        /// </summary>        public const string DefaultWorkingDirectory = "";         /// <summary>        /// Default value for the <see cref="EnvironmentalVariables"/> property.        /// </summary>        public const string DefaultEnvironmentalVariables = "";         /// <summary>        /// Default value for the <see cref="CreateNoWindow"/> property.        /// </summary>        public const bool DefaultCreateNoWindow = true;         /// <summary>        /// Default value for the <see cref="WindowStyle"/> property.        /// </summary>        public const string DefaultWindowStyle = "Normal";         /// <summary>        /// Default value for the <see cref="ErrorDialog"/> property.        /// </summary>        public const bool DefaultErrorDialog = false;         /// <summary>        /// Default value for the <see cref="Domain"/> property.        /// </summary>        public const string DefaultDomain = "";         /// <summary>        /// Default value for the <see cref="UserName"/> property.        /// </summary>        public const string DefaultUserName = "";         /// <summary>        /// Default value for the <see cref="Password"/> property.        /// </summary>        public const string DefaultPassword = "";         /// <summary>        /// Default value for the <see cref="LoadUserProfile"/> property.        /// </summary>        public const bool DefaultLoadUserProfile = false;         /// <summary>        /// Default value for the <see cref="InitialInputFileName"/> property.        /// </summary>        public const string DefaultInitialInputFileName = "";         /// <summary>        /// Default value for the <see cref="InitialInputProcessingDelay"/> property.        /// </summary>        public const int DefaultInitialInputProcessingDelay = 2000;         /// <summary>        /// Default value for the <see cref="RedirectOutputToHostEnvironment"/> property.        /// </summary>        public const bool DefaultRedirectOutputToHostEnvironment = true;         /// <summary>        /// Default value for the <see cref="RedirectErrorToHostEnvironment"/> property.        /// </summary>        public const bool DefaultRedirectErrorToHostEnvironment = true;         /// <summary>        /// Default value for the <see cref="ProcessOutputAsLogMessages"/> property.        /// </summary>        public const bool DefaultProcessOutputAsLogMessages = false;         /// <summary>        /// Default value for the <see cref="LogMessageTextExpression"/> property.        /// </summary>        public const string DefaultLogMessageTextExpression = @"(?<=.*msg\s*\=\s*\"")[^\""]*(?=\"")";         /// <summary>        /// Default value for the <see cref="LogMessageLevelExpression"/> property.        /// </summary>        public const string DefaultLogMessageLevelExpression = @"(?<=.*lvl\s*\=\s*)[^\s]*(?=\s|$)";         /// <summary>        /// Default value for the <see cref="LogMessageLevelMappings"/> property.        /// </summary>        public const string DefaultLogMessageLevelMappings = "info=Info; warn=Waning; error=Error; critical=Critical; debug=Debug";         /// <summary>        /// Default value for the <see cref="ForceKillOnDispose"/> property.        /// </summary>        public const bool DefaultForceKillOnDispose = true;         /// <summary>        /// Default value for the <see cref="TrackProcessStatistics"/> property.        /// </summary>        public const bool DefaultTrackProcessStatistics = true;         // Fields        private readonly Process m_process;        private readonly Dictionary<string, MessageLevel> m_messageLevelMap;        private readonly ProcessUtilizationCalculator m_processUtilizationCalculator;        private readonly ChildProcessManager m_childProcessManager;        private Regex m_logMessageTextExpression;        private Regex m_logMessageLevelExpression;        private bool m_supportsTemporalProcessing;        private long m_inputLinesProcessed;        private long m_outputLinesProcessed;        private long m_errorLinesProcessed;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="ProcessLauncher"/> class.        /// </summary>        public ProcessLauncher()        {            m_process = new Process();            m_messageLevelMap = new Dictionary<string, MessageLevel>(StringComparer.OrdinalIgnoreCase);            m_processUtilizationCalculator = new ProcessUtilizationCalculator();             // In Windows environments, make sure child processes can be terminated if parent is terminated - even if termination is not graceful            if (!Common.IsPosixEnvironment)                m_childProcessManager = new ChildProcessManager();        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets the flag indicating if this adapter supports temporal processing.        /// </summary>        [ConnectionStringParameter,        Description("Define the flag indicating if this adapter supports temporal processing."),        DefaultValue(DefaultSupportsTemporalProcessing)]        public override bool SupportsTemporalProcessing => m_supportsTemporalProcessing;         /// <summary>        /// Gets or sets the path and filename of executable to launch as a new process.        /// </summary>        [ConnectionStringParameter,        Description("Define the path and filename of executable to launch as a new process.")]        public string FileName { get; set; }         /// <summary>        /// Gets or sets any command line arguments to use when launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define any command line arguments to use when launching the process."),        DefaultValue(DefaultArguments)]        public string Arguments { get; set; } = DefaultArguments;         /// <summary>        /// Gets or sets working directory to use when launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define working directory to use when launching the process. Defaults to path of executable to launch."),        DefaultValue(DefaultWorkingDirectory)]        public string WorkingDirectory { get; set; } = DefaultWorkingDirectory;         /// <summary>        /// Gets or sets any needed environmental variables that should be set or updated before launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define any needed environmental variables that should be set or updated before launching the process. Use connection string format to define variables, for example: variable1=value1; variable2=value2"),        DefaultValue(DefaultEnvironmentalVariables)]        public string EnvironmentalVariables { get; set; } = DefaultEnvironmentalVariables;         /// <summary>        /// Gets or sets flag that determines if a new window should be created when launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if a new window should be created when launching the process. This property is only applicable when adapter hosting environment can interact with a user interface."),        DefaultValue(DefaultCreateNoWindow)]        public bool CreateNoWindow { get; set; } = DefaultCreateNoWindow;         /// <summary>        /// Gets or sets window style to use when launching the process if creating a window.        /// </summary>        [ConnectionStringParameter,        Description("Define window style to use when launching the process if creating a window. This property is only applicable when adapter hosting environment can interact with a user interface."),        DefaultValue(typeof(ProcessWindowStyle), DefaultWindowStyle)]        public ProcessWindowStyle WindowStyle { get; set; } = (ProcessWindowStyle)Enum.Parse(typeof(ProcessWindowStyle), DefaultWindowStyle);         /// <summary>        /// Gets or sets flag that determines if an error dialog should be displayed if the process cannot be launched.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if an error dialog should be displayed if the process cannot be launched. This property is only applicable when adapter hosting environment can interact with a user interface."),        DefaultValue(DefaultErrorDialog)]        public bool ErrorDialog { get; set; } = DefaultErrorDialog;         /// <summary>        /// Gets or sets the user domain to use when creating an authentication context before launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define the user domain to use when creating an authentication context before launching the process. Leave blank to use authentication context of hosting environment."),        DefaultValue(DefaultDomain)]        public string Domain { get; set; } = DefaultDomain;         /// <summary>        /// Gets or sets the user name to use when creating an authentication context before launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define the user name to use when creating an authentication context before launching the process. Leave blank to use authentication context of hosting environment."),        DefaultValue(DefaultUserName)]        public string UserName { get; set; } = DefaultUserName;         /// <summary>        /// Gets or sets the user password to use when creating an authentication context before launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define the user password to use when creating an authentication context before launching the process. Leave blank to use authentication context of hosting environment."),        DefaultValue(DefaultPassword)]        public string Password { get; set; } = DefaultPassword;         /// <summary>        /// Gets or sets flag that determines if Windows user profile should be loaded from the registry before launching the process.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if Windows user profile should be loaded from the registry before launching the process."),        DefaultValue(DefaultLoadUserProfile)]        public bool LoadUserProfile { get; set; } = DefaultLoadUserProfile;         /// <summary>        /// Gets or sets filename that contains text to use as standard input into process after it has been launched.        /// </summary>        [ConnectionStringParameter,        Description("Define filename that contains text to use as input into process after it has been launched. UTF-8 encoding assumed."),        DefaultValue(DefaultInitialInputFileName)]        public string InitialInputFileName { get; set; } = DefaultInitialInputFileName;         /// <summary>        /// Gets or sets the processing delay, in milliseconds, before text defined in the <see cref="InitialInputFileName"/> is sent to the launched process.        /// </summary>        [ConnectionStringParameter,        Description("Define the processing delay, in milliseconds, before text defined in the InitialInputFileName is sent to the launched process."),        DefaultValue(DefaultInitialInputProcessingDelay)]        public int InitialInputProcessingDelay { get; set; } = DefaultInitialInputProcessingDelay;         /// <summary>        /// Gets or sets flag that determines if process standard output should be redirected to the hosting environment.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if process standard output should be redirected to the hosting environment."),        DefaultValue(DefaultRedirectOutputToHostEnvironment)]        public bool RedirectOutputToHostEnvironment { get; set; } = DefaultRedirectOutputToHostEnvironment;         /// <summary>        /// Gets or sets flag that determines if process standard error output should be redirected to the hosting environment.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if process standard error output should be redirected to the hosting environment."),        DefaultValue(DefaultRedirectErrorToHostEnvironment)]        public bool RedirectErrorToHostEnvironment { get; set; } = DefaultRedirectErrorToHostEnvironment;         /// <summary>        /// Gets or sets flag that determines if redirected output should be attempted to be parsed as formatted log messages, e.g.: lvl=info msg="log message".        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if redirected output should be attempted to be parsed as formatted log messages, e.g.: lvl=info msg=\"log message\"."),        DefaultValue(DefaultProcessOutputAsLogMessages)]        public bool ProcessOutputAsLogMessages { get; set; } = DefaultProcessOutputAsLogMessages;         /// <summary>        /// Gets or sets regular expression used to find log message text when redirected output is processed as formatted log messages.        /// </summary>        [ConnectionStringParameter,        Description("Define regular expression used to find log message text when redirected output is processed as formatted log messages. Multiple matches will be joined as a single string."),        DefaultValue(DefaultLogMessageTextExpression)]        public string LogMessageTextExpression { get; set; } = DefaultLogMessageTextExpression;         /// <summary>        /// Gets or sets regular expression used to find log message level when redirected output is processed as formatted log messages.        /// </summary>        [ConnectionStringParameter,        Description("Define regular expression used to find log message level when redirected output is processed as formatted log messages."),        DefaultValue(DefaultLogMessageLevelExpression)]        public string LogMessageLevelExpression { get; set; } = DefaultLogMessageLevelExpression;         /// <summary>        /// Gets or sets log level mappings to use when redirected output is processed as formatted log messages.        /// </summary>        [ConnectionStringParameter,        Description("Define log level mappings to use when redirected output is processed as formatted log messages. Use connection string format as \"log_level_name=GSF.Diagnostics.MessageLevel\", for example: info=Info; warn=Waning; error=Error; critical=Critical; debug=Debug"),        DefaultValue(DefaultLogMessageLevelMappings)]        public string LogMessageLevelMappings { get; set; } = DefaultLogMessageLevelMappings;         /// <summary>        /// Gets or sets flag that determines if launched process should be forcibly terminated when adapter is disposed.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if launched process should be forcibly terminated when adapter is disposed."),        DefaultValue(DefaultForceKillOnDispose)]        public bool ForceKillOnDispose { get; set; } = DefaultForceKillOnDispose;         /// <summary>        /// Gets or sets the interval over which to calculate lunched process utilization.        /// </summary>        [ConnectionStringParameter,        Description("Define the interval over which to update the lunched process CPU utilization. Set to 0 to disable utilization calculations."),        DefaultValue(ProcessUtilizationCalculator.DefaultUpdateInterval)]        public int UtilizationUpdateInterval { get; set; } = ProcessUtilizationCalculator.DefaultUpdateInterval;         /// <summary>        /// Gets or sets flag that determines if statistics should be tracked for launched process.        /// </summary>        [ConnectionStringParameter,        Description("Define flag that determines if statistics should be tracked for launched process."),        DefaultValue(DefaultTrackProcessStatistics)]        public bool TrackProcessStatistics { get; set; } = DefaultTrackProcessStatistics;         #region [ Hidden Properties ]         // The following common adapter properties are marked as never browse by an        // editor since the properties are not used by the ProcessLauncher adapter         /// <summary>        /// Property hidden - not used by <see cref="ProcessLauncher"/>.        /// </summary>        [EditorBrowsable(EditorBrowsableState.Never)]        public new MeasurementKey[] InputMeasurementKeys        {            get            {                return base.InputMeasurementKeys;            }            set            {                base.InputMeasurementKeys = value;            }        }         /// <summary>        /// Property hidden - not used by <see cref="ProcessLauncher"/>.        /// </summary>        [EditorBrowsable(EditorBrowsableState.Never)]        public new IMeasurement[] OutputMeasurements        {            get            {                return base.OutputMeasurements;            }            set            {                base.OutputMeasurements = value;            }        }         /// <summary>        /// Property hidden - not used by <see cref="ProcessLauncher"/>.        /// </summary>        [EditorBrowsable(EditorBrowsableState.Never)]        public new int FramesPerSecond        {            get            {                return base.FramesPerSecond;            }            set            {                base.FramesPerSecond = value;            }        }         /// <summary>        /// Property hidden - not used by <see cref="ProcessLauncher"/>.        /// </summary>        [EditorBrowsable(EditorBrowsableState.Never)]        public new double LagTime        {            get            {                return base.LagTime;            }            set            {                base.LagTime = value;            }        }         /// <summary>        /// Property hidden - not used by <see cref="ProcessLauncher"/>.        /// </summary>        [EditorBrowsable(EditorBrowsableState.Never)]        public new double LeadTime        {            get            {                return base.LeadTime;            }            set            {                base.LeadTime = value;            }        }         #endregion         /// <summary>        /// Returns the detailed status of the data input source.        /// </summary>        /// <remarks>        /// Derived classes should extend status with implementation specific information.        /// </remarks>        public override string Status        {            get            {                StringBuilder status = new StringBuilder();                 if (string.IsNullOrEmpty(WorkingDirectory))                    WorkingDirectory = Directory.GetCurrentDirectory();                 status.Append(base.Status);                status.AppendLine($"       Executable filename: {FilePath.TrimFileName(FileName, 51)}");                status.AppendLine($"         Working directory: {FilePath.TrimFileName(WorkingDirectory, 51)}");                 if (Initialized)                {                    try                    {                        FileVersionInfo version = m_process.MainModule.FileVersionInfo;                         if (!string.IsNullOrWhiteSpace(version.FileVersion))                            status.AppendLine($"   Executable file version: {version.FileVersion}");                         if (!string.IsNullOrWhiteSpace(version.FileDescription))                            status.AppendLine($"    Executable description: {version.FileDescription}");

Lines 1–500 of 990.

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