using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; using NLog; namespace ATxCommon { public static class SystemChecks { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); /// <summary> /// Get the available physical memory in MB. /// </summary> /// <returns>Available physical memory in MB or -1 in case of an error.</returns> public static long GetFreeMemory() { try { var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem"); foreach (var mo in searcher.Get()) { var queryObj = (ManagementObject)mo; return Convert.ToInt64(queryObj["FreePhysicalMemory"]) / 1024; } } catch (Exception ex) { Log.Trace("Error in GetFreeMemory: {0}", ex.Message); } return -1; } /// <summary> /// Get the CPU usage in percent over all cores. /// </summary> /// <returns>CPU usage in percent or -1 if an error occured.</returns> public static int GetCpuUsage() { // TODO: fix bug #36 return 0; try { Log.Trace("Querying WMI for CPU load..."); var watch = Stopwatch.StartNew(); var queryString = "SELECT Name, PercentProcessorTime " + "FROM Win32_PerfFormattedData_PerfOS_Processor"; var opts = new EnumerationOptions { Timeout = new TimeSpan(0, 0, 10), ReturnImmediately = false, }; var searcher = new ManagementObjectSearcher("", queryString, opts); Int32 usageInt32 = -1; var managementObjects = searcher.Get(); if (managementObjects.Count == 0) { Log.Error("No objects returned from WMI!"); watch.Stop(); Log.Debug("WMI query took {0} ms.", watch.ElapsedMilliseconds); return -1; } Log.Debug("WMI query returned {0} objects.", managementObjects.Count); foreach (var mo in managementObjects) { var obj = (ManagementObject)mo; var usage = obj["PercentProcessorTime"]; var name = obj["Name"]; usageInt32 = Convert.ToInt32(usage); Log.Debug("CPU usage {1}: {0}", usageInt32, name); } watch.Stop(); Log.Debug("WMI query took {0} ms.", watch.ElapsedMilliseconds); return usageInt32; /* var cpuTimes = searcher.Get() .Cast<ManagementObject>() .Select(mo => new { Name = mo["Name"], Usage = mo["PercentProcessorTime"] } ) .ToList(); if (cpuTimes.Count == 0) return -1; Log.Trace("WMI query returned {0} objects.", cpuTimes.Count); // The '_Total' value represents the average usage across all cores, // and is the best representation of overall CPU usage var query = cpuTimes.Where(x => x.Name.ToString() == "_Total").Select(x => x.Usage); var cpuUsage = query.SingleOrDefault(); usageInt32 = Convert.ToInt32(cpuUsage); Log.Trace("CPU usage: {0}", usageInt32); return usageInt32; */ } catch (Exception ex) { Log.Trace("Error in GetCpuUsage: {0}", ex.Message); } Log.Trace("Failed querying CPU usage!"); return -1; } /// <summary> /// Validate the WMI query methods work correctly and create a summary or warning message. /// </summary> /// <returns>A summary with current readings from WMI or a warning message.</returns> public static string WmiSummary() { var failed = new List<string>(); var load = GetCpuUsage(); var free = GetFreeMemory(); var summary = $"CPU load: {load}\n" + $"Free system memory: {Conv.MegabytesToString(free)}\n"; Log.Warn(summary); if (load == -1) { failed.Add("CPU load"); } if (free == -1) { failed.Add("free RAM"); } if (failed.Count > 0) { summary = "*******************************************************\n" + "WARNING: Checking system parameters via WMI failed for:\n"; foreach (var property in failed) { summary += $" - {property}\n"; } summary += "\n" + "-------------------------------------------------------\n" + "Limits configured for these properties will be IGNORED!\n" + "-------------------------------------------------------\n\n"; Log.Error(summary); } return summary; } /// <summary> /// Get the free space of a drive in bytes. /// </summary> /// /// <param name="drive">The drive name, e.g. "c:".</param> /// <returns>Free space of a drive in bytes, zero if an error occured.</returns> public static long GetFreeDriveSpace(string drive) { try { var dInfo = new DriveInfo(drive); return dInfo.TotalFreeSpace; } catch (Exception ex) { // log this as an error which then also gets sent via email (if configured) and // let the rate-limiter take care of not flooding the admin with mails: Log.Error("Error in GetFreeDriveSpace({0}): {1}", drive, ex.Message); } return 0; } /// <summary> /// Check all configured disks for their free space and send a notification /// if necessary (depending on the configured delta time). /// </summary> public static string CheckFreeDiskSpace(List<Serializables.DriveToCheck> drives) { var msg = ""; foreach (var driveToCheck in drives) { var freeSpace = GetFreeDriveSpace(driveToCheck.DriveName); if (freeSpace >= driveToCheck.SpaceThreshold * Conv.GigaBytes) { Log.Trace("Drive [{0}] free space: {1}, above threshold ({2})", driveToCheck.DriveName, Conv.BytesToString(freeSpace), Conv.GigabytesToString(driveToCheck.SpaceThreshold)); continue; } msg += $"Drive [{driveToCheck.DriveName}] " + $"free space: {Conv.BytesToString(freeSpace)} " + $"(threshold: {Conv.GigabytesToString(driveToCheck.SpaceThreshold)})\n"; } return msg; } /// <summary> /// Compares all processes against the ProcessNames in the BlacklistedProcesses list. /// </summary> /// <returns>Returns the name of the first matching process, an empty string otherwise.</returns> public static string CheckForBlacklistedProcesses(List<string> processNames) { foreach (var running in Process.GetProcesses()) { try { foreach (var blacklisted in processNames) { if (running.ProcessName.ToLower().Equals(blacklisted)) { return blacklisted; } } } catch (Exception ex) { Log.Error("Error in checkProcesses(): {0}", ex.Message); } } return ""; } /// <summary> /// Check if a user is currently logged into Windows. /// /// WARNING: this DOES NOT ACCOUNT for users logged in via RDP!! /// </summary> /// See https://stackoverflow.com/questions/5218778/ for the RDP problem. public static bool NoUserIsLoggedOn() { var username = ""; try { var searcher = new ManagementObjectSearcher("SELECT UserName " + "FROM Win32_ComputerSystem"); var collection = searcher.Get(); username = (string)collection.Cast<ManagementBaseObject>().First()["UserName"]; } catch (Exception ex) { Log.Error("Error in getCurrentUsername(): {0}", ex.Message); } return username == ""; } } }