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
///
/// Serializable Xml Object used to store Monitor Configuration data
///
[XmlRoot("configuration", Namespace = "Monitor", IsNullable = false)]
public class MonitorConfiguration : ICloneable
{
///
/// MonitoredProcesses Member Tag <> holds list of ProcessExe's to Monitor
///
public MonitoredProcessesW MonitoredProcesses = null;
///
/// MonitoredServices Member Tag <> holds list of Services to Monitor
///
public MonitoredServicesW MonitoredServices = null;
///
/// Monitor Configuration - Constructor
///
public MonitorConfiguration()
{
MonitoredProcesses = new MonitoredProcessesW();
MonitoredServices = new MonitoredServicesW();
}
#region Internal Monitor Properties N' Methods
///
/// True if Either Any Processes or Any Services are Configured
///
internal bool ConfiguredHasAny { get { return (MonitoredProcesses.ProcessExes.Length > 0 || MonitoredServices.ServiceExes.Length > 0); } }
///
/// True if Any Processes are Configured
///
internal bool ConfiguredHasAnyProcesses { get { return (MonitoredProcesses.ProcessExes.Length > 0); } }
///
/// True if Any Services are Configured
///
internal bool ConfiguredHasAnyServices { get { return (MonitoredServices.ServiceExes.Length > 0); } }
///
/// Clears all Start and Fail Times for Both Processes and Services
///
internal void ClearAllStartNFailTimes()
{
MonitoredProcesses.ClearProcessExeStartNFailTimes();
MonitoredServices.ClearServiceExeStartNFailTimes();
}
///
/// Check to see if there are any Start or Fail Times,
/// if there aren't. Consider this configuration as newly loaded.
///
/// true, if there are any Start or Fail Times recorded, false otherwise
internal bool HasAnyStartNFailTimes()
{
if (MonitoredProcesses.HasAnyProcessExeStartNFailTimes() ||
MonitoredServices.HasAnyServiceExeStartNFailTimes())
return true;
else
return false;
}
#endregion
///
/// MonitoredProcessesW - (Wrapper) around ProcessExe ArrayList to work with XML
///
public class MonitoredProcessesW : IComparable, ICloneable
{
#region Private Members
private ArrayList m_ArrayList;
#endregion
#region Construction
///
/// Constructor for MonitoredProcessesW
///
public MonitoredProcessesW()
{
m_ArrayList = new ArrayList();
}
#endregion
#region XML Process Element
///
/// Property useful to directly Get/Set the ProcessExes as an Array
///
[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
///
/// Use this to get all processes settings for a specific Process Exe File
///
/// specific process exe to get settings for
/// a list of process settings for the specified exe or empty list if not found
public ProcessExe[] GetProcessExesByProcessExeFileNameNPath(string ProcessExeFileNameNPath)
{
List foundProcessExes = new List();
foreach (ProcessExe _cmpProcessExe in m_ArrayList)
{
if (String.Compare(_cmpProcessExe.ProcessExeFileNameNPath, ProcessExeFileNameNPath, true) == 0)
foundProcessExes.Add(_cmpProcessExe);
}
return foundProcessExes.ToArray();
}
///
/// Use this to get all processes settings for a specific Process Exe FileName without Extension
///
/// specific process exe fileName without Extension to get settings for
/// a list of process settings for the specified exe or empty list if not found
public ProcessExe[] GetProcessExesByProcessExeFileNameWithoutExtension(string ProcessExeFileNameWithoutExtension)
{
List foundProcessExes = new List();
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
///
/// Helper function to Add a Process Exe
///
/// ProcessExe to Add
/// true if successfull, false otherwise
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;
}
///
/// Helper function to Add a Process Exe
///
/// specify a fileName and path for a process
/// specify a command-line params to use for the process
/// specify the working directory to use for the process
/// true if successfull, false otherwise
//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;
}
///
/// Helper function to check whether we can Add a Process Exe
///
/// specify a fileName and path for a process
/// specify a command-line params to use for the process
/// specify the working directory to use for the process
/// true if successfull, false otherwise
//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
///
/// Helper function to Remove a Process Exe
///
/// specify a fileName and path for a process
/// specify a command-line params to use for the process
/// specify the working directory to use for the process
/// true if successfull, false otherwise
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
///
/// Clears all ProcessExes from the List
///
public void Clear() { m_ArrayList.Clear(); }
///
/// Clears each Process's Start and Fail Time
///
public void ClearProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) p.ClearStartNFailTimes(); }
///
/// Check to see if there are any Start or Fail Times,
/// if there aren't. Consider this configuration as newly loaded.
///
/// true, if there are any Start or Fail Times recorded, false otherwise
public bool HasAnyProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) { if (p.LastFailedTimes.Count > 0) return true; } return false; }
///
/// Retrieve the Process Exe that matches the specified PID
///
public ProcessExe ClearPidInformation() { foreach (ProcessExe p in ProcessExes) { p.PID = 0; } return null; }
///
/// 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
///
/// true, if any PID info is incomplete
public bool HasIncompletePidInformation() { foreach (ProcessExe p in ProcessExes) { if (p.PID <= 0) return true; } return false; }
///
/// 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 *
///
/// if set to true, includes Pids in an error state, false otherwise * should always use false *
/// an array of pids mapped
//public uint[] QueryAllPidInfo(bool bIncludeErrorStatePids = false) { List pids = new List(); 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 pids = new List();
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();
}
///
/// Retrieve the number of Processes now considered in an error state.
///
/// Number of Pids in an error state
public uint NumberOfPidsInErrorState() { uint nErrorCount = 0; foreach (ProcessExe p in ProcessExes) { if (p.InErrorState) nErrorCount++; } return nErrorCount; }
///
/// Retrieve the Process Exe that matches the specified PID
///
/// PID to look four
/// ProcessExe that matches the PID, null otherwise (if not found)
public ProcessExe GetProcessExeForPid(uint PID) { foreach (ProcessExe p in ProcessExes) { if (p.PID == PID) return p; } return null; }
#endregion
#region Private Helper Functions
///
/// Enforce Uniqueness, when adding Process Exe's to the DS
///
/// a ProcessExe to check
/// the found ProcessExe that matches the check
/// true if unique, false otherwise
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;
}
///
/// Enforce that the called didn't specify to exclude this Process
///
/// a ProcessExe to check
/// true if not exclusive, false otherwise
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;
}
///
/// Enforce that the Process/Exe File must exist on the system, prior being added
///
/// true if exists, false otherwise
private bool ProcessExeFileExistsLocally(ProcessExe processExe)
{
return System.IO.File.Exists(processExe.ProcessExeFileNameNPath);
}
#endregion
#region IComparable Members
///
/// Compares tow MonitoredProcessesW Objects with each other
///
/// Pass in a valid MonitoredProcessesW Object
/// 0 if equal, -1 if obj is smaller, 1 if obj is bigger
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
///
/// Performs a Shallow Copy of MonitoredProcessesW
///
/// a new MonitoredProcessesW Object
public object Clone()
{
MonitoredProcessesW w = new MonitoredProcessesW();
w.m_ArrayList = (ArrayList)this.m_ArrayList.Clone();
return w;
}
#endregion
}
///
/// Process Exe (Image) Class that contains all settings to monitor
/// a process / executable image
///
public class ProcessExe : IComparable, ICloneable
{
#region Public Properties
///
/// specify a fileName and path for a process
///
[XmlText]
public string ProcessExeFileNameNPath { get { return _ProcessExeFileNameNPath; } set { if (!String.IsNullOrEmpty(value)) _ProcessExeFileNameNPath = value; } }
private string _ProcessExeFileNameNPath = "";
///
/// specify a command-line params to use for the process
///
[XmlAttribute("CommandLinePrms")]
public string CommandLinePrms { get { if (String.IsNullOrEmpty(_CommandLinePrms)) return ""; else return _CommandLinePrms; } set { if (!String.IsNullOrEmpty(value)) _CommandLinePrms = value; } }
private string _CommandLinePrms = "";
///
/// specify the working directory to use for the process
///
[XmlAttribute("WorkingDirectory")]
public string WorkingDirectory { get { if(String.IsNullOrEmpty(_WorkingDirectory)) return ""; else return _WorkingDirectory; } set { if (!String.IsNullOrEmpty(value)) _WorkingDirectory = value; } }
private string _WorkingDirectory;
///
/// specify if process is allowed to be restarted
///
[XmlAttribute("AllowRestart")]
public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } }
private bool _AllowRestart = true;
#endregion
#region Construction
///
/// Default Constructor * Don't use this * used only by serialization
///
public ProcessExe()
{
this.PID = 0;
this.InErrorState = false;
}
///
/// Main Constructor * Use this *
///
/// specify a fileName and path for a process
/// specify a command-line params to use for the process
/// specify the working directory to use for the process
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
///
/// The name of the Process
///
internal string ProcessName
{
get
{
if (!String.IsNullOrEmpty(this.ProcessExeFileNameNPath))
{
string ProcessName = Path.GetFileNameWithoutExtension(this.ProcessExeFileNameNPath);
return ProcessName.ToLower();
}
return String.Empty;
}
}
///
/// Clears all Start N' Fail Times for a process
///
internal void ClearStartNFailTimes()
{
LastStartedTimes.Clear();
LastStartedTimes.Clear();
}
///
/// Keep Track if this Process is in an Error State
///
internal bool InErrorState;
///
/// Internal Variable to Keep track of PID
///
internal uint PID;
///
/// Keep track of all Last Failed DT Stamps
///
internal readonly List LastFailedTimes = new List();
///
/// Keep track of all Last Started DT Stamps
///
internal readonly List LastStartedTimes = new List();
///
/// Returns the Last Failed Stamp
///
internal DateTime LastFailTime
{
get
{
if (LastFailedTimes.Count > 0)
return LastFailedTimes.Last();
else
return DateTime.MinValue;
}
}
///
/// Allow specific amount of time to have passed since Last-Fail
///
/// timespan that has to have occured since Last-Fail Time
/// true if the Last-Fail Exceeded the specified amount of time, false otherwise
internal bool LastFailTimeTimeoutExceeded(TimeSpan ts)
{
if (LastFailedTimes.Count > 0)
{
TimeSpan diff = DateTime.Now - LastFailTime;
if (diff < ts)
return false;
}
return true;
}
///
/// Returns the Last Started DT Stamp
///
internal DateTime LastStartTime
{
get
{
if (LastStartedTimes.Count > 0)
return LastStartedTimes.Last();
else
return DateTime.MinValue;
}
}
///
/// Allow specific amount of time to have passed since Last-Start
///
/// timespan that has to have occured since Last-Start Time
/// true if the Last-Start Exceeded the specified amount of time, false otherwise
internal bool LastStartTimeTimeoutExceeded(TimeSpan ts)
{
if (LastStartedTimes.Count > 0)
{
TimeSpan diff = DateTime.Now - LastStartTime;
if (diff < ts)
return false;
}
return true;
}
///
/// Add a FailTime to keep track of for the given process
///
/// Fail-Time
/// max capacity to store
//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);
}
}
}
///
/// Add a StartTime to keep track of for the given process
///
/// Start-Time
/// max capacity to store
//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);
}
}
}
///
/// Retrieves the Number of times the process failed in the given timespan
///
/// TimeSpan to subtract from DateTime.Now
/// number of times this Process Failed in the given timespan
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;
}
///
/// Checks to see if the Number of times the process failed in the given timespan
/// exceeds nMaxTry
///
/// number of max times it is allowed to fail
/// TimeSpan to subtract from DateTime.Now
/// true if number of times in the given timespan exceeds nMaxTry, false otherwise
internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
{
if (LastFailedTimes.Count == 0)
return false;
int nCount = GetFailTimesInGivenTimeSpan(span);
return (nCount > nMaxTry);
}
///
/// Retrieves the Number of times the process Started in the given timespan
///
/// TimeSpan to subtract from DateTime.Now
/// number of times this Process Started in the given timespan
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;
}
///
/// Checks to see if the Number of times the process started in the given timespan
/// exceeds nMaxTry
///
/// number of max times it is allowed to have started
/// TimeSpan to subtract from DateTime.Now
/// true if number of times in the given timespan exceeds nMaxTry, false otherwise
internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
{
if (LastStartedTimes.Count == 0)
return false;
int nCount = GetStartTimesInGivenTimeSpan(span);
return (nCount > nMaxTry);
}
///
/// 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
///
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
///
/// Compare the ProcessExe
///
/// a valid ProcessExe Obj
/// 0 if equal, -1 if obj is smaller, +1 if obj is bigger
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
///
/// Clones the ProcessExe Object
///
/// a new ProcessExe Object
public object Clone()
{
ProcessExe process = new ProcessExe(this.ProcessExeFileNameNPath, this.CommandLinePrms, this.WorkingDirectory);
process.AllowRestart = this.AllowRestart;
return process;
}
#endregion
}
///
/// MonitoredServicesW - (Wrapper) around ServiceExe ArrayList to work with XML
///
public class MonitoredServicesW : IComparable, ICloneable
{
#region Private Members
private ArrayList m_ArrayList;
#endregion
#region Construction
///
/// Constructor for MonitoredServicesW
///
public MonitoredServicesW()
{
m_ArrayList = new ArrayList();
}
#endregion
#region XML Service Element
///
/// Property useful to directly Get/Set the ServiceExes as an Array
///
[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
///
/// Use this to get the ServiceExe Object for the Specified Service Name
///
/// Name of Service To look for
/// the Service Exe Object or null, if not found
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
///
/// Helper function to Add a Service Exe
///
/// a valid ServiceExe Object
/// true if successfull, false otherwise
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;
}
///
/// Helper function to Add a Service Exe
///
/// Name of Service
/// true if successfull, false otherwise
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
///
/// Helper function to Remove a Service Exe
///
/// Name of Service
/// true if successfull, false otherwise
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
///
/// Clears all ServiceExes from the List
///
public void Clear() { m_ArrayList.Clear(); }
///
/// Clears each Services's Start and Fail Time
///
public void ClearServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) s.ClearStartNFailTimes(); }
///
/// Check to see if there are any Start or Fail Times,
/// if there aren't. Consider this configuration as newly loaded.
///
/// true, if there are any Start or Fail Times recorded, false otherwise
public bool HasAnyServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) { if (s.LastFailedTimes.Count > 0) return true; } return false; }
///
/// Retrieve a list of all Services that have been mapped
///
/// if set to true, includes Services in an error state, false otherwise * should always use false *
/// if set true, includes Services that are marked as NonRestart
///
public string[] QueryAllServiceInfo(bool bIncludeErrorStateServices, bool bIncludeNonRestartServices)
{
List services = new List();
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
///
/// Enforce Uniqueness, when adding Service Exe's to the DS
///
/// a valid ServiceExe Object
/// true if unique, false otherwise
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;
}
///
/// Enforce that the Service must exist on the system, prior being added
///
/// a valid ServiceExe Object
/// true if exists, false otherwise
private bool ServiceExistsLocally(ServiceExe serviceExe)
{
bool bExists = ServiceW.DoesServiceExist(serviceExe.Name);
return bExists;
}
#endregion
#region IComparable Members
///
/// Compares two MonitoredServicesW Objects
///
/// a valid MonitoredServicesW object
/// 0 if equal, -1 if obj is less, +1 if obj is more
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
///
/// Creates a shallow copy of MonitoredServicesW Obj
///
/// a new MonitoredServicesW Obj
public object Clone()
{
MonitoredServicesW sw = new MonitoredServicesW();
sw.m_ArrayList = (ArrayList)this.m_ArrayList.Clone();
return sw;
}
#endregion
}
///
/// Service Name/Path Class that contains all settings to monitor a service
///
public class ServiceExe : IComparable, ICloneable
{
#region Public Properties
///
/// specify a Service Name
///
[XmlText]
public string Name { get { return _Name; } set { if(!String.IsNullOrEmpty(value)) _Name = value; } }
private string _Name;
///
/// specify if service is allowed to be restarted
///
[XmlAttribute("AllowRestart")]
public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } }
private bool _AllowRestart = true;
#endregion
#region Construction
///
/// Default Constructor * Don't use this * used only by serialization
///
public ServiceExe()
{
this.InErrorState = false;
}
///
/// Main Constructor * Use this *
///
/// specify the service Name
public ServiceExe(string Name)
{
if (!String.IsNullOrEmpty(Name))
this.Name = Name.Trim();
this.InErrorState = false;
}
#endregion
#region Internal Helper Functions
///
/// Returns true if the passed in Name matches this object's members *same as IComparable,
/// but func doesn't need creation of an object*
///
/// specify the service Name
/// true if matched, false otherwise
internal bool FoundMatch(string Name)
{
if (Name != null)
{
if (String.Compare(this.Name.Trim(), Name.Trim(), true) == 0)
return true;
}
return false;
}
///
/// Clears all Start N' Fail Times for a process
///
internal void ClearStartNFailTimes()
{
LastStartedTimes.Clear();
LastStartedTimes.Clear();
}
///
/// Internal Variable to keep track if this Process is in an Error State
///
internal bool InErrorState;
///
/// Keep track of all Last Failed DT Stamps
///
internal readonly List LastFailedTimes = new List();
///
/// Keep track of all Last Started DT Stamps
///
internal readonly List LastStartedTimes = new List();
///
/// Last Failed DT Stamp
///
internal DateTime LastFailTime
{
get
{
if (LastFailedTimes.Count > 0)
return LastFailedTimes.Last();
else
return DateTime.MinValue;
}
}
///
/// Allow specific amount of time to have passed since Last-Fail
///
/// timespan that has to have occured since Last-Fail Time
/// true if the Last-Fail Exceeded the specified amount of time, false otherwise
internal bool LastFailTimeTimeoutExceeded(TimeSpan ts)
{
if (LastFailedTimes.Count > 0)
{
TimeSpan diff = DateTime.Now - LastFailTime;
if (diff < ts)
return false;
}
return true;
}
///
/// Checks to see if the Number of times the process failed in the given timespan
/// exceeds nMaxTry
///
/// number of max times it is allowed to fail
/// TimeSpan to subtract from DateTime.Now
/// true if number of times in the given timespan exceeds nMaxTry, false otherwise
internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
{
if (LastFailedTimes.Count == 0)
return false;
int nCount = GetFailTimesInGivenTimeSpan(span);
return (nCount > nMaxTry);
}
///
/// Last Started DT Stamp
///
internal DateTime LastStartTime
{
get
{
if (LastStartedTimes.Count > 0)
return LastStartedTimes.Last();
else
return DateTime.MinValue;
}
}
///
/// Allow specific amount of time to have passed since Last-Start
///
/// timespan that has to have occured since Last-Start Time
/// true if the Last-Start Exceeded the specified amount of time, false otherwise
internal bool LastStartTimeTimeoutExceeded(TimeSpan ts)
{
if (LastStartedTimes.Count > 0)
{
TimeSpan diff = DateTime.Now - LastStartTime;
if (diff < ts)
return false;
}
return true;
}
///
/// Checks to see if the Number of times the process started in the given timespan
/// exceeds nMaxTry
///
/// number of max times it is allowed to have started
/// TimeSpan to subtract from DateTime.Now
/// true if number of times in the given timespan exceeds nMaxTry, false otherwise
internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry)
{
if (LastStartedTimes.Count == 0)
return false;
int nCount = GetStartTimesInGivenTimeSpan(span);
return (nCount > nMaxTry);
}
///
/// Add a FailTime to keep track of for the given Service
///
/// Fail-Time
/// max capacity to store
//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);
}
}
}
///
/// Add a StartTime to keep track of for the given Service
///
/// Start-Time
/// max capacity to store
//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);
}
}
}
///
/// Retrieves the Number of times the process failed / Started in the given timespan
///
/// TimeSpan to subtract from DateTime.Now
/// number of times this Process Failed in the given timespan
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;
}
///
/// Retrieves the Number of times the process Started in the given timespan
///
/// TimeSpan to subtract from DateTime.Now
/// number of times this Process Started in the given timespan
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
///
/// Compare the ServiceExe
///
/// a valid ServiceExe Obj
/// 0 if equal, -1 if obj is smaller, +1 if obj is bigger
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
///
/// Clones the ServiceExe Object
///
/// a new ServiceExe Object
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
///
/// Object is responsible for Loading/Saving the MonitorConfiguration to
/// and From XML File.
///
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 MonitoredProcessNamesExclusionList = null;
#endregion
#region Constructor
///
/// Construct a MonitorDataStore Object, responsible for loading and
/// saving MonitorConfiguration from/to XML.
///
/// a valid filename and path To load/store MonitorConfiguration
/// List of Process Names that will always be consider excluded * invalid * to add to configuration
/// Thrown if Empty dsFileNameNPath is passed in
//public MonitorDataStore(string dsFileNameNPath, List ExclusionListProcessNames = null, DelegateCollection.Void_Param1_Exception_Func exceptionHandler = null)
public MonitorDataStore(string dsFileNameNPath, List 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(_dsFileNameNPath);
}
#endregion
#region Public Static Accessor Methods
///
/// Use this function to force a Data Refresh the next time ReadData() is called
///
public bool ForceRefreshOnNext_ReadData { get { return _bForceRefreshOnNextRead; } set { _bForceRefreshOnNextRead = value; } }
///
/// Read the XML Configuration Data (From a File in the System) or from Cache
///
/// set to true to force a reload of configuration from the disk
/// Exception Handler Function, if an Exception is Thrown
/// an XMLDataStore object or Null if error occured
//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(_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(_dsFileNameNPath);
}
return _RunTimeWatchdogXMLConfigurationData;
}
///
/// Writes the XML Configuration Data passed in out to Disk
///
/// The XML Data To Write out
/// Exception Handler Function, if an Exception is Thrown
public void SaveData(MonitorConfiguration data)
{
try
{
_xmlserializer.WriteToFile(data, _dsFileNameNPath);
lock (_lock)
{
_RunTimeWatchdogXMLConfigurationData = data;
}
}
catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); }
}
///
/// Writes the XML Configuration Data that is currently stored in this Object out to Disk
///
/// Exception Handler Function, if an Exception is Thrown
public void SaveData()
{
try
{
_xmlserializer.WriteToFile(_RunTimeWatchdogXMLConfigurationData, _dsFileNameNPath);
}
catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); }
}
#endregion
}
}