// // NtpqPeerMapper.cs // // Author: // Carsten Sonne Larsen // // Copyright (c) 2013 Carsten Sonne Larsen // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using Ntp.Analyzer.Log; using Ntp.Analyzer.Objects.Live; using Ntp.Data; using Ntp.System; namespace Ntp.Analyzer.Data.Live { /// /// Ntpq mapper parses status importLines from ntpq into objects. /// public sealed class NtpqPeerMapper : DataMapper { public NtpqPeerMapper (FileInfo file, LogBase log) : base(log) { this.file = file; } public NtpqPeerMapper (ServerName server, LogBase log) : base(log) { this.server = server; } public NtpqPeerMapper (string[] lines, LogBase log) : base(log) { this.lines = lines; } private readonly FileInfo file; private readonly string[] lines; private readonly ServerName server; private readonly Object locker = new Object (); private List entries; /// /// Read all data from ntpq in a sequential manner. /// /// The enumerator. public override IEnumerator GetEnumerator () { lock (locker) { Import (); return entries.GetEnumerator (); } } private void Import () { if (entries != null) return; entries = new List (); if (file != null) Import (file); else if (server != null) Import (server); else Import (lines); } /// /// Import the specified importLines. /// /// /// Lines to import. /// private void Import (IEnumerable importLines) { List linesToParse = new List (importLines); linesToParse.RemoveRange (0, 2); foreach (string line in linesToParse) { AssociationEntry entry = ParseLine (line); entries.Add (entry); } } /// /// Import the specified importFile. /// /// The import file. /// No filename specified for import importFile. private void Import (FileInfo importFile) { if (importFile == null) throw new ArgumentException ("No filename specified for import importFile."); ReadFromStream (new StreamReader (importFile.OpenRead ())); } /// /// Import data from the specified importServer. /// /// Server to import from. private void Import (ServerName importServer) { ShellCommand command = new ShellCommand ( "ntpq", "-np " + importServer, Log) { ErrorMessage = "Unexpected error while reading peer stats" }; StreamReader reader = command.Execute (); if (reader == null) return; ReadFromStream (reader); } private void ReadFromStream (StreamReader reader) { // Skip header reader.ReadLine (); reader.ReadLine (); while (reader.Peek () != -1) { string line = reader.ReadLine (); if (line == null || line.Trim () == String.Empty) continue; AssociationEntry entry = ParseLine (line); entries.Add (entry); } } /// /// Parses the line. /// /// /// The line parsed to an object. /// /// /// Line to parse. /// /// /// 1 2 3 4 5 6 7 /// 01234567890123456789012345678901234567890123456789012345678901234567890123456789 /// remote refid st t when poll reach delay offset jitter /// ============================================================================== /// #guard.net.innom 193.190.230.66 2 u 956 1024 376 1.753 0.603 0.450 /// private AssociationEntry ParseLine (string line) { char state = line [0]; string remote = line.Substring (1, 16).Trim (); string refid = line.Substring (17, 16).Trim (); int stratus = Convert.ToInt32 (line.Substring (33, 2).Trim ()); char t = line [36]; int lastPoll = CalcLastPoll (line); int pollFrequency = Convert.ToInt32 (line.Substring (43, 4).Trim ()); int reach = Convert.ToInt32 (line.Substring (49, 3).Trim ()); double delay = Convert.ToDouble (line.Substring (53, 8).Trim ()); double offset = Convert.ToDouble (line.Substring (61, 9).Trim ()); double jitter = Convert.ToDouble (line.Substring (70, 8).Trim ()); AssociationEntry entry = new AssociationEntry ( state, remote, refid, stratus, t, lastPoll, pollFrequency, reach, delay, offset, jitter); return entry; } /// /// Calculates the value of the last poll. /// /// /// The last poll. /// /// /// Line to parse value from. /// private int CalcLastPoll (string line) { string lastPollString = line.Substring (38, 4); char lastPollUnit = lastPollString.Substring (lastPollString.Length - 1, 1) [0]; string lastPollValue = lastPollString.Substring (0, lastPollString.Length - 1).Trim (); switch (lastPollUnit) { case 'm': return Convert.ToInt32 (lastPollValue) * 60; case 'h': return Convert.ToInt32 (lastPollValue) * 60 * 60; case 'd': return Convert.ToInt32 (lastPollValue) * 60 * 60 * 24; case '-': return -1; default: return Convert.ToInt32 (lastPollString); } } } }