/contrib/ntp/scripts/ntpsweep.in

https://bitbucket.org/freebsd/freebsd-head/ · Autoconf · 300 lines · 227 code · 30 blank · 43 comment · 45 complexity · 120a942f50b5e246d652cd2b86b27ea1 MD5 · raw file

  1. #! @PATH_PERL@ -w
  2. #
  3. # $Id$
  4. #
  5. # DISCLAIMER
  6. #
  7. # Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.
  8. #
  9. # Permission to use, copy, modify and distribute this software and its
  10. # documentation for any purpose and without fee is hereby granted,
  11. # provided that the above copyright notice appears in all copies and
  12. # that both the copyright notice and this permission notice appear in
  13. # supporting documentation. This software is supported as is and without
  14. # any express or implied warranties, including, without limitation, the
  15. # implied warranties of merchantability and fitness for a particular
  16. # purpose. The name Origin B.V. must not be used to endorse or promote
  17. # products derived from this software without prior written permission.
  18. #
  19. # Hans Lambermont <ntpsweep@lambermont.dyndns.org>
  20. require 5.0; # But actually tested on 5.004 ;)
  21. use Getopt::Long; # GetOptions()
  22. use strict;
  23. my $version = 1.3;
  24. (my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
  25. # Hardcoded paths/program names
  26. my $ntpdate = "ntpdate";
  27. my $ntpq = "ntpq";
  28. # no STDOUT buffering
  29. $| = 1;
  30. my ($help, $single_host, $showpeers, $maxlevel, $strip, $askversion);
  31. my $res = GetOptions("help!" => \$help,
  32. "host=s" => \$single_host,
  33. "peers!" => \$showpeers,
  34. "maxlevel=s" => \$maxlevel,
  35. "strip=s" => \$strip,
  36. "version!" => \$askversion);
  37. if ($askversion) {
  38. print("$version\n");
  39. exit 0;
  40. }
  41. if ($help || ((@ARGV != 1) && !$single_host)) {
  42. warn <<EOF;
  43. This is $program, version $version
  44. Copyright (C) 1999,2000 Hans Lambermont and Origin B.V. Disclaimer inside.
  45. Usage:
  46. $program [--help|--peers|--strip <string>|--maxlevel <level>|--version] \\
  47. <file>|[--host <hostname>]
  48. Description:
  49. $program prints per host given in <file> the NTP stratum level, the
  50. clock offset in seconds, the daemon version, the operating system and
  51. the processor. Optionally recursing through all peers.
  52. Options:
  53. --help
  54. Print this short help text and exit.
  55. --version
  56. Print version ($version) and exit.
  57. <file>
  58. Specify hosts file. File format is one hostname or ip number per line.
  59. Lines beginning with # are considered as comment.
  60. --host <hostname>
  61. Speficy a single host, bypassing the need for a hosts file.
  62. --peers
  63. Recursively list all peers a host synchronizes to.
  64. An '= ' before a peer means a loop. Recursion stops here.
  65. --maxlevel <level>
  66. Traverse peers up to this level (4 is a reasonable number).
  67. --strip <string>
  68. Strip <string> from hostnames.
  69. Examples:
  70. $program myhosts.txt --strip .foo.com
  71. $program --host some.host --peers --maxlevel 4
  72. EOF
  73. exit 1;
  74. }
  75. my $hostsfile = shift;
  76. my (@hosts, @known_hosts);
  77. my (%known_host_info, %known_host_peers);
  78. sub read_hosts()
  79. {
  80. local *HOSTS;
  81. open (HOSTS, $hostsfile) ||
  82. die "$program: FATAL: unable to read $hostsfile: $!\n";
  83. while (<HOSTS>) {
  84. next if /^\s*(#|$)/; # comment/empty
  85. chomp;
  86. push(@hosts, $_);
  87. }
  88. close(HOSTS);
  89. }
  90. # translate IP to hostname if possible
  91. sub ip2name {
  92. my($ip) = @_;
  93. my($addr, $name, $aliases, $addrtype, $length, @addrs);
  94. $addr = pack('C4', split(/\./, $ip));
  95. ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr($addr, 2);
  96. if ($name) {
  97. # return lower case name
  98. return("\L$name");
  99. } else {
  100. return($ip);
  101. }
  102. }
  103. # item_in_list($item, @list): returns 1 if $item is in @list, 0 if not
  104. sub item_in_list {
  105. my($item, @list) = @_;
  106. my($i);
  107. foreach $i (@list) {
  108. return 1 if ($item eq $i);
  109. }
  110. return 0;
  111. }
  112. sub scan_host($;$;$) {
  113. my($host, $level, @trace) = @_;
  114. my $stratum = 0;
  115. my $offset = 0;
  116. my $daemonversion = "";
  117. my $system = "";
  118. my $processor = "";
  119. my @peers;
  120. my $known_host = 0;
  121. if (&item_in_list($host, @known_hosts)) {
  122. $known_host = 1;
  123. } else {
  124. # ntpdate part
  125. open(NTPDATE, "$ntpdate -bd $host 2>/dev/null |") ||
  126. die "Cannot open ntpdate pipe: $!\n";
  127. while (<NTPDATE>) {
  128. /^stratum\s+(\d+).*$/ && do {
  129. $stratum = $1;
  130. };
  131. /^offset\s+([0-9.-]+)$/ && do {
  132. $offset = $1;
  133. };
  134. }
  135. close(NTPDATE);
  136. # got answers ? If so, go on.
  137. if ($stratum) {
  138. # ntpq part
  139. my $ntpqparams = "-c 'rv 0 processor,system,daemon_version'";
  140. open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
  141. die "Cannot open ntpq pipe: $!\n";
  142. while (<NTPQ>) {
  143. /daemon_version="(.*)"/ && do {
  144. $daemonversion = $1;
  145. };
  146. /system="([^"]*)"/ && do {
  147. $system = $1;
  148. };
  149. /processor="([^"]*)"/ && do {
  150. $processor = $1;
  151. };
  152. }
  153. close(NTPQ);
  154. # Shorten daemon_version string.
  155. $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//;
  156. $daemonversion =~ s/version=//;
  157. $daemonversion =~ s/(x|)ntpd //;
  158. $daemonversion =~ s/(\(|\))//g;
  159. $daemonversion =~ s/beta/b/;
  160. $daemonversion =~ s/multicast/mc/;
  161. # Shorten system string
  162. $system =~ s/UNIX\///;
  163. $system =~ s/RELEASE/r/;
  164. $system =~ s/CURRENT/c/;
  165. # Shorten processor string
  166. $processor =~ s/unknown//;
  167. }
  168. # got answers ? If so, go on.
  169. if ($daemonversion) {
  170. # ntpq again, find out the peers this time
  171. if ($showpeers) {
  172. my $ntpqparams = "-pn";
  173. open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") ||
  174. die "Cannot open ntpq pipe: $!\n";
  175. while (<NTPQ>) {
  176. /^No association ID's returned$/ && do {
  177. last;
  178. };
  179. /^ remote/ && do {
  180. next;
  181. };
  182. /^==/ && do {
  183. next;
  184. };
  185. /^( |x|\.|-|\+|#|\*|o)([^ ]+)/ && do {
  186. push(@peers, ip2name($2));
  187. next;
  188. };
  189. print "ERROR: $_";
  190. }
  191. close(NTPQ);
  192. }
  193. }
  194. # Add scanned host to known_hosts array
  195. push(@known_hosts, $host);
  196. if ($stratum) {
  197. $known_host_info{$host} = sprintf("%2d %9.3f %-11s %-12s %s",
  198. $stratum, $offset, substr($daemonversion,0,11),
  199. substr($system,0,12), substr($processor,0,9));
  200. } else {
  201. # Stratum level 0 is consider invalid
  202. $known_host_info{$host} = sprintf(" ?");
  203. }
  204. $known_host_peers{$host} = [@peers];
  205. }
  206. if ($stratum || $known_host) { # Valid or known host
  207. my $printhost = ' ' x $level . $host;
  208. # Shorten host string
  209. if ($strip) {
  210. $printhost =~ s/$strip//;
  211. }
  212. # append number of peers in brackets if requested and valid
  213. if ($showpeers && ($known_host_info{$host} ne " ?")) {
  214. $printhost .= " (" . @{$known_host_peers{$host}} . ")";
  215. }
  216. # Finally print complete host line
  217. printf("%-32s %s\n",
  218. substr($printhost,0,32), $known_host_info{$host});
  219. if ($showpeers && (eval($maxlevel ? $level < $maxlevel : 1))) {
  220. my $peer;
  221. push(@trace, $host);
  222. # Loop through peers
  223. foreach $peer (@{$known_host_peers{$host}}) {
  224. if (&item_in_list($peer, @trace)) {
  225. # we've detected a loop !
  226. $printhost = ' ' x ($level + 1) . "= " . $peer;
  227. # Shorten host string
  228. if ($strip) {
  229. $printhost =~ s/$strip//;
  230. }
  231. printf("%-32s %s\n",
  232. substr($printhost,0,32));
  233. } else {
  234. if (substr($peer,0,3) ne "127") {
  235. &scan_host($peer, $level + 1, @trace);
  236. }
  237. }
  238. }
  239. }
  240. } else { # We did not get answers from this host
  241. my $printhost = ' ' x $level . $host;
  242. # Shorten host string
  243. if ($strip) {
  244. $printhost =~ s/$strip//;
  245. }
  246. printf("%-32s ?\n", substr($printhost,0,32));
  247. }
  248. }
  249. sub scan_hosts()
  250. {
  251. my $host;
  252. for $host (@hosts) {
  253. my @trace;
  254. push(@trace, $host);
  255. scan_host($host, 0, @trace);
  256. }
  257. }
  258. # Main program
  259. if ($single_host) {
  260. push(@hosts, $single_host);
  261. } else {
  262. &read_hosts($hostsfile);
  263. }
  264. # Print header
  265. print <<EOF;
  266. Host st offset(s) version system processor
  267. --------------------------------+--+---------+-----------+------------+---------
  268. EOF
  269. &scan_hosts();
  270. exit 0;