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 } }