1699 lines
70 KiB
C#
1699 lines
70 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Xml.Serialization;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using Yaulw.Process;
|
|
using Yaulw.Other;
|
|
using Yaulw.Xml;
|
|
|
|
namespace Yaulw.Monitor
|
|
{
|
|
#region XML Data Definition
|
|
|
|
/// <remarks>
|
|
/// Serializable Xml Object used to store Monitor Configuration data
|
|
/// </remarks>
|
|
[XmlRoot("configuration", Namespace = "Monitor", IsNullable = false)]
|
|
public class MonitorConfiguration : ICloneable
|
|
{
|
|
/// <summary>
|
|
/// MonitoredProcesses Member Tag <> holds list of ProcessExe's to Monitor
|
|
/// </summary>
|
|
public MonitoredProcessesW MonitoredProcesses = null;
|
|
|
|
/// <summary>
|
|
/// MonitoredServices Member Tag <> holds list of Services to Monitor
|
|
/// </summary>
|
|
public MonitoredServicesW MonitoredServices = null;
|
|
|
|
/// <summary>
|
|
/// Monitor Configuration - Constructor
|
|
/// </summary>
|
|
public MonitorConfiguration()
|
|
{
|
|
MonitoredProcesses = new MonitoredProcessesW();
|
|
MonitoredServices = new MonitoredServicesW();
|
|
}
|
|
|
|
#region Internal Monitor Properties N' Methods
|
|
|
|
/// <summary>
|
|
/// True if Either Any Processes or Any Services are Configured
|
|
/// </summary>
|
|
internal bool ConfiguredHasAny { get { return (MonitoredProcesses.ProcessExes.Length > 0 || MonitoredServices.ServiceExes.Length > 0); } }
|
|
|
|
/// <summary>
|
|
/// True if Any Processes are Configured
|
|
/// </summary>
|
|
internal bool ConfiguredHasAnyProcesses { get { return (MonitoredProcesses.ProcessExes.Length > 0); } }
|
|
|
|
/// <summary>
|
|
/// True if Any Services are Configured
|
|
/// </summary>
|
|
internal bool ConfiguredHasAnyServices { get { return (MonitoredServices.ServiceExes.Length > 0); } }
|
|
|
|
/// <summary>
|
|
/// Clears all Start and Fail Times for Both Processes and Services
|
|
/// </summary>
|
|
internal void ClearAllStartNFailTimes()
|
|
{
|
|
MonitoredProcesses.ClearProcessExeStartNFailTimes();
|
|
MonitoredServices.ClearServiceExeStartNFailTimes();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if there are any Start or Fail Times,
|
|
/// if there aren't. Consider this configuration as newly loaded.
|
|
/// </summary>
|
|
/// <returns>true, if there are any Start or Fail Times recorded, false otherwise</returns>
|
|
internal bool HasAnyStartNFailTimes()
|
|
{
|
|
if (MonitoredProcesses.HasAnyProcessExeStartNFailTimes() ||
|
|
MonitoredServices.HasAnyServiceExeStartNFailTimes())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// MonitoredProcessesW - (Wrapper) around ProcessExe ArrayList to work with XML
|
|
/// </summary>
|
|
public class MonitoredProcessesW : IComparable, ICloneable
|
|
{
|
|
#region Private Members
|
|
|
|
private ArrayList m_ArrayList;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Constructor for MonitoredProcessesW
|
|
/// </summary>
|
|
public MonitoredProcessesW()
|
|
{
|
|
m_ArrayList = new ArrayList();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region XML Process Element
|
|
|
|
/// <summary>
|
|
/// Property useful to directly Get/Set the ProcessExes as an Array
|
|
/// </summary>
|
|
[XmlElement("Process")]
|
|
public ProcessExe[] ProcessExes
|
|
{
|
|
get
|
|
{
|
|
ProcessExe[] processExes = new ProcessExe[m_ArrayList.Count];
|
|
m_ArrayList.CopyTo(processExes);
|
|
return processExes;
|
|
}
|
|
set
|
|
{
|
|
if (value == null) return;
|
|
ProcessExe[] processExes = (ProcessExe[])value;
|
|
m_ArrayList.Clear();
|
|
foreach (ProcessExe processExe in processExes)
|
|
AddProcessExe(processExe);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Get Functions
|
|
|
|
/// <summary>
|
|
/// Use this to get all processes settings for a specific Process Exe File
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameNPath">specific process exe to get settings for</param>
|
|
/// <returns>a list of process settings for the specified exe or empty list if not found</returns>
|
|
public ProcessExe[] GetProcessExesByProcessExeFileNameNPath(string ProcessExeFileNameNPath)
|
|
{
|
|
List<ProcessExe> foundProcessExes = new List<ProcessExe>();
|
|
foreach (ProcessExe _cmpProcessExe in m_ArrayList)
|
|
{
|
|
if (String.Compare(_cmpProcessExe.ProcessExeFileNameNPath, ProcessExeFileNameNPath, true) == 0)
|
|
foundProcessExes.Add(_cmpProcessExe);
|
|
}
|
|
return foundProcessExes.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use this to get all processes settings for a specific Process Exe FileName without Extension
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameWithoutExtension">specific process exe fileName without Extension to get settings for</param>
|
|
/// <returns>a list of process settings for the specified exe or empty list if not found</returns>
|
|
public ProcessExe[] GetProcessExesByProcessExeFileNameWithoutExtension(string ProcessExeFileNameWithoutExtension)
|
|
{
|
|
List<ProcessExe> foundProcessExes = new List<ProcessExe>();
|
|
foreach (ProcessExe _cmpProcessExe in m_ArrayList)
|
|
{
|
|
if (String.Compare(Path.GetFileNameWithoutExtension(_cmpProcessExe.ProcessExeFileNameNPath), ProcessExeFileNameWithoutExtension, true) == 0)
|
|
foundProcessExes.Add(_cmpProcessExe);
|
|
}
|
|
return foundProcessExes.ToArray();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Add Functions
|
|
|
|
/// <summary>
|
|
/// Helper function to Add a Process Exe
|
|
/// </summary>
|
|
/// <param name="processExe">ProcessExe to Add</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
public bool AddProcessExe(ProcessExe processExe)
|
|
{
|
|
bool bAddSuccess = false;
|
|
if (processExe != null && !String.IsNullOrEmpty(processExe.ProcessExeFileNameNPath))
|
|
{
|
|
if (processExe.CommandLinePrms == null)
|
|
processExe.CommandLinePrms = String.Empty;
|
|
if (processExe.WorkingDirectory == null)
|
|
processExe.WorkingDirectory = String.Empty;
|
|
|
|
ProcessExe pFound = null;
|
|
if (ProcessExeFileExistsLocally(processExe) && IsNotExcludedProcessExe(processExe) && IsUniqueProcessExe(processExe, out pFound))
|
|
{
|
|
bAddSuccess = (m_ArrayList.Add(processExe) >= 0);
|
|
}
|
|
else if (pFound != null)
|
|
{
|
|
// if Working Directory isn't the same, we allow updating it via AddProcessExe Call * Perform Update *
|
|
bAddSuccess = (String.Compare(pFound.WorkingDirectory, processExe.WorkingDirectory, true) != 0);
|
|
if (bAddSuccess)
|
|
pFound.WorkingDirectory = processExe.WorkingDirectory;
|
|
}
|
|
}
|
|
return bAddSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function to Add a Process Exe
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameNPath">specify a fileName and path for a process</param>
|
|
/// <param name="CommandLinePrms">specify a command-line params to use for the process</param>
|
|
/// <param name="WorkingDirectory">specify the working directory to use for the process</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
//public bool AddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory = "")
|
|
public bool AddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory)
|
|
{
|
|
bool bAddSuccess = false;
|
|
if (!String.IsNullOrEmpty(ProcessExeFileNameNPath))
|
|
{
|
|
if (CommandLinePrms == null)
|
|
CommandLinePrms = String.Empty;
|
|
if (WorkingDirectory == null)
|
|
WorkingDirectory = String.Empty;
|
|
|
|
ProcessExe processToAdd = new ProcessExe(Yaulw.Win32.Functions.GetLongFileNameNPathOrPath(ProcessExeFileNameNPath.Trim()), CommandLinePrms.Trim(), WorkingDirectory.Trim());
|
|
ProcessExe pFound = null;
|
|
if (ProcessExeFileExistsLocally(processToAdd) && IsNotExcludedProcessExe(processToAdd) && IsUniqueProcessExe(processToAdd, out pFound))
|
|
{
|
|
bAddSuccess = (m_ArrayList.Add(processToAdd) >= 0);
|
|
}
|
|
else if (pFound != null)
|
|
{
|
|
// if Working Directory isn't the same, we allow updating it via AddProcessExe Call * Perform Update *
|
|
bAddSuccess = (String.Compare(pFound.WorkingDirectory, processToAdd.WorkingDirectory, true) != 0);
|
|
if (bAddSuccess)
|
|
pFound.WorkingDirectory = processToAdd.WorkingDirectory;
|
|
}
|
|
}
|
|
return bAddSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function to check whether we can Add a Process Exe
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameNPath">specify a fileName and path for a process</param>
|
|
/// <param name="CommandLinePrms">specify a command-line params to use for the process</param>
|
|
/// <param name="WorkingDirectory">specify the working directory to use for the process</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
//public bool CanAddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory = "")
|
|
public bool CanAddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory)
|
|
{
|
|
bool bCanAddSuccess = false;
|
|
if (!String.IsNullOrEmpty(ProcessExeFileNameNPath))
|
|
{
|
|
if (CommandLinePrms == null)
|
|
CommandLinePrms = String.Empty;
|
|
if (WorkingDirectory == null)
|
|
WorkingDirectory = String.Empty;
|
|
|
|
ProcessExe processToAdd = new ProcessExe(Yaulw.Win32.Functions.GetLongFileNameNPathOrPath(ProcessExeFileNameNPath.Trim()), CommandLinePrms.Trim(), WorkingDirectory.Trim());
|
|
ProcessExe pFound = null;
|
|
if (ProcessExeFileExistsLocally(processToAdd) && IsNotExcludedProcessExe(processToAdd) && IsUniqueProcessExe(processToAdd, out pFound))
|
|
{
|
|
bCanAddSuccess = true;
|
|
}
|
|
else if(pFound != null)
|
|
{
|
|
// if Working Directory isn't the same, we allow updating it via AddProcessExe Call
|
|
bCanAddSuccess = (String.Compare(pFound.WorkingDirectory, processToAdd.WorkingDirectory, true) != 0);
|
|
}
|
|
}
|
|
return bCanAddSuccess;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Remove Functions
|
|
|
|
/// <summary>
|
|
/// Helper function to Remove a Process Exe
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameNPath">specify a fileName and path for a process</param>
|
|
/// <param name="CommandLinePrms">specify a command-line params to use for the process</param>
|
|
/// <param name="WorkingDirectory">specify the working directory to use for the process</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
public bool RemoveProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms)
|
|
{
|
|
if (!String.IsNullOrEmpty(ProcessExeFileNameNPath))
|
|
{
|
|
if (CommandLinePrms == null)
|
|
CommandLinePrms = String.Empty;
|
|
|
|
int nIndex = -1;
|
|
for (int i = 0; i < m_ArrayList.Count; ++i)
|
|
{
|
|
ProcessExe _cmpProcessExe = (ProcessExe)m_ArrayList[i];
|
|
if (_cmpProcessExe.FoundMatch(ProcessExeFileNameNPath.Trim(), CommandLinePrms.Trim()))
|
|
{
|
|
nIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nIndex != -1)
|
|
{
|
|
m_ArrayList.RemoveAt(nIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Clear N' Query Functions
|
|
|
|
/// <summary>
|
|
/// Clears all ProcessExes from the List
|
|
/// </summary>
|
|
public void Clear() { m_ArrayList.Clear(); }
|
|
|
|
/// <summary>
|
|
/// Clears each Process's Start and Fail Time
|
|
/// </summary>
|
|
public void ClearProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) p.ClearStartNFailTimes(); }
|
|
|
|
/// <summary>
|
|
/// Check to see if there are any Start or Fail Times,
|
|
/// if there aren't. Consider this configuration as newly loaded.
|
|
/// </summary>
|
|
/// <returns>true, if there are any Start or Fail Times recorded, false otherwise</returns>
|
|
public bool HasAnyProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) { if (p.LastFailedTimes.Count > 0) return true; } return false; }
|
|
|
|
/// <summary>
|
|
/// Retrieve the Process Exe that matches the specified PID
|
|
/// </summary>
|
|
public ProcessExe ClearPidInformation() { foreach (ProcessExe p in ProcessExes) { p.PID = 0; } return null; }
|
|
|
|
/// <summary>
|
|
/// Check to see if any of the Processes Configured has incomplete PID information, meaning that it never got started,
|
|
/// or failed to start, or was never mapped
|
|
/// </summary>
|
|
/// <returns>true, if any PID info is incomplete</returns>
|
|
public bool HasIncompletePidInformation() { foreach (ProcessExe p in ProcessExes) { if (p.PID <= 0) return true; } return false; }
|
|
|
|
/// <summary>
|
|
/// Retrieve a list of all Pids that have been mapped.
|
|
/// Make sure to call HasIncompletePidInformation() prior calling this if you want to make sure all pid are returned.
|
|
/// * Function only returns valid pids *
|
|
/// </summary>
|
|
/// <param name="bIncludeErrorStatePids">if set to true, includes Pids in an error state, false otherwise * should always use false *</param>
|
|
/// <returns>an array of pids mapped</returns>
|
|
//public uint[] QueryAllPidInfo(bool bIncludeErrorStatePids = false) { List<uint> pids = new List<uint>(); foreach (ProcessExe p in ProcessExes) { if (p.PID > 0) { if (bIncludeErrorStatePids && p.InErrorState) { pids.Add(p.PID); } else if (!bIncludeErrorStatePids && !p.InErrorState) { pids.Add(p.PID); } } } return pids.ToArray(); }
|
|
public uint[] QueryAllPidInfo(bool bIncludeErrorStatePids, bool bIncludeNonRestartPids)
|
|
{
|
|
List<uint> pids = new List<uint>();
|
|
foreach (ProcessExe p in ProcessExes)
|
|
{
|
|
if (p.PID > 0)
|
|
{
|
|
// bError = true && bInclude = true (include all)
|
|
// bError = false && bInclude = true (include all that don't have an error)
|
|
// bError = true && bInclude = false (include all that aren't restart)
|
|
// bError = false && bInclude = false (include none that have either)
|
|
if (bIncludeErrorStatePids && bIncludeNonRestartPids)
|
|
{
|
|
pids.Add(p.PID);
|
|
}
|
|
else if (!bIncludeErrorStatePids && bIncludeNonRestartPids)
|
|
{
|
|
if (!p.InErrorState)
|
|
pids.Add(p.PID);
|
|
}
|
|
else if (bIncludeErrorStatePids && !bIncludeNonRestartPids)
|
|
{
|
|
if (p.AllowRestart)
|
|
pids.Add(p.PID);
|
|
}
|
|
else if (!bIncludeErrorStatePids && !bIncludeNonRestartPids)
|
|
{
|
|
if (!p.InErrorState && p.AllowRestart)
|
|
pids.Add(p.PID);
|
|
}
|
|
}
|
|
}
|
|
return pids.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve the number of Processes now considered in an error state.
|
|
/// </summary>
|
|
/// <returns>Number of Pids in an error state</returns>
|
|
public uint NumberOfPidsInErrorState() { uint nErrorCount = 0; foreach (ProcessExe p in ProcessExes) { if (p.InErrorState) nErrorCount++; } return nErrorCount; }
|
|
|
|
/// <summary>
|
|
/// Retrieve the Process Exe that matches the specified PID
|
|
/// </summary>
|
|
/// <param name="PID">PID to look four</param>
|
|
/// <returns>ProcessExe that matches the PID, null otherwise (if not found)</returns>
|
|
public ProcessExe GetProcessExeForPid(uint PID) { foreach (ProcessExe p in ProcessExes) { if (p.PID == PID) return p; } return null; }
|
|
|
|
#endregion
|
|
|
|
#region Private Helper Functions
|
|
|
|
/// <summary>
|
|
/// Enforce Uniqueness, when adding Process Exe's to the DS
|
|
/// </summary>
|
|
/// <param name="processExe">a ProcessExe to check</param>
|
|
/// <param name="foundP">the found ProcessExe that matches the check</param>
|
|
/// <returns>true if unique, false otherwise</returns>
|
|
private bool IsUniqueProcessExe(ProcessExe processExe, out ProcessExe foundP)
|
|
{
|
|
foundP = null;
|
|
foreach (ProcessExe _cmpProcessExe in m_ArrayList)
|
|
{
|
|
// If ProcessExe FileName and Path, as well as CommandLine Prms Match, then it is NOT unique
|
|
if (_cmpProcessExe.FoundMatch(processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms))
|
|
{
|
|
foundP = _cmpProcessExe;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enforce that the called didn't specify to exclude this Process
|
|
/// </summary>
|
|
/// <param name="processExe">a ProcessExe to check</param>
|
|
/// <returns>true if not exclusive, false otherwise</returns>
|
|
private bool IsNotExcludedProcessExe(ProcessExe processExe)
|
|
{
|
|
// * Allow caller via the Statically SPecified Exclusion list to filter out processes
|
|
// that are considered invalid *
|
|
if (MonitorDataStore.MonitoredProcessNamesExclusionList != null &&
|
|
MonitorDataStore.MonitoredProcessNamesExclusionList.Count > 0)
|
|
{
|
|
foreach (string ProcessName in MonitorDataStore.MonitoredProcessNamesExclusionList)
|
|
{
|
|
if (String.Compare(ProcessName, Path.GetFileNameWithoutExtension(processExe.ProcessExeFileNameNPath), true) == 0)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enforce that the Process/Exe File must exist on the system, prior being added
|
|
/// </summary>
|
|
/// <returns>true if exists, false otherwise</returns>
|
|
private bool ProcessExeFileExistsLocally(ProcessExe processExe)
|
|
{
|
|
return System.IO.File.Exists(processExe.ProcessExeFileNameNPath);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IComparable Members
|
|
|
|
/// <summary>
|
|
/// Compares tow MonitoredProcessesW Objects with each other
|
|
/// </summary>
|
|
/// <param name="obj">Pass in a valid MonitoredProcessesW Object</param>
|
|
/// <returns>0 if equal, -1 if obj is smaller, 1 if obj is bigger</returns>
|
|
public int CompareTo(object obj)
|
|
{
|
|
if (obj is MonitoredProcessesW)
|
|
{
|
|
MonitoredProcessesW w = (MonitoredProcessesW)obj;
|
|
|
|
// First, do a simple count comparison
|
|
int wListCount = w.m_ArrayList.Count;
|
|
int thisListCount = this.m_ArrayList.Count;
|
|
if (wListCount == 0 && thisListCount != 0)
|
|
return -1;
|
|
else if (wListCount == 0 && thisListCount == 0)
|
|
return 0;
|
|
else if (wListCount != 0 && thisListCount == 0)
|
|
return 1;
|
|
else if (wListCount < thisListCount)
|
|
return -1;
|
|
else if (wListCount > thisListCount)
|
|
return 1;
|
|
|
|
// The count must be the same * Hence, now we must actually
|
|
// compare each member to make sure that the lists are the same *
|
|
// ~Before we do this we should sort both lists, so that we can correctly compare
|
|
ArrayList List1 = (ArrayList)w.m_ArrayList.Clone();
|
|
ArrayList List2 = (ArrayList)this.m_ArrayList.Clone();
|
|
List1.Sort();
|
|
List2.Sort();
|
|
|
|
// ~Iterate this Object's array list and compare each member
|
|
int nCompare = 0;
|
|
for (int i = 0; i < thisListCount; ++i)
|
|
{
|
|
ProcessExe p1 = (ProcessExe)List1[i];
|
|
ProcessExe p2 = (ProcessExe)List2[i];
|
|
nCompare = p1.CompareTo(p2);
|
|
if (nCompare != 0)
|
|
break;
|
|
}
|
|
|
|
// If they are both still exactly equal, then let's
|
|
// now also compare the order
|
|
if (nCompare == 0)
|
|
{
|
|
for (int i = 0; i < w.m_ArrayList.Count; ++i)
|
|
{
|
|
ProcessExe p1 = (ProcessExe) w.m_ArrayList[i];
|
|
ProcessExe p2 = (ProcessExe) this.m_ArrayList[i];
|
|
nCompare = p1.CompareTo(p2);
|
|
if (nCompare != 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return nCompare;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("object is not a MonitoredProcessesW");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICloneable Members
|
|
|
|
/// <summary>
|
|
/// Performs a Shallow Copy of MonitoredProcessesW
|
|
/// </summary>
|
|
/// <returns>a new MonitoredProcessesW Object</returns>
|
|
public object Clone()
|
|
{
|
|
MonitoredProcessesW w = new MonitoredProcessesW();
|
|
w.m_ArrayList = (ArrayList)this.m_ArrayList.Clone();
|
|
return w;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process Exe (Image) Class that contains all settings to monitor
|
|
/// a process / executable image
|
|
/// </summary>
|
|
public class ProcessExe : IComparable, ICloneable
|
|
{
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// specify a fileName and path for a process
|
|
/// </summary>
|
|
[XmlText]
|
|
public string ProcessExeFileNameNPath { get { return _ProcessExeFileNameNPath; } set { if (!String.IsNullOrEmpty(value)) _ProcessExeFileNameNPath = value; } }
|
|
private string _ProcessExeFileNameNPath = "";
|
|
|
|
/// <summary>
|
|
/// specify a command-line params to use for the process
|
|
/// </summary>
|
|
[XmlAttribute("CommandLinePrms")]
|
|
public string CommandLinePrms { get { if (String.IsNullOrEmpty(_CommandLinePrms)) return ""; else return _CommandLinePrms; } set { if (!String.IsNullOrEmpty(value)) _CommandLinePrms = value; } }
|
|
private string _CommandLinePrms = "";
|
|
|
|
|
|
/// <summary>
|
|
/// specify the working directory to use for the process
|
|
/// </summary>
|
|
[XmlAttribute("WorkingDirectory")]
|
|
public string WorkingDirectory { get { if(String.IsNullOrEmpty(_WorkingDirectory)) return ""; else return _WorkingDirectory; } set { if (!String.IsNullOrEmpty(value)) _WorkingDirectory = value; } }
|
|
private string _WorkingDirectory;
|
|
|
|
/// <summary>
|
|
/// specify if process is allowed to be restarted
|
|
/// </summary>
|
|
[XmlAttribute("AllowRestart")]
|
|
public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } }
|
|
private bool _AllowRestart = true;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Default Constructor * Don't use this * used only by serialization
|
|
/// </summary>
|
|
public ProcessExe()
|
|
{
|
|
this.PID = 0;
|
|
this.InErrorState = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main Constructor * Use this *
|
|
/// </summary>
|
|
/// <param name="ProcessExeFileNameNPath">specify a fileName and path for a process</param>
|
|
/// <param name="CommandLinePrms">specify a command-line params to use for the process</param>
|
|
/// <param name="WorkingDirectory">specify the working directory to use for the process</param>
|
|
public ProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory)
|
|
{
|
|
if(!String.IsNullOrEmpty(ProcessExeFileNameNPath))
|
|
this.ProcessExeFileNameNPath = ProcessExeFileNameNPath.Trim();
|
|
if(!String.IsNullOrEmpty(CommandLinePrms))
|
|
this.CommandLinePrms = CommandLinePrms.Trim();
|
|
if(!String.IsNullOrEmpty(WorkingDirectory))
|
|
this.WorkingDirectory = WorkingDirectory.Trim();
|
|
this.PID = 0;
|
|
this.InErrorState = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Helper Functions N Properties
|
|
|
|
/// <summary>
|
|
/// The name of the Process
|
|
/// </summary>
|
|
internal string ProcessName
|
|
{
|
|
get
|
|
{
|
|
if (!String.IsNullOrEmpty(this.ProcessExeFileNameNPath))
|
|
{
|
|
string ProcessName = Path.GetFileNameWithoutExtension(this.ProcessExeFileNameNPath);
|
|
return ProcessName.ToLower();
|
|
}
|
|
return String.Empty;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all Start N' Fail Times for a process
|
|
/// </summary>
|
|
internal void ClearStartNFailTimes()
|
|
{
|
|
LastStartedTimes.Clear();
|
|
LastStartedTimes.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Keep Track if this Process is in an Error State
|
|
/// </summary>
|
|
internal bool InErrorState;
|
|
|
|
/// <summary>
|
|
/// Internal Variable to Keep track of PID
|
|
/// </summary>
|
|
internal uint PID;
|
|
|
|
/// <summary>
|
|
/// Keep track of all Last Failed DT Stamps
|
|
/// </summary>
|
|
internal readonly List<DateTime> LastFailedTimes = new List<DateTime>();
|
|
|
|
/// <summary>
|
|
/// Keep track of all Last Started DT Stamps
|
|
/// </summary>
|
|
internal readonly List<DateTime> LastStartedTimes = new List<DateTime>();
|
|
|
|
/// <summary>
|
|
/// Returns the Last Failed Stamp
|
|
/// </summary>
|
|
internal DateTime LastFailTime
|
|
{
|
|
get
|
|
{
|
|
if (LastFailedTimes.Count > 0)
|
|
return LastFailedTimes.Last();
|
|
else
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow specific amount of time to have passed since Last-Fail
|
|
/// </summary>
|
|
/// <param name="ts">timespan that has to have occured since Last-Fail Time</param>
|
|
/// <returns>true if the Last-Fail Exceeded the specified amount of time, false otherwise</returns>
|
|
internal bool LastFailTimeTimeoutExceeded(TimeSpan ts)
|
|
{
|
|
if (LastFailedTimes.Count > 0)
|
|
{
|
|
TimeSpan diff = DateTime.Now - LastFailTime;
|
|
if (diff < ts)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the Last Started DT Stamp
|
|
/// </summary>
|
|
internal DateTime LastStartTime
|
|
{
|
|
get
|
|
{
|
|
if (LastStartedTimes.Count > 0)
|
|
return LastStartedTimes.Last();
|
|
else
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow specific amount of time to have passed since Last-Start
|
|
/// </summary>
|
|
/// <param name="ts">timespan that has to have occured since Last-Start Time</param>
|
|
/// <returns>true if the Last-Start Exceeded the specified amount of time, false otherwise</returns>
|
|
internal bool LastStartTimeTimeoutExceeded(TimeSpan ts)
|
|
{
|
|
if (LastStartedTimes.Count > 0)
|
|
{
|
|
TimeSpan diff = DateTime.Now - LastStartTime;
|
|
if (diff < ts)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a FailTime to keep track of for the given process
|
|
/// </summary>
|
|
/// <param name="dtFail">Fail-Time</param>
|
|
/// <param name="nMaxCapacity">max capacity to store</param>
|
|
//internal void AddFailTime(DateTime dtFail, int nMaxCapacity = 1)
|
|
internal void AddFailTime(DateTime dtFail, int nMaxCapacity)
|
|
{
|
|
if (dtFail != DateTime.MinValue)
|
|
{
|
|
// Make sure to always at least store one
|
|
if (nMaxCapacity < 1)
|
|
nMaxCapacity = 1;
|
|
|
|
// Add and adjust according to capacity
|
|
LastFailedTimes.Add(dtFail);
|
|
if (LastFailedTimes.Count > nMaxCapacity)
|
|
{
|
|
int nRemoveCount = nMaxCapacity - LastFailedTimes.Count;
|
|
for (int i = 0; i < nRemoveCount; ++i)
|
|
LastFailedTimes.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a StartTime to keep track of for the given process
|
|
/// </summary>
|
|
/// <param name="dtStart">Start-Time</param>
|
|
/// <param name="nMaxCapacity">max capacity to store</param>
|
|
//internal void AddStartTime(DateTime dtStart, int nMaxCapacity = 1)
|
|
internal void AddStartTime(DateTime dtStart, int nMaxCapacity)
|
|
{
|
|
if (dtStart != DateTime.MinValue)
|
|
{
|
|
// Make sure to always at least store one
|
|
if (nMaxCapacity < 1)
|
|
nMaxCapacity = 1;
|
|
|
|
// Add and adjust according to capacity
|
|
LastStartedTimes.Add(dtStart);
|
|
if (LastStartedTimes.Count > nMaxCapacity)
|
|
{
|
|
int nRemoveCount = nMaxCapacity - LastStartedTimes.Count;
|
|
for (int i = 0; i < nRemoveCount; ++i)
|
|
LastStartedTimes.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the Number of times the process failed in the given timespan
|
|
/// </summary>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>number of times this Process Failed in the given timespan</returns>
|
|
internal int GetFailTimesInGivenTimeSpan(TimeSpan span)
|
|
{
|
|
if (LastFailedTimes.Count == 0)
|
|
return 0;
|
|
|
|
int nCount = 0;
|
|
DateTime dtMin = DateTime.Now - span;
|
|
foreach (DateTime dt in LastFailedTimes)
|
|
{
|
|
if (dt >= dtMin)
|
|
++nCount;
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if the Number of times the process failed in the given timespan
|
|
/// exceeds nMaxTry
|
|
/// </summary>
|
|
/// <param name="nMaxTry">number of max times it is allowed to fail</param>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>true if number of times in the given timespan exceeds nMaxTry, false otherwise</returns>
|
|
internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
|
|
{
|
|
if (LastFailedTimes.Count == 0)
|
|
return false;
|
|
|
|
int nCount = GetFailTimesInGivenTimeSpan(span);
|
|
return (nCount > nMaxTry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the Number of times the process Started in the given timespan
|
|
/// </summary>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>number of times this Process Started in the given timespan</returns>
|
|
internal int GetStartTimesInGivenTimeSpan(TimeSpan span)
|
|
{
|
|
if (LastStartedTimes.Count == 0)
|
|
return 0;
|
|
|
|
int nCount = 0;
|
|
DateTime dtMin = DateTime.Now - span;
|
|
foreach (DateTime dt in LastStartedTimes)
|
|
{
|
|
if (dt >= dtMin)
|
|
++nCount;
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if the Number of times the process started in the given timespan
|
|
/// exceeds nMaxTry
|
|
/// </summary>
|
|
/// <param name="nMaxTry">number of max times it is allowed to have started</param>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>true if number of times in the given timespan exceeds nMaxTry, false otherwise</returns>
|
|
internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
|
|
{
|
|
if (LastStartedTimes.Count == 0)
|
|
return false;
|
|
|
|
int nCount = GetStartTimesInGivenTimeSpan(span);
|
|
return (nCount > nMaxTry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the passed in ProcessExeFileNameNPath, CommandLinePrms
|
|
/// matches this object's members * NOT THE SAME AS IComparable *
|
|
/// IComparable does a Full Compare, whereas this only Compares ProcessExeFileNameNPath and CommandLinePrms
|
|
/// ~IComparable also includes WorkingDirectory
|
|
/// </summary>
|
|
internal bool FoundMatch(string ProcessExeFileNameNPath, string CommandLinePrms)
|
|
{
|
|
if ((String.Compare(this.ProcessExeFileNameNPath.Trim(), ProcessExeFileNameNPath.Trim(), true) == 0) &&
|
|
(String.Compare(this.CommandLinePrms.Trim(), CommandLinePrms.Trim(), true) == 0))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IComparable Members
|
|
|
|
/// <summary>
|
|
/// Compare the ProcessExe
|
|
/// </summary>
|
|
/// <param name="obj">a valid ProcessExe Obj</param>
|
|
/// <returns>0 if equal, -1 if obj is smaller, +1 if obj is bigger</returns>
|
|
public int CompareTo(object obj)
|
|
{
|
|
if (obj is ProcessExe)
|
|
{
|
|
ProcessExe p = (ProcessExe)obj;
|
|
int nCompare = String.Compare(p.ProcessExeFileNameNPath.Trim(), this.ProcessExeFileNameNPath.Trim(), true);
|
|
if (nCompare == 0)
|
|
nCompare = String.Compare(p.CommandLinePrms.Trim(), this.CommandLinePrms.Trim(), true);
|
|
if (nCompare == 0)
|
|
nCompare = String.Compare(p.WorkingDirectory.Trim(), this.WorkingDirectory.Trim(), true);
|
|
if (nCompare == 0)
|
|
nCompare = this.AllowRestart.CompareTo(p.AllowRestart);
|
|
return nCompare;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("object is not a ProcessExe");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICloneable Members
|
|
|
|
/// <summary>
|
|
/// Clones the ProcessExe Object
|
|
/// </summary>
|
|
/// <returns>a new ProcessExe Object</returns>
|
|
public object Clone()
|
|
{
|
|
ProcessExe process = new ProcessExe(this.ProcessExeFileNameNPath, this.CommandLinePrms, this.WorkingDirectory);
|
|
process.AllowRestart = this.AllowRestart;
|
|
return process;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// MonitoredServicesW - (Wrapper) around ServiceExe ArrayList to work with XML
|
|
/// </summary>
|
|
public class MonitoredServicesW : IComparable, ICloneable
|
|
{
|
|
#region Private Members
|
|
|
|
private ArrayList m_ArrayList;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Constructor for MonitoredServicesW
|
|
/// </summary>
|
|
public MonitoredServicesW()
|
|
{
|
|
m_ArrayList = new ArrayList();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region XML Service Element
|
|
|
|
/// <summary>
|
|
/// Property useful to directly Get/Set the ServiceExes as an Array
|
|
/// </summary>
|
|
[XmlElement("Service")]
|
|
public ServiceExe[] ServiceExes
|
|
{
|
|
get
|
|
{
|
|
ServiceExe[] serviceExes = new ServiceExe[m_ArrayList.Count];
|
|
m_ArrayList.CopyTo(serviceExes);
|
|
return serviceExes;
|
|
}
|
|
set
|
|
{
|
|
if (value == null) return;
|
|
ServiceExe[] serviceExes = (ServiceExe[])value;
|
|
m_ArrayList.Clear();
|
|
foreach (ServiceExe serviceExe in serviceExes)
|
|
AddServiceExe(serviceExe);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Get Function
|
|
|
|
/// <summary>
|
|
/// Use this to get the ServiceExe Object for the Specified Service Name
|
|
/// </summary>
|
|
/// <param name="Name">Name of Service To look for</param>
|
|
/// <returns>the Service Exe Object or null, if not found</returns>
|
|
public ServiceExe GetServiceExeByServiceName(string Name)
|
|
{
|
|
if (String.IsNullOrEmpty(Name))
|
|
{
|
|
foreach (ServiceExe _cmpServiceExe in m_ArrayList)
|
|
{
|
|
if (String.Compare(_cmpServiceExe.Name, Name, true) == 0)
|
|
return _cmpServiceExe;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Add Functions
|
|
|
|
/// <summary>
|
|
/// Helper function to Add a Service Exe
|
|
/// </summary>
|
|
/// <param name="serviceExe">a valid ServiceExe Object</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
public bool AddServiceExe(ServiceExe serviceExe)
|
|
{
|
|
bool bAddSuccess = false;
|
|
if (serviceExe != null && !String.IsNullOrEmpty(serviceExe.Name))
|
|
{
|
|
if (ServiceExistsLocally(serviceExe) && IsUniqueServiceExe(serviceExe))
|
|
bAddSuccess = (m_ArrayList.Add(serviceExe) >= 0);
|
|
}
|
|
return bAddSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function to Add a Service Exe
|
|
/// </summary>
|
|
/// <param name="Name">Name of Service</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
public bool AddServiceExe(string Name)
|
|
{
|
|
bool bAddSuccess = false;
|
|
if (!String.IsNullOrEmpty(Name))
|
|
{
|
|
ServiceExe serviceToAdd = new ServiceExe(Name.Trim());
|
|
if (ServiceExistsLocally(serviceToAdd) && IsUniqueServiceExe(serviceToAdd))
|
|
bAddSuccess = (m_ArrayList.Add(serviceToAdd) >= 0);
|
|
}
|
|
return bAddSuccess;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Remove Function
|
|
|
|
/// <summary>
|
|
/// Helper function to Remove a Service Exe
|
|
/// </summary>
|
|
/// <param name="Name">Name of Service</param>
|
|
/// <returns>true if successfull, false otherwise</returns>
|
|
public bool RemoveServiceExe(string Name)
|
|
{
|
|
if (!String.IsNullOrEmpty(Name))
|
|
{
|
|
int nIndex = -1;
|
|
for (int i = 0; i < m_ArrayList.Count; ++i)
|
|
{
|
|
ServiceExe _cmpServiceExe = (ServiceExe)m_ArrayList[i];
|
|
if (_cmpServiceExe.FoundMatch(Name))
|
|
{
|
|
nIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nIndex != -1)
|
|
{
|
|
m_ArrayList.RemoveAt(nIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Clear N' Query Functions
|
|
|
|
/// <summary>
|
|
/// Clears all ServiceExes from the List
|
|
/// </summary>
|
|
public void Clear() { m_ArrayList.Clear(); }
|
|
|
|
/// <summary>
|
|
/// Clears each Services's Start and Fail Time
|
|
/// </summary>
|
|
public void ClearServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) s.ClearStartNFailTimes(); }
|
|
|
|
/// <summary>
|
|
/// Check to see if there are any Start or Fail Times,
|
|
/// if there aren't. Consider this configuration as newly loaded.
|
|
/// </summary>
|
|
/// <returns>true, if there are any Start or Fail Times recorded, false otherwise</returns>
|
|
public bool HasAnyServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) { if (s.LastFailedTimes.Count > 0) return true; } return false; }
|
|
|
|
/// <summary>
|
|
/// Retrieve a list of all Services that have been mapped
|
|
/// </summary>
|
|
/// <param name="bIncludeErrorStateServices">if set to true, includes Services in an error state, false otherwise * should always use false *</param>
|
|
/// <param name="bIncludeNonRestartServices">if set true, includes Services that are marked as NonRestart</param>
|
|
/// <returns></returns>
|
|
public string[] QueryAllServiceInfo(bool bIncludeErrorStateServices, bool bIncludeNonRestartServices)
|
|
{
|
|
List<string> services = new List<string>();
|
|
foreach (ServiceExe s in ServiceExes)
|
|
{
|
|
if (!String.IsNullOrEmpty(s.Name))
|
|
{
|
|
// bError = true && bInclude = true (include all)
|
|
// bError = false && bInclude = true (include all that don't have an error)
|
|
// bError = true && bInclude = false (include all that aren't restart)
|
|
// bError = false && bInclude = false (include none that have either)
|
|
if (bIncludeErrorStateServices && bIncludeNonRestartServices)
|
|
{
|
|
services.Add(s.Name);
|
|
}
|
|
else if (!bIncludeErrorStateServices && bIncludeNonRestartServices)
|
|
{
|
|
if (!s.InErrorState)
|
|
services.Add(s.Name);
|
|
}
|
|
else if (bIncludeErrorStateServices && !bIncludeNonRestartServices)
|
|
{
|
|
if (s.AllowRestart)
|
|
services.Add(s.Name);
|
|
}
|
|
else if (!bIncludeErrorStateServices && !bIncludeNonRestartServices)
|
|
{
|
|
if (!s.InErrorState && s.AllowRestart)
|
|
services.Add(s.Name);
|
|
}
|
|
}
|
|
}
|
|
return services.ToArray();
|
|
}
|
|
#endregion
|
|
|
|
#region Private Helper Functions
|
|
|
|
/// <summary>
|
|
/// Enforce Uniqueness, when adding Service Exe's to the DS
|
|
/// </summary>
|
|
/// <param name="serviceExe">a valid ServiceExe Object</param>
|
|
/// <returns>true if unique, false otherwise</returns>
|
|
private bool IsUniqueServiceExe(ServiceExe serviceExe)
|
|
{
|
|
foreach (ServiceExe _cmpServiceExe in m_ArrayList)
|
|
{
|
|
// If ServiceExe Name Matches, then it is NOT unique
|
|
if (_cmpServiceExe.FoundMatch(serviceExe.Name))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enforce that the Service must exist on the system, prior being added
|
|
/// </summary>
|
|
/// <param name="serviceExe">a valid ServiceExe Object</param>
|
|
/// <returns>true if exists, false otherwise</returns>
|
|
private bool ServiceExistsLocally(ServiceExe serviceExe)
|
|
{
|
|
bool bExists = ServiceW.DoesServiceExist(serviceExe.Name);
|
|
return bExists;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IComparable Members
|
|
|
|
/// <summary>
|
|
/// Compares two MonitoredServicesW Objects
|
|
/// </summary>
|
|
/// <param name="obj">a valid MonitoredServicesW object</param>
|
|
/// <returns>0 if equal, -1 if obj is less, +1 if obj is more</returns>
|
|
public int CompareTo(object obj)
|
|
{
|
|
if (obj is MonitoredServicesW)
|
|
{
|
|
MonitoredServicesW sw = (MonitoredServicesW)obj;
|
|
|
|
// First, do a simple count comparison
|
|
int wListCount = sw.m_ArrayList.Count;
|
|
int thisListCount = this.m_ArrayList.Count;
|
|
|
|
if (wListCount == 0 && thisListCount != 0)
|
|
return -1;
|
|
else if (wListCount == 0 && thisListCount == 0)
|
|
return 0;
|
|
else if (wListCount != 0 && thisListCount == 0)
|
|
return 1;
|
|
else if (wListCount < thisListCount)
|
|
return -1;
|
|
else if (wListCount > thisListCount)
|
|
return 1;
|
|
|
|
// The count must be the same * Hence, now we must actually
|
|
// compare each member to make sure that the lists are the same *
|
|
// ~but before we do that, we must sort the List first
|
|
ArrayList List1 = (ArrayList) sw.m_ArrayList.Clone();
|
|
ArrayList List2 = (ArrayList) this.m_ArrayList.Clone();
|
|
List1.Sort();
|
|
List2.Sort();
|
|
|
|
// ~Iterate this Object's array list and compare each member
|
|
int nCompare = 0;
|
|
for (int i = 0; i < thisListCount; ++i)
|
|
{
|
|
ServiceExe s1 = (ServiceExe)List1[i];
|
|
ServiceExe s2 = (ServiceExe)List2[i];
|
|
nCompare = s1.CompareTo(s2);
|
|
if (nCompare != 0)
|
|
break;
|
|
}
|
|
|
|
// If they are both still exactly equal, then let's
|
|
// now also compare the order
|
|
if (nCompare == 0)
|
|
{
|
|
for (int i = 0; i < sw.m_ArrayList.Count; ++i)
|
|
{
|
|
ServiceExe s1 = (ServiceExe)sw.m_ArrayList[i];
|
|
ServiceExe s2 = (ServiceExe)this.m_ArrayList[i];
|
|
nCompare = s1.CompareTo(s2);
|
|
if (nCompare != 0)
|
|
break;
|
|
}
|
|
}
|
|
return nCompare;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("object is not a MonitoredServicesW");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICloneable Members
|
|
|
|
/// <summary>
|
|
/// Creates a shallow copy of MonitoredServicesW Obj
|
|
/// </summary>
|
|
/// <returns>a new MonitoredServicesW Obj</returns>
|
|
public object Clone()
|
|
{
|
|
MonitoredServicesW sw = new MonitoredServicesW();
|
|
sw.m_ArrayList = (ArrayList)this.m_ArrayList.Clone();
|
|
return sw;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Service Name/Path Class that contains all settings to monitor a service
|
|
/// </summary>
|
|
public class ServiceExe : IComparable, ICloneable
|
|
{
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// specify a Service Name
|
|
/// </summary>
|
|
[XmlText]
|
|
public string Name { get { return _Name; } set { if(!String.IsNullOrEmpty(value)) _Name = value; } }
|
|
private string _Name;
|
|
|
|
/// <summary>
|
|
/// specify if service is allowed to be restarted
|
|
/// </summary>
|
|
[XmlAttribute("AllowRestart")]
|
|
public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } }
|
|
private bool _AllowRestart = true;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Default Constructor * Don't use this * used only by serialization
|
|
/// </summary>
|
|
public ServiceExe()
|
|
{
|
|
this.InErrorState = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Main Constructor * Use this *
|
|
/// </summary>
|
|
/// <param name="Name">specify the service Name</param>
|
|
public ServiceExe(string Name)
|
|
{
|
|
if (!String.IsNullOrEmpty(Name))
|
|
this.Name = Name.Trim();
|
|
this.InErrorState = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Helper Functions
|
|
|
|
/// <summary>
|
|
/// Returns true if the passed in Name matches this object's members *same as IComparable,
|
|
/// but func doesn't need creation of an object*
|
|
/// </summary>
|
|
/// <param name="Name">specify the service Name</param>
|
|
/// <returns>true if matched, false otherwise</returns>
|
|
internal bool FoundMatch(string Name)
|
|
{
|
|
if (Name != null)
|
|
{
|
|
if (String.Compare(this.Name.Trim(), Name.Trim(), true) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all Start N' Fail Times for a process
|
|
/// </summary>
|
|
internal void ClearStartNFailTimes()
|
|
{
|
|
LastStartedTimes.Clear();
|
|
LastStartedTimes.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal Variable to keep track if this Process is in an Error State
|
|
/// </summary>
|
|
internal bool InErrorState;
|
|
|
|
/// <summary>
|
|
/// Keep track of all Last Failed DT Stamps
|
|
/// </summary>
|
|
internal readonly List<DateTime> LastFailedTimes = new List<DateTime>();
|
|
|
|
/// <summary>
|
|
/// Keep track of all Last Started DT Stamps
|
|
/// </summary>
|
|
internal readonly List<DateTime> LastStartedTimes = new List<DateTime>();
|
|
|
|
/// <summary>
|
|
/// Last Failed DT Stamp
|
|
/// </summary>
|
|
internal DateTime LastFailTime
|
|
{
|
|
get
|
|
{
|
|
if (LastFailedTimes.Count > 0)
|
|
return LastFailedTimes.Last();
|
|
else
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow specific amount of time to have passed since Last-Fail
|
|
/// </summary>
|
|
/// <param name="ts">timespan that has to have occured since Last-Fail Time</param>
|
|
/// <returns>true if the Last-Fail Exceeded the specified amount of time, false otherwise</returns>
|
|
internal bool LastFailTimeTimeoutExceeded(TimeSpan ts)
|
|
{
|
|
if (LastFailedTimes.Count > 0)
|
|
{
|
|
TimeSpan diff = DateTime.Now - LastFailTime;
|
|
if (diff < ts)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if the Number of times the process failed in the given timespan
|
|
/// exceeds nMaxTry
|
|
/// </summary>
|
|
/// <param name="nMaxTry">number of max times it is allowed to fail</param>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>true if number of times in the given timespan exceeds nMaxTry, false otherwise</returns>
|
|
internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
|
|
{
|
|
if (LastFailedTimes.Count == 0)
|
|
return false;
|
|
|
|
int nCount = GetFailTimesInGivenTimeSpan(span);
|
|
return (nCount > nMaxTry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Last Started DT Stamp
|
|
/// </summary>
|
|
internal DateTime LastStartTime
|
|
{
|
|
get
|
|
{
|
|
if (LastStartedTimes.Count > 0)
|
|
return LastStartedTimes.Last();
|
|
else
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow specific amount of time to have passed since Last-Start
|
|
/// </summary>
|
|
/// <param name="ts">timespan that has to have occured since Last-Start Time</param>
|
|
/// <returns>true if the Last-Start Exceeded the specified amount of time, false otherwise</returns>
|
|
internal bool LastStartTimeTimeoutExceeded(TimeSpan ts)
|
|
{
|
|
if (LastStartedTimes.Count > 0)
|
|
{
|
|
TimeSpan diff = DateTime.Now - LastStartTime;
|
|
if (diff < ts)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if the Number of times the process started in the given timespan
|
|
/// exceeds nMaxTry
|
|
/// </summary>
|
|
/// <param name="nMaxTry">number of max times it is allowed to have started</param>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>true if number of times in the given timespan exceeds nMaxTry, false otherwise</returns>
|
|
internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
|
|
{
|
|
if (LastStartedTimes.Count == 0)
|
|
return false;
|
|
|
|
int nCount = GetStartTimesInGivenTimeSpan(span);
|
|
return (nCount > nMaxTry);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a FailTime to keep track of for the given Service
|
|
/// </summary>
|
|
/// <param name="dtFail">Fail-Time</param>
|
|
/// <param name="nMaxCapacity">max capacity to store</param>
|
|
//internal void AddFailTime(DateTime dtFail, int nMaxCapacity = 1)
|
|
internal void AddFailTime(DateTime dtFail, int nMaxCapacity)
|
|
{
|
|
if (dtFail != DateTime.MinValue)
|
|
{
|
|
// Make sure to always at least store one
|
|
if (nMaxCapacity < 1)
|
|
nMaxCapacity = 1;
|
|
|
|
// Add and adjust according to capacity
|
|
LastFailedTimes.Add(dtFail);
|
|
if (LastFailedTimes.Count > nMaxCapacity)
|
|
{
|
|
int nRemoveCount = nMaxCapacity - LastFailedTimes.Count;
|
|
for (int i = 0; i < nRemoveCount; ++i)
|
|
LastFailedTimes.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a StartTime to keep track of for the given Service
|
|
/// </summary>
|
|
/// <param name="dtStart">Start-Time</param>
|
|
/// <param name="nMaxCapacity">max capacity to store</param>
|
|
//internal void AddStartTime(DateTime dtStart, int nMaxCapacity = 1)
|
|
internal void AddStartTime(DateTime dtStart, int nMaxCapacity)
|
|
{
|
|
if (dtStart != DateTime.MinValue)
|
|
{
|
|
// Make sure to always at least store one
|
|
if (nMaxCapacity < 1)
|
|
nMaxCapacity = 1;
|
|
|
|
// Add and adjust according to capacity
|
|
LastStartedTimes.Add(dtStart);
|
|
if (LastStartedTimes.Count > nMaxCapacity)
|
|
{
|
|
int nRemoveCount = nMaxCapacity - LastStartedTimes.Count;
|
|
for (int i = 0; i < nRemoveCount; ++i)
|
|
LastStartedTimes.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the Number of times the process failed / Started in the given timespan
|
|
/// </summary>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>number of times this Process Failed in the given timespan</returns>
|
|
internal int GetFailTimesInGivenTimeSpan(TimeSpan span)
|
|
{
|
|
if (LastFailedTimes.Count == 0)
|
|
return 0;
|
|
|
|
int nCount = 0;
|
|
DateTime dtMin = DateTime.Now - span;
|
|
foreach (DateTime dt in LastFailedTimes)
|
|
{
|
|
if (dt >= dtMin)
|
|
++nCount;
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the Number of times the process Started in the given timespan
|
|
/// </summary>
|
|
/// <param name="span">TimeSpan to subtract from DateTime.Now</param>
|
|
/// <returns>number of times this Process Started in the given timespan</returns>
|
|
internal int GetStartTimesInGivenTimeSpan(TimeSpan span)
|
|
{
|
|
if (LastStartedTimes.Count == 0)
|
|
return 0;
|
|
|
|
int nCount = 0;
|
|
DateTime dtMin = DateTime.Now - span;
|
|
foreach (DateTime dt in LastStartedTimes)
|
|
{
|
|
if (dt >= dtMin)
|
|
++nCount;
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IComparable Members
|
|
|
|
/// <summary>
|
|
/// Compare the ServiceExe
|
|
/// </summary>
|
|
/// <param name="obj">a valid ServiceExe Obj</param>
|
|
/// <returns>0 if equal, -1 if obj is smaller, +1 if obj is bigger</returns>
|
|
public int CompareTo(object obj)
|
|
{
|
|
if (obj is ServiceExe)
|
|
{
|
|
ServiceExe s = (ServiceExe)obj;
|
|
int nCompare = String.Compare(s.Name.Trim(), this.Name.Trim(), true);
|
|
if (nCompare == 0)
|
|
{
|
|
nCompare = this.AllowRestart.CompareTo(s.AllowRestart);
|
|
}
|
|
return nCompare;
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("object is not a ServiceExe");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICloneable Members
|
|
|
|
/// <summary>
|
|
/// Clones the ServiceExe Object
|
|
/// </summary>
|
|
/// <returns>a new ServiceExe Object</returns>
|
|
public object Clone()
|
|
{
|
|
ServiceExe service = new ServiceExe(this.Name);
|
|
service.AllowRestart = this.AllowRestart;
|
|
return service;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region ICloneable Members
|
|
|
|
public object Clone()
|
|
{
|
|
MonitorConfiguration config = new MonitorConfiguration();
|
|
config.MonitoredProcesses = (MonitoredProcessesW) this.MonitoredProcesses.Clone();
|
|
config.MonitoredServices = (MonitoredServicesW) this.MonitoredServices.Clone();
|
|
return config;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <remarks>
|
|
/// Object is responsible for Loading/Saving the MonitorConfiguration to
|
|
/// and From XML File.
|
|
/// </remarks>
|
|
public class MonitorDataStore
|
|
{
|
|
#region Private Members
|
|
|
|
private MonitorConfiguration _RunTimeWatchdogXMLConfigurationData = null;
|
|
private XSerializer _xmlserializer = null;
|
|
private string _dsFileNameNPath = "";
|
|
private DelegateCollection.Void_Param1_Exception_Func _exceptionHandler = null;
|
|
private bool _bForceRefreshOnNextRead = false;
|
|
private object _lock = new object();
|
|
|
|
#endregion
|
|
|
|
#region internal statics
|
|
|
|
internal static List<string> MonitoredProcessNamesExclusionList = null;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Construct a MonitorDataStore Object, responsible for loading and
|
|
/// saving MonitorConfiguration from/to XML.
|
|
/// </summary>
|
|
/// <param name="dsFileNameNPath">a valid filename and path To load/store MonitorConfiguration</param>
|
|
/// <param name="ExclusionListProcessNames">List of Process Names that will always be consider excluded * invalid * to add to configuration</param>
|
|
/// <exception cref="ArgumentException">Thrown if Empty dsFileNameNPath is passed in</exception>
|
|
//public MonitorDataStore(string dsFileNameNPath, List<string> ExclusionListProcessNames = null, DelegateCollection.Void_Param1_Exception_Func exceptionHandler = null)
|
|
public MonitorDataStore(string dsFileNameNPath, List<string> ExclusionListProcessNames, DelegateCollection.Void_Param1_Exception_Func exceptionHandler)
|
|
{
|
|
if (String.IsNullOrEmpty(dsFileNameNPath))
|
|
throw new ArgumentException("Invalid dsFileNameNPath");
|
|
|
|
_dsFileNameNPath = dsFileNameNPath;
|
|
MonitoredProcessNamesExclusionList = ExclusionListProcessNames;
|
|
_exceptionHandler = exceptionHandler;
|
|
|
|
// Initialize Serializer And Try to Read File configuration
|
|
_xmlserializer = new XSerializer();
|
|
_RunTimeWatchdogXMLConfigurationData = _xmlserializer.ReadFromFile<MonitorConfiguration>(_dsFileNameNPath);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Static Accessor Methods
|
|
|
|
/// <summary>
|
|
/// Use this function to force a Data Refresh the next time ReadData() is called
|
|
/// </summary>
|
|
public bool ForceRefreshOnNext_ReadData { get { return _bForceRefreshOnNextRead; } set { _bForceRefreshOnNextRead = value; } }
|
|
|
|
/// <summary>
|
|
/// Read the XML Configuration Data (From a File in the System) or from Cache
|
|
/// </summary>
|
|
/// <param name="bForceRefresh">set to true to force a reload of configuration from the disk</param>
|
|
/// <param name="exceptionHandler">Exception Handler Function, if an Exception is Thrown</param>
|
|
/// <returns>an XMLDataStore object or Null if error occured</returns>
|
|
//public WatchdogConfiguration ReadData(bool bForceRefresh = false)
|
|
public MonitorConfiguration ReadData(bool bForceRefresh)
|
|
{
|
|
try
|
|
{
|
|
// Force Refresh on next Call?
|
|
if (_bForceRefreshOnNextRead)
|
|
{
|
|
bForceRefresh = true;
|
|
_bForceRefreshOnNextRead = false;
|
|
}
|
|
|
|
// Load Configuration From the XML File
|
|
if (bForceRefresh || (_RunTimeWatchdogXMLConfigurationData == null))
|
|
{
|
|
// XML could have been modified by the user, hence we it is unverified
|
|
MonitorConfiguration UnverifiedConfig = _xmlserializer.ReadFromFile<MonitorConfiguration>(_dsFileNameNPath);
|
|
if(UnverifiedConfig != null)
|
|
{
|
|
// Make sure that all ProcessExes are Unique (ProcessExe and CommandLine and WorkingDirectory * together * are unique)
|
|
MonitorConfiguration VerifiedConfig = new MonitorConfiguration();
|
|
|
|
// calling AddProcessExe() will enforce uniqueness
|
|
foreach (MonitorConfiguration.ProcessExe p in UnverifiedConfig.MonitoredProcesses.ProcessExes)
|
|
VerifiedConfig.MonitoredProcesses.AddProcessExe(p);
|
|
|
|
// calling AddServiceExe() will enforce uniqueness
|
|
foreach (MonitorConfiguration.ServiceExe s in UnverifiedConfig.MonitoredServices.ServiceExes)
|
|
VerifiedConfig.MonitoredServices.AddServiceExe(s);
|
|
|
|
// always use the valid configuration
|
|
lock (_lock)
|
|
{
|
|
_RunTimeWatchdogXMLConfigurationData = VerifiedConfig;
|
|
}
|
|
|
|
// save the verified configuration xml * potentially fixing any issue that were in it *
|
|
SaveData();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); }
|
|
|
|
// No File Exists, write out a blank file * and load a default * blank * configuration
|
|
// This function should NEVER, EVER pass out null
|
|
if (_RunTimeWatchdogXMLConfigurationData == null)
|
|
{
|
|
SaveData(null);
|
|
_RunTimeWatchdogXMLConfigurationData = _xmlserializer.ReadFromFile<MonitorConfiguration>(_dsFileNameNPath);
|
|
}
|
|
return _RunTimeWatchdogXMLConfigurationData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the XML Configuration Data passed in out to Disk
|
|
/// </summary>
|
|
/// <param name="data">The XML Data To Write out</param>
|
|
/// <param name="exceptionHandler">Exception Handler Function, if an Exception is Thrown</param>
|
|
public void SaveData(MonitorConfiguration data)
|
|
{
|
|
try
|
|
{
|
|
_xmlserializer.WriteToFile<MonitorConfiguration>(data, _dsFileNameNPath);
|
|
lock (_lock)
|
|
{
|
|
_RunTimeWatchdogXMLConfigurationData = data;
|
|
}
|
|
}
|
|
catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the XML Configuration Data that is currently stored in this Object out to Disk
|
|
/// </summary>
|
|
/// <param name="exceptionHandler">Exception Handler Function, if an Exception is Thrown</param>
|
|
public void SaveData()
|
|
{
|
|
try
|
|
{
|
|
_xmlserializer.WriteToFile<MonitorConfiguration>(_RunTimeWatchdogXMLConfigurationData, _dsFileNameNPath);
|
|
}
|
|
catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|