Mercurial > servermonitor
view ServerMonitor/Objects/Checks/FileCheck.cs @ 35:2ffb0bda7705
Increase wait time after system resume to begin running checks to prevent false positives.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Sat, 13 Jul 2019 12:18:21 -0400 |
parents | 88ca7e4fc023 |
children |
line wrap: on
line source
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text.RegularExpressions; namespace ServerMonitorApp { /// <summary>Checks file metadata on a remote server.</summary> [DisplayName("File check"), Description("Check file size or modified time"), DisplayWeight(12)] public class FileCheck : SshCheck { /// <summary>The command to execute. Must return the output format of GNU ls(1) with the long-iso time style.</summary> /// <remarks>Would be better to not rely on the output of ls.</remarks> public override string Command => string.Format(FileCommand, Regex.Replace(File, "^~", "$HOME")); /// <summary>The command to execute, with a placeholder of {0} for the file to check.</summary> protected string FileCommand { get { // Invert because POSIX says so. int timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).Hours * -1; // Set the date format to long-iso since it's easy to parse. // Set the time zone to the match the client time zone so comparisons are reliable. return "export TIME_STYLE=long-iso; export TZ=UTC" + timeZoneOffset + "; ls -ld \"{0}\""; } } /// <summary>The path of the file to check.</summary> public string File { get; set; } /// <summary>Whether the file size will be checked.</summary> public bool CheckFileSize { get; set; } /// <summary>Whether the file is expected to be less than or greater than a certain size.</summary> public bool FileSizeLessThan { get; set; } /// <summary>The size to compare the file against in bytes.</summary> public int FileSize { get; set; } /// <summary>The size to compare the file against in the selected size units.</summary> public double FileSizeInSelectedUnits { get => Math.Round(ConvertBytesToSelectedUnits(FileSize), 1); set => FileSize = ConvertSelectedUnitsToBytes(value); } /// <summary>The units of the file size.</summary> public SizeUnits FileSizeUnits { get; set; } /// <summary>Whether the file modified time will be checked.</summary> public bool CheckDateModified { get; set; } /// <summary>Whether the file is expected to be older than or newer than a certain date.</summary> public bool DateModifiedOlderThan { get; set; } /// <summary>The number of time units to compare then file modified time against.</summary> public double DateModified { get; set; } /// <summary>The units of the file date modified.</summary> public TimeUnits DateModifiedUnits { get; set; } public FileCheck() { // Set general SSH check settings for file checks. CheckExitCode = true; ExitCode = 0; } /// <summary>Processes the output of the file check command.</summary> protected override List<CheckResult> ProcessCommandResult(string output, int exitCode) { // Check for general SSH failures. List<CheckResult> results = base.ProcessCommandResult(output, exitCode); // If there was an error running the command, fail immediately. if (results.Any(r => r.Failed)) return results; // Make sure there is only a single line of output. if (output.Split('\n').Length > 1) { results.Add(Fail("ls output was more than one line: " + output)); } else { // Split the string into tokens on whitespace. string[] tokens = output.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); if (CheckFileSize) { // Check file size. if (int.TryParse(tokens[4], out int bytes)) { // Prepare a human-readable message with the comparison results. string message = string.Format("File size is {0} {1}", Math.Round(ConvertBytesToSelectedUnits(bytes), 1), FileSizeUnits); if ((bytes < FileSize && FileSizeLessThan) || (bytes > FileSize && !FileSizeLessThan)) results.Add(Pass(message)); else results.Add(Fail(message)); } else { results.Add(Fail("Unable to parse ls output as integer: " + tokens[4])); } } if (CheckDateModified) { // Check date modified. // Tokens[5] contains the date, tokens[6] contains the time. string dateString = tokens[5] + " " + tokens[6]; if (DateTime.TryParse(dateString, out DateTime modified)) { string message = string.Format("Last modified date is {0}", modified); // Determine how old the file is. The command output is in the client time zone. TimeSpan age = DateTime.Now.Subtract(modified); double ageCompare = DateModifiedUnits == TimeUnits.Minute ? age.TotalMinutes : DateModifiedUnits == TimeUnits.Hour ? age.TotalHours : age.TotalDays; if ((ageCompare > DateModified && DateModifiedOlderThan) || (ageCompare < DateModified && !DateModifiedOlderThan)) results.Add(Pass(message)); else results.Add(Fail(message)); } else { results.Add(Fail("Unable to parse ls output as date: " + dateString)); } } } return results; } /// <summary>Validates file check options.</summary> public override string Validate(bool saving = true) { string message = base.Validate(); if (File.IsNullOrEmpty()) message += "File is required." + Environment.NewLine; if (!CheckFileSize && !CheckDateModified) message += "At least one check must be enabled." + Environment.NewLine; if (CheckFileSize && FileSize < 0) message += "File size must be at least 0." + Environment.NewLine; if (CheckDateModified && DateModified <= 0) message += "Date modified must be greater than 0." + Environment.NewLine; return message; } /// <summary>Converts bytes to a different size unit.</summary> /// <param name="sizeInBytes">The size to convert in bytes.</param> /// <returns>The size in the units used by this file check.</returns> private double ConvertBytesToSelectedUnits(int sizeInBytes) { return sizeInBytes / Math.Pow(1024, (double)FileSizeUnits); } /// <summary>Converts a size in a different unit to bytes.</summary> /// <param name="sizeInSelectedUnits">The size to convert in the units used by this file check.</param> /// <returns>The size in bytes.</returns> private int ConvertSelectedUnitsToBytes(double sizeInSelectedUnits) { return (int)(sizeInSelectedUnits * Math.Pow(1024, (int)FileSizeUnits)); } } }