summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/scripts')
-rw-r--r--contrib/ntp/scripts/Makefile.am1
-rw-r--r--contrib/ntp/scripts/Makefile.in210
-rw-r--r--contrib/ntp/scripts/README30
-rw-r--r--contrib/ntp/scripts/calc_tickadj38
-rwxr-xr-xcontrib/ntp/scripts/checktime79
-rw-r--r--contrib/ntp/scripts/fixautomakedepsmagic28
-rwxr-xr-xcontrib/ntp/scripts/hpadjtime.sh18
-rw-r--r--contrib/ntp/scripts/mkver.in31
-rw-r--r--contrib/ntp/scripts/monitoring/README154
-rw-r--r--contrib/ntp/scripts/monitoring/loopwatch.config.SAMPLE89
-rw-r--r--contrib/ntp/scripts/monitoring/lr.pl145
-rw-r--r--contrib/ntp/scripts/monitoring/ntp.pl478
-rw-r--r--contrib/ntp/scripts/monitoring/ntploopstat457
-rw-r--r--contrib/ntp/scripts/monitoring/ntploopwatch1667
-rw-r--r--contrib/ntp/scripts/monitoring/ntptrap463
-rw-r--r--contrib/ntp/scripts/monitoring/timelocal.pl77
-rwxr-xr-xcontrib/ntp/scripts/ntp-groper95
-rwxr-xr-xcontrib/ntp/scripts/ntp-restart9
-rw-r--r--contrib/ntp/scripts/ntpver.in7
-rwxr-xr-xcontrib/ntp/scripts/plot_summary.pl333
-rw-r--r--contrib/ntp/scripts/rc1/postinstall2
-rw-r--r--contrib/ntp/scripts/rc1/preinstall6
-rw-r--r--contrib/ntp/scripts/rc1/preremove4
-rw-r--r--contrib/ntp/scripts/rc1/prototype19
-rw-r--r--contrib/ntp/scripts/rc1/xntp29
-rw-r--r--contrib/ntp/scripts/rc2/local.ntpd64
-rw-r--r--contrib/ntp/scripts/stats.ulrich.patches1003
-rw-r--r--contrib/ntp/scripts/stats/README39
-rw-r--r--contrib/ntp/scripts/stats/README.stats246
-rw-r--r--contrib/ntp/scripts/stats/README.timecodes149
-rwxr-xr-xcontrib/ntp/scripts/stats/clock.awk431
-rwxr-xr-xcontrib/ntp/scripts/stats/dupe.awk8
-rwxr-xr-xcontrib/ntp/scripts/stats/ensemble.S5
-rwxr-xr-xcontrib/ntp/scripts/stats/ensemble.awk17
-rwxr-xr-xcontrib/ntp/scripts/stats/etf.S15
-rwxr-xr-xcontrib/ntp/scripts/stats/etf.awk19
-rwxr-xr-xcontrib/ntp/scripts/stats/itf.S5
-rwxr-xr-xcontrib/ntp/scripts/stats/itf.awk19
-rwxr-xr-xcontrib/ntp/scripts/stats/loop.S7
-rwxr-xr-xcontrib/ntp/scripts/stats/loop.awk45
-rwxr-xr-xcontrib/ntp/scripts/stats/loop_summary2
-rwxr-xr-xcontrib/ntp/scripts/stats/peer.awk68
-rwxr-xr-xcontrib/ntp/scripts/stats/psummary.awk82
-rwxr-xr-xcontrib/ntp/scripts/stats/summary.sh88
-rwxr-xr-xcontrib/ntp/scripts/stats/tdata.S5
-rwxr-xr-xcontrib/ntp/scripts/stats/tdata.awk45
-rw-r--r--contrib/ntp/scripts/summary.pl357
-rw-r--r--contrib/ntp/scripts/support/README73
-rw-r--r--contrib/ntp/scripts/support/bin/monl213
-rw-r--r--contrib/ntp/scripts/support/bin/mvstats23
-rw-r--r--contrib/ntp/scripts/support/conf/hp300.hp3000
-rw-r--r--contrib/ntp/scripts/support/conf/hp700.hp7000
-rw-r--r--contrib/ntp/scripts/support/conf/hp700.hp700.faui470
-rw-r--r--contrib/ntp/scripts/support/conf/hp800.hp8000
-rw-r--r--contrib/ntp/scripts/support/conf/ntp.conf3
-rw-r--r--contrib/ntp/scripts/support/conf/sun3.sun30
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4.faui010
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4.faui100
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4.faui450
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4c0
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4c.Lucifer0
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4m0
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4m.faui420
-rw-r--r--contrib/ntp/scripts/support/conf/sun4.sun4m.faui45m0
-rw-r--r--contrib/ntp/scripts/support/conf/tickconf19
-rw-r--r--contrib/ntp/scripts/support/etc/cron18
-rw-r--r--contrib/ntp/scripts/support/etc/crontab8
-rw-r--r--contrib/ntp/scripts/support/etc/install67
-rw-r--r--contrib/ntp/scripts/support/etc/rc198
-rw-r--r--contrib/ntp/scripts/support/etc/setup72
70 files changed, 7882 insertions, 0 deletions
diff --git a/contrib/ntp/scripts/Makefile.am b/contrib/ntp/scripts/Makefile.am
new file mode 100644
index 0000000..c3bb684
--- /dev/null
+++ b/contrib/ntp/scripts/Makefile.am
@@ -0,0 +1 @@
+noinst_SCRIPTS = mkver ntpver
diff --git a/contrib/ntp/scripts/Makefile.in b/contrib/ntp/scripts/Makefile.in
new file mode 100644
index 0000000..008640b
--- /dev/null
+++ b/contrib/ntp/scripts/Makefile.in
@@ -0,0 +1,210 @@
+# Makefile.in generated automatically by automake 1.4a from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_FLAG =
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_alias = @build_alias@
+build_triplet = @build@
+host_alias = @host_alias@
+host_triplet = @host@
+target_alias = @target_alias@
+target_triplet = @target@
+AMTAR = @AMTAR@
+AMTARFLAGS = @AMTARFLAGS@
+AWK = @AWK@
+CC = @CC@
+CFLAGS = @CFLAGS@
+CHUTEST = @CHUTEST@
+CLKTEST = @CLKTEST@
+CPP = @CPP@
+DCFD = @DCFD@
+LDFLAGS = @LDFLAGS@
+LIBPARSE = @LIBPARSE@
+LIBRSAREF = @LIBRSAREF@
+LN_S = @LN_S@
+MAKEINFO = @MAKEINFO@
+MAKE_ADJTIMED = @MAKE_ADJTIMED@
+MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@
+MAKE_LIBPARSE = @MAKE_LIBPARSE@
+MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@
+MAKE_LIBRSAREF = @MAKE_LIBRSAREF@
+MAKE_NTPTIME = @MAKE_NTPTIME@
+MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@
+MAKE_TICKADJ = @MAKE_TICKADJ@
+PACKAGE = @PACKAGE@
+PATH_SH = @PATH_SH@
+PROPDELAY = @PROPDELAY@
+RANLIB = @RANLIB@
+RSAREF = @RSAREF@
+TESTDCF = @TESTDCF@
+U = @U@
+VERSION = @VERSION@
+
+
+noinst_SCRIPTS = mkver ntpver
+subdir = scripts
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES = mkver ntpver
+SCRIPTS = $(noinst_SCRIPTS)
+
+DIST_SOURCES =
+DIST_COMMON = README Makefile.am Makefile.in mkver.in ntpver.in
+
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+GZIP_ENV = --best
+all: all-redirect
+.SUFFIXES:
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+mkver: $(top_builddir)/config.status mkver.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+ntpver: $(top_builddir)/config.status ntpver.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+tags: TAGS
+TAGS:
+
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+distdir: $(DISTFILES)
+ here=`cd $(top_builddir) && pwd`; \
+ top_distdir=`cd $(top_distdir) && pwd`; \
+ distdir=`cd $(distdir) && pwd`; \
+ cd $(top_srcdir) \
+ && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu scripts/Makefile
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am:
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am:
+uninstall: uninstall-am
+all-am: Makefile $(SCRIPTS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_STRIP_FLAG=-s install
+installdirs:
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: tags distdir info-am info dvi-am dvi check check-am \
+installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all install-strip installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/ntp/scripts/README b/contrib/ntp/scripts/README
new file mode 100644
index 0000000..37442f1
--- /dev/null
+++ b/contrib/ntp/scripts/README
@@ -0,0 +1,30 @@
+README file for directory ./scripts of the NTP Version 4 distribution
+
+This directory contains shell and perl script files for the configuration,
+monitoring and support of NTP installations. See the README and RELNOTES
+files in the parent directory for directions on how to use these files.
+
+calc_tickadj Calculates "optimal" value for tick given ntp.drift file
+
+mkver.in script to create new version numbers for all sources
+
+monitoring directory containing perl scripts useful for monitoring
+ operations
+
+rc1 start/stop scripts for NTP
+
+rc2 start/stop script for NTP
+
+ntp-groper script useful for reaching out and rattling the cages of
+ NTP peers to see if animals are inside the bars
+
+ntp-restart script useful for killing and restarting the NTP daemon
+
+ntpver What version of the NTP daemon is running?
+
+stats directory containing awk and shell scripts useful for
+ maintaining statistics summaries of clockstats, loopstats
+ and peerstats files
+
+support directory containing shell and perl scripts useful for
+ configuration and monitoring of NTP subnets
diff --git a/contrib/ntp/scripts/calc_tickadj b/contrib/ntp/scripts/calc_tickadj
new file mode 100644
index 0000000..f7a9f9f
--- /dev/null
+++ b/contrib/ntp/scripts/calc_tickadj
@@ -0,0 +1,38 @@
+#! /usr/local/bin/perl
+#
+# drift of 104.8576 -> +1 tick. Base of 10000 ticks.
+#
+# 970306 HMS Deal with nanoseconds. Fix sign of adjustments.
+
+$df="/etc/ntp.drift";
+# Assumes a 100Hz box with "tick" of 10000
+# Someday, we might call "tickadj" for better values...
+$base=10000; # tick: 1,000,000 / HZ
+$cvt=104.8576; # 2 ** 20 / $base
+$v1=0.;
+$v2="";
+
+if (open(DF, $df))
+ {
+ if ($_=<DF>)
+ {
+ ($v1, $v2) = split;
+ }
+
+ while ($v1 < 0)
+ {
+ $v1 += $cvt;
+ $base--;
+ }
+
+ while ($v1 > $cvt)
+ {
+ $v1 -= $cvt;
+ $base++;
+ }
+ }
+
+printf("%.3f (drift)\n", $v1);
+
+printf("%d usec; %d nsec\n", $base, ($base + ($v1/$cvt)) * 1000);
+
diff --git a/contrib/ntp/scripts/checktime b/contrib/ntp/scripts/checktime
new file mode 100755
index 0000000..850e2ec
--- /dev/null
+++ b/contrib/ntp/scripts/checktime
@@ -0,0 +1,79 @@
+#!/usr/local/bin/perl
+#!/usr/local/bin/perl -d
+#
+# This script compares the time of several machines with the
+# time on the local host.
+#
+# Use or modify it as you wish.
+#
+# As the original author is only expecting 14 minutes of fame,
+# leaving his name attached would be appreciated.
+#
+# R. Gary Cutbill <rgary@chrysalis.com>
+# 21 April 1999
+#
+$tol=2.0;
+$|=1;
+print "Time Check";
+
+open(HOSTS,"ypcat hosts.byaddr |"); # get a list of hosts from the yp server.
+
+while ($line=<HOSTS>) { # loop for each host getting the offset compared to localhost
+ ($addr,$host,$aliases)=split(/\s+/,$line,3);
+ $res=`/usr/local/bin/ntptrace -m 1 -r 1 -t 1 $host`;
+ print ".";
+ chop $res;
+ push (@results,$res);
+}
+print "\n";
+
+
+#
+# Sort the list of hosts, and print out there offsets
+# from the local host.
+#
+@list=sort appropriately @results;
+foreach $i ( @list ) {
+
+ @dargs=split(/\s+/,$i);
+ if ( $dargs[1] eq "\*Timeout\*" ) {
+ print "$i\n";
+ chop $dargs[0];
+ push(@down,$dargs[0]);
+ } else {
+ printf "%-25s %7s %3s %6s %10s %5s %8s %8s\n",@dargs;
+ if ( ( $dargs[4] > $tol ) || ( $dargs[4] < -$tol ) ) {
+ chop $dargs[0];
+ push(@toofarout,$dargs[0]); }
+ }
+}
+#
+# When the above list finishes, hosts that are different by +/- $tol (two seconds)
+# are in @toofarout. Hosts that are down are in @down. They are treated the same
+# way here, but you might want to do something different depending on your site.
+#
+# print a set of suggested rsh commands to run on the hosts that
+# don't have "good" time. "restartntp" is left as an excersize to the reader.
+# I usually use it to kill a running xntpd, ntpdate some server, and the start xntp
+# again.
+#
+print "\nConsider:\n";
+foreach $i ( (@down,@toofarout) ) {
+ print " rsh $i sudo restartntp\n";
+}
+
+
+#
+# sort the results from the list. First by stratum, then by time deviation
+# Put hosts that didn't respond (timed out) on the bottom.
+#
+sub appropriately {
+ @af=split(/\s+/,$a);
+ @bf=split(/\s+/,$b);
+ $aba= ($af[4]<0)?-$af[4]:$af[4];
+ $abb= ($bf[4]<0)?-$bf[4]:$bf[4];
+
+ ( $af[1] ne $bf[1] ) ? $bf[1] cmp $af[1] :
+ ( ( $af[2] != $bf[2] ) ? ( $bf[2] <=> $af[2] ) :
+ ( ( $aba != $abb ) ? ( $abb <=> $aba ) : ($af[0] cmp $bf[0] ) ) );
+}
diff --git a/contrib/ntp/scripts/fixautomakedepsmagic b/contrib/ntp/scripts/fixautomakedepsmagic
new file mode 100644
index 0000000..ec82bba
--- /dev/null
+++ b/contrib/ntp/scripts/fixautomakedepsmagic
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+prog=`basename $0`
+
+
+t=/tmp/$prog.$$
+
+trap 'rm -f ${t} ; exit 1' 1 3 15
+
+while [ $# -gt 0 ]; do
+ f=$1
+ shift
+ sed -e '/^DEPS_MAGIC :=/,/^-include \$/s/^/#/' $f > $t
+ c="diff $f $t"
+ echo $c
+ $c
+ tstatus=$?
+ if [ $tstatus = 0 ]; then
+ echo "$prog":" $f not modified"
+ elif [ ! -w $f ]; then
+ echo "$prog":" $f not not writable"
+ else
+ c="cp $t $f"
+ echo $c
+ $c
+ fi
+ rm -f $t
+done
diff --git a/contrib/ntp/scripts/hpadjtime.sh b/contrib/ntp/scripts/hpadjtime.sh
new file mode 100755
index 0000000..3de2a40
--- /dev/null
+++ b/contrib/ntp/scripts/hpadjtime.sh
@@ -0,0 +1,18 @@
+#! /bin/sh
+val=1
+if [ -f /bin/uname -o -f /usr/bin/uname ]; then
+ set `uname -a | tr '[A-Z]' '[a-z]'`
+ case "$1" in
+ hp-ux) case "$3" in
+ *.10.*) val=1 ;;
+ *.09.03 | *.09.10) case "$5" in
+ 9000/3*) val=1 ;;
+ *) val=0 ;;
+ esac ;;
+ *) val=0 ;;
+ esac
+ ;;
+ *)
+ esac
+fi
+exit $val
diff --git a/contrib/ntp/scripts/mkver.in b/contrib/ntp/scripts/mkver.in
new file mode 100644
index 0000000..79d83f8
--- /dev/null
+++ b/contrib/ntp/scripts/mkver.in
@@ -0,0 +1,31 @@
+#!@PATH_SH@
+PROG=${1-UNKNOWN}
+
+ConfStr="$PROG"
+
+case "@LIBRSAREF@" in
+ '') ;;
+ *) ConfStr="$ConfStr RSAREF" ;;
+esac
+
+ConfStr="$ConfStr @VERSION@ `date`"
+
+if [ ! -f .version ]; then
+ echo 0 > .version
+fi
+RUN="`cat .version`"
+RUN="`expr $RUN + 1`"
+echo $RUN > .version
+
+ConfStr="$ConfStr (${RUN})"
+
+echo "Version <${ConfStr}>";
+
+rm -f version.c
+cat > version.c << -EoF-
+/*
+ * version file for $PROG
+ */
+#include <config.h>
+const char * Version = "${ConfStr}";
+-EoF-
diff --git a/contrib/ntp/scripts/monitoring/README b/contrib/ntp/scripts/monitoring/README
new file mode 100644
index 0000000..fa8ad8b
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/README
@@ -0,0 +1,154 @@
+This directory contains support for monitoring the local clock of xntp daemons.
+
+WARNING: The scripts and routines contained in this directory are bete realease!
+ Do not depend on their correct operation. They are, however, in regular
+ use at University of Erlangen-Nuernberg. No severe problems are known
+ for this code.
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+PLEASE THINK TWICE BEFORE STARTING MONITORING REMOTE XNTP DEAMONS !!!!
+MONITORING MAY INCREASE THE LOAD OF THE DEAMON MONITORED AND MAY
+INCREASE THE NETWORK LOAD SIGNIFICANTLY
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+Files are:
+
+README:
+ This file
+
+ntptrap:
+ perl script to log ntp mode 6 trap messages.
+
+ It sends a set_trap request to each server given and dumps the
+ trap messages received. It handles refresh of set_trap.
+ Currently it handles only NTP V2, however the NTP V3 servers
+ also accept v2 requests. It will not interpret v3 system and peer
+ stati correctly.
+
+ usage:
+ ntptrap [-n] [-p <port>] [-l <debug-output>] servers...
+
+ -n: do not send set_trap requests
+
+ port: port to listen for responses
+ useful if you have a configured trap
+
+ debug-output: file to write trace output to (for debugging)
+
+ This script convinced me that ntp trap messages are only of
+ little use.
+
+ntploopstat:
+ perl script to gather loop info statistics from xntpd via mode 7
+ LOOP_INFO requests.
+
+ This script collects data to allow monitoring of remote xntp servers
+ where it is not possible to directly access the loopstats file
+ produced by xntpd itself. Of course, it can be used to sample
+ a local server if it is not configured to produce a loopstats file.
+
+ Please note, this program poses a high load on the server as
+ a communication takes place every delay seconds ! USE WITH CARE !
+
+ usage:
+ ntploopstat [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+
+ delay: number of seconds to wait between samples
+ default: 60 seconds
+ timeout: number of seconds to wait for reply
+ default 12 seconds
+ logfile: file to log samples to
+ default: loopstats:<ntpserver>:
+ (note the trailing colon)
+ This name actually is a prefix.
+ The file name is dynamically derived by appending
+ the name of the month the sample belongs to.
+ Thus all samples of a month end up in the same file.
+
+ the format of the files generated is identical to the format used by
+ xntpd with the loopstats file:
+ MJD <seconds since midnight UTC> offset frequency compliance
+
+ if a timeout occurs the next sample is tried after delay/2 seconds
+
+ The script will terminate after MAX_FAIL (currently 60) consecutive errors.
+ Errors are counted for:
+ - error on send call
+ - error on select call
+ - error on recv call
+ - short packet received
+ - bad packet
+ - error on open for logfile
+
+ntploopwatch:
+ perl script to display loop filter statistics collected by ntploopstat
+ or dumped directly by xntpd.
+
+ Gnuplot is used to produce a graphical representation of the sample
+ values, that have been preprocessed and analysed by this script.
+
+ It can either be called to produce a printout of specific data set or
+ used to continously monitor the values. Monitoring is achieved by
+ periodically reprocessing the logfiles, which are updated regularly
+ either by a running ntploopstat process or by the running xntpd.
+
+ usage:
+ to watch statistics permanently:
+ ntploopwatch [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+
+ to get a single print out specify also
+ -P<printer> [-s<samples>]
+ [-S <start-time>] [-E <end-time>]
+ [-O <MaxOffs>] [-o <MinOffs>]
+
+ level: level of verbosity for debugging
+ config-file: file to read configurable settings from
+ On each iteration it is checked and reread
+ if it has been changed
+ default: loopwatch.config
+ working-dir: specify working directory for process, affects
+ interpretation of relative file names
+
+ All other flags are only useful with printing plots, as otherwise
+ command line values would be replaced by settings from the config file.
+
+ printer: specify printer to print plot
+ BSD print systems semantics apply; if printer is omitted
+ the name "ps" is used; plots are prepared using
+ PostScript, thus the printer should best accept
+ postscript input
+
+ For the following see also the comments in loopwatch.config.SAMPLE
+
+ samples: use last # samples from input data
+ start-time: ignore input samples before this date
+ end-time: ignore input samples after this date
+ if both start-time and end-time are specified
+ a given samples value is ignored
+ MaxOffs:
+ MinOffs: restrict value range
+
+loopwatch.config.SAMPLE:
+ sample config file for ntploopwatch
+ each configurable option is explained there
+
+lr.pl:
+ linear regression package used by ntploopwatch to compute
+ linear approximations for frequency and offset values
+ within display range
+
+timelocal.pl:
+ used during conversion of ISO_DATE_TIME values specified in loopwatch
+ config files to unix epoch values (seconds since 1970-01-01_00:00_00 UTC)
+
+ A version of this file is distributed with perl-4.x, however,
+ it has a bug related to dates crossing 1970, causing endless loops..
+ The version contained here has been fixed.
+
+ntp.pl:
+ perl support for ntp v2 mode 6 message handling
+ WARNING: This code is beta level - it triggers a memory leak;
+ as for now it is not quite clear, wether this is caused by a
+ bug in perl or by bad usage of perl within this script.
+
diff --git a/contrib/ntp/scripts/monitoring/loopwatch.config.SAMPLE b/contrib/ntp/scripts/monitoring/loopwatch.config.SAMPLE
new file mode 100644
index 0000000..8cefea3
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/loopwatch.config.SAMPLE
@@ -0,0 +1,89 @@
+# sample configuration and control file for ntploowatch
+#
+# delay: sampling interval in seconds
+delay=60
+# samples: use only last # samples
+samples=600
+# DO NOT USE srcprefix in shared config files
+# srcprefix: name of file to read samples from
+# current time suffix (month name) is appended
+# defaults to "./var@$STATHOST/loopstats."
+# The string "$STATHOST"is replaced by the name of the host
+# being monitored
+#srcprefix=./var@$STATHOST/loopstats.
+#
+# showoffs: yes/no control display of offset values
+showoffs=yes
+#
+# showfreq: yes/no control display of frequency values
+showfreq=yes
+#
+# showcmpl: yes/no control display of compliance values
+showcmpl=no
+#
+# showoreg: yes/no control display of linear regression of offset values
+showoreg=no
+#
+# showfreg: yes/no control display of linear regression of frequency values
+showfreg=no
+#
+# timebase: dynamic/ISO_DATE_TIME point of zero for linear regression
+# ISO_DATE_TIME: yyyy-mm-dd_hh:mm:ss.ms
+# values are interpreted using local time zone
+# parts omitted from front default to current date/time
+# parts omitted from end default to lowest permitted values
+# to get aa:bb being interpreted as minutes:seconds use aa:bb.0
+# for dynamic '00:00:00.0 of current day' is used
+timebase=dynamic
+#
+# freqbase: dynamic/<baseval>
+# if a number is given, subtract this from sampling values for display
+# if dynamic is selected, freqbase is adjusted to fit into the range of
+# offset values
+freqbase=dynamic
+#
+# cmplscale: dynamic/<scaling>
+# if a number is given, the sampling values are divided by this number
+# if dynamic is selected, cmplscale is adjusted to fit into the range of
+# offset values
+cmplscale=dynamic
+#
+# DumbScale: 0/1
+# 0 enables dynamic adjust of value ranges for freqbase and cmplscale
+# timescale is labeled with human readable times
+# 1 only uses explicit scaling for numbers
+# timescale is labeled with hours relative to timebase
+DumbScale=0
+#
+# StartTime: none/ISO_DATE_TIME
+# ignore any samples before the specified date
+StartTime=none
+#
+# EndTime: none/ISO_DATE_TIME
+# ignore any samples after the specified date
+#
+# if both StartTime and EndTime are specified
+# the value specified for samples is ignored
+EndTime=none
+#
+# MaxOffs: none/<number>
+# limit display (y-axis) to values not larger than <number>
+MaxOffset=none
+#
+# MinOffs: none/<number>
+# limit display (y-axis) to values not smaller than <number>
+MinOffset=none
+
+#
+# verbose: <number>
+# specify level for debugging
+# default is 0 for printing and 1 for monitoring
+# level 1 will just print a timestamp for any display update
+# (this is every delay seconds)
+verbose=1
+#
+# deltaT: <seconds>
+# mark `holes' in the sample data grater than <seconds>
+# by a break in the plot
+# default: 512 seconds
+deltaT=512
diff --git a/contrib/ntp/scripts/monitoring/lr.pl b/contrib/ntp/scripts/monitoring/lr.pl
new file mode 100644
index 0000000..02c7550
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/lr.pl
@@ -0,0 +1,145 @@
+;#
+;# lr.pl,v 3.1 1993/07/06 01:09:08 jbj Exp
+;#
+;#
+;# Linear Regression Package for perl
+;# to be 'required' from perl
+;#
+;# Copyright (c) 1992
+;# Frank Kardel, Rainer Pruy
+;# Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+##
+## y = A + Bx
+##
+## B = (n * Sum(xy) - Sum(x) * Sum(y)) / (n * Sum(x^2) - Sum(x)^2)
+##
+## A = (Sum(y) - B * Sum(x)) / n
+##
+
+##
+## interface
+##
+*lr_init = *lr'lr_init; #';# &lr_init(tag); initialize data set for tag
+*lr_sample = *lr'lr_sample; #';# &lr_sample(x,y,tag); enter sample
+*lr_Y = *lr'lr_Y; #';# &lr_Y(x,tag); compute y for given x
+*lr_X = *lr'lr_X; #';# &lr_X(y,tag); compute x for given y
+*lr_r = *lr'lr_r; #';# &lr_r(tag); regression coeffizient
+*lr_cov = *lr'lr_cov; #';# &lr_cov(tag); covariance
+*lr_A = *lr'lr_A; #';# &lr_A(tag);
+*lr_B = *lr'lr_B; #';# &lr_B(tag);
+*lr_sigma = *lr'lr_sigma; #';# &lr_sigma(tag); standard deviation
+*lr_mean = *lr'lr_mean; #';# &lr_mean(tag);
+#########################
+
+package lr;
+
+sub tagify
+{
+ local($tag) = @_;
+ if (defined($tag))
+ {
+ *lr_n = eval "*${tag}_n";
+ *lr_sx = eval "*${tag}_sx";
+ *lr_sx2 = eval "*${tag}_sx2";
+ *lr_sxy = eval "*${tag}_sxy";
+ *lr_sy = eval "*${tag}_sy";
+ *lr_sy2 = eval "*${tag}_sy2";
+ }
+}
+
+sub lr_init
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ $lr_n = 0;
+ $lr_sx = 0.0;
+ $lr_sx2 = 0.0;
+ $lr_sxy = 0.0;
+ $lr_sy = 0.0;
+ $lr_sy2 = 0.0;
+}
+
+sub lr_sample
+{
+ local($_x, $_y) = @_;
+
+ &tagify($_[$[+2]) if defined($_[$[+2]);
+
+ $lr_n++;
+ $lr_sx += $_x;
+ $lr_sy += $_y;
+ $lr_sxy += $_x * $_y;
+ $lr_sx2 += $_x**2;
+ $lr_sy2 += $_y**2;
+}
+
+sub lr_B
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 1 unless ($lr_n * $lr_sx2 - $lr_sx**2);
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / ($lr_n * $lr_sx2 - $lr_sx**2);
+}
+
+sub lr_A
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sy - &lr_B * $lr_sx) / $lr_n;
+}
+
+sub lr_Y
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return &lr_A + &lr_B * $_[$[];
+}
+
+sub lr_X
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($_[$[] - &lr_A) / &lr_B;
+}
+
+sub lr_r
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ local($s) = ($lr_n * $lr_sx2 - $lr_sx**2) * ($lr_n * $lr_sy2 - $lr_sy**2);
+
+ return 1 unless $s;
+
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / sqrt($s);
+}
+
+sub lr_cov
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sxy - $lr_sx * $lr_sy / $lr_n) / ($lr_n - 1);
+}
+
+sub lr_sigma
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 1;
+ return sqrt(($lr_sy2 - ($lr_sy * $lr_sy) / $lr_n) / ($lr_n));
+}
+
+sub lr_mean
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 0;
+ return $lr_sy / $lr_n;
+}
+
+&lr_init();
+
+1;
diff --git a/contrib/ntp/scripts/monitoring/ntp.pl b/contrib/ntp/scripts/monitoring/ntp.pl
new file mode 100644
index 0000000..ea9e69c
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/ntp.pl
@@ -0,0 +1,478 @@
+#!/local/bin/perl
+;#
+;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+package ntp;
+
+$NTP_version = 2;
+$ctrl_mode=6;
+
+$byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
+$MAX_DATA = 468;
+
+$sequence = 0; # initial sequence number incred before used
+$pad=4;
+$do_auth=0; # no possibility today
+$keyid=0;
+;#list if known keys (passwords)
+%KEYS = ( 0, "\200\200\200\200\200\200\200\200",
+ );
+
+;#-----------------------------------------------------------------------------
+;# access routines for ntp control packet
+ ;# NTP control message format
+ ;# C LI|VN|MODE LI 2bit=00 VN 3bit=2(3) MODE 3bit=6 : $byte1
+ ;# C R|E|M|Op R response E error M more Op opcode
+ ;# n sequence
+ ;# n status
+ ;# n associd
+ ;# n offset
+ ;# n count
+ ;# a+ data (+ padding)
+ ;# optional authentication data
+ ;# N key
+ ;# N2 checksum
+
+;# first bye of packet
+sub pkt_LI { return ($_[$[] >> 6) & 0x3; }
+sub pkt_VN { return ($_[$[] >> 3) & 0x7; }
+sub pkt_MODE { return ($_[$[] ) & 0x7; }
+
+;# second byte of packet
+sub pkt_R { return ($_[$[] & 0x80) == 0x80; }
+sub pkt_E { return ($_[$[] & 0x40) == 0x40; }
+sub pkt_M { return ($_[$[] & 0x20) == 0x20; }
+sub pkt_OP { return $_[$[] & 0x1f; }
+
+;#-----------------------------------------------------------------------------
+
+sub setkey
+{
+ local($id,$key) = @_;
+
+ $KEYS{$id} = $key if (defined($key));
+ if (! defined($KEYS{$id}))
+ {
+ warn "Key $id not yet specified - key not changed\n";
+ return undef;
+ }
+ return ($keyid,$keyid = $id)[$[];
+}
+
+;#-----------------------------------------------------------------------------
+sub numerical { $a <=> $b; }
+
+;#-----------------------------------------------------------------------------
+
+sub send #'
+{
+ local($fh,$opcode, $associd, $data,$address) = @_;
+ $fh = caller(0)."'$fh";
+
+ local($junksize,$junk,$packet,$offset,$ret);
+ $offset = 0;
+
+ $sequence++;
+ while(1)
+ {
+ $junksize = length($data);
+ $junksize = $MAX_DATA if $junksize > $MAX_DATA;
+
+ ($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
+ $packet
+ = pack("C2n5a".(($junk eq "") ? 0 : &pad($junksize+12,$pad)-12),
+ $byte1,
+ ($opcode & 0x1f) | ($data ? 0x20 : 0),
+ $sequence,
+ 0, $associd,
+ $offset, $junksize, $junk);
+ if ($do_auth)
+ {
+ ;# not yet
+ }
+ $offset += $junksize;
+
+ if (defined($address))
+ {
+ $ret = send($fh, $packet, 0, $address);
+ }
+ else
+ {
+ $ret = send($fh, $packet, 0);
+ }
+
+ if (! defined($ret))
+ {
+ warn "send failed: $!\n";
+ return undef;
+ }
+ elsif ($ret != length($packet))
+ {
+ warn "send failed: sent only $ret from ".length($packet). "bytes\n";
+ return undef;
+ }
+ return $sequence unless $data;
+ }
+}
+
+;#-----------------------------------------------------------------------------
+;# status interpretation
+;#
+sub getval
+{
+ local($val,*list) = @_;
+
+ return $list{$val} if defined($list{$val});
+ return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
+ return "unknown-$val";
+}
+
+;#---------------------------------
+;# system status
+;#
+;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
+sub ssw_LI { return ($_[$[] >> 14) & 0x3; }
+sub ssw_CS { return ($_[$[] >> 8) & 0x3f; }
+sub ssw_SECnt { return ($_[$[] >> 4) & 0xf; }
+sub ssw_SECode { return $_[$[] & 0xf; }
+
+%LI = ( 0, "leap_none", 1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
+%ClockSource = (0, "sync_unspec",
+ 1, "sync_lf_clock",
+ 2, "sync_uhf_clock",
+ 3, "sync_hf_clock",
+ 4, "sync_local_proto",
+ 5, "sync_ntp",
+ 6, "sync_udp/time",
+ 7, "sync_wristwatch",
+ "-", "ClockSource",
+ );
+
+%SystemEvent = (0, "event_unspec",
+ 1, "event_restart",
+ 2, "event_fault",
+ 3, "event_sync_chg",
+ 4, "event_sync/strat_chg",
+ 5, "event_clock_reset",
+ 6, "event_bad_date",
+ 7, "event_clock_excptn",
+ "-", "event",
+ );
+sub LI
+{
+ &getval(&ssw_LI($_[$[]),*LI);
+}
+sub ClockSource
+{
+ &getval(&ssw_CS($_[$[]),*ClockSource);
+}
+
+sub SystemEvent
+{
+ &getval(&ssw_SECode($_[$[]),*SystemEvent);
+}
+
+sub system_status
+{
+ return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]),
+ &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"),
+ &SystemEvent($_[$[]));
+}
+;#---------------------------------
+;# peer status
+;#
+;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
+sub psw_PStat_config { return ($_[$[] & 0x8000) == 0x8000; }
+sub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; }
+sub psw_PStat_authentic { return ($_[$[] & 0x2000) == 0x2000; }
+sub psw_PStat_reach { return ($_[$[] & 0x1000) == 0x1000; }
+sub psw_PStat_sane { return ($_[$[] & 0x0800) == 0x0800; }
+sub psw_PStat_dispok { return ($_[$[] & 0x0400) == 0x0400; }
+sub psw_PStat { return ($_[$[] >> 10) & 0x3f; }
+sub psw_PSel { return ($_[$[] >> 8) & 0x3; }
+sub psw_PCnt { return ($_[$[] >> 4) & 0xf; }
+sub psw_PCode { return $_[$[] & 0xf; }
+
+%PeerSelection = (0, "sel_reject",
+ 1, "sel_candidate",
+ 2, "sel_selcand",
+ 3, "sel_sys.peer",
+ "-", "PeerSel",
+ );
+%PeerEvent = (0, "event_unspec",
+ 1, "event_ip_err",
+ 2, "event_authen",
+ 3, "event_unreach",
+ 4, "event_reach",
+ 5, "event_clock_excptn",
+ 6, "event_stratum_chg",
+ "-", "event",
+ );
+
+sub PeerSelection
+{
+ &getval(&psw_PSel($_[$[]),*PeerSelection);
+}
+sub PeerEvent
+{
+ &getval(&psw_PCode($_[$[]),*PeerEvent);
+}
+
+sub peer_status
+{
+ local($x) = ("");
+ $x .= "config," if &psw_PStat_config($_[$[]);
+ $x .= "authenable," if &psw_PStat_authenable($_[$[]);
+ $x .= "authentic," if &psw_PStat_authentic($_[$[]);
+ $x .= "reach," if &psw_PStat_reach($_[$[]);
+ $x .= &psw_PStat_sane($_[$[]) ? "sane," : "insane,";
+ $x .= "hi_disp," unless &psw_PStat_dispok($_[$[]);
+
+ $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]),
+ &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"),
+ &PeerEvent($_[$[]));
+ return $x;
+}
+
+;#---------------------------------
+;# clock status
+;#
+;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
+sub csw_CStat { return ($_[$[] >> 8) & 0xff; }
+sub csw_CEvnt { return $_[$[] & 0xff; }
+
+%ClockStatus = (0, "clk_nominal",
+ 1, "clk_timeout",
+ 2, "clk_badreply",
+ 3, "clk_fault",
+ 4, "clk_prop",
+ 5, "clk_baddate",
+ 6, "clk_badtime",
+ "-", "clk",
+ );
+
+sub clock_status
+{
+ return sprintf("%s, last %s",
+ &getval(&csw_CStat($_[$[]),*ClockStatus),
+ &getval(&csw_CEvnt($_[$[]),*ClockStatus));
+}
+
+;#---------------------------------
+;# error status
+;#
+;# format: |Err|reserved| Err=8bit
+;#
+sub esw_Err { return ($_[$[] >> 8) & 0xff; }
+
+%ErrorStatus = (0, "err_unspec",
+ 1, "err_auth_fail",
+ 2, "err_invalid_fmt",
+ 3, "err_invalid_opcode",
+ 4, "err_unknown_assoc",
+ 5, "err_unknown_var",
+ 6, "err_invalid_value",
+ 7, "err_adm_prohibit",
+ );
+
+sub error_status
+{
+ return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus));
+}
+
+;#-----------------------------------------------------------------------------
+;#
+;# cntrl op name translation
+
+%CntrlOpName = (1, "read_status",
+ 2, "read_variables",
+ 3, "write_variables",
+ 4, "read_clock_variables",
+ 5, "write_clock_variables",
+ 6, "set_trap",
+ 7, "trap_response",
+ 31, "unset_trap", # !!! unofficial !!!
+ "-", "cntrlop",
+ );
+
+sub cntrlop_name
+{
+ return &getval($_[$[],*CntrlOpName);
+}
+
+;#-----------------------------------------------------------------------------
+
+$STAT_short_pkt = 0;
+$STAT_pkt = 0;
+
+;# process a NTP control message (response) packet
+;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
+;# $ret: undef --> not yet complete
+;# "" --> complete packet received
+;# "ERROR" --> error during receive, bad packet, ...
+;# else --> error packet - list may contain useful info
+
+
+sub handle_packet
+{
+ local($pkt,$from) = @_; # parameters
+ local($len_pkt) = (length($pkt));
+;# local(*FRAGS,*lastseen);
+ local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
+ local($autch_keyid,$auth_cksum);
+
+ $STAT_pkt++;
+ if ($len_pkt < 12)
+ {
+ $STAT_short_pkt++;
+ return ("ERROR","short packet received");
+ }
+
+ ;# now break packet apart
+ ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
+ unpack("C2n5a".($len_pkt-12),$pkt);
+ $data=substr($data,$[,$count);
+ if ((($len_pkt - 12) - &pad($count,4)) >= 12)
+ {
+ ;# looks like an authenticator
+ ($auth_keyid,$auth_cksum) =
+ unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
+ $STAT_auth++;
+ ;# no checking of auth_cksum (yet ?)
+ }
+
+ if (&pkt_VN($li_vn_mode) != $NTP_version)
+ {
+ $STAT_bad_version++;
+ return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored");
+ }
+
+ if (&pkt_MODE($li_vn_mode) != $ctrl_mode)
+ {
+ $STAT_bad_mode++;
+ return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored");
+ }
+
+ ;# handle single fragment fast
+ if ($offset == 0 && &pkt_M($r_e_m_op) == 0)
+ {
+ $STAT_single_frag++;
+ if (&pkt_E($r_e_m_op))
+ {
+ $STAT_err_pkt++;
+ return (&error_status($status),
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ else
+ {
+ return ("",
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ }
+ else
+ {
+ ;# fragment - set up local name space
+ $id = "$from$seq".&pkt_OP($r_e_m_op);
+ $ID{$id} = 1;
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ $STAT_frag++;
+
+ $lastseen = 1 if !&pkt_M($r_e_m_op);
+ if (!defined(%FRAGS))
+ {
+ # (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ $FRAGS{$offset} = $data;
+ ;# save other info
+ @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
+ }
+ else
+ {
+ # (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ ;# add frag to previous - combine on the fly
+ if (defined($FRAGS{$offset}))
+ {
+ $STAT_dup_frag++;
+ return ("ERROR","duplicate fragment at $offset seq=$seq");
+ }
+
+ $FRAGS{$offset} = $data;
+
+ undef($loff);
+ foreach $off (sort numerical keys(%FRAGS))
+ {
+ next unless defined($FRAGS{$off});
+ if (defined($loff) &&
+ ($loff + length($FRAGS{$loff})) == $off)
+ {
+ $FRAGS{$loff} .= $FRAGS{$off};
+ delete $FRAGS{$off};
+ last;
+ }
+ $loff = $off;
+ }
+
+ ;# return packet if all frags arrived
+ ;# at most two frags with possible padding ???
+ if ($lastseen && defined($FRAGS{0}) &&
+ (((scalar(@x=sort numerical keys(%FRAGS)) == 2) &&
+ (length($FRAGS{0}) + 8) > $x[$[+1]) ||
+ (scalar(@x=sort numerical keys(%FRAGS)) < 2)))
+ {
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""),
+ $FRAGS{0},@FRAGS);
+ &pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ &main'clear_timeout($id);
+ return @x;
+ }
+ else
+ {
+ &main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
+ }
+ }
+ return (undef);
+ }
+}
+
+sub handle_packet_timeout
+{
+ local($id) = @_;
+ local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]);
+
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"),
+ $FRAGS{0},@FRAGS[$[ .. $[+4]);
+ $STAT_frag_timeout++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ return @x;
+}
+
+
+sub pad
+{
+ return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);
+}
+
+1;
diff --git a/contrib/ntp/scripts/monitoring/ntploopstat b/contrib/ntp/scripts/monitoring/ntploopstat
new file mode 100644
index 0000000..75cdff2
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/ntploopstat
@@ -0,0 +1,457 @@
+#!/local/bin/perl -w--*-perl-*-
+;#
+;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp
+;#
+;# Poll NTP server using NTP mode 7 loopinfo request.
+;# Log info and timestamp to file for processing by ntploopwatch.
+;#
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#################################################################
+;#
+;# The format written to the logfile is the same as used by xntpd
+;# for the loopstats file.
+;# This script however allows to gather loop filter statistics from
+;# remote servers where you do not have access to the loopstats logfile.
+;#
+;# Please note: Communication delays affect the accuracy of the
+;# timestamps recorded. Effects from these delays will probably
+;# not show up, as timestamps are recorded to the second only.
+;# (Should have implemented &gettimeofday()..)
+;#
+
+$0 =~ s!^.*/([^/]+)$!\1!; # beautify script name
+
+$ntpserver = 'localhost'; # default host to poll
+$delay = 60; # default sampling rate
+ ;# keep it shorter than minpoll (=64)
+ ;# to get all values
+
+require "ctime.pl";
+;# handle bug in early ctime distributions
+$ENV{'TZ'} = 'MET' unless defined($ENV{'TZ'}) || $] > 4.010;
+
+if (defined(@ctime'MoY))
+{
+ *MonthName = *ctime'MoY;
+}
+else
+{
+ @MonthName = ('Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec');
+}
+
+;# this routine can be redefined to point to syslog if necessary
+sub msg
+{
+ return unless $verbose;
+
+ print STDERR "$0: ";
+ printf STDERR @_;
+}
+
+;#############################################################
+;#
+;# process command line
+$usage = <<"E-O-S";
+
+usage:
+ $0 [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+E-O-S
+
+while($_ = shift)
+{
+ /^-v(\d*)$/ && ($verbose=($1 eq '') ? 1 : $1,1) && next;
+ /^-d(\d*)$/ &&
+ do {
+ ($1 ne '') && ($delay = $1,1) && next;
+ @ARGV || die("$0: delay value missing after -d\n$usage");
+ $delay = shift;
+ ($delay >= 0) || die("$0: bad delay value \"$delay\"\n$usage");
+ next;
+ };
+ /^-l$/ &&
+ do {
+ @ARGV || die("$0: logfile missing after -l\n$usage");
+ $logfile = shift;
+ next;
+ };
+ /^-t(\d*(\.\d*)?)$/ &&
+ do {
+ ($1 ne '') && ($timeout = $1,1) && next;
+ @ARGV || die("$0: timeout value missing after -t\n$usage\n");
+ $timeout = shift;
+ ($timeout > 0) ||
+ die("$0: bad timeout value \"$timeout\"\n$usage");
+ next;
+ };
+
+ /^-/ && die("$0: unknown option \"$_\"\n$usage");
+
+ ;# any other argument is server to poll
+ $ntpserver = $_;
+ last;
+}
+
+if (@ARGV)
+{
+ warn("unexpected arguments: ".join(" ",@ARGV).".\n");
+ die("$0: too many servers specified\n$usage");
+}
+
+;# logfile defaults to include server name
+;# The name of the current month is appended and
+;# the file is opened and closed for each sample.
+;#
+$logfile = "loopstats:$ntpserver." unless defined($logfile);
+$timeout = 12.0 unless defined($timeout); # wait $timeout seconds for reply
+
+$MAX_FAIL = 60; # give up after $MAX_FAIL failed polls
+
+
+$MJD_1970 = 40587;
+
+if (eval 'require "syscall.ph";')
+{
+ if (defined(&SYS_gettimeofday))
+ {
+ ;# assume standard
+ ;# gettimeofday(struct timeval *tp,struct timezone *tzp)
+ ;# syntax for gettimeofday syscall
+ ;# tzp = NULL -> undef
+ ;# tp = (long,long)
+ eval 'sub time { local($tz) = pack("LL",0,0);
+ (&msg("gettimeofday failed: $!\n"),
+ return (time))
+ unless syscall(&SYS_gettimeofday,$tz,undef) == 0;
+ local($s,$us) = unpack("LL",$tz);
+ return $s + $us/1000000; }';
+ local($t1,$t2,$t3);
+ $t1 = time;
+ eval '$t2 = &time;';
+ $t3 = time;
+ die("$0: gettimeofday failed: $@.\n") if defined($@) && $@;
+ die("$0: gettimeofday inconsistency time=$t1,gettimeofday=$t2,time=$t2\n")
+ if (int($t1) != int($t2) && int($t3) != int($t2));
+ &msg("Using gettimeofday for timestamps\n");
+ }
+ else
+ {
+ warn("No gettimeofday syscall found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+ }
+}
+else
+{
+ warn("No syscall.ph file found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+}
+
+
+;#------------------+
+;# from ntp_request.h
+;#------------------+
+
+;# NTP mode 7 packet format:
+;# Byte 1: ResponseBit MoreBit Version(3bit) Mode(3bit)==7
+;# Byte 2: AuthBit Sequence # - 0 - 127 see MoreBit
+;# Byte 3: Implementation #
+;# Byte 4: Request Code
+;#
+;# Short 1: Err(3bit) NumItems(12bit)
+;# Short 2: MBZ(3bit)=0 DataItemSize(12bit)
+;# 0 - 500 byte Data
+;# if AuthBit is set:
+;# Long: KeyId
+;# 2xLong: AuthCode
+
+;#
+$IMPL_XNTPD = 2;
+$REQ_LOOP_INFO = 8;
+
+
+;# request packet for REQ_LOOP_INFO:
+;# B1: RB=0 MB=0 V=2 M=7
+;# B2: S# = 0
+;# B3: I# = IMPL_XNTPD
+;# B4: RC = REQ_LOOP_INFO
+;# S1: E=0 NI=0
+;# S2: MBZ=0 DIS=0
+;# data: 32 byte 0 padding
+;# 8byte timestamp if encryption, 0 padding otherwise
+$loopinfo_reqpkt =
+ pack("CCCC nn x32 x8", 0x17, 0, $IMPL_XNTPD, $REQ_LOOP_INFO, 0, 0);
+
+;# ignore any auth data in packets
+$loopinfo_response_size =
+ 1+1+1+1+2+2 # header size like request pkt
+ + 8 # l_fp last_offset
+ + 8 # l_fp drift_comp
+ + 4 # u_long compliance
+ + 4 # u_long watchdog_timer
+ ;
+$loopinfo_response_fmt = "C4n2N2N2NN";
+$loopinfo_response_fmt_v2 = "C4n2N2N2N2N";
+
+;#
+;# prepare connection to server
+;#
+
+;# workaround for broken socket.ph on dynix_ptx
+eval 'sub INTEL {1;}' unless defined(&INTEL);
+eval 'sub ATT {1;}' unless defined(&ATT);
+
+require "sys/socket.ph";
+
+require 'netinet/in.ph';
+
+;# if you do not have netinet/in.ph enable the following lines
+;#eval 'sub INADDR_ANY { 0x00000000; }' unless defined(&INADDR_ANY);
+;#eval 'sub IPPRORO_UDP { 17; }' unless defined(&IPPROTO_UDP);
+
+if ($ntpserver =~ /^((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)$/)
+{
+ local($a,$b,$c,$d) = ($1,$3,$5,$7);
+ $a = oct($a) if defined($2);
+ $b = oct($b) if defined($4);
+ $c = oct($c) if defined($6);
+ $d = oct($d) if defined($8);
+ $server_addr = pack("C4", $a,$b,$c,$d);
+
+ $server_mainname
+ = (gethostbyaddr($server_addr,&AF_INET))[$[] || $ntpserver;
+}
+else
+{
+ ($server_mainname,$server_addr)
+ = (gethostbyname($ntpserver))[$[,$[+4];
+
+ die("$0: host \"$ntpserver\" is unknown\n")
+ unless defined($server_addr);
+}
+&msg ("Address of server \"$ntpserver\" is \"%d.%d.%d.%d\"\n",
+ unpack("C4",$server_addr));
+
+$proto_udp = (getprotobyname('udp'))[$[+2] || &IPPROTO_UDP;
+
+$ntp_port =
+ (getservbyname('ntp','udp'))[$[+2] ||
+ (warn "Could not get port number for service \"ntp/udp\" using 123\n"),
+ ($ntp_port=123);
+
+;#
+0 && &SOCK_DGRAM; # satisfy perl -w ...
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) ||
+ die("Cannot open socket: $!\n");
+
+bind(S, pack("S n N x8", &AF_INET, 0, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+
+&msg("Listening at address %d.%d.%d.%d port %d\n",
+ unpack("C4",$my_addr), $my_port);
+
+$server_inaddr = pack("Sna4x8", &AF_INET, $ntp_port, $server_addr);
+
+;############################################################
+;#
+;# the main loop:
+;# send request
+;# get reply
+;# wait til next sample time
+
+undef($lasttime);
+$lostpacket = 0;
+
+while(1)
+{
+ $stime = &time;
+
+ &msg("Sending request $stime...\n");
+
+ $ret = send(S,$loopinfo_reqpkt,0,$server_inaddr);
+
+ if (! defined($ret) || $ret < length($loopinfo_reqpkt))
+ {
+ warn("$0: send failed ret=($ret): $!\n");
+ $fail++;
+ next;
+ }
+
+ &msg("Waiting for reply...\n");
+
+ $mask = ""; vec($mask,fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ if (! defined($ret))
+ {
+ warn("$0: select failed: $!\n");
+ $fail++;
+ next;
+ }
+ elsif ($ret == 0)
+ {
+ warn("$0: request to $ntpserver timed out ($timeout seconds)\n");
+ ;# do not count this event as failure
+ ;# it usually this happens due to dropped udp packets on noisy and
+ ;# havily loaded lines, so just try again;
+ $lostpacket = 1;
+ next;
+ }
+
+ &msg("Receiving reply...\n");
+
+ $len = 520; # max size of a mode 7 packet
+ $reply = ""; # just make it defined for -w
+ $ret = recv(S,$reply,$len,0);
+
+ if (!defined($ret))
+ {
+ warn("$0: recv failed: $!\n");
+ $fail++;
+ next;
+ }
+
+ $etime = &time;
+ &msg("Received at\t$etime\n");
+
+ ;#$time = ($stime + $etime) / 2; # symmetric delay assumed
+ $time = $etime; # the above assumption breaks for X25
+ ;# so taking etime makes timestamps be a
+ ;# little late, but keeps them increasing
+ ;# monotonously
+
+ &msg(sprintf("Reply from %d.%d.%d.%d took %f seconds\n",
+ (unpack("SnC4",$ret))[$[+2 .. $[+5], ($etime - $stime)));
+
+ if ($len < $loopinfo_response_size)
+ {
+ warn("$0: short packet ($len bytes) received ($loopinfo_response_size bytes expected\n");
+ $fail++;
+ next;
+ }
+
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl,$watchdog)
+ = unpack($loopinfo_response_fmt,$reply);
+
+ ;# check reply
+ if (($s1 >> 12) != 0) # error !
+ {
+ die("$0: got error reply ".($s1>>12)."\n");
+ }
+ if (($b1 != 0x97 && $b1 != 0x9f) || # Reply NotMore V=2 M=7
+ ($b2 != 0 && $b2 != 0x80) || # S=0 Auth no/yes
+ $b3 != $IMPL_XNTPD || # ! IMPL_XNTPD
+ $b4 != $REQ_LOOP_INFO || # Ehh.. not loopinfo reply ?
+ $s1 != 1 || # ????
+ ($s2 != 24 && $s2 != 28) #
+ )
+ {
+ warn("$0: Bad/unexpected reply from server:\n");
+ warn(" \"".unpack("H*",$reply)."\"\n");
+ warn(" ".sprintf("b1=%x b2=%x b3=%x b4=%x s1=%d s2=%d\n",
+ $b1,$b2,$b3,$b4,$s1,$s2));
+ $fail++;
+ next;
+ }
+ elsif ($s2 == 28)
+ {
+ ;# seems to be a version 2 xntpd
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl_i,$compl_f,$watchdog)
+ = unpack($loopinfo_response_fmt_v2,$reply);
+ $compl = &lfptoa($compl_i, $compl_f);
+ }
+
+ $time -= $watchdog;
+
+ $offset = &lfptoa($offset_i, $offset_f);
+ $drift = &lfptoa($drift_i, $drift_f);
+
+ &log($time,$offset,$drift,$compl) && ($fail = 0);;
+}
+continue
+{
+ die("$0: Too many failures - terminating\n") if $fail > $MAX_FAIL;
+ &msg("Sleeping " . ($lostpacket ? ($delay / 2) : $delay) . " seconds...\n");
+
+ sleep($lostpacket ? ($delay / 2) : $delay);
+ $lostpacket = 0;
+}
+
+sub log
+{
+ local($time,$offs,$freq,$cmpl) = @_;
+ local($y,$m,$d);
+ local($fname,$suff) = ($logfile);
+
+
+ ;# silently drop sample if distance to last sample is too low
+ if (defined($lasttime) && ($lasttime + 2) >= $time)
+ {
+ &msg("Dropped packet - old sample\n");
+ return 1;
+ }
+
+ ;# $suff determines which samples end up in the same file
+ ;# could have used $year (;-) or WeekOfYear, DayOfYear,....
+ ;# Change it to your suit...
+
+ ($d,$m,$y) = (localtime($time))[$[+3 .. $[+5];
+ $suff = sprintf("%04d%02d%02d",$y+1900,$m+1,$d);
+ $fname .= $suff;
+ if (!open(LOG,">>$fname"))
+ {
+ warn("$0: open($fname) failed: $!\n");
+ $fail++;
+ return 0;
+ }
+ else
+ {
+ ;# file format
+ ;# MJD seconds offset drift compliance
+ printf LOG ("%d %.3lf %.8lf %.7lf %d\n",
+ int($time/86400)+$MJD_1970,
+ $time - int($time/86400) * 86400,
+ $offs,$freq,$cmpl);
+ close(LOG);
+ $lasttime = $time;
+ }
+ return 1;
+}
+
+;# see ntp_fp.h to understand this
+sub lfptoa
+{
+ local($i,$f) = @_;
+ local($sign) = 1;
+
+
+ if ($i & 0x80000000)
+ {
+ if ($f == 0)
+ {
+ $i = -$i;
+ }
+ else
+ {
+ $f = -$f;
+ $i = ~$i;
+ $i += 1; # 2s complement
+ }
+ $sign = -1;
+ ;#print "NEG: $i $f\n";
+ }
+ else
+ {
+ ;#print "POS: $i $f\n";
+ }
+ ;# unlike xntpd I have perl do the dirty work.
+ ;# Using floats here may affect precision, but
+ ;# currently these bits aren't significant anyway
+ return $sign * ($i + $f/2**32);
+}
diff --git a/contrib/ntp/scripts/monitoring/ntploopwatch b/contrib/ntp/scripts/monitoring/ntploopwatch
new file mode 100644
index 0000000..db661d3
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/ntploopwatch
@@ -0,0 +1,1667 @@
+#!/usr/bin/perl -w
+;# --*-perl-*--
+;#
+;# /src/NTP/ntp-4/scripts/monitoring/ntploopwatch,v 4.3 1999/02/21 12:18:38 kardel RELEASE_19990228_A
+;# RELEASE_19990228_A
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992-1998
+;# Rainer Pruy, Friedrich-Alexander Universität Erlangen-Nürnberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!$1!;
+$F = ' ' x length($0);
+$|=1;
+
+$ENV{'SHELL'} = '/bin/sh'; # use bourne shell
+
+undef($config);
+undef($workdir);
+undef($PrintIt);
+undef($samples);
+undef($StartTime);
+undef($EndTime);
+($a,$b) if 0; # keep -w happy
+$usage = <<"E-O-P";
+usage:
+ to watch statistics permanently:
+ $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+ $F [-h <hostname>]
+
+ to get a single print out specify also
+ $F -P[<printer>] [-s<samples>]
+ $F [-S <start-time>] [-E <end-time>]
+ $F [-Y <MaxOffs>] [-y <MinOffs>]
+
+If You like long option names, You can use:
+ -help
+ -c +config
+ -d +directory
+ -h +host
+ -v +verbose[=<level>]
+ -P +printer[=<printer>]
+ -s +samples[=<samples>]
+ -S +starttime
+ -E +endtime
+ -Y +maxy
+ -y +miny
+
+If <printer> contains a '/' (slash character) output is directed to
+a file of this name instead of delivered to a printer.
+E-O-P
+
+;# add directory to look for lr.pl and timelocal.pl (in front of current list)
+unshift(@INC,"/usr/local/xntp/monitoring");
+
+require "lr.pl"; # linear regresion routines
+
+$MJD_1970 = 40587; # from ntp.h (V3)
+$RecordSize = 48; # usually a line fits into 42 bytes
+$MinClip = 1; # clip Y scales with greater range than this
+
+;# largest extension of Y scale from mean value, factor for standart deviation
+$FuzzLow = 2.2; # for side closer to zero
+$FuzzBig = 1.8; # for side farther from zero
+
+require "ctime.pl";
+require "timelocal.pl";
+;# early distributions of ctime.pl had a bug
+$ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
+if (defined(@ctime'MoY))
+{
+ *Month=*ctime'MoY;
+ *Day=*ctime'DoW;
+} # ' re-sync emacs fontification
+else
+{
+ @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+ @Day = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+}
+print @ctime'DoW if 0; # ' re-sync emacs fontification
+
+;# max number of days per month
+@MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+
+;# config settable parameters
+$delay = 60;
+$srcprefix = "./var\@\$STATHOST/loopstats.";
+$showoffs = 1;
+$showfreq = 1;
+$showcmpl = 0;
+$showoreg = 0;
+$showfreg = 0;
+undef($timebase);
+undef($freqbase);
+undef($cmplscale);
+undef($MaxY);
+undef($MinY);
+$deltaT = 512; # indicate sample data gaps greater than $deltaT seconds
+$verbose = 1;
+
+while($_ = shift(@ARGV))
+{
+ (/^[+-]help$/) && die($usage);
+
+ (/^-c$/ || /^\+config$/) &&
+ (@ARGV || die($usage), $config = shift(@ARGV), next);
+
+ (/^-d$/ || /^\+directory$/) &&
+ (@ARGV || die($usage), $workdir = shift(@ARGV), next);
+
+ (/^-h$/ || /^\+host$/) &&
+ (@ARGV || die($usage), $STATHOST = shift, next);
+
+ (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
+ ($verbose=($1 eq "") ? 1 : $1, next);
+
+ (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
+ ($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
+
+ (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
+ (($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
+
+ (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
+ (@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
+
+ (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
+ (@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
+
+ (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
+ (@ARGV || die($usage), $MaxY = shift, next);
+
+ (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
+ (@ARGV || die($usage), $MinY = shift, next);
+
+ die("$0: unexpected argument \"$_\"\n$usage");
+}
+
+if (defined($workdir))
+{
+ chdir($workdir) ||
+ die("$0: failed to change working dir to \"$workdir\": $!\n");
+}
+
+$PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
+
+if (!defined($PrintIt))
+{
+ defined($samples) &&
+ print "WARNING: your samples value may be shadowed by config file settings\n";
+ defined($StartTime) &&
+ print "WARNING: your StartTime value may be shadowed by config file settings\n";
+ defined($EndTime) &&
+ print "WARNING: your EndTime value may be shadowed by config file settings\n";
+ defined($MaxY) &&
+ print "WARNING: your MaxY value may be shadowed by config file settings\n";
+ defined($MinY) &&
+ print "WARNING: your MinY value may be shadowed by config file settings\n";
+
+ ;# check operating environment
+ ;#
+ ;# gnuplot usually has X support
+ ;# I vaguely remember there was one with sunview support
+ ;#
+ ;# If Your plotcmd can display graphics using some other method
+ ;# (Tek window,..) fix the following test
+ ;# (or may be, just disable it)
+ ;#
+ !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
+ die("Need window system to monitor statistics\n");
+}
+
+;# configuration file
+$config = "loopwatch.config" unless defined($config);
+($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!$1!
+ unless defined($STATHOST);
+($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/$1/;
+
+$srcprefix =~ s/\$STATHOST/$STATHOST/g;
+
+;# plot command
+@plotcmd=("gnuplot",
+ '-title', "Ntp loop filter statistics $STATHOST",
+ '-name', "NtpLoopWatch_$STATTAG");
+$tmpfile = "/tmp/ntpstat.$$";
+
+;# other variables
+$doplot = ""; # assembled command for @plotcmd to display plot
+undef($laststat);
+
+;# plot value ranges
+undef($mintime);
+undef($maxtime);
+undef($minoffs);
+undef($maxoffs);
+undef($minfreq);
+undef($maxfreq);
+undef($mincmpl);
+undef($maxcmpl);
+undef($miny);
+undef($maxy);
+
+;# stop operation if plot command dies
+sub sigchld
+{
+ local($pid) = wait;
+ unlink($tmpfile);
+ warn(sprintf("%s: %s died: exit status: %d signal %d\n",
+ $0,
+ (defined($Plotpid) && $Plotpid == $pid)
+ ? "plotcmd" : "unknown child $pid",
+ $?>>8,$? & 0xff)) if $?;
+ exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
+}
+&sigchld if 0;
+$SIG{'CHLD'} = "sigchld";
+$SIG{'CLD'} = "sigchld";
+
+sub abort
+{
+ unlink($tmpfile);
+ defined($Plotpid) && kill('TERM',$Plotpid);
+ die("$0: received signal SIG$_[$[] - exiting\n");
+}
+&abort if 0; # make -w happy - &abort IS used
+$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
+
+;#
+sub abs
+{
+ ($_[$[] < 0) ? -($_[$[]) : $_[$[];
+}
+
+sub boolval
+{
+ local($v) = ($_[$[]);
+
+ return 1 if ($v eq 'yes') || ($v eq 'y');
+ return 1 if ($v =~ /^[0-9]*$/) && ($v != 0);
+ return 0;
+}
+
+;#####################
+;# start of real work
+
+print "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
+
+$Plotpid = open(PLOT,"|-");
+select((select(PLOT),$|=1)[$[]); # make PLOT line bufferd
+
+defined($Plotpid) ||
+ die("$0: failed to start plot command: $!\n");
+
+unless ($Plotpid)
+{
+ ;# child == plot command
+ close(STDOUT);
+ open(STDOUT,">&STDERR") ||
+ die("$0: failed to redirect STDOUT of plot command: $!\n");
+
+ print STDOUT "plot command running as $$\n";
+
+ exec @plotcmd;
+ die("$0: failed to exec (@plotcmd): $!\n");
+ exit(1); # in case ...
+}
+
+sub read_config
+{
+ local($at) = (stat($config))[$[+9];
+ local($_,$c,$v);
+
+ (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
+ return if (defined($laststat) && ($laststat == $at));
+ $laststat = $at;
+
+ print "reading configuration from \"$config\"\n" if $verbose;
+
+ open(CF,"<$config") ||
+ (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
+ return);
+ while(<CF>)
+ {
+ chop;
+ s/^([^\#]*[^\#\s]?)\s*\#.*$//;
+ next if /^\s*$/;
+
+ s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/$1=$2/;
+
+ ($c,$v) = split(/=/,$_,2);
+ print "processing \"$c=$v\"\n" if $verbose > 3;
+ ($c eq "delay") && ($delay = $v,1) && next;
+ ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
+ ($samples = $v,1) && next;
+ ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
+ && next;
+ ($c eq 'showoffs') &&
+ ($showoffs = boolval($v),1) && next;
+ ($c eq 'showfreq') &&
+ ($showfreq = boolval($v),1) && next;
+ ($c eq 'showcmpl') &&
+ ($showcmpl = boolval($v),1) && next;
+ ($c eq 'showoreg') &&
+ ($showoreg = boolval($v),1) && next;
+ ($c eq 'showfreg') &&
+ ($showfreg = boolval($v),1) && next;
+
+ ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
+
+ ($c eq 'freqbase' ||
+ $c eq 'cmplscale') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq 'dynamic')
+ {
+ eval "undef(\$$c);";
+ }
+ else
+ {
+ eval "\$$c = \$v;";
+ }
+ next;
+ };
+ ($c eq 'timebase') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq "dynamic")
+ {
+ undef($timebase);
+ }
+ else
+ {
+ $timebase=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'EndTime') &&
+ do {
+ next if defined($EndTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($EndTime);
+ }
+ else
+ {
+ $EndTime=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'StartTime') &&
+ do {
+ next if defined($StartTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($StartTime);
+ }
+ else
+ {
+ $StartTime=&date_time_spec2seconds($v);
+ }
+ };
+
+ ($c eq 'MaxY') &&
+ do {
+ next if defined($MaxY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MaxY);
+ }
+ else
+ {
+ $MaxY=$v;
+ }
+ };
+
+ ($c eq 'MinY') &&
+ do {
+ next if defined($MinY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MinY);
+ }
+ else
+ {
+ $MinY=$v;
+ }
+ };
+
+ ($c eq 'deltaT') &&
+ do {
+ if (!defined($v) || $v eq "")
+ {
+ undef($deltaT);
+ }
+ else
+ {
+ $deltaT = $v;
+ }
+ next;
+ };
+ ($c eq 'verbose') && ! defined($PrintIt) &&
+ do {
+ if (!defined($v) || $v == 0)
+ {
+ $verbose = 0;
+ }
+ else
+ {
+ $verbose = $v;
+ }
+ next;
+ };
+ ;# otherwise: silently ignore unrecognized config line
+ }
+ close(CF);
+ ;# set show defaults when nothing selected
+ $showoffs = $showfreq = $showcmpl = 1
+ unless $showoffs || $showfreq || $showcmpl;
+ if ($verbose > 3)
+ {
+ print "new configuration:\n";
+ print " delay\t= $delay\n";
+ print " samples\t= $samples\n";
+ print " srcprefix\t= $srcprefix\n";
+ print " showoffs\t= $showoffs\n";
+ print " showfreq\t= $showfreq\n";
+ print " showcmpl\t= $showcmpl\n";
+ print " showoreg\t= $showoreg\n";
+ print " showfreg\t= $showfreg\n";
+ printf " timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
+ printf " freqbase\t= %s\n",defined($freqbase) ?"$freqbase":"dynamic";
+ printf " cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
+ printf " StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
+ printf " EndTime\t= %s", defined($EndTime) ? &ctime($EndTime):"none\n";
+ printf " MaxY\t= %s",defined($MaxY)? $MaxY :"none\n";
+ printf " MinY\t= %s",defined($MinY)? $MinY :"none\n";
+ print " verbose\t= $verbose\n";
+ }
+print "configuration file read\n" if $verbose > 2;
+}
+
+sub make_doplot
+{
+ local($c) = ("");
+ local($fmt)
+ = ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
+ local($regfmt)
+ = ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
+
+ $doplot = " set title 'NTP loopfilter statistics for $STATHOST " .
+ "(last $LastCnt samples from $srcprefix*)'\n";
+
+ local($xts,$xte,$i,$t);
+
+ local($s,$c) = ("");
+
+ ;# number of integral seconds to get at least 12 tic marks on x axis
+ $t = int(($maxtime - $mintime) / 12 + 0.5);
+ $t = 1 unless $t; # prevent $t to be zero
+ foreach $i (30,
+ 60,5*60,15*60,30*60,
+ 60*60,2*60*60,6*60*60,12*60*60,
+ 24*60*60,48*60*60)
+ {
+ last if $t < $i;
+ $t = $t - ($t % $i);
+ }
+ print "time label resolution: $t seconds\n" if $verbose > 1;
+
+ ;# make gnuplot use wall clock time labels instead of NTP seconds
+ for ($c="", $i = $mintime - ($mintime % $t);
+ $i <= $maxtime + $t;
+ $i += $t, $c=",")
+ {
+ $s .= $c;
+ ((int($i / $t) % 2) &&
+ ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
+ (($t <= 60) &&
+ ($s .= sprintf("'%d:%02d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1,$[+0],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 2*60*60) &&
+ ($s .= sprintf("'%d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 12*60*60) &&
+ ($s .= sprintf("'%s %d:00' %lf",
+ $Day[(localtime($i))[$[+6]],
+ (localtime($i))[$[+2],
+ ($i - $LastTimeBase)/3600)))
+ || ($s .= sprintf("'%d.%d-%d:00' %lf",
+ (localtime($i))[$[+3,$[+4,$[+2],
+ ($i - $LastTimeBase)/3600));
+ }
+ $doplot .= "set xtics ($s)\n";
+
+ chop($xts = &ctime($mintime));
+ chop($xte = &ctime($maxtime));
+ $doplot .= "set xlabel 'Start: $xts -- Time Scale -- End: $xte'\n";
+ $doplot .= "set yrange [" ;
+ $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
+ $doplot .= ':';
+ $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
+ $doplot .= "]\n";
+
+ $doplot .= " plot";
+ $c = "";
+ $showoffs &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,2,
+ "offset",
+ $minoffs,$maxoffs,
+ "[ms]"),
+ $c = ",");
+ $LastCmplScale = 1 if ! defined($LastCmplScale);
+ $showcmpl &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,4,
+ "compliance" .
+ (&abs($LastCmplScale) > 1
+ ? " / $LastCmplScale"
+ : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
+ $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
+ ""),
+ $c = ",");
+ $LastFreqBase = 0 if ! defined($LastFreqBase);
+ $LastFreqBaseString = "?" if ! defined($LastFreqBaseString);
+ $FreqScale = 1 if ! defined($FreqScale);
+ $FreqScaleInv = 1 if ! defined($FreqScaleInv);
+ $showfreq &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,3,
+ "frequency" .
+ ($LastFreqBase > 0
+ ? " - $LastFreqBaseString"
+ : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
+ $minfreq * $FreqScale - $LastFreqBase,
+ $maxfreq * $FreqScale - $LastFreqBase,
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $showoreg && $showoffs &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('offs'),&lr_A('offs'),
+ "offset ",
+ &lr_B('offs'),
+ ((&lr_A('offs')) < 0 ? '-' : '+'),
+ &abs(&lr_A('offs')), &lr_r('offs'),
+ "[ms]"),
+ $c = ",");
+ $showfreg && $showfreq &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('freq') * $FreqScale,
+ (&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase,
+ "frequency",
+ &lr_B('freq') * $FreqScale,
+ ((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
+ &abs((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase),
+ &lr_r('freq'),
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $doplot .= "\n";
+}
+
+%F_key = ();
+%F_name = ();
+%F_size = ();
+%F_mtime = ();
+%F_first = ();
+%F_last = ();
+
+sub genfile
+{
+ local($cnt,$in,$out,@fpos) = @_;
+
+ local(@F,@t,$t,$lastT) = ();
+ local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
+ local($lm,$l,@f);
+
+ local($sdir,$sname);
+
+ ;# allocate some storage for the tables
+ ;# otherwise realloc may get into troubles
+ if (defined($StartTime) && defined($EndTime))
+ {
+ $l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
+ }
+ else
+ {
+ $l = $cnt + 10;
+ }
+ print "preextending arrays to $l entries\n" if $verbose > 2;
+ $#break = $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
+ $#time = $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
+ $#offs = $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
+ $#freq = $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
+ $#cmpl = $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
+ $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
+ $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
+ ;# now reduce size again
+ $#break = $[ - 1;
+ $#time = $[ - 1;
+ $#offs = $[ - 1;
+ $#freq = $[ - 1;
+ $#cmpl = $[ - 1;
+ $#loffset = $[ - 1;
+ $#filekey = $[ - 1;
+ print "memory allocation ready\n" if $verbose > 2;
+ sleep(3) if $verbose > 1;
+
+ $fpos[$[] = '' if !defined($fpos[$[]);
+
+ if (index($in,"/") < $[)
+ {
+ $sdir = ".";
+ $sname = $in;
+ }
+ else
+ {
+ ($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
+ $sname = "" unless defined($sname);
+ }
+
+ $Ltime = -1 if ! defined($Ltime);
+ if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
+ grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
+
+ {
+ print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
+ if $verbose > 1;
+
+ ;# rescan directory on changes
+ $Lsdir = $sdir;
+ $Ltime = (stat($sdir))[$[+9];
+ </X{> if 0; # dummy line - calm down my formatter
+ local(@newfiles) = < ${in}*[0-9] >;
+ local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
+
+ foreach $name (@newfiles)
+ {
+ ($st_dev,$st_ino,$st_size,$st_mtime) =
+ (stat($name))[$[,$[+1,$[+7,$[+9];
+ $modified = 0;
+ $key = sprintf("%lx|%lu", $st_dev, $st_ino);
+
+ print "candidate file \"$name\"",
+ (defined($st_dev) ? "" : " failed: $!"),"\n"
+ if $verbose > 2;
+
+ if (! defined($F_key{$name}) || $F_key{$name} ne $key)
+ {
+ $F_key{$name} = $key;
+ $modified++;
+ }
+ if (!defined($F_name{$key}) || $F_name{$key} ne $name)
+ {
+ $F_name{$key} = $name;
+ $modified++;
+ }
+ if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
+ {
+ $F_size{$key} = $st_size;
+ $modified++;
+ }
+ if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
+ {
+ $F_mtime{$key} = $st_mtime;
+ $modified++;
+ }
+ if ($modified)
+ {
+ print "new data \"$name\" key: $key;\n" if $verbose > 1;
+ print " size: $st_size; mtime: $st_mtime;\n"
+ if $verbose > 1;
+ $F_last{$key} = $F_first{$key} = $st_mtime;
+ $F_first{$key}--; # prevent zero divide later on
+ ;# now compute derivated attributes
+ open(IN, "<$name") ||
+ do {
+ warn "$0: failed to open \"$name\": $!";
+ next;
+ };
+
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_first{$key} = $t;
+ print "\tfound first entry: $t ",&ctime($t)
+ if $verbose > 4;
+ last;
+ }
+ seek(IN,
+ ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
+ 0);
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_last{$key} = $t;
+ $_ = <IN>;
+ print "\tfound last entry: $t ", &ctime($t)
+ if $verbose > 4 && ! defined($_);
+ last unless defined($_);
+ redo;
+ ;# Ok, calm down...
+ ;# using $_ = <IN> in conjunction with redo
+ ;# is semantically equivalent to the while loop, but
+ ;# I needed a one line look ahead and this solution
+ ;# was what I thought of first
+ ;# and.. If you do not like it dont look
+ }
+ close(IN);
+ print(" first: ",$F_first{$key},
+ " last: ",$F_last{$key},"\n") if $verbose > 1;
+ }
+ }
+ ;# now reclaim memory used for files no longer referenced ...
+ local(%Names);
+ grep($Names{$_} = 1,@newfiles);
+ foreach (keys %F_key)
+ {
+ next if defined($Names{$_});
+ delete $F_key{$_};
+ $verbose > 2 && print "no longer referenced: \"$_\"\n";
+ }
+ %Names = ();
+
+ grep($Names{$_} = 1,values(%F_key));
+ foreach (keys %F_name)
+ {
+ next if defined($Names{$_});
+ delete $F_name{$_};
+ $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
+ }
+ foreach (keys %F_size)
+ {
+ next if defined($Names{$_});
+ delete $F_size{$_};
+ $verbose > 2 && print "unref size($_)\n";
+ }
+ foreach (keys %F_mtime)
+ {
+ next if defined($Names{$_});
+ delete $F_mtime{$_};
+ $verbose > 2 && print "unref mtime($_)\n";
+ }
+ foreach (keys %F_first)
+ {
+ next if defined($Names{$_});
+ delete $F_first{$_};
+ $verbose > 2 && print "unref first($_)\n";
+ }
+ foreach (keys %F_last)
+ {
+ next if defined($Names{$_});
+ delete $F_last{$_};
+ $verbose > 2 && print "unref last($_)\n";
+ }
+ ;# create list sorted by time
+ @F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
+ if ($verbose > 1)
+ {
+ print "Resulting file list:\n";
+ foreach (@F_files)
+ {
+ print "\t$_\t$F_name{$_}\n";
+ }
+ }
+ }
+
+ printf("processing %s; output \"$out\" (%d input files)\n",
+ ((defined($StartTime) && defined($EndTime))
+ ? "time range"
+ : (defined($StartTime) ? "$cnt samples from StartTime" :
+ (defined($EndTime) ? "$cnt samples to EndTime" :
+ "last $cnt samples"))),
+ scalar(@F_files))
+ if $verbose > 1;
+
+ ;# open output file - will be input for plotcmd
+ open(OUT,">$out") ||
+ do {
+ warn("$0: cannot create \"$out\": $!\n");
+ };
+
+ @f = @F_files;
+ if (defined($StartTime))
+ {
+ while (@f && ($F_last{$f[$[]} < $StartTime))
+ {
+ print("shifting ", $F_name{$f[$[]},
+ " last: ", $F_last{$f[$[]},
+ " < StartTime: $StartTime\n")
+ if $verbose > 3;
+ shift(@f);
+ }
+
+
+ }
+ if (defined($EndTime))
+ {
+ while (@f && ($F_first{$f[$#f]} > $EndTime))
+ {
+ print("popping ", $F_name{$f[$#f]},
+ " first: ", $F_first{$f[$#f]},
+ " > EndTime: $EndTime\n")
+ if $verbose > 3;
+ pop(@f);
+ }
+ }
+
+ if (@f)
+ {
+ if (defined($StartTime))
+ {
+ print "guess start according to StartTime ($StartTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'start')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('start', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('start' , $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ if ($StartTime <= $F_first{$f[$[]})
+ {
+ $fpos[$[+2] = 0;
+ }
+ else
+ {
+ $fpos[$[+2] =
+ int($F_size{$f[$[]} *
+ (($StartTime - $F_first{$f[$[]})/
+ ($F_last{$f[$[]} - $F_first{$f[$[]})));
+ $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
+ ? 0 : $fpos[$[+2] - 2 * $RecordSize;
+ ;# anyway as the data may contain "time holes"
+ ;# our heuristics may baldly fail
+ ;# so just start at 0
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ elsif (defined($EndTime))
+ {
+ print "guess starting point according to EndTime ($EndTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'end')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+ if ($EndTime < $F_last{$x[$[]})
+ {
+ ;# last file will only be used partially
+ $s = int($F_size{$x[$[]} *
+ (($EndTime - $F_first{$x[$[]}) /
+ ($F_last{$x[$[]} - $F_first{$x[$[]})));
+ $s = int($s/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ ;# start is in the same file
+ $fpos[$[+1] = $x[$[];
+ $fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $x[$[]);
+ }
+ else
+ {
+ shift(@x);
+ }
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local($_);
+ while($_ = shift(@x))
+ {
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ print "guessing starting point according to count ($cnt)\n"
+ if $verbose > 3;
+ ;# guess offset to get last available $cnt samples
+ if ($fpos[$[] eq 'cnt')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ print "old positioning applies\n" if $verbose > 3;
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+
+ local($_);
+ while($_ = shift(@x))
+ {
+ print "examing \"$_\" $c samples still needed\n"
+ if $verbose > 4;
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ if (!defined($fpos[$[+2]))
+ {
+ print "no starting point yet - using start of data\n"
+ if $verbose > 2;
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ }
+ print "Ooops, no suitable input file ??\n"
+ if $verbose > 1 && @f <= 0;
+
+ printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
+ $fpos[$[+1],
+ $F_name{$fpos[$[+1]},
+ $fpos[$[+2],
+ scalar(@f))
+ if $verbose > 2;
+
+ $lm = 1;
+ $l = 0;
+ foreach $key (@f)
+ {
+ $file = $F_name{$key};
+ print "processing file \"$file\"\n" if $verbose > 2;
+
+ open(IN,"<$file") ||
+ (warn("$0: cannot read \"$file\": $!\n"), next);
+
+ ;# try to seek to a position nearer to the start of the interesting lines
+ ;# should always affect only first item in @f
+ ($key eq $fpos[$[+1]) &&
+ (($verbose > 1) &&
+ print("Seeking to offset $fpos[$[+2]\n"),
+ seek(IN,$fpos[$[+2],0) ||
+ warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
+
+ while(<IN>)
+ {
+ $l++;
+ ($verbose > 3) &&
+ (($l % $lm) == 0 && print("\t$l lines read\n") &&
+ (($l == 2) && ($lm = 10) ||
+ ($l == 100) && ($lm = 100) ||
+ ($l == 500) && ($lm = 500) ||
+ ($l == 1000) && ($lm = 1000) ||
+ ($l == 5000) && ($lm = 5000) ||
+ ($l == 10000) && ($lm = 10000)));
+
+ @F = split;
+
+ next if @F < 6; # no valid input line is this short
+ next if $F[$[] eq "";
+ next if ($F[$[] !~ /^\d+$/);
+ ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
+ die("$0: unexpected input line: >$_<\n");
+
+ ;# modified Julian to UNIX epoch
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1]; # add seconds + fraction
+
+ ;# multiply offset by 1000 to get ms - try to avoid float op
+ (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/$1$2.$3/) &&
+ $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
+ || ($F[$[+2] *= 1000);
+
+
+ ;# skip samples out of specified time range
+ next if (defined($StartTime) && $StartTime > $t);
+ next if (defined($EndTime) && $EndTime < $t);
+
+ next if defined($lastT) && $t < $lastT; # backward in time ??
+
+ push(@offs,$F[$[+2]);
+ push(@freq,$F[$[+3] * (2**20/10**6));
+ push(@cmpl,$F[$[+5]);
+
+ push(@break, (defined($lastT) && ($t - $lastT > $deltaT)));
+ $lastT = $t;
+ push(@time,$t);
+ push(@loffset, tell(IN) - length($_));
+ push(@filekey, $key);
+
+ shift(@break),shift(@time),shift(@offs),
+ shift(@freq), shift(@cmpl),shift(@loffset),
+ shift(@filekey)
+ if @time > $cnt &&
+ ! (defined($StartTime) && defined($EndTime));
+
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ close(IN);
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ print "input scanned ($l lines/",scalar(@time)," samples)\n"
+ if $verbose > 1;
+
+ &lr_init('offs');
+ &lr_init('freq');
+
+ if (@time)
+ {
+ local($_,@F);
+
+ local($timebase) unless defined($timebase);
+ local($freqbase) unless defined($freqbase);
+ local($cmplscale) unless defined($cmplscale);
+
+ undef $mintime;
+ undef $maxtime;
+ undef $minoffs;
+ undef $maxoffs;
+ undef $minfreq;
+ undef $maxfreq;
+ undef $mincmpl;
+ undef $maxcmpl;
+ undef $miny;
+ undef $maxy ;
+
+ print "computing ranges\n" if $verbose > 2;
+
+ $LastCnt = @time;
+
+ ;# @time is in ascending order (;-)
+ $mintime = $time[$[];
+ $maxtime = $time[$#time];
+ unless (defined($timebase))
+ {
+ local($time,@X) = (time);
+ @X = localtime($time);
+
+ ;# compute today 00:00:00
+ $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
+
+ }
+ $LastTimeBase = $timebase;
+
+ if ($showoffs)
+ {
+ local($i,$m,$f);
+
+ $minoffs = &min(@offs);
+ $maxoffs = &max(@offs);
+
+ ;# I know, it is not perl style using indices to access arrays,
+ ;# but I have to proccess two arrays in sync, non-destructively
+ ;# (otherwise a (shift(@a1),shift(a2)) would do),
+ ;# I dont like to make copies of these arrays as they may be huge
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$offs[$i],'offs'),$i++
+ while $i <= $#time;
+
+ ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
+
+ $i = &lr_sigma('offs');
+ $m = &lr_mean('offs');
+
+ print "mean offset: $m sigma: $i\n" if $verbose > 2;
+
+ if (($maxoffs - $minoffs) > $MinClip)
+ {
+ $f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
+ $miny = (($m - $minoffs) <= ($f * $i))
+ ? $minoffs : ($m - $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxoffs - $m) <= ($f * $i))
+ ? $maxoffs : ($m + $f * $i);
+ }
+ else
+ {
+ $miny = $minoffs;
+ $maxy = $maxoffs;
+ }
+ ($maxy-$miny) == 0 &&
+ (($maxy,$miny)
+ = (($maxoffs - $minoffs) > 0)
+ ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print "offset min clipped from $minoffs to $miny\n"
+ if $verbose > 2 && $minoffs != $miny;
+ print "offset max clipped from $maxoffs to $maxy\n"
+ if $verbose > 2 && $maxoffs != $maxy;
+ }
+
+ if ($showfreq)
+ {
+ local($i,$m);
+
+ $minfreq = &min(@freq);
+ $maxfreq = &max(@freq);
+
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq,'freq'),
+ $i++
+ while $i <= $#time;
+
+ $i = &lr_sigma('freq');
+ $m = &lr_mean('freq') + $minfreq;
+
+ print "mean frequency: $m sigma: $i\n" if $verbose > 2;
+
+ if (defined($maxy))
+ {
+ local($s) =
+ ($maxfreq - $minfreq)
+ ? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
+
+ if (defined($freqbase))
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScale = 10 ** int(log($s)/log(10) - 0.9999);
+ $FreqScaleInv =
+ ("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" :
+ ($FreqScale == 1 ? "" : (1/$FreqScale));
+
+ $freqbase = ($maxfreq + $minfreq)/ 2 * $FreqScale; #$m * $FreqScale;
+ $freqbase -= ($maxy + $miny) / 2; #&lr_mean('offs');
+
+ ;# round resulting freqbase
+ ;# to precision of min max difference
+ $s = -12;
+ $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1
+ unless ($maxfreq-$minfreq) < 1e-12;
+ $s = 10 ** $s;
+ $freqbase = int($freqbase / $s) * $s;
+ }
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $freqbase = $m unless defined($freqbase);
+ if (($maxfreq - $minfreq) > $MinClip)
+ {
+ $f = (&abs($minfreq) < &abs($maxfreq))
+ ? $FuzzLow : $FuzzBig;
+ $miny = (($freqbase - $minfreq) <= ($f * $i))
+ ? ($minfreq-$freqbase) : (- $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxfreq - $freqbase) <= ($f * $i))
+ ? ($maxfreq-$freqbase) : ($f * $i);
+ }
+ else
+ {
+ $miny = $minfreq - $freqbase;
+ $maxy = $maxfreq - $freqbase;
+ }
+ ($maxy - $miny) == 0 &&
+ (($maxy,$miny) =
+ (($maxfreq - $minfreq) > 0)
+ ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print("frequency min clipped from ",$minfreq-$freqbase,
+ " to $miny\n")
+ if $verbose > 2 && $miny != ($minfreq - $freqbase);
+ print("frequency max clipped from ",$maxfreq-$freqbase,
+ " to $maxy\n")
+ if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
+ }
+ $LastFreqBaseString =
+ sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
+ $LastFreqBase = $freqbase;
+ print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
+ if $verbose > 5;
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $LastFreqBase = 0;
+ $LastFreqBaseString = "";
+ }
+
+ if ($showcmpl)
+ {
+ $mincmpl = &min(@cmpl);
+ $maxcmpl = &max(@cmpl);
+
+ if (!defined($cmplscale))
+ {
+ if (defined($maxy))
+ {
+ local($cmp)
+ = (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
+ $cmplscale = $cmp == $maxy ? 1 : -1;
+
+ foreach (0.01, 0.02, 0.05,
+ 0.1, 0.2, 0.25, 0.4, 0.5,
+ 1, 2, 4, 5,
+ 10, 20, 25, 50,
+ 100, 200, 250, 500, 1000)
+ {
+ $cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
+ }
+ }
+ else
+ {
+ $cmplscale = 1;
+ $miny = $mincmpl ? 0 : -$MinClip;
+ $maxy = $maxcmpl+$MinClip;
+ }
+ }
+ $LastCmplScale = $cmplscale;
+ }
+ else
+ {
+ $LastCmplScale = 1;
+ }
+
+ print "creating plot command input file\n" if $verbose > 2;
+
+
+ print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
+ print OUT ("# timebase is: ",&ctime($LastTimeBase))
+ if defined($LastTimeBase);
+ print OUT ("# frequency is offset by ",
+ ($LastFreqBase >= 0 ? "+" : "-"),
+ "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
+ print OUT ("# compliance is scaled by $LastCmplScale\n");
+ print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
+
+ printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
+ (shift(@break) ? "\n" : ""),
+ (shift(@time) - $LastTimeBase)/3600,
+ shift(@offs),
+ shift(@freq) * $FreqScale - $LastFreqBase,
+ shift(@cmpl) / $LastCmplScale)
+ while(@time);
+ }
+ else
+ {
+ ;# prevent plotcmd from processing empty file
+ print "Creating plot command dummy...\n" if $verbose > 2;
+ print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
+ &lr_sample(0,1,'offs');
+ &lr_sample(1,1,'offs');
+ &lr_sample(0,2,'freq');
+ &lr_sample(1,2,'freq');
+ @time = (0, 1); $maxtime = 1; $mintime = 0;
+ @offs = (1, 1); $maxoffs = 1; $minoffs = 1;
+ @freq = (2, 2); $maxfreq = 2; $minfreq = 2;
+ @cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
+ $LastCnt = 2;
+ $LastFreqBase = 0;
+ $LastCmplScale = 1;
+ $LastTimeBase = 0;
+ $miny = -$MinClip;
+ $maxy = 3 + $MinClip;
+ }
+ close(OUT);
+
+ print "plot command input file created\n"
+ if $verbose > 2;
+
+
+ if (($fpos[$[] eq 'cnt' && scalar(@loffset) >= $cnt) ||
+ ($fpos[$[] eq 'start' && $mintime <= $StartTime) ||
+ ($fpos[$[] eq 'end'))
+ {
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ else # found to few lines - next time start search earlier in file
+ {
+ if ($fpos[$[] eq 'start')
+ {
+ ;# the timestamps we got for F_first and F_last guaranteed
+ ;# that no file is left out
+ ;# the only thing that could happen is:
+ ;# we guessed the starting point wrong
+ ;# compute a new guess from the first record found
+ ;# if this equals our last guess use data of first record
+ ;# otherwise try new guess
+
+ if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
+ {
+ local($noff);
+ $noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
+ $noff = 0 if $noff < 0;
+
+ return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
+ }
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
+ {
+ ;# try to start earlier in file
+ ;# if we already started at the beginning
+ ;# try to use previous file
+ ;# this assumes distance to better starting point is at most one file
+ ;# the primary guess at top of genfile() should usually allow this
+ ;# assumption
+ ;# if the offset of the first sample used is within
+ ;# a different file than we guessed it must have occurred later
+ ;# in the sequence of files
+ ;# this only can happen if our starting file did not contain
+ ;# a valid sample from the starting point we guessed
+ ;# however this does not invalidate our assumption, no check needed
+ local($noff,$key);
+ if ($fpos[$[+2] > 0)
+ {
+ $noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
+ $noff = 0 if $noff < 0;
+ return (@fpos[$[,$[+1],$noff);
+ }
+ else
+ {
+ if ($fpos[$[+1] eq $F_files[$[])
+ {
+ ;# first file - and not enough samples
+ ;# use data of first sample
+ return ($fpos[$[], $filekey[$[], $loffset[$[]);
+ }
+ else
+ {
+ ;# search key of previous file
+ $key = $F_files[$[];
+ @F = reverse(@F_files);
+ while ($_ = shift(@F))
+ {
+ if ($_ eq $fpos[$[+1])
+ {
+ $key = shift(@F) if @F;
+ last;
+ }
+ }
+ $noff = int($F_size{$key} / $RecordSize);
+ $noff -= $cnt - @loffset;
+ $noff = 0 if $noff < 0;
+ $noff *= $RecordSize;
+ return ($fpos[$[], $key, $noff);
+ }
+ }
+ }
+ else
+ {
+ return ();
+ }
+
+ return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
+
+ ;# EOF - 1.1 * avg(line) * $cnt
+ local($val) = $loffset[$#loffset]
+ - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
+ return ($val < 0) ? 0 : $val;
+ }
+}
+
+$Ltime = -1 if ! defined($Ltime);
+$LastFreqBase = 0;
+$LastFreqBaseString = "??";
+
+;# initial setup of plot
+print "initialize plotting\n" if $verbose;
+if (defined($PrintIt))
+{
+ if ($PrintIt =~ m,/,)
+ {
+ print "Saving plot to file $PrintIt\n";
+ print PLOT "set output '$PrintIt'\n";
+ }
+ else
+ {
+ print "Printing plot on printer $PrintIt\n";
+ print PLOT "set output '| lpr -P$PrintIt -h'\n";
+ }
+ print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
+}
+print PLOT "set grid\n";
+print PLOT "set tics out\n";
+print PLOT "set format y '%g '\n";
+printf PLOT "set time 47\n" unless defined($PrintIt);
+
+@filepos =();
+while(1)
+{
+ print &ctime(time) if $verbose;
+
+ ;# update diplay characteristics
+ &read_config;# unless defined($PrintIt);
+
+ unlink($tmpfile);
+ @filepos = &genfile($samples,$srcprefix,$tmpfile,@filepos);
+
+ ;# make plotcmd display samples
+ &make_doplot;
+ print "Displaying plot...\n" if $verbose > 1;
+ print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
+ print PLOT $doplot;
+}
+continue
+{
+ if (defined($PrintIt))
+ {
+ delete $SIG{'CHLD'};
+ print PLOT "quit\n";
+ close(PLOT);
+ if ($PrintIt =~ m,/,)
+ {
+ print "Plot saved to file $PrintIt\n";
+ }
+ else
+ {
+ print "Plot spooled to printer $PrintIt\n";
+ }
+ unlink($tmpfile);
+ exit(0);
+ }
+ ;# wait $delay seconds
+ print "waiting $delay seconds ..." if $verbose > 2;
+ sleep($delay);
+ print " continuing\n" if $verbose > 2;
+ undef($LastFreqBaseString);
+}
+
+
+sub date_time_spec2seconds
+{
+ local($_) = @_;
+ ;# a date_time_spec consistes of:
+ ;# YYYY-MM-DD_HH:MM:SS.ms
+ ;# values can be omitted from the beginning and default than to
+ ;# values of current date
+ ;# values omitted from the end default to lowest possible values
+
+ local($time) = time;
+ local($sec,$min,$hour,$mday,$mon,$year)
+ = localtime($time);
+
+ local($last) = ();
+
+ s/^\D*(.*\d)\D*/$1/; # strip off garbage
+
+ PARSE:
+ {
+ if (s/^(\d{4})(-|$)//)
+ {
+ if ($1 < 1970)
+ {
+ warn("$0: can not handle years before 1970 - year $1 ignored\n");
+ return undef;
+ }
+ elsif ( $1 >= 2070)
+ {
+ warn("$0: can not handle years past 2070 - year $1 ignored\n");
+ return undef;
+ }
+ else
+ {
+ $year = $1 % 100; # 0<= $year < 100
+ ;# - interpreted 70 .. 99,00 .. 69
+ }
+ $last = $[ + 5;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
+ return(undef)
+ if $2 eq '';
+ }
+
+ if (s/^(\d{1,2})(-|$)//)
+ {
+ warn("$0: implausible month $1\n"),return(undef)
+ if $1 < 1 || $1 > 12;
+ $mon = $1 - 1;
+ $last = $[ + 4;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
+ if defined($last);
+
+ }
+
+ if (s/^(\d{1,2})([_ ]|$)//)
+ {
+ warn("$0: implausible month day $1 for month ".($mon+1)." (".
+ $MaxNumDaysPerMonth[$mon].")$mon\n"),
+ return(undef)
+ if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
+ $mday = $1;
+ $last = $[ + 3;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ ;# now we face a problem:
+ ;# if ! defined($last) a prefix of "07:"
+ ;# can be either 07:MM or 07:ss
+ ;# to get the second interpretation make the user add
+ ;# a msec fraction part and check for this special case
+ if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >= 60;
+ warn("$0: implausible second $1\n"), return undef
+ if $2 < 0 || $2 >= 60;
+ $min = $1;
+ $sec = $2;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
+ return undef;
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible hour $1\n"), return undef
+ if $1 < 0 || $1 > 24;
+ $hour = $1;
+ $last = $[ + 2;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $min = $1;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2}(\.\d+)?)//)
+ {
+ warn("$0: implausible second $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $sec = $1;
+ $last = $[;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
+ return undef;
+ }
+ }
+
+ return $time unless defined($last);
+
+ $sec = 0 if $last > $[;
+ $min = 0 if $last > $[ + 1;
+ $hour = 0 if $last > $[ + 2;
+ $mday = 1 if $last > $[ + 3;
+ $mon = 0 if $last > $[ + 4;
+ local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
+
+ ;# $rtime may be off if daylight savings time is in effect at given date
+ return $rtime + ($sec - int($sec))
+ if $hour == (localtime($rtime))[$[+2];
+ return
+ &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
+ + ($sec - int($sec));
+}
+
+
+sub min
+{
+ local($m) = shift;
+
+ grep((($m > $_) && ($m = $_),0),@_);
+ $m;
+}
+
+sub max
+{
+ local($m) = shift;
+
+ grep((($m < $_) && ($m = $_),0),@_);
+ $m;
+}
diff --git a/contrib/ntp/scripts/monitoring/ntptrap b/contrib/ntp/scripts/monitoring/ntptrap
new file mode 100644
index 0000000..5a1bcb1
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/ntptrap
@@ -0,0 +1,463 @@
+#!/local/bin/perl --*-perl-*-
+;#
+;# ntptrap,v 3.1 1993/07/06 01:09:15 jbj Exp
+;#
+;# a client for the xntp mode 6 trap mechanism
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!$1!; # strip to filename
+;# enforce STDOUT and STDERR to be line buffered
+$| = 1;
+select((select(STDERR),$|=1)[$[]);
+
+;#######################################
+;# load utility routines and definitions
+;#
+require('ntp.pl'); # implementation of the NTP protocol
+use Socket;
+
+#eval { require('sys/socket.ph'); require('netinet/in.ph') unless defined(&INADDR_ANY); } ||
+#do {
+ #die("$0: $@") unless $[ == index($@, "Can't locate ");
+ #warn "$0: $@";
+ #warn "$0: supplying some default definitions\n";
+ #eval 'sub INADDR_ANY { 0; } sub AF_INET {2;} sub SOCK_DGRAM {2;} 1;' || die "$0: $@";
+#};
+require('getopts.pl'); # option parsing
+require('ctime.pl'); # date/time formatting
+
+;######################################
+;# define some global constants
+;#
+$BASE_TIMEOUT=10;
+$FRAG_TIMEOUT=10;
+$MAX_TRY = 5;
+$REFRESH_TIME=60*15; # 15 minutes (server uses 1 hour)
+$ntp'timeout = $FRAG_TIMEOUT; #';
+$ntp'timeout if 0;
+
+;######################################
+;# now process options
+;#
+sub usage
+{
+ die("usage: $0 [-n] [-p <port>] [-l <logfile>] [host] ...\n");
+}
+
+$opt_l = "/dev/null"; # where to write debug messages to
+$opt_p = 0; # port to use locally - (0 does mean: will be choosen by kernel)
+
+&usage unless &Getopts('l:p:');
+&Getopts if 0; # make -w happy
+
+@Hosts = ($#ARGV < $[) ? ("localhost") : @ARGV;
+
+;# setup for debug output
+$DEBUGFILE=$opt_l;
+$DEBUGFILE="&STDERR" if $DEBUGFILE eq '-';
+
+open(DEBUG,">>$DEBUGFILE") || die("Cannot open \"$DEBUGFILE\": $!\n");
+select((select(DEBUG),$|=1)[$[]);
+
+;# &log prints a single trap record (adding a (local) time stamp)
+sub log
+{
+ chop($date=&ctime(time));
+ print "$date ",@_,"\n";
+}
+
+sub debug
+{
+ print DEBUG @_,"\n";
+}
+;#
+$proto_udp = (getprotobyname('udp'))[$[+2] ||
+ (warn("$0: Could not get protocoll number for 'udp' using 17"), 17);
+
+$ntp_port = (getservbyname('ntp','udp'))[$[+2] ||
+ (warn("$0: Could not get port number for service ntp/udp using 123"), 123);
+
+;#
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) || die("Cannot open socket: $!\n");
+
+;#
+bind(S, pack("S n a4 x8", &AF_INET, $opt_p, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+&log(sprintf("Listening at address %d.%d.%d.%d port %d",
+ unpack("C4",$my_addr), $my_port));
+
+;# disregister with all servers in case of termination
+sub cleanup
+{
+ &log("Aborted by signal \"$_[$[]\"") if defined($_[$[]);
+
+ foreach (@Hosts)
+ {
+ if ( ! defined($Host{$_}) )
+ {
+ print "no info for host '$_'\n";
+ next;
+ }
+ &ntp'send(S,31,0,"",pack("Sna4x8",&AF_INET,$ntp_port,$Host{$_})); #';
+ }
+ close(S);
+ exit(2);
+}
+
+$SIG{'HUP'} = 'cleanup';
+$SIG{'INT'} = 'cleanup';
+$SIG{'QUIT'} = 'cleanup';
+$SIG{'TERM'} = 'cleanup';
+
+0 && $a && $b;
+sub timeouts # sort timeout id array
+{
+ $TIMEOUTS{$a} <=> $TIMEOUTS{$b};
+}
+
+;# a Request element looks like: pack("a4SC",addr,associd,op)
+@Requests= ();
+
+;# compute requests for set trap control msgs to each host given
+{
+ local($name,$addr);
+
+ foreach (@Hosts)
+ {
+ if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
+ {
+ ($name,$addr) =
+ (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[,$[+4];
+ unless (defined($name))
+ {
+ $name = sprintf("[[%d.%d.%d.%d]]",$1,$2,$3,$4);
+ $addr = pack("C4",$1,$2,$3,$4);
+ }
+ }
+ else
+ {
+ ($name,$addr) = (gethostbyname($_))[$[,$[+4];
+ unless (defined($name))
+ {
+ warn "$0: unknown host \"$_\" - ignored\n";
+ next;
+ }
+ }
+ next if defined($Host{$name});
+ $Host{$name} = $addr;
+ $Host{$_} = $addr;
+ push(@Requests,pack("a4SC",$addr,0,6)); # schedule a set trap request for $name
+ }
+}
+
+sub hostname
+{
+ local($addr) = @_;
+ return $HostName{$addr} if defined($HostName{$addr});
+ local($name) = gethostbyaddr($addr,&AF_INET);
+ &debug(sprintf("hostname(%d.%d.%d.%d) = \"%s\"",unpack("C4",$addr),$name))
+ if defined($name);
+ defined($name) && ($HostName{$addr} = $name) && (return $name);
+ &debug(sprintf("Failed to get name for %d.%d.%d.%d",unpack("C4",$addr)));
+ return sprintf("[%d.%d.%d.%d]",unpack("C4",$addr));
+}
+
+;# when no hosts were given on the commandline no requests have been scheduled
+&usage unless (@Requests);
+
+&debug(sprintf("%d request(s) scheduled",scalar(@Requests)));
+grep(&debug(" - ".$_),keys(%Host));
+
+;# allocate variables;
+$addr="";
+$assoc=0;
+$op = 0;
+$timeout = 0;
+$ret="";
+%TIMEOUTS = ();
+%TIMEOUT_PROCS = ();
+@TIMEOUTS = ();
+
+$len = 512;
+$buf = " " x $len;
+
+while (1)
+{
+ if (@Requests || @TIMEOUTS) # if there is some work pending
+ {
+ if (@Requests)
+ {
+ ($addr,$assoc,$op) = unpack("a4SC",($req = shift(@Requests)));
+ &debug(sprintf("Request: %s: %s(%d)",&hostname($addr), &ntp'cntrlop_name($op), $assoc)); #';))
+ $ret = &ntp'send(S,$op,$assoc,"", #'(
+ pack("Sna4x8",&AF_INET,$ntp_port,$addr));
+ &set_timeout("retry-".unpack("H*",$req),time+$BASE_TIMEOUT,
+ sprintf("&retry(\"%s\");",unpack("H*",$req)));
+
+ last unless (defined($ret)); # warn called by ntp'send();
+
+ ;# if there are more requests just have a quick look for new messages
+ ;# otherwise grant server time for a response
+ $timeout = @Requests ? 0 : $BASE_TIMEOUT;
+ }
+ if ($timeout && @TIMEOUTS)
+ {
+ ;# ensure not to miss a timeout
+ if ($timeout + time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ $timeout = $TIMEOUTS{$TIMEOUTS[$[]} - time;
+ $timeout = 0 if $timeout < 0;
+ }
+ }
+ }
+ else
+ {
+ ;# no work yet - wait for some messages dropping in
+ ;# usually this will not hapen as the refresh semantic will
+ ;# always have a pending timeout
+ undef($timeout);
+ }
+
+ vec($mask="",fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ warn("$0: select: $!\n"),last if $ret < 0; # give up on error return from select
+
+ if ($ret == 0)
+ {
+ ;# timeout
+ if (@TIMEOUTS && time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ ;# handle timeout
+ $timeout_proc =
+ (delete $TIMEOUT_PROCS{$TIMEOUTS[$[]},
+ delete $TIMEOUTS{shift(@TIMEOUTS)})[$[];
+ eval $timeout_proc;
+ die "timeout eval (\"$timeout_proc\"): $@\n" if $@;
+ }
+ ;# else: there may be something to be sent
+ }
+ else
+ {
+ ;# data avail
+ $from = recv(S,$buf,$len,0);
+ ;# give up on error return from recv
+ warn("$0: recv: $!\n"), last unless (defined($from));
+
+ $from = (unpack("Sna4",$from))[$[+2]; # keep host addr only
+ ;# could check for ntp_port - but who cares
+ &debug("-Packet from ",&hostname($from));
+
+ ;# stuff packet into ntp mode 6 receive machinery
+ ($ret,$data,$status,$associd,$op,$seq,$auth_keyid) =
+ &ntp'handle_packet($buf,$from); # ';
+ &debug(sprintf("%s uses auth_keyid %d",&hostname($from),$auth_keyid)) if defined($auth_keyid);
+ next unless defined($ret);
+
+ if ($ret eq "")
+ {
+ ;# handle packet
+ ;# simple trap response messages have neither timeout nor retries
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op))) unless $op == 7;
+ delete $RETRY{pack("a4SC",$from,$associd,$op)} unless $op == 7;
+
+ &process_response($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid);
+ }
+ else
+ {
+ ;# some kind of error
+ &log(sprintf("%50s: %s: %s",(gethostbyaddr($from,&AF_INET))[$[],$ret,$data));
+ if ($ret ne "TIMEOUT" && $ret ne "ERROR")
+ {
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op)));
+ }
+ }
+ }
+
+}
+
+warn("$0: terminating\n");
+&cleanup;
+exit 0;
+
+;##################################################
+;# timeout support
+;#
+sub set_timeout
+{
+ local($id,$time,$proc) = @_;
+
+ $TIMEOUTS{$id} = $time;
+ $TIMEOUT_PROCS{$id} = $proc;
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ chop($date=&ctime($time));
+ &debug(sprintf("Schedule timeout \"%s\" for %s", $id, $date));
+}
+
+sub clear_timeout
+{
+ local($id) = @_;
+ delete $TIMEOUTS{$id};
+ delete $TIMEOUT_PROCS{$id};
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ &debug("Clear timeout \"$id\"");
+}
+
+0 && &refresh;
+sub refresh
+{
+ local($addr) = @_[$[];
+ $addr = pack("H*",$addr);
+ &debug(sprintf("Refreshing trap for %s", &hostname($addr)));
+ push(@Requests,pack("a4SC",$addr,0,6));
+}
+
+0 && &retry;
+sub retry
+{
+ local($tag) = @_;
+ $tag = pack("H*",$tag);
+ $RETRY{$tag} = 0 if (!defined($RETRY{$tag}));
+
+ if (++$RETRY{$tag} > $MAX_TRY)
+ {
+ &debug(sprintf("Retry failed: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ return;
+ }
+ &debug(sprintf("Retrying: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ push(@Requests,$tag);
+}
+
+sub process_response
+{
+ local($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid) = @_;
+
+ $msg="";
+ if ($op == 7) # trap response
+ {
+ $msg .= sprintf("%40s trap#%-5d",
+ &hostname($from),$seq);
+ &debug (sprintf("\nTrap %d associd %d:\n%s\n===============\n",$seq,$associd,$data));
+ if ($associd == 0) # system event
+ {
+ $msg .= " SYSTEM ";
+ $evnt = &ntp'SystemEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases add additional info
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ ($refid) = ($data =~ /refid=([\w\.]+)/);
+ $msg .= "stratum=$stratum refid=$refid";
+ if ($refid =~ /\[?(\d+)\.(\d+)\.(\d+)\.(\d+)/)
+ {
+ local($x) = (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET));
+ $msg .= " " . $x if defined($x)
+ }
+ if ($evnt eq "event_sync_chg")
+ {
+ $msg .= sprintf("%s %s ",
+ &ntp'LI($status), #',
+ &ntp'ClockSource($status) #'
+ );
+ }
+ elsif ($evnt eq "event_sync/strat_chg")
+ {
+ ($peer) = ($data =~ /peer=([0-9]+)/);
+ $msg .= " peer=$peer";
+ }
+ elsif ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ }
+ else # peer event
+ {
+ $msg .= sprintf("peer %5d ",$associd);
+ ($srcadr) = ($data =~ /srcadr=\[?([\d\.]+)/);
+ $msg .= sprintf("%-18s %40s ", "[$srcadr]",
+ &hostname(pack("C4",split(/\./,$srcadr))));
+ $evnt = &ntp'PeerEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases include additional info
+ if ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ;#&debug("----\n$data\n====\n");
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ ;# no clockvars included - post a cv request
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ elsif ($evnt eq "event_stratum_chg")
+ {
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ $msg .= "new stratum $stratum";
+ }
+ }
+ }
+ elsif ($op == 6) # set trap resonse
+ {
+ &debug("Set trap ok from ",&hostname($from));
+ &set_timeout("refresh-".unpack("H*",$from),time+$REFRESH_TIME,
+ sprintf("&refresh(\"%s\");",unpack("H*",$from)));
+ return;
+ }
+ elsif ($op == 4) # read clock variables response
+ {
+ ;# status of clock
+ $msg .= sprintf(" %40s ", &hostname($from));
+ if ($associd == 0)
+ {
+ $msg .= "system clock status: ";
+ }
+ else
+ {
+ $msg .= sprintf("peer %5d clock",$associd);
+ }
+ $msg .= sprintf("%-32s",&ntp'clock_status($status)); #');
+ ($device) = ($data =~ /device=\"([^\"]+)\"/);
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ elsif ($op == 31) # unset trap response (UNOFFICIAL op)
+ {
+ ;# clear timeout
+ &debug("Clear Trap ok from ",&hostname($from));
+ &clear_timeout("refresh-".unpack("H*",$from));
+ return;
+ }
+ else # unexpected response
+ {
+ $msg .= "unexpected response to op $op assoc=$associd";
+ $msg .= sprintf(" status=%04x",$status);
+ }
+ &log($msg);
+}
diff --git a/contrib/ntp/scripts/monitoring/timelocal.pl b/contrib/ntp/scripts/monitoring/timelocal.pl
new file mode 100644
index 0000000..d0f73a2
--- /dev/null
+++ b/contrib/ntp/scripts/monitoring/timelocal.pl
@@ -0,0 +1,77 @@
+;# timelocal.pl
+;#
+;# Usage:
+;# $time = timelocal($sec,$min,$hours,$mday,$mon,$year,$junk,$junk,$isdst);
+;# $time = timegm($sec,$min,$hours,$mday,$mon,$year);
+
+;# These routines are quite efficient and yet are always guaranteed to agree
+;# with localtime() and gmtime(). We manage this by caching the start times
+;# of any months we've seen before. If we know the start time of the month,
+;# we can always calculate any time within the month. The start times
+;# themselves are guessed by successive approximation starting at the
+;# current time, since most dates seen in practice are close to the
+;# current date. Unlike algorithms that do a binary search (calling gmtime
+;# once for each bit of the time value, resulting in 32 calls), this algorithm
+;# calls it at most 6 times, and usually only once or twice. If you hit
+;# the month cache, of course, it doesn't call it at all.
+
+;# timelocal is implemented using the same cache. We just assume that we're
+;# translating a GMT time, and then fudge it when we're done for the timezone
+;# and daylight savings arguments. The timezone is determined by examining
+;# the result of localtime(0) when the package is initialized. The daylight
+;# savings offset is currently assumed to be one hour.
+
+CONFIG: {
+ package timelocal;
+
+ @epoch = localtime(0);
+ $tzmin = $epoch[2] * 60 + $epoch[1]; # minutes east of GMT
+ if ($tzmin > 0) {
+ $tzmin = 24 * 60 - $tzmin; # minutes west of GMT
+ $tzmin -= 24 * 60 if $epoch[5] == 70; # account for the date line
+ }
+
+ $SEC = 1;
+ $MIN = 60 * $SEC;
+ $HR = 60 * $MIN;
+ $DAYS = 24 * $HR;
+ $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
+}
+
+sub timegm {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS;
+}
+
+sub timelocal {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS
+ + $tzmin * $MIN - 60 * 60 * ($_[8] != 0);
+}
+
+package timelocal;
+
+sub cheat {
+ $year = $_[5];
+ $month = $_[4];
+ $guess = $^T;
+ @g = gmtime($guess);
+ $year += $YearFix if $year < $epoch[5];
+ while ($diff = $year - $g[5]) {
+ $guess += $diff * (364 * $DAYS);
+ @g = gmtime($guess);
+ }
+ while ($diff = $month - $g[4]) {
+ $guess += $diff * (28 * $DAYS);
+ @g = gmtime($guess);
+ }
+ $g[3]--;
+ $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS;
+ $cheat{$ym} = $guess;
+}
diff --git a/contrib/ntp/scripts/ntp-groper b/contrib/ntp/scripts/ntp-groper
new file mode 100755
index 0000000..1fd0cfe
--- /dev/null
+++ b/contrib/ntp/scripts/ntp-groper
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# ntpgroper host ...
+#
+# This script checks each hostname given as an argument to see if
+# it is running NTP. It reports one of the following messages (assume
+# the host is named "dumbo.hp.com":
+#
+# dumbo.hp.com not registered in DNS
+# dumbo.hp.com not responding to ping
+# dumbo.hp.com refused ntpq connection
+# dumbo.hp.com not responding to NTP
+# dumbo.hp.com answers NTP version 2, stratum: 3, ref: telford.nsa.hp.com
+# dumbo.hp.com answers NTP version 3, stratum: 3, ref: telford.nsa.hp.com
+#
+# It ain't pretty, but it is kinda useful.
+#
+# Walter Underwood, 11 Feb 1993, wunder@hpl.hp.com
+#
+# converted to /bin/sh from /bin/ksh by scott@ee.udel.edu 24 Mar 1993
+
+PATH="/usr/local/etc:$PATH" export PATH
+
+verbose=1
+logfile=/tmp/cntp-log$$
+ntpqlog=/tmp/cntp-ntpq$$
+
+# I wrap the whole thing in parens so that it is possible to redirect
+# all the output somewhere, if desired.
+(
+for host in $*
+do
+ # echo "Trying $host."
+
+ gethost $host > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not registered in DNS"
+ continue
+ fi
+
+ ping $host 64 1 > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not responding to ping"
+ continue
+ fi
+
+ # Attempt to contact with version 3 ntp, then try version 2.
+ for version in 3 2
+ do
+
+ ntpq -c "ntpversion $version" -p $host > $ntpqlog 2>&1
+
+ if fgrep -s 'Connection refused' $ntpqlog
+ then
+ echo "$host refused ntpq connection"
+ break
+ fi
+
+ responding=1
+ fgrep -s 'timed out, nothing received' $ntpqlog > /dev/null && responding=0
+
+ if [ $responding -eq 1 ]
+ then
+ ntpq -c "ntpversion $version" -c rl $host > $ntpqlog
+
+ # First we extract the reference ID (usually a host or a clock)
+ synchost=`fgrep "refid=" $ntpqlog`
+ #synchost=${synchost##*refid=} # strip off the beginning of the line
+ #synchost=${synchost%%,*} # strip off the end
+ synchost=`expr "$synchost" : '.*refid=\([^,]*\),.*'`
+
+ # Next, we get the stratum
+ stratum=`fgrep "stratum=" $ntpqlog`
+ #stratum=${stratum##*stratum=}
+ #stratum=${stratum%%,*}
+ stratum=`expr "$stratum" : '.*stratum=\([^,]*\),.*'`
+
+ echo "$host answers NTP version $version, stratum: $stratum, ref: $synchost"
+ break;
+ fi
+
+ if [ $version -eq 2 -a $responding -eq 0 ]
+ then
+ echo "$host not responding to NTP"
+ fi
+ done
+done
+)
+# ) >> $logfile
+
+if [ -f $ntpqlog ]; then
+ rm $ntpqlog
+fi
diff --git a/contrib/ntp/scripts/ntp-restart b/contrib/ntp/scripts/ntp-restart
new file mode 100755
index 0000000..d2023f0
--- /dev/null
+++ b/contrib/ntp/scripts/ntp-restart
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# This script can be used to kill and restart the NTP daemon. Edit the
+# /usr/local/bin/xntpd line to fit.
+#
+kill -INT `ps -ax | egrep "xntpd" | egrep -v "egrep" | sed 's/^\([ 0-9]*\) .*/\1'/`
+sleep 10
+/usr/local/bin/xntpd
+exit 0
diff --git a/contrib/ntp/scripts/ntpver.in b/contrib/ntp/scripts/ntpver.in
new file mode 100644
index 0000000..be36897
--- /dev/null
+++ b/contrib/ntp/scripts/ntpver.in
@@ -0,0 +1,7 @@
+#!@PATH_SH@
+# print version string of NTP daemon
+# Copyright (c) 1997 by Ulrich Windl
+# Modified 970318: Harlan Stenn: rewritten...
+# usage: ntpver hostname
+
+ntpq -c "rv 0 daemon_version" $* | awk '/daemon_version/ { print $2 }'
diff --git a/contrib/ntp/scripts/plot_summary.pl b/contrib/ntp/scripts/plot_summary.pl
new file mode 100755
index 0000000..5be0182
--- /dev/null
+++ b/contrib/ntp/scripts/plot_summary.pl
@@ -0,0 +1,333 @@
+#!/usr/bin/perl -w
+# $Id: plot_summary.pl,v 1.1.1.1 1999/05/26 00:48:25 stenn Exp $
+#
+# Use Gnuplot to display data in summary files produced by summary.pl.
+# This script requires GNUPLOT 3.6 (pre 3.6 beta 319)!
+#
+# Copyright (c) 1997, Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 5.003; # "never tested with any other version of Perl"
+use strict;
+
+use Time::Local;
+use Getopt::Long;
+
+# parse command line
+my $summary_dir = "/tmp";
+my $identifier = `hostname`; # origin of these data
+chomp $identifier; # remove newline
+my $offset_limit = 0.128; # limit of absolute offset
+my $output_file = ""; # output file defaults to stdout
+my $output_file_number = 1; # numbering of output files
+my $gnuplot_terminal = $ENV{DISPLAY} ? "x11" : "dumb";
+my $wait_after_plot = 1;
+my @peer_list = ();
+
+my %options = ("directory=s" => \$summary_dir,
+ "identifier=s" => \$identifier,
+ "offset-limit=f" => \$offset_limit,
+ "output-file=s" => \$output_file,
+ "peer=s@" => \@peer_list,
+ "plot-term=s" => \$gnuplot_terminal,
+ "wait-after-plot!" => \$wait_after_plot,
+ );
+if ( !GetOptions(%options) ) {
+ print STDERR "usage: $0\n";
+ my $opt;
+ foreach $opt (sort(keys %options)) {
+ print STDERR "\t--$opt ";
+ if ( ref($options{$opt}) eq "ARRAY" ) {
+ print STDERR "(" . join (" ", @{$options{$opt}}) . ")\n";
+ } else {
+ print STDERR "(${$options{$opt}})\n";
+ }
+ }
+ print STDERR "\n";
+ die;
+}
+
+chomp $identifier;
+die "illegal offset-limit: $offset_limit" unless $offset_limit > 0.0;
+$offset_limit *= 1e6; # scale to microseconds
+
+# return the smallest value in the given list
+sub min
+{
+ my ($result, @rest) = @_;
+ map { $result = $_ if ($_ < $result) } @rest;
+ return($result);
+}
+
+# return the largest value in the given list
+sub max
+{
+ my ($result, @rest) = @_;
+ map { $result = $_ if ($_ > $result) } @rest;
+ return($result);
+}
+
+# maybe open alternate output file
+sub open_output
+{
+ my $file;
+ if ($output_file) {
+ while ( -r ($file = "$output_file$output_file_number") ) {
+ ++$output_file_number;
+ }
+ open TOUCH, ">$file" and close TOUCH or die "$file: $!";
+ print "set output \"$file\"\n";
+ }
+}
+
+# make Gnuplot wait
+sub maybe_add_pause
+{
+ print "pause -1 \"Press key to continue...\"\n" if $wait_after_plot;
+}
+
+# plot data from loop summary
+sub do_loop
+{
+ my $fname = shift;
+ my $line;
+ my $out_file = "/tmp/tempdata$$";
+ my $cmd_file = "/tmp/tempcmd$$";
+ my ($first_day, $day_out) = ("", 0);
+ my ($lower_bound, $upper_bound, $rms);
+ my ($min_offs, $max_offs) = (1e9, -1e9);
+ my ($min_rms, $max_rms) = (1e9, -1e9);
+ open INPUT, "$fname" or die "$fname: $!";
+ open OUTPUT, ">$out_file" or die "$out_file: $!";
+ my @Fld;
+ while (<INPUT>) {
+ chop; # strip record separator
+ @Fld = split;
+ if ($#Fld == 0) {
+# loops.19960405
+ $_ = $Fld[0]; s/.*([12]\d{3}[01]\d[0-3]\d)$/$1/;
+ m/(\d{4})(\d{2})(\d{2})/;
+ $line = timegm (59, 59, 23, $3, $2 - 1, $1 - 1900, 0, 0, 0);
+ $line = int $line / 86400; # days relative to 1970
+ $first_day = "$1-$2-$3 ($line)" unless $day_out;
+ next;
+ }
+ if ($#Fld != 8) {
+ warn "Illegal number of fields in file $fname, line $.";
+ next;
+ }
+# loop 216, 856106+/-874041.5, rms 117239.8, freq 67.52+/-10.335, var 4.850
+ $_ = $Fld[1]; s/,/ /; $line .= " $_";
+ $_ = $Fld[2]; m:(.+?)\+/-(.+),:;
+ $lower_bound = $1 - $2;
+ $upper_bound = $1 + $2;
+ $line .= "$1 $lower_bound $upper_bound";
+ $min_offs = min($min_offs, $lower_bound);
+ $max_offs = max($max_offs, $upper_bound);
+ $_ = $Fld[4]; s/,/ /; $rms = $_;
+ $min_rms = min($min_rms, $rms);
+ $max_rms = max($max_rms, $rms);
+ $line .= " $rms";
+ $_ = $Fld[6]; m:(.+?)\+/-(.+),:;
+ $line .= " $1 " . ($1-$2) . " " . ($1+$2);
+ $line .= " $Fld[8]";
+ print OUTPUT "$line\n";
+ $day_out = 1;
+# 9621 216 856106 -17935.5 1730147.5 117239.8 67.52 57.185 77.855 4.850
+ }
+ close INPUT;
+ close OUTPUT or die "close failed on $out_file: $!";
+ my $ylimit = "[";
+ if ($min_offs < -$offset_limit) {
+ $ylimit .= "-$offset_limit";
+ }
+ $ylimit .= ":";
+ if ($max_offs > $offset_limit) {
+ $ylimit .= "$offset_limit";
+ }
+ if ( $ylimit eq "[:" ) {
+ $ylimit = "";
+ } else {
+ $ylimit = "[] $ylimit]";
+ }
+ open OUTPUT, "> $cmd_file" or die "$cmd_file: $!";
+ my $oldfh = select OUTPUT;
+ print "set term $gnuplot_terminal\n";
+ open_output;
+ print "set grid\n";
+ print "set title \"Loop Summary for $identifier: " .
+ "Daily mean values since $first_day\\n" .
+ "(Offset limit is $offset_limit microseconds)\"\n";
+ print "set ylabel \"[us]\"\n";
+ print "set data style yerrorbars\n";
+ print "set multiplot\n";
+ print "set size 1, 0.5\n";
+ print "set lmargin 8\n";
+ print "set origin 0, 0.5\n";
+ print "plot $ylimit \"$out_file\"" .
+ " using 1:3:4:5 title \"mean offset\", ";
+ print "\"$out_file\" using 1:(\$3-\$6/2) " .
+ "title \"(sigma low)\" with lines, ";
+ print "\"$out_file\" using 1:3 smooth bezier " .
+ "title \"(Bezier med)\" with lines, ";
+ print "\"$out_file\" using 1:(\$3+\$6/2) " .
+ "title \"(sigma high)\" with lines\n";
+ print "set ylabel \"[ppm]\"\n";
+ print "set origin 0, 0.0\n";
+ print "set title\n";
+ print "set xlabel \"Days relative to 1970\"\n";
+ print "plot \"$out_file\" using 1:7:8:9 title \"mean frequency\", ";
+ print "\"$out_file\" using 1:(\$7-\$10/2) " .
+ "title \"(sigma low)\" with lines, ";
+ print "\"$out_file\" using 1:7 smooth bezier " .
+ "title \"(Bezier med)\" with lines, ";
+ print "\"$out_file\" using 1:(\$7+\$10/2) " .
+ "title \"(sigma high)\" with lines\n";
+ print "set nomultiplot\n";
+ maybe_add_pause;
+
+ my $ylimit = "[";
+ if ($min_rms < -$offset_limit) {
+ $ylimit .= "-$offset_limit";
+ }
+ $ylimit .= ":";
+ if ($max_rms > $offset_limit) {
+ $ylimit .= "$offset_limit";
+ }
+ if ( $ylimit eq "[:" ) {
+ $ylimit = "";
+ } else {
+ $ylimit = "[] $ylimit]";
+ }
+
+ open_output;
+ print "set title \"Loop Summary for $identifier: " .
+ "Standard deviation since $first_day\\n" .
+ "(Offset limit is $offset_limit microseconds)\"\n";
+ print "set xlabel\n";
+ print "set ylabel \"[us]\"\n";
+ print "set origin 0, 0.5\n";
+ print "set data style linespoints\n";
+ print "set multiplot\n";
+ print "plot $ylimit \"$out_file\" using 1:6 title \"Offset\", ";
+ print "\"$out_file\" using 1:6 smooth bezier " .
+ "title \"(Bezier)\" with lines\n";
+ print "set title\n";
+ print "set origin 0, 0.0\n";
+ print "set xlabel \"Days relative to 1970\"\n";
+ print "set ylabel \"[ppm]\"\n";
+ print "plot \"$out_file\" using 1:10 title \"Frequency\", ";
+ print "\"$out_file\" using 1:10 smooth bezier " .
+ "title \"(Bezier)\" with lines\n";
+ print "set nomultiplot\n";
+ maybe_add_pause;
+
+ close OUTPUT or die "close failed on $cmd_file: $!";
+ select $oldfh;
+ print `gnuplot $cmd_file`;
+ unlink $cmd_file;
+ unlink $out_file;
+}
+
+# plot data form peer summary
+sub do_peer
+{
+ my $fname = shift;
+ my $peer = shift;
+ my $out_file = "/tmp/tempdata$$";
+ my $cmd_file = "/tmp/tempcmd$$";
+ my $line;
+ my ($first_day, $day_out) = ("", 0);
+ open INPUT, "$fname" or die "$fname: $!";
+ open OUTPUT, ">$out_file" or die "$out_file: $!";
+ my @Fld;
+ while (<INPUT>) {
+ chop; # strip record separator
+ @Fld = split;
+ if ($#Fld == 0) {
+# peers.19960405
+ $_ = $Fld[0]; s/.*([12]\d{3}[01]\d[0-3]\d)$/$1/;
+ m/(\d{4})(\d{2})(\d{2})/ or next;
+ $line = timegm (59, 59, 23, $3, $2 - 1, $1 - 1900, 0, 0, 0);
+ $line = int $line / 86400; # days relative to 1970
+ $first_day = "$1-$2-$3 ($line)" unless $day_out;
+ next;
+ }
+ if ($#Fld != 7) {
+ warn "Illegal number of fields in file $fname, line $.";
+ next;
+ }
+ next if ($Fld[0] ne $peer);
+# ident cnt mean rms max delay dist disp
+# 127.127.8.1 38 30.972 189.867 1154.607 0.000 879.760 111.037
+ $Fld[0] = $line;
+ print OUTPUT join(' ', @Fld) . "\n";
+# 9969 38 30.972 189.867 1154.607 0.000 879.760 111.037
+ $day_out = 1;
+ }
+ close INPUT;
+ close OUTPUT or die "close failed on $out_file: $!";
+ die "no data found for peer $peer" if !$day_out;
+ open OUTPUT, "> $cmd_file" or die "$cmd_file: $!";
+ my $oldfh = select OUTPUT;
+ print "set term $gnuplot_terminal\n";
+ open_output;
+ print "set grid\n";
+ print "set multiplot\n";
+ print "set lmargin 8\n";
+ print "set size 1, 0.34\n";
+ print "set origin 0, 0.66\n";
+ print "set title " .
+ "\"Peer Summary for $peer on $identifier since $first_day\"\n";
+ print "set data style linespoints\n";
+ print "set ylabel \"[us]\"\n";
+ print "plot \"$out_file\" using 1:3 title \"mean offset\", ";
+ print "\"$out_file\" using 1:3 smooth bezier " .
+ "title \"(Bezier)\" with lines, ";
+ print "\"$out_file\" using 1:(\$3-\$7/2) " .
+ "title \"(sigma low)\" with lines, ";
+ print "\"$out_file\" using 1:(\$3+\$7/2) " .
+ "title \"(sigma high)\" with lines\n";
+ print "set title\n";
+ print "set origin 0, 0.34\n";
+ print "set size 1, 0.32\n";
+ print "set ylabel\n";
+ print "plot \"$out_file\" using 1:7 title \"dist\", ";
+ print "\"$out_file\" using 1:7 smooth bezier " .
+ "title \"(Bezier)\" with lines\n";
+ print "set origin 0, 0.00\n";
+ print "set size 1, 0.35\n";
+ print "set xlabel \"Days relative to 1970\"\n";
+ print "plot \"$out_file\" using 1:8 title \"disp\", ";
+ print "\"$out_file\" using 1:8 smooth bezier " .
+ "title \"(Bezier)\" with lines\n";
+ print "set nomultiplot\n";
+ maybe_add_pause;
+
+ select $oldfh;
+ close OUTPUT or die "close failed on $cmd_file: $!";
+ print `gnuplot $cmd_file`;
+ unlink $cmd_file;
+ unlink $out_file;
+}
+
+
+my $loop_summary ="$summary_dir/loop_summary";
+my $peer_summary ="$summary_dir/peer_summary";
+my $clock_summary="$summary_dir/clock_summary";
+
+do_loop $loop_summary;
+map { do_peer $peer_summary, $_ } @peer_list;
diff --git a/contrib/ntp/scripts/rc1/postinstall b/contrib/ntp/scripts/rc1/postinstall
new file mode 100644
index 0000000..d84b8c5
--- /dev/null
+++ b/contrib/ntp/scripts/rc1/postinstall
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/xntp start
diff --git a/contrib/ntp/scripts/rc1/preinstall b/contrib/ntp/scripts/rc1/preinstall
new file mode 100644
index 0000000..aa18639
--- /dev/null
+++ b/contrib/ntp/scripts/rc1/preinstall
@@ -0,0 +1,6 @@
+#!/bin/sh
+if [ -x /etc/init.d/xntp ]
+then
+ /etc/init.d/xntp stop
+fi
+exit 0
diff --git a/contrib/ntp/scripts/rc1/preremove b/contrib/ntp/scripts/rc1/preremove
new file mode 100644
index 0000000..b870151
--- /dev/null
+++ b/contrib/ntp/scripts/rc1/preremove
@@ -0,0 +1,4 @@
+#!/bin/sh
+/etc/init.d/xntp stop
+
+exit 0
diff --git a/contrib/ntp/scripts/rc1/prototype b/contrib/ntp/scripts/rc1/prototype
new file mode 100644
index 0000000..3de20b0
--- /dev/null
+++ b/contrib/ntp/scripts/rc1/prototype
@@ -0,0 +1,19 @@
+!default 755 root bin
+i pkginfo
+i preinstall
+i postinstall
+i preremove
+f none /etc/init.d/xntp=xntp 0755 root other
+l none /etc/rc2.d/S79xntp=/etc/init.d/xntp
+l none /etc/rc1.d/K79xntp=/etc/init.d/xntp
+l none /etc/rc0.d/K79xntp=/etc/init.d/xntp
+f none /usr/sbin/xntpd=xntpd/xntpd 0555 root other
+f none /usr/sbin/xntpdc=xntpdc/xntpdc 0555 root other
+f none /usr/sbin/ntpq=ntpq/ntpq 0555 root other
+f none /usr/sbin/ntptrace=ntptrace/ntptrace 0555 root other
+f none /usr/sbin/ntpdate=ntpdate/ntpdate 0555 root other
+f none /usr/share/man/man1m/xntpd.1m=doc/xntpd.8 0444 root other
+f none /usr/share/man/man1m/xntpdc.1m=doc/xntpdc.8 0444 root other
+f none /usr/share/man/man1m/ntpdate.1m=doc/ntpdate.8 0444 root other
+f none /usr/share/man/man1m/ntpq.1m=doc/ntpq.8 0444 root other
+f none /usr/share/man/man1m/ntptrace.1m=doc/ntptrace.8 0444 root other
diff --git a/contrib/ntp/scripts/rc1/xntp b/contrib/ntp/scripts/rc1/xntp
new file mode 100644
index 0000000..227b943
--- /dev/null
+++ b/contrib/ntp/scripts/rc1/xntp
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+killproc() { # kill named processes
+ pid=`/usr/bin/ps -e |
+ /usr/bin/grep $1 |
+ /usr/bin/sed -e 's/^ *//' -e 's/ .*//'`
+ [ "$pid" != "" ] && kill $pid
+}
+
+case "$1" in
+'start')
+ ps -e | grep xntpd > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ echo "ntp daemon already running. ntp start aborted"
+ exit 0
+ fi
+ if [ -f /etc/inet/ntp.conf -a -x /usr/sbin/xntpd ]
+ then
+ /usr/sbin/xntpd -c /etc/inet/ntp.conf
+ fi
+ ;;
+'stop')
+ killproc xntpd
+ ;;
+*)
+ echo "Usage: /etc/init.d/xntp { start | stop }"
+ ;;
+esac
diff --git a/contrib/ntp/scripts/rc2/local.ntpd b/contrib/ntp/scripts/rc2/local.ntpd
new file mode 100644
index 0000000..ba53e05
--- /dev/null
+++ b/contrib/ntp/scripts/rc2/local.ntpd
@@ -0,0 +1,64 @@
+#! /usr/bin/perl -w
+# 980904 Harlan Stenn - created
+
+# vvv CHANGE THESE vvv
+
+$ps = "/bin/ps x |";
+
+$ntp_conf = "/etc/ntp.conf";
+$ntpd = "/usr/local/bin/xntpd";
+$ntpdate = "/usr/local/bin/ntpdate -b -s 10.0.0.1 10.0.0.2";
+
+# ^^^ CHANGE THESE ^^^
+
+{
+ if (0)
+ {
+ }
+ elsif ($ARGV[0] eq "start")
+ {
+ @pidlist = pidlist($ntpd);
+ if (defined(@pidlist))
+ {
+ warn "NTP is already running\n";
+ }
+ else
+ {
+ if ( -f $ntp_conf && -x $ntpd )
+ {
+ system ($ntpdate);
+ system ($ntpd." -c ".$ntp_conf);
+ }
+ }
+ }
+ elsif ($ARGV[0] eq "stop")
+ {
+ @pidlist = pidlist($ntpd);
+ kill 'TERM', @pidlist if (scalar(@pidlist) > 0);
+ }
+ else
+ {
+ die "Usage: $0 {start,stop}\n";
+ }
+}
+
+sub pidlist ($)
+ {
+ my ($target) = @_;
+ my ($qt) = quotemeta($target);
+ my @pids;
+
+ open(PS, $ps) || die "Can't run ps: $!\n";
+ while (<PS>)
+ {
+ chomp;
+ next unless (/$qt/);
+ print "Got <$_>\n";
+ if (/^\s*(\d+)\s+/)
+ {
+ push @pids, $1;
+ }
+ }
+ close(PS);
+ return @pids;
+ }
diff --git a/contrib/ntp/scripts/stats.ulrich.patches b/contrib/ntp/scripts/stats.ulrich.patches
new file mode 100644
index 0000000..fe642f6
--- /dev/null
+++ b/contrib/ntp/scripts/stats.ulrich.patches
@@ -0,0 +1,1003 @@
+
+Received: from huey2.ee.udel.edu by mail.eecis.udel.edu id aa25207;
+ 10 Dec 1997 10:39 EST
+Received: from copland.udel.edu by huey.udel.edu id aa16958;
+ 10 Dec 1997 10:39 EST
+Received: from rrzs2.rz.uni-regensburg.de (rrzs2.rz.uni-regensburg.de [132.199.1.2]) by copland.udel.edu (8.8.5/8.7.3) with ESMTP id KAA21293 for <mills@udel.edu>; Wed, 10 Dec 1997 10:39:12 -0500 (EST)
+Received: from ngate.ngate.uni-regensburg.de (ngate.rz.uni-regensburg.de [132.199.3.13])
+ by rrzs2.rz.uni-regensburg.de (8.8.5/8.8.5) with SMTP id QAA19974
+ for <mills@udel.edu>; Wed, 10 Dec 1997 16:38:42 +0100 (MET)
+Received: from rkdvmks1.ngate.uni-regensburg.de by ngate.ngate.uni-regensburg.de; Wed, 10 Dec 97 16:39 MET
+Received: from rkdvmks1.ngate.uni-regensburg.de by kgate.ngate.uni-regensburg.de; Wed, 10 Dec 97 15:38 GMT
+Received: from RKDVMKS1/SpoolDir by rkdvmks1.ngate.uni-regensburg.de (Mercury 1.32);
+ 10 Dec 97 16:38:34 +0100
+Received: from SpoolDir by RKDVMKS1 (Mercury 1.32); 10 Dec 97 16:38:06 +0100
+From: Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
+Organization: Universitaet Regensburg, Klinikum
+To: mills@udel.edu
+Date: Wed, 10 Dec 1997 16:38:04 +0100
+MIME-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-transfer-encoding: 7BIT
+Subject: Big patch to scripts/monitoring
+Priority: normal
+X-mailer: Pegasus Mail for Windows (v2.53/R1)
+Message-ID: <103AB9D209F5@rkdvmks1.ngate.uni-regensburg.de>
+
+Dave, there's another big patch against scripts/monitoring. The Perl
+programs in there are rather old. As they are quite nice thogh, I
+decided to update them for the latest version of Perl, namely 5.004.
+
+I'll include the description of changes and the patch.
+
+Ulrich
+
+Here is a description of changes made in ntploopwatch:
+
+Corrected most warnings that PERL 5.004 might emit. Corrected signal
+handlers to make them work again. Corrected scaling (at least I
+think). Supported case when output is sent to file or printer and
+there is no X11 available. Fixed number of month in xlabels of the
+plot.
+
+Added the ability to specify printer for GNUplot other that
+PostScript. Use hostname if $STATHOST is not given in configuration
+file. Corrected verbosity level for some messages. Added ability to
+specify a non-standard print command.
+
+Perl 5.004_04 does no longer ignore ``-w--*-perl-*-''. Made some
+cosmetic changes and added a timescale of 10 minutes.
+
+(Other programs are functionally unchanged. lr.pl is a PERL module now.)
+
+
+Index: README
+===================================================================
+RCS file: /home/windl/NTP/mon-REP/monitoring/README,v
+retrieving revision 1.1.1.1
+retrieving revision 1.2
+diff -u -r1.1.1.1 -r1.2
+--- README 1993/08/24 19:29:34 1.1.1.1
++++ README 1997/10/23 17:51:37 1.2
+@@ -1,14 +1,14 @@
+ This directory contains support for monitoring the local clock of xntp daemons.
+
+-WARNING: The scripts and routines contained in this directory are bete realease!
+- Do not depend on their correct operation. They are, however, in regular
+- use at University of Erlangen-Nuernberg. No severe problems are known
+- for this code.
++WARNING: The scripts and routines contained in this directory are beta
++ release! Do not depend on their correct operation. They are,
++ however, in regular use at University of Erlangen-Nuernberg.
++ No severe problems are known for this code.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ PLEASE THINK TWICE BEFORE STARTING MONITORING REMOTE XNTP DEAMONS !!!!
+ MONITORING MAY INCREASE THE LOAD OF THE DEAMON MONITORED AND MAY
+-INCREASE THE NETWORK LOAD SIGNIFICANTLY
++INCREASE THE NETWORK LOAD SIGNIFICANTLY
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+@@ -23,8 +23,8 @@
+ It sends a set_trap request to each server given and dumps the
+ trap messages received. It handles refresh of set_trap.
+ Currently it handles only NTP V2, however the NTP V3 servers
+- also accept v2 requests. It will not interpret v3 system and peer
+- stati correctly.
++ also accept v2 requests. It will not interpret v3 system and
++ peer stati correctly.
+
+ usage:
+ ntptrap [-n] [-p <port>] [-l <debug-output>] servers...
+@@ -72,7 +72,9 @@
+
+ if a timeout occurs the next sample is tried after delay/2 seconds
+
+- The script will terminate after MAX_FAIL (currently 60) consecutive errors.
++ The script will terminate after MAX_FAIL (currently 60)
++ consecutive errors.
++
+ Errors are counted for:
+ - error on send call
+ - error on select call
+@@ -114,10 +116,10 @@
+ command line values would be replaced by settings from the config file.
+
+ printer: specify printer to print plot
+- BSD print systems semantics apply; if printer is omitted
+- the name "ps" is used; plots are prepared using
+- PostScript, thus the printer should best accept
+- postscript input
++ BSD print systems semantics apply; if printer
++ is omitted the name "ps" is used; plots are
++ prepared using PostScript, thus the printer
++ should best accept postscript input
+
+ For the following see also the comments in loopwatch.config.SAMPLE
+
+@@ -139,8 +141,10 @@
+ within display range
+
+ timelocal.pl:
+- used during conversion of ISO_DATE_TIME values specified in loopwatch
+- config files to unix epoch values (seconds since 1970-01-01_00:00_00 UTC)
++
++ used during conversion of ISO_DATE_TIME values specified in
++ loopwatch config files to unix epoch values (seconds since
++ 1970-01-01_00:00_00 UTC)
+
+ A version of this file is distributed with perl-4.x, however,
+ it has a bug related to dates crossing 1970, causing endless loops..
+Index: lr.pl
+===================================================================
+RCS file: /home/windl/NTP/mon-REP/monitoring/lr.pl,v
+retrieving revision 1.1.1.1
+retrieving revision 1.4
+diff -u -r1.1.1.1 -r1.4
+--- lr.pl 1993/08/24 19:29:33 1.1.1.1
++++ lr.pl 1997/10/23 17:51:37 1.4
+@@ -9,9 +9,14 @@
+ ;# Frank Kardel, Rainer Pruy
+ ;# Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ ;#
++;# Copyright (c) 1997 by
++;# Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
++;# (Converted to a PERL 5.004 package)
+ ;#
+ ;#############################################################
+
++package lr;
++
+ ##
+ ## y = A + Bx
+ ##
+@@ -23,123 +28,124 @@
+ ##
+ ## interface
+ ##
+-*lr_init = *lr'lr_init; #';# &lr_init(tag); initialize data set for tag
+-*lr_sample = *lr'lr_sample; #';# &lr_sample(x,y,tag); enter sample
+-*lr_Y = *lr'lr_Y; #';# &lr_Y(x,tag); compute y for given x
+-*lr_X = *lr'lr_X; #';# &lr_X(y,tag); compute x for given y
+-*lr_r = *lr'lr_r; #';# &lr_r(tag); regression coeffizient
+-*lr_cov = *lr'lr_cov; #';# &lr_cov(tag); covariance
+-*lr_A = *lr'lr_A; #';# &lr_A(tag);
+-*lr_B = *lr'lr_B; #';# &lr_B(tag);
+-*lr_sigma = *lr'lr_sigma; #';# &lr_sigma(tag); standard deviation
+-*lr_mean = *lr'lr_mean; #';# &lr_mean(tag);
++;# init(tag); initialize data set for tag
++;# sample(x, y, tag); enter sample
++;# Y(x, tag); compute y for given x
++;# X(y, tag); compute x for given y
++;# r(tag); regression coefficient
++;# cov(tag); covariance
++;# A(tag);
++;# B(tag);
++;# sigma(tag); standard deviation
++;# mean(tag);
+ #########################
+
+-package lr;
+-
+-sub tagify
+-{
+- local($tag) = @_;
+- if (defined($tag))
+- {
+- *lr_n = eval "*${tag}_n";
+- *lr_sx = eval "*${tag}_sx";
+- *lr_sx2 = eval "*${tag}_sx2";
+- *lr_sxy = eval "*${tag}_sxy";
+- *lr_sy = eval "*${tag}_sy";
+- *lr_sy2 = eval "*${tag}_sy2";
+- }
+-}
+-
+-sub lr_init
++sub init
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- $lr_n = 0;
+- $lr_sx = 0.0;
+- $lr_sx2 = 0.0;
+- $lr_sxy = 0.0;
+- $lr_sy = 0.0;
+- $lr_sy2 = 0.0;
++ $self->{n} = 0;
++ $self->{sx} = 0.0;
++ $self->{sx2} = 0.0;
++ $self->{sxy} = 0.0;
++ $self->{sy} = 0.0;
++ $self->{sy2} = 0.0;
+ }
+
+-sub lr_sample
++sub sample($$$)
+ {
+- local($_x, $_y) = @_;
+-
+- &tagify($_[$[+2]) if defined($_[$[+2]);
++ my $self = shift;
++ my($_x, $_y) = @_;
+
+- $lr_n++;
+- $lr_sx += $_x;
+- $lr_sy += $_y;
+- $lr_sxy += $_x * $_y;
+- $lr_sx2 += $_x**2;
+- $lr_sy2 += $_y**2;
++ ++($self->{n});
++ $self->{sx} += $_x;
++ $self->{sy} += $_y;
++ $self->{sxy} += $_x * $_y;
++ $self->{sx2} += $_x**2;
++ $self->{sy2} += $_y**2;
+ }
+
+-sub lr_B
++sub B($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return 1 unless ($lr_n * $lr_sx2 - $lr_sx**2);
+- return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / ($lr_n * $lr_sx2 - $lr_sx**2);
++ return 1 unless ($self->{n} * $self->{sx2} - $self->{sx}**2);
++ return ($self->{n} * $self->{sxy} - $self->{sx} * $self->{sy})
++ / ($self->{n} * $self->{sx2} - $self->{sx}**2);
+ }
+
+-sub lr_A
++sub A($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return ($lr_sy - &lr_B * $lr_sx) / $lr_n;
++ return ($self->{sy} - B($self) * $self->{sx}) / $self->{n};
+ }
+
+-sub lr_Y
++sub Y($$)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return &lr_A + &lr_B * $_[$[];
++ return A($self) + B($self) * $_[$[];
+ }
+
+-sub lr_X
++sub X($$)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return ($_[$[] - &lr_A) / &lr_B;
++ return ($_[$[] - A($self)) / B($self);
+ }
+
+-sub lr_r
++sub r($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- local($s) = ($lr_n * $lr_sx2 - $lr_sx**2) * ($lr_n * $lr_sy2 - $lr_sy**2);
++ my $s = ($self->{n} * $self->{sx2} - $self->{sx}**2)
++ * ($self->{n} * $self->{sy2} - $self->{sy}**2);
+
+ return 1 unless $s;
+
+- return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / sqrt($s);
++ return ($self->{n} * $self->{sxy} - $self->{sx} * $self->{sy}) / sqrt($s);
+ }
+
+-sub lr_cov
++sub cov($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return ($lr_sxy - $lr_sx * $lr_sy / $lr_n) / ($lr_n - 1);
++ return ($self->{sxy} - $self->{sx} * $self->{sy} / $self->{n})
++ / ($self->{n} - 1);
+ }
+
+-sub lr_sigma
++sub sigma($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return 0 if $lr_n <= 1;
+- return sqrt(($lr_sy2 - ($lr_sy * $lr_sy) / $lr_n) / ($lr_n));
++ return 0 if $self->{n} <= 1;
++ return sqrt(($self->{sy2} - ($self->{sy} * $self->{sy}) / $self->{n})
++ / ($self->{n}));
+ }
+
+-sub lr_mean
++sub mean($)
+ {
+- &tagify($_[$[]) if defined($_[$[]);
++ my $self = shift;
+
+- return 0 if $lr_n <= 0;
+- return $lr_sy / $lr_n;
++ return 0 if $self->{n} <= 0;
++ return $self->{sy} / $self->{n};
+ }
+
+-&lr_init();
++sub new
++{
++ my $class = shift;
++ my $self = {
++ (n => undef,
++ sx => undef,
++ sx2 => undef,
++ sxy => undef,
++ sy => undef,
++ sy2 => undef)
++ };
++ bless $self, $class;
++ init($self);
++ return $self;
++}
+
+ 1;
+Index: ntp.pl
+===================================================================
+RCS file: /home/windl/NTP/mon-REP/monitoring/ntp.pl,v
+retrieving revision 1.1.1.1
+retrieving revision 1.3
+diff -u -r1.1.1.1 -r1.3
+--- ntp.pl 1993/08/24 19:29:34 1.1.1.1
++++ ntp.pl 1997/10/23 18:19:41 1.3
+@@ -1,4 +1,4 @@
+-#!/local/bin/perl
++#!/usr/bin/perl -w
+ ;#
+ ;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
+ ;#
+@@ -43,7 +43,7 @@
+ ;# N key
+ ;# N2 checksum
+
+-;# first bye of packet
++;# first byte of packet
+ sub pkt_LI { return ($_[$[] >> 6) & 0x3; }
+ sub pkt_VN { return ($_[$[] >> 3) & 0x7; }
+ sub pkt_MODE { return ($_[$[] ) & 0x7; }
+@@ -223,6 +223,7 @@
+ {
+ &getval(&psw_PSel($_[$[]),*PeerSelection);
+ }
++
+ sub PeerEvent
+ {
+ &getval(&psw_PCode($_[$[]),*PeerEvent);
+@@ -394,14 +395,14 @@
+ $lastseen = 1 if !&pkt_M($r_e_m_op);
+ if (!defined(%FRAGS))
+ {
+- (&pkt_M($r_e_m_op) ? " more" : "")."\n";
++ print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
+ $FRAGS{$offset} = $data;
+ ;# save other info
+ @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
+ }
+ else
+ {
+- (&pkt_M($r_e_m_op) ? " more" : "")."\n";
++ print((&pkt_M($r_e_m_op) ? " more" : "")."\n");
+ ;# add frag to previous - combine on the fly
+ if (defined($FRAGS{$offset}))
+ {
+Index: ntploopstat
+===================================================================
+RCS file: /home/windl/NTP/mon-REP/monitoring/ntploopstat,v
+retrieving revision 1.1.1.1
+retrieving revision 1.3
+diff -u -r1.1.1.1 -r1.3
+--- ntploopstat 1993/08/24 19:29:32 1.1.1.1
++++ ntploopstat 1997/11/23 19:07:12 1.3
+@@ -1,4 +1,5 @@
+-#!/local/bin/perl -w--*-perl-*-
++#!/usr/bin/perl -w
++# --*-perl-*-
+ ;#
+ ;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp
+ ;#
+@@ -22,7 +23,7 @@
+ ;# (Should have implemented &gettimeofday()..)
+ ;#
+
+-$0 =~ s!^.*/([^/]+)$!\1!; # beautify script name
++$0 =~ s!^.*/([^/]+)$!$1!; # beautify script name
+
+ $ntpserver = 'localhost'; # default host to poll
+ $delay = 60; # default sampling rate
+Index: ntploopwatch
+===================================================================
+RCS file: /home/windl/NTP/mon-REP/monitoring/ntploopwatch,v
+retrieving revision 1.1.1.1
+retrieving revision 1.14
+diff -u -r1.1.1.1 -r1.14
+--- ntploopwatch 1993/10/22 14:28:18 1.1.1.1
++++ ntploopwatch 1997/12/07 17:06:36 1.14
+@@ -1,4 +1,5 @@
+-#!/local/bin/perl -w--*-perl-*-
++#!/usr/bin/perl -w
++#--*-perl-*-
+ ;#
+ ;# ntploopwatch,v 3.1 1993/07/06 01:09:13 jbj Exp
+ ;#
+@@ -9,12 +10,17 @@
+ ;# Copyright (c) 1992
+ ;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ ;#
++;# Copyright (c) 1997
++;# Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
++;# (Corrections for Perl5 and other fixes)
+ ;#
+ ;#############################################################
+-$0 =~ s!^.*/([^/]+)$!\1!;
++$0 =~ s!^.*/([^/]+)$!$1!;
+ $F = ' ' x length($0);
+ $|=1;
+
++use 5.004; # require 5.004
++
+ $ENV{'SHELL'} = '/bin/sh'; # use bourne shell
+
+ undef($config);
+@@ -23,17 +29,17 @@
+ undef($samples);
+ undef($StartTime);
+ undef($EndTime);
+-($a,$b) if 0; # keep -w happy
++
+ $usage = <<"E-O-P";
+ usage:
+- to watch statistics permanently:
++ to watch statistics periodically:
+ $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+ $F [-h <hostname>]
+
+ to get a single print out specify also
+- $F -P[<printer>] [-s<samples>]
+- $F [-S <start-time>] [-E <end-time>]
+- $F [-Y <MaxOffs>] [-y <MinOffs>]
++ $F -P[[<term_type:>]<printer>]
++ $F [-s<samples>] [-S <start-time>] [-E <end-time>]
++ $F [-Y <MaxOffs>] [-y <MinOffs>]
+
+ If You like long option names, You can use:
+ -help
+@@ -41,21 +47,28 @@
+ -d +directory
+ -h +host
+ -v +verbose[=<level>]
+- -P +printer[=<printer>]
++ -P +printer[=[<term_type>:]<printer>[:<print_cmd>]]
+ -s +samples[=<samples>]
+ -S +starttime
+ -E +endtime
+ -Y +maxy
+ -y +miny
+
++If <printer> is prefixed with a <term_type> and a colon, the <term_type>
++given is used as terminal fpr gnuplot.
+ If <printer> contains a '/' (slash character) output is directed to
+ a file of this name instead of delivered to a printer.
++If suffix :<print_cmd> is given, the suffix is used as the print command,
++and <printer> is ignored. If blanks are needed inside <term_type> or
++<print_cmd>, just use underscores instead.
+ E-O-P
+
+ ;# add directory to look for lr.pl and timelocal.pl (in front of current list)
+ unshift(@INC,"/src/NTP/v3/xntp/monitoring");
+
+ require "lr.pl"; # linear regresion routines
++my $lr_offs = new lr;
++my $lr_freq = new lr;
+
+ $MJD_1970 = 40587; # from ntp.h (V3)
+ $RecordSize = 48; # usually a line fits into 42 bytes
+@@ -171,16 +184,20 @@
+
+ ;# configuration file
+ $config = "loopwatch.config" unless defined($config);
+-($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!\1!
++($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]+)$!$1!
+ unless defined($STATHOST);
+-($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/\1/;
++if ($STATHOST eq $config) {
++ require "hostname.pl";
++ $STATHOST = hostname();
++}
++($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/$1/;
+
+ $srcprefix =~ s/\$STATHOST/$STATHOST/g;
+
+ ;# plot command
+-@plotcmd=("gnuplot",
+- '-title', "Ntp loop filter statistics $STATHOST",
+- '-name', "NtpLoopWatch_$STATTAG");
++@plotcmd=("gnuplot");
++push(@plotcmd, '-title', "NTP loop filter statistics for $STATHOST",
++ '-name', "NtpLoopWatch_$STATTAG") unless $PrintIt;
+ $tmpfile = "/tmp/ntpstat.$$";
+
+ ;# other variables
+@@ -211,9 +228,8 @@
+ $?>>8,$? & 0xff)) if $?;
+ exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
+ }
+-&sigchld if 0;
+-$SIG{'CHLD'} = "sigchld";
+-$SIG{'CLD'} = "sigchld";
++$SIG{'CHLD'} = \&sigchld;
++$SIG{'CLD'} = \&sigchld;
+
+ sub abort
+ {
+@@ -221,8 +237,7 @@
+ defined($Plotpid) && kill('TERM',$Plotpid);
+ die("$0: received signal SIG$_[$[] - exiting\n");
+ }
+-&abort if 0; # make -w happy - &abort IS used
+-$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
++$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = \&abort;
+
+ ;#
+ sub abs
+@@ -248,7 +263,7 @@
+ open(STDOUT,">&STDERR") ||
+ die("$0: failed to redirect STDOUT of plot command: $!\n");
+
+- print STDOUT "plot command running as $$\n";
++ print STDOUT "plot command has PID $$\n";
+
+ exec @plotcmd;
+ die("$0: failed to exec (@plotcmd): $!\n");
+@@ -275,128 +290,83 @@
+ s/^([^\#]*[^\#\s]?)\s*\#.*$//;
+ next if /^\s*$/;
+
+- s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/\1=\2/;
++ s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/$1=$2/;
+
+- ($c,$v) = split(/=/,$_,2);
++ ($c,$v) = ($1, $2);
+ print "processing \"$c=$v\"\n" if $verbose > 3;
+- ($c eq "delay") && ($delay = $v,1) && next;
+- ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
+- ($samples = $v,1) && next;
+- ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
+- && next;
+- ($c eq 'showoffs') &&
+- ($showoffs = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+- ($c eq 'showfreq') &&
+- ($showfreq = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+- ($c eq 'showcmpl') &&
+- ($showcmpl = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+- ($c eq 'showoreg') &&
+- ($showoreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+- ($c eq 'showfreg') &&
+- ($showfreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+-
+- ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
+-
+- ($c eq 'freqbase' ||
+- $c eq 'cmplscale') &&
+- do {
+- if (! defined($v) || $v eq "" || $v eq 'dynamic')
+- {
+- eval "undef(\$$c);";
+- }
+- else
+- {
+- eval "\$$c = \$v;";
+- }
+- next;
+- };
+- ($c eq 'timebase') &&
+- do {
+- if (! defined($v) || $v eq "" || $v eq "dynamic")
+- {
+- undef($timebase);
+- }
+- else
+- {
+- $timebase=&date_time_spec2seconds($v);
+- }
+- };
+- ($c eq 'EndTime') &&
+- do {
+- next if defined($EndTime) && defined($PrintIt);
+- if (! defined($v) || $v eq "" || $v eq "none")
+- {
+- undef($EndTime);
+- }
+- else
+- {
+- $EndTime=&date_time_spec2seconds($v);
+- }
+- };
+- ($c eq 'StartTime') &&
+- do {
+- next if defined($StartTime) && defined($PrintIt);
+- if (! defined($v) || $v eq "" || $v eq "none")
+- {
+- undef($StartTime);
+- }
+- else
+- {
+- $StartTime=&date_time_spec2seconds($v);
+- }
+- };
+-
+- ($c eq 'MaxY') &&
+- do {
+- next if defined($MaxY) && defined($PrintIt);
+- if (! defined($v) || $v eq "" || $v eq "none")
+- {
+- undef($MaxY);
+- }
+- else
+- {
+- $MaxY=$v;
+- }
+- };
+-
+- ($c eq 'MinY') &&
+- do {
+- next if defined($MinY) && defined($PrintIt);
+- if (! defined($v) || $v eq "" || $v eq "none")
+- {
+- undef($MinY);
+- }
+- else
+- {
+- $MinY=$v;
+- }
+- };
+-
+- ($c eq 'deltaT') &&
+- do {
+- if (!defined($v) || $v eq "")
+- {
+- undef($deltaT);
+- }
+- else
+- {
+- $deltaT = $v;
+- }
+- next;
+- };
+- ($c eq 'verbose') && ! defined($PrintIt) &&
+- do {
+- if (!defined($v) || $v == 0)
+- {
+- $verbose = 0;
+- }
+- else
+- {
+- $verbose = $v;
+- }
+- next;
+- };
+- ;# otherwise: silently ignore unrecognized config line
++ if ($c eq "delay") {
++ $delay = $v;
++ } elsif ($c eq 'samples') {
++ $samples = $v if (!defined($PrintIt) || !defined($samples));
++ } elsif ($c eq 'srcprefix') {
++ ($srcprefix = $v) =~ s/\$STATHOST/$STATHOST/g;
++ } elsif ($c eq 'showoffs') {
++ $showoffs = ($v =~ /^yes$|^y$|^1$/);
++ } elsif ($c eq 'showfreq') {
++ $showfreq = ($v =~ /^yes$|^y$|^1$/);
++ } elsif ($c eq 'showcmpl') {
++ $showcmpl = ($v =~ /^yes$|^y$|^1$/);
++ } elsif ($c eq 'showoreg') {
++ $showoreg = ($v =~ /^yes$|^y$|^1$/);
++ } elsif ($c eq 'showfreg') {
++ $showfreg = ($v =~ /^yes$|^y$|^1$/);
++ } elsif ($c eq 'exit') {
++ unlink($tmpfile); die("$0: exit by config request\n");
++ } elsif ($c eq 'freqbase' || $c eq 'cmplscale') {
++ if (! defined($v) || $v eq "" || $v eq 'dynamic') {
++ eval "undef(\$$c);";
++ } else {
++ eval "\$$c = \$v;";
++ }
++ } elsif ($c eq 'timebase') {
++ if (! defined($v) || $v eq "" || $v eq "dynamic") {
++ undef($timebase);
++ } else {
++ $timebase=&date_time_spec2seconds($v);
++ }
++ } elsif ($c eq 'EndTime') {
++ next if defined($EndTime) && defined($PrintIt);
++ if (! defined($v) || $v eq "" || $v eq "none") {
++ undef($EndTime);
++ } else {
++ $EndTime=&date_time_spec2seconds($v);
++ }
++ } elsif ($c eq 'StartTime') {
++ next if defined($StartTime) && defined($PrintIt);
++ if (! defined($v) || $v eq "" || $v eq "none") {
++ undef($StartTime);
++ } else {
++ $StartTime=&date_time_spec2seconds($v);
++ }
++ } elsif ($c eq 'MaxY') {
++ next if defined($MaxY) && defined($PrintIt);
++ if (! defined($v) || $v eq "" || $v eq "none") {
++ undef($MaxY);
++ } else {
++ $MaxY=$v;
++ }
++ } elsif ($c eq 'MinY') {
++ next if defined($MinY) && defined($PrintIt);
++ if (! defined($v) || $v eq "" || $v eq "none") {
++ undef($MinY);
++ } else {
++ $MinY=$v;
++ }
++ } elsif ($c eq 'deltaT') {
++ if (!defined($v) || $v eq "") {
++ undef($deltaT);
++ } else {
++ $deltaT = $v;
++ }
++ } elsif ($c eq 'verbose' && ! defined($PrintIt)) {
++ if (!defined($v) || $v == 0) {
++ $verbose = 0;
++ } else {
++ $verbose = $v;
++ }
++ } else {
++ ;# otherwise: silently ignore unrecognized config line
++ }
+ }
+ close(CF);
+ ;# set show defaults when nothing selected
+@@ -413,16 +383,16 @@
+ print " showcmpl\t= $showcmpl\n";
+ print " showoreg\t= $showoreg\n";
+ print " showfreg\t= $showfreg\n";
+- printf " timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
+- printf " freqbase\t= %s\n",defined($freqbase) ?"$freqbase":"dynamic";
+- printf " cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
+- printf " StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
+- printf " EndTime\t= %s", defined($EndTime) ? &ctime($EndTime):"none\n";
+- printf " MaxY\t= %s",defined($MaxY)? $MaxY :"none\n";
+- printf " MinY\t= %s",defined($MinY)? $MinY :"none\n";
++ printf " timebase\t= %s", defined($timebase) ? &ctime($timebase) : "dynamic\n";
++ printf " freqbase\t= %s\n", defined($freqbase) ? "$freqbase" : "dynamic";
++ printf " cmplscale\t= %s\n", defined($cmplscale) ? "$cmplscale" : "dynamic";
++ printf " StartTime\t= %s", defined($StartTime) ? &ctime($StartTime) : "none\n";
++ printf " EndTime\t= %s", defined($EndTime) ? &ctime($EndTime):"none\n";
++ printf " MaxY\t= %s", defined($MaxY) ? $MaxY : "none\n";
++ printf " MinY\t= %s", defined($MinY) ? $MinY : "none\n";
+ print " verbose\t= $verbose\n";
+ }
+-print "configuration file read\n" if $verbose > 2;
++ print "configuration file read\n" if $verbose > 2;
+ }
+
+ sub make_doplot
+@@ -443,10 +413,9 @@
+ ;# number of integral seconds to get at least 12 tic marks on x axis
+ $t = int(($maxtime - $mintime) / 12 + 0.5);
+ $t = 1 unless $t; # prevent $t to be zero
+- foreach $i (30,
+- 60,5*60,15*60,30*60,
+- 60*60,2*60*60,6*60*60,12*60*60,
+- 24*60*60,48*60*60)
++ foreach $i (30, 60,
++ 5*60, 10*60, 15*60, 30*60, 60*60,
++ 2*60*60, 6*60*60, 12*60*60, 24*60*60, 48*60*60)
+ {
+ last if $t < $i;
+ $t = $t - ($t % $i);
+@@ -458,31 +427,30 @@
+ $i <= $maxtime + $t;
+ $i += $t, $c=",")
+ {
++ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($i);
++
+ $s .= $c;
+ ((int($i / $t) % 2) &&
+ ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
+ (($t <= 60) &&
+ ($s .= sprintf("'%d:%02d:%02d' %lf",
+- (localtime($i))[$[+2,$[+1,$[+0],
+- ($i - $LastTimeBase)/3600)))
++ $hour, $min, $sec, ($i - $LastTimeBase) / 3600)))
+ || (($t <= 2*60*60) &&
+ ($s .= sprintf("'%d:%02d' %lf",
+- (localtime($i))[$[+2,$[+1],
+- ($i - $LastTimeBase)/3600)))
++ $hour, $min, ($i - $LastTimeBase) / 3600)))
+ || (($t <= 12*60*60) &&
+ ($s .= sprintf("'%s %d:00' %lf",
+- $Day[(localtime($i))[$[+6]],
+- (localtime($i))[$[+2],
+- ($i - $LastTimeBase)/3600)))
++ $Day[$wday], $hour,
++ ($i - $LastTimeBase) / 3600)))
+ || ($s .= sprintf("'%d.%d-%d:00' %lf",
+- (localtime($i))[$[+3,$[+4,$[+2],
+- ($i - $LastTimeBase)/3600));
++ $mday, $mon + 1, $hour,
++ ($i - $LastTimeBase) / 3600));
+ }
+ $doplot .= "set xtics ($s)\n";
+
+ chop($xts = &ctime($mintime));
+ chop($xte = &ctime($maxtime));
+- $doplot .= "set xlabel 'Start: $xts -- Time Scale -- End: $xte'\n";
++ $doplot .= "set xlabel 'Start: $xts -- Time Scale -- End: $xte'\n";
+ $doplot .= "set yrange [" ;
+ $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
+ $doplot .= ':';
+@@ -518,22 +486,22 @@
+ $c = ",");
+ $showoreg && $showoffs &&
+ ($doplot .= sprintf($regfmt, $c,
+- &lr_B('offs'),&lr_A('offs'),
+- "offset ",
+- &lr_B('offs'),
+- ((&lr_A('offs')) < 0 ? '-' : '+'),
+- &abs(&lr_A('offs')), &lr_r('offs'),
++ $lr_offs->B(),$lr_offs->A(),
++ "offset",
++ $lr_offs->B(),
++ (($lr_offs->A()) < 0 ? '-' : '+'),
++ &abs($lr_offs->A()), $lr_offs->r(),
+ "[ms]"),
+ $c = ",");
+ $showfreg && $showfreq &&
+ ($doplot .= sprintf($regfmt, $c,
+- &lr_B('freq') * $FreqScale,
+- (&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase,
++ $lr_freq->B() * $FreqScale,
++ ($lr_freq->A() + $minfreq) * $FreqScale - $LastFreqBase,
+ "frequency",
+- &lr_B('freq') * $FreqScale,
+- ((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
+- &abs((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase),
+- &lr_r('freq'),
++ $lr_freq->B() * $FreqScale,
++ (($lr_freq->A() + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
++ &abs(($lr_freq->A() + $minfreq) * $FreqScale - $LastFreqBase),
++ $lr_freq->r(),
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $doplot .= "\n";
+@@ -583,7 +551,7 @@
+ $#loffset = $[ - 1;
+ $#filekey = $[ - 1;
+ print "memory allocation ready\n" if $verbose > 2;
+- sleep(3) if $verbose > 1;
++ sleep(3) if $verbose > 2;
+
+ if (index($in,"/") < $[)
+ {
+@@ -606,7 +574,6 @@
+ ;# rescan directory on changes
+ $Lsdir = $sdir;
+ $Ltime = (stat($sdir))[$[+9];
+- </X{> if 0; # dummy line - calm down my formatter
+ local(@newfiles) = < ${in}*[0-9] >;
+ local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
+
+@@ -626,7 +593,7 @@
+ $F_key{$name} = $key;
+ $modified++;
+ }
+- if (!defined($F_name{$key}) || $F_name{$key} != $name)
++ if (!defined($F_name{$key}) || $F_name{$key} ne $name)
+ {
+ $F_name{$key} = $name;
+ $modified++;
+@@ -643,9 +610,9 @@
+ }
+ if ($modified)
+ {
+- print "new data \"$name\" key: $key;\n" if $verbose > 1;
++ print "new data \"$name\" key: $key;\n" if $verbose > 2;
+ print " size: $st_size; mtime: $st_mtime;\n"
+- if $verbose > 1;
++ if $verbose > 2;
+ $F_last{$key} = $F_first{$key} = $st_mtime;
+ $F_first{$key}--; # prevent zero divide later on
+ ;# now compute derivated attributes
+@@ -692,7 +659,7 @@
+ }
+ close(IN);
+ print(" first: ",$F_first{$key},
+- " last: ",$F_last{$key},"\n") if $verbose > 1;
++ " last: ",$F_last{$key},"\n") if $verbose > 2;
+ }
+ }
+ ;# now reclaim memory used for files no longer referenced ...
+@@ -739,7 +706,7 @@
+ }
+ ;# create list sorted by time
+ @F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
+- if ($verbose > 1)
++ if ($verbose > 2)
+ {
+ print "Resulting file list:\n";
+ foreach (@F_files)
+@@ -797,7 +764,7 @@
+ print "guess start according to StartTime ($StartTime)\n"
+ if $verbose > 3;
+
+- if ($fpos[$[] eq 'start')
++ if (defined $fpos[$[] && $fpos[$[] eq 'start')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+@@ -1001,9 +968,9 @@
+ $t += $F[$[+1]; # add seconds + fraction
+
+ ;# multiply offset by 1000 to get ms - try to avoid float op
+- (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/\1\2.\3/) &&
++ (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/$1$2.$3/) &&
+ $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
+- || $F[$[+2] *= 1000;
++ || ($F[$[+2] *= 1000);
+
+
+ ;# skip samples out of specified time range
+@@ -1036,8 +1003,8 @@
+ print "input scanned ($l lines/",scalar(@time)," samples)\n"
+ if $verbose > 1;
+
+- &lr_init('offs');
+- &lr_init('freq');
++ $lr_offs->init();
++ $lr_freq->init();
+
+ if (@time)
+ {
+@@ -1047,17 +1014,17 @@
+ local($freqbase) unless defined($freqbase);
+ local($cmplscale) unless defined($cmplscale);
+
+- undef($mintime,$maxtime,$minoffs,$maxoffs,
+- $minfreq,$maxfreq,$mincmpl,$maxcmpl,
+- $miny,$maxy);
++ undef $mintime; undef $maxtime; undef $minoffs; undef $maxoffs;
++ undef $minfreq; undef $maxfreq; undef $mincmpl; undef $maxcmpl;
++ undef $miny; undef $maxy;
+
+ print "computing ranges\n" if $verbose > 2;
+
+ $LastCnt = @time;
+
+ ;# @time is in ascending order (;-)
+- $mintime = @time[$[];
+- $maxtime = @time[$#time];
++ $mintime = $time[$[];
++ $maxtime = $time[$#time];
+ unless (defined($timebase))
+ {
+ local($time,@X) = (time);
+@@ -1081,24 +1048,24 @@
+ ;# (otherwise a (shift(@a1),shift(a2)) would do),
+ ;# I dont like to make copies of these arrays as they may be huge
+ $i = $[;
+- &lr_sample(($time[$i]-$timebase)/3600,$offs[$
+
diff --git a/contrib/ntp/scripts/stats/README b/contrib/ntp/scripts/stats/README
new file mode 100644
index 0000000..6808963
--- /dev/null
+++ b/contrib/ntp/scripts/stats/README
@@ -0,0 +1,39 @@
+Statistics processing scripts (README)
+
+This directory contains a number of scripts for use with the filegen
+facility. Those files ending in .awk are for the Unix awk utility, while
+those ending in .sh are for the csh utility. Normally, the summary.sh
+script is called from a cron job once per day. This script processes the
+daily loopstats, peerstats and clockstats files produced by the daemon,
+updates the loop_summary, peer_summary and clock_summary archive files,
+and deletes the daily files.
+
+In the case of the Austron 2201A GPS receiver, the clockstats file
+contains a wealth of additional monitoring data. These data are summarized
+and writted to the clock_summary file, then a series of special files are
+constructed for later processing by the S utility.
+
+The summary.sh script invokes a number of awk scripts to actually produce
+the data. This may result in multiple scans of the same input file.
+The input file is deleted after processing. In fact, the shell scripts will
+process all input files found of the correct type in chronological order,
+deleting each one as it is scanned, except the current day file.
+
+The summary.sh script can produce input files for the S utility, if it
+is found on the search path. This utility makes PostScript graphs of the
+loopstats data for each day, as well as various statistics produced by
+the Austorn 220aA GPS receiver. The S utility is automatically run
+as a background job. Its control files have the .S extension.
+
+The psummary.awk script can be used to scan the peer_summary file and
+construct an historical reprise of the daily summaries.
+
+The file formats are documented in the README.stats file and in the
+scripts themselves. Further detail on the radio clock ASCII timecode
+formats and related data are in the README.timecode file.
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+1 November 1993
+Revised 12 April 1994
diff --git a/contrib/ntp/scripts/stats/README.stats b/contrib/ntp/scripts/stats/README.stats
new file mode 100644
index 0000000..aa8e77f
--- /dev/null
+++ b/contrib/ntp/scripts/stats/README.stats
@@ -0,0 +1,246 @@
+Statistics file formats (README.stats)
+
+The xntp3 daemon can produce a variety of statistics files which are
+useful for maintenance, evaluation and retrospective calibration
+purposes. See the xntpd.8 man page for instructions on how to configure
+this feature. Since these files can become rather large and cumbersome,
+they are ordinarily reduced to summary form by running the summary.sh
+shell script once per day, week or month, as appropriate. There are
+three file collections presently defined: peerstats, loopstats and
+clockstats, each of which is described in this note.
+
+peerstats
+
+The following data are collected in the peerstats files. The files are
+reduced to summary data using the peer.sh shell script. See the peer.awk
+script for further information. A line in the file is produced upon
+reception of each valid update from a configured peer.
+
+ 49236 30.756 140.173.96.1 9474 0.000603 0.37532
+
+ 49236 modified Julian day number
+ 30.756 time of day (s) past midnight UTC
+ 140.173.96.1 peer identifier (IP address or receiver identifier)
+ 9474 peer status word (hex) (see NTP specification)
+ 0.000603 offset (s)
+ 0.08929 delay (s)
+ 0.37532 dispersion (s)
+
+loopstats
+
+The following data are collected in the loopstats files. The files are
+reduced to summary data using the loop.sh shell script. See the loop.awk
+script for further information. A line in the file is produced at each
+valid update of the local clock.
+
+ 49236 11.897 -0.000004 -35.9384 0
+
+ 49236 modified Julian day number
+ 11.897 time of day (s) past midnight UTC
+ -0.000004 time offset (s)
+ -35.9384 frequency offset (ppm)
+ 0 phase-lock loop time constant
+
+clockstats
+
+The following data are collected in the clockstats files. The files are
+reduced to summary data using the clock.sh shell script, which also
+updates the ensemble, etf, itf and tdata data files as well. See the
+clock.awk, ensemble.awk, etf.awk, itf.awk and tdta.awk scripts for
+further information. A line in the file is produced at each valid update
+received from a configured radio clock. Data are at present recorded for
+several radios. The first part of each data line is similar for all
+radios, e.g.:
+
+ 49234 60517.826 127.127.4.1 93 247 16:48:21.814
+
+ 49234 modified Julian day number
+ 60517.826 time of day (s) past midnight UTC
+ 127.127.4.1 receiver identifier (Spectracom 8170/Netclock-2)
+ 93 247 16:48:21.814 timecode (format varies)
+
+In the case of the Austron GPS receiver, a good deal of additional
+information is extracted from the radio, as described below. The formats
+shown consist of one line with all the fields shown in order. The
+timecode formats specific to each radio follow. See the file
+README.timecodes for detailed information on the timecode formats used
+by these radios.
+
+Spectracom 8170/Netclock-2 WWVB receiver
+
+ 49234 60517.826 127.127.4.1 ?A93 247 16:48:21.814
+
+ The '?' and 'A' characters are present only when the receiver is
+ unsynchronized; otherwise, they are replaced by space ' ' characters.
+
+IRIG audio decoder
+
+ 49234 60517.826 127.127.6.0 247 16:48:21?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Austron 2200A/2201A GPS receiver
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Depending on the installed options, the Austron 2200A/2201A recognizes a
+number of special commands that report various data items. See the
+refclock_as2201.c source module for a list of the commands used. These
+data are collected only if the following line is included in the
+configuration file ntp.conf:
+
+ fudge 127.127.10.1 flag4 1 # enable extended statistics collection
+
+The format of each data line returned is summarized in the following
+list.
+
+External time/frequency data (requires input buffer option IN)
+
+These data determine the deviations of external time/frequency inputs
+relative to receiver oscillator time. The following data are typical
+using an external cesium oscillator PPS and 5-MHz outputs.
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814 ETF
+
+ -85.9 time interval (ns)
+ -89.0 average time interval (ns)
+ 4.0 time interval sigma (ns)
+ +1.510E-11 time interval rate
+ -4.500E-11 deltaf/f
+ +1.592E-11 average deltaf/f
+ 5.297E-13 sigma deltaf/f
+ 500 number of samples
+
+Model and option identifiers
+
+These data show the receiver model number and option configuration.
+
+ 49234 60708.848 127.127.10.1 93:247:16:51:32.817 ID;OPT;VER
+
+ GPS 2201A model ident (must be "GPS 2200A" or "GPS 2201A")
+ TTY1 rs232 option present (required)
+ TC1 IRIG option present (optional)
+ LORAN LORAN assist option present (optional)
+ IN input buffer option present (optional)
+ OUT1 output buffer option present (required)
+ B.00 data processor software version ("B.00" or later)
+ B.00 signal processor software version ("B.00" or later)
+ 28-Apr-93 software version date ("28-Apr-93" or later)
+
+Internal time/frequency data
+
+These data determine the deviations of the receiver oscillator with
+respect to satellite time.
+
+ 49234 60564.846 127.127.10.1 93:247:16:49:08.816 ITF
+
+ COCO current mode (must be "COCO")
+ 0 code coast mode (must be zero)
+ +6.6152E-08 code sigma (s)
+ -3.5053E-08 code delta t (s)
+ -4.0361E-11 deltat/t
+ -6.4746E-11 oscillator ageing rate
+ 500.00 loop time constant
+ 4.984072 electrical tuning (V)
+
+GPS/LORAN ensemble data (requires LORAN assist option LORAN)
+
+These data determine the deviations and weights to calculate ensemble
+time from GPS and LORAN data.
+
+ 49234 60596.852 127.127.10.1 93:247:16:49:40.812 LORAN ENSEMBLE
+
+ +9.06E-08 GPS t (s)
+ +3.53E-08 GPS sigma (s)
+ .532 GPS weight
+ +3.71E-08 LORAN t (s)
+ +3.76E-08 LORAN sigma (s)
+ .468 LORAN weight
+ +6.56E-08 ensemble t
+ +6.94E-08 ensemble sigma (s)
+
+LORAN stationkeeping data (requires LORAN assist option LORAN)
+
+These data determine which stations of the LORAN chain are being
+tracked, together with individual signal/noise ratios, deviations and
+weights.
+
+ 49234 60532.850 127.127.10.1 93:247:16:48:36.820 LORAN TDATA
+
+ M station identifier; data follows
+ OK status (must be "OK" for tracking)
+ 0 cw flag
+ 0 sw flag
+ 1162.17 time of arrival
+ -4.6 snr (-30.0 if not "OK" status)
+ 1.67E-07 2-sample phase-time deviation
+ .507 weight (included only if "OK" status)
+ W AQ 0 0 3387.80 -31.0 station identifier and data
+ X OK 0 0 1740.27 -11.2 2.20E-07 .294 station identifier and data
+ Y OK 0 0 2180.71 -4.6 2.68E-07 .198 station identifier and data
+ Z CV 0 0 3392.94 -30.0 station identifier and data
+
+Oscillator status and environment
+
+These data determine the receiver oscillator type, mode, status and
+environment. Nominal operating conditions are shown below.
+
+ 49234 60628.847 127.127.10.1 93:247:16:50:12.817 OSC;ET;TEMP
+
+ 1121 Software Control oscillator model and mode (must be
+ "Software Control")
+ Locked status (must be "Locked")
+ 4.979905 electrical tuning (V)
+ 44.81 oscillator cavity temperature
+
+Receiver position, status and offsets
+
+These data determine the receiver position and elevation, together with
+programmable delay corrections for the antenna cable and receiver.
+
+ 49234 60788.847 127.127.10.1 93:247:16:52:52.817 POS;PPS;PPSOFF
+
+ +39:40:48.425 receiver latitude (N)
+ -075:45:02.392 receiver longitude (E)
+ +74.09 receiver elevation (m)
+ Stored position status (must be "Stored")
+ UTC PPS/PPM alignment (must be "UTC")
+ 0 receiver delay (ns) (should be zero for calibrated
+ receiver)
+ 200 cable delay (ns)
+ 0 user time bias (ns) (must be zero)
+
+Satellite tracking status
+
+These data determine how many satellites are being tracked. At the
+present state of constellation development, there should be at least
+three visible satellites in view. Much of the time the maximum of
+seven are being tracked; rarely this number drops to two.
+
+ 49234 60612.850 127.127.10.1 93:247:16:49:56.820 TRSTAT
+
+ 24 T satellite prn and status (T = track, A = acquire)
+ 16 A 13 T 20 T 18 T 07 T 12 T list continued
+
+UTC leap-second information
+
+These data determine when the next leap second is to occur. The exact
+method to use is obscure.
+
+ 49234 60548.847 127.127.10.1 93:247:16:48:52.818 UTC
+
+ -1.2107E-08 A0 term (s)
+ -1.2790E-13 A1 term (s)
+ +9.0000E+00 current leap seconds (s)
+ +2.0480E+05 time for leap seconds (s)
+ +2.0100E+02 week number for delta leap (weeks)
+ +1.9100E+02 week number for future leap (weeks)
+ +4.0000E+00 day number for future leap (days)
+ +9.0000E+00 future leap seconds (s)
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/contrib/ntp/scripts/stats/README.timecodes b/contrib/ntp/scripts/stats/README.timecodes
new file mode 100644
index 0000000..00b5ba5
--- /dev/null
+++ b/contrib/ntp/scripts/stats/README.timecodes
@@ -0,0 +1,149 @@
+Radio Timecode Formats (README.timecodes)
+
+Following are examples of the serial timecode formats used by various
+timecode receivers as given in the instruction manuals. These examples
+are intended only for illustration and not as the basis of system
+design. The following symbols are used to identify the timecode
+character that begins a subfield. The values given after this symbol
+represent the character offset from the beginning of the timecode string
+as edited to remove control characters.
+
+C on-time character (start bit)
+Y year of century
+T time of day
+D day of year or month/day
+A alarm indicator (format specific)
+Q quality indicator (format specific)
+<LF> ASCII line feed (hex 0a)
+<CR> ASCII carriage return (hex 0d)
+<SP> ASCII space (hex 20)
+
+In order to promote uniform behavior in the various implementations, it
+is useful to have a common interpretation of alarm conditions and signal
+quality. When the alarm indicator it on, the receiver is not operating
+correctly or has never synchronized to the broadcast signal. When the
+alarm indicator is off and the quality indicator is on, the receiver has
+synchronized to the broadcast signal, then lost the signal and is
+coasting on its internal oscillator.
+
+In the following uppercase letters, punctuation marks and spaces <SP>
+stand for themselves; lowercase letters stand for fields as described.
+Special characters other than <LF>, <CR> and <SP> are preceded by ^.
+
+Spectracom 8170 and Netclock/2 WWV Synchonized Clock (format 0)
+
+"<CR><LF>i ddd hh:mm:ss TZ=zz<CR><LF>"
+ C A D T
+
+ poll: ?; offsets: Y = none, D = 3, T = 7, A = 0, Q = none
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ ddd = day of year
+ hh:mm:ss = hours, minutes, seconds
+ zz = timezone offset (hours from UTC)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours
+
+ example: " 216 15:36:43 TZ=0"
+ A D T
+
+Netclock/2 WWV Synchonized Clock (format 2)
+
+"<CR><LF>iqyy ddd hh:mm:ss.fff ld"
+ C AQY D T
+
+ poll: ?; offsets: Y = 2, D = 5, T = 9, A = 0, Q = 1
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ q = quality indicator (<SP> < 1ms, A < 10 ms, B < 100 ms, C < 500
+ ms, D > 500 ms)
+ yy = year (as broadcast)
+ ddd = day of year
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ l = leap-second warning (L indicates leap at end of month)
+ d = standard/daylight time indicator (<SP> standard, D daylight)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours; unlock condition is indicated by
+ other than <SP> at Q, with time since last lock indicated by the
+ letter code A < 13 min, B < 1.5 hr, C < 7 hr, D > 7 hr.
+
+ example: " 92 216 15:36:43.640 D"
+ AQ D T
+
+TrueTime 468-DC Satellite Synchronized Clock (and other TrueTime
+receivers)
+
+"<CR><LF><^A>ddd:hh:mm:ssq<CR>"
+ D T QC
+
+ poll: none; offsets: Y = none, D = 0, T = 4, A = 12, Q = 12
+ hh:mm:ss = hours, minutes, seconds
+ q = quality/alarm indicator (<SP> = locked, ? = alarm)
+
+ Note: alarm condition is indicated by ? at A, which occurs during
+ initial synchronization and when received signal is lost for an
+ extended period; unlock condition is indicated by other than <SP>
+ at Q
+
+ example: "216:15:36:43 "
+ D T Q
+
+Heath GC-1000 Most Accurate Clock (WWV/H)
+
+"<CR>hh:mm:ss.f dd/mm/yy<CR>"
+ C T A D
+
+ poll: none; offsets: Y = none, D = 15, T = 0, A = 9, Q = none
+ hh:mm:ss = hours, minutes, seconds
+ f = deciseconds (? when out of spec)
+ dd/mm = day, month
+ yy = year of century (from DIPswitches)
+
+ Note: 0?:??:??.? is displayed before synch is first established and
+ hh:mm:ss.? once synch is established and then lost again for about
+ a day.
+
+ example: "15:36:43.6 04/08/91"
+ T A D Y
+
+PST/Traconex 1020 Time Source (WWV/H) (firmware revision V4.01)
+
+"frdzycchhSSFTttttuuxx<CR>" "ahh:mm:ss.fffs<CR>" "yy/dd/mm/ddd<CR>"
+ A Q T Y D
+
+ poll: "QMQDQT"; offsets: Y = 0, D = 3 T = 1,, A = 11, Q = 13
+ f = frequency enable (O = all frequencies enabled)
+ r = baud rate (3 = 1200, 6 = 9600)
+ d = features indicator (@ = month/day display enabled)
+ z = time zone (0 = UTC)
+ y = year (5 = 1991)
+ cc = WWV propagation delay (52 = 22 ms)
+ hh = WWVH propagation delay (81 = 33 ms)
+ SS = status (80 or 82 = operating correctly)
+ F = current receive frequency (1-5 = 2.5, 5, 10, 15, 20 MHz)
+ T = transmitter (C = WWV, H = WWVH)
+ tttt = time since last update (minutes)
+ uu = flush character (03 = ^C)
+ xx = 94 (unknown) (firmware revision X4.01.999 only)
+
+ a = AM/PM indicator (A = AM, P = PM, <SP> - 24-hour format)
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ s = daylight-saving indicator (<SP> standard, D daylight)
+
+ yy = year of century (from DIPswitches)
+ dd/mm/ddd = day of month, month of year, day of year
+
+ Note: The alarm condition is indicated by other than ? at A, which
+ occurs during initial synchronization and when received signal is
+ lost for an extended period. A receiver unlock condition is
+ indicated by other than "0000" in the tttt subfield at Q.
+
+ example: "O3@055281824C00000394 91/08/04/216 15:36:43.640"
+ T Y D T
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/contrib/ntp/scripts/stats/clock.awk b/contrib/ntp/scripts/stats/clock.awk
new file mode 100755
index 0000000..ef62da9
--- /dev/null
+++ b/contrib/ntp/scripts/stats/clock.awk
@@ -0,0 +1,431 @@
+# awk program to scan clockstat files and report errors/statistics
+#
+# usage: awk -f check.awk clockstats
+#
+# This program works for the following radios:
+# PST/Traconex 1020 WWV reciever
+# Arbiter 1088 GPS receiver
+# Spectracom 8170/Netclock-2 WWVB receiver
+# IRIG audio decoder
+# Austron 2200A/2201A GPS receiver (see README.austron file)
+#
+BEGIN {
+ etf_min = osc_vmin = osc_tmin = 1e9
+ etf_max = osc_vmax = osc_tmax = -1e9
+}
+#
+# scan all records in file
+#
+{
+ #
+ # select PST/Traconex WWV records
+ # 00:00:37.234 96/07/08/190 O6@0:5281825C07510394
+ #
+ if (NF >= 4 && $3 == "127.127.3.1") {
+ if (substr($6, 14, 4) > "0010")
+ wwv_sync++
+ if (substr($6, 13, 1) == "C")
+ wwv_wwv++
+ if (substr($6, 13, 1) == "H")
+ wwv_wwvh++
+ x = substr($6, 12, 1)
+ if (x == "1")
+ wwv_2.5++
+ else if (x == "2")
+ wwv_5++
+ else if (x == "3")
+ wwv_10++
+ else if (x == "4")
+ wwv_15++
+ else if (x == "5")
+ wwv_20++
+ continue
+ }
+ #
+ # select Arbiter GPS records
+ # 96 190 00:00:37.000 0 V=08 S=44 T=3 P=10.6 E=00
+ # N39:42:00.951 W075:46:54.880 210.55 2.50 0.00
+ #
+ if (NF >= 4 && $3 == "127.127.11.1") {
+ if (NF > 8) {
+ arb_count++
+ if ($7 != 0)
+ arb_sync++
+ x = substr($10, 3, 1)
+ if (x == "0")
+ arb_0++
+ else if (x == "1")
+ arb_1++
+ else if (x == "2")
+ arb_2++
+ else if (x == "3")
+ arb_3++
+ else if (x == "4")
+ arb_4++
+ else if (x == "5")
+ arb_5++
+ else if (x == "6")
+ arb_6++
+ } else if (NF == 8) {
+ arbn++
+ arb_mean += $7
+ arb_rms += $7 * $7
+ if (arbn > 0) {
+ x = $7 - arb_val
+ arb_var += x * x
+ }
+ arb_val = $7
+ }
+ continue
+ }
+ #
+ # select Spectracom WWVB records
+ # see summary for decode
+ # 96 189 23:59:32.248 D
+ #
+ if (NF >= 4 && $3 == "127.127.4.1") {
+ if ($4 == "SIGNAL" || NF > 7)
+ printf "%s\n", $0
+ else {
+ wwvb_count++
+ if ($4 ~ /\?/)
+ wwvb_x++
+ else if ($4 ~ /A/)
+ wwvb_a++
+ else if ($4 ~ /B/)
+ wwvb_b++
+ else if ($4 ~ /C/)
+ wwvb_c++
+ else if ($4 ~ /D/)
+ wwvb_d++
+ }
+ continue
+ }
+ #
+ # select IRIG audio decoder records
+ # see summary for decode
+ #
+ if (NF >= 4 && $3 == "127.127.6.0") {
+ irig_count++
+ if ($5 ~ /\?/)
+ irig_error++
+ continue
+ }
+ #
+ # select Austron GPS LORAN ENSEMBLE records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $6 == "ENSEMBLE") {
+ ensemble_count++
+ if ($9 <= 0)
+ ensemble_badgps++
+ else if ($12 <= 0)
+ ensemble_badloran++
+ else {
+ if ($13 > 200e-9 || $13 < -200e-9)
+ ensemble_200++
+ else if ($13 > 100e-9 || $13 < -100e-9)
+ ensemble_100++
+ ensemble_mean += $13
+ ensemble_rms += $13 * $13
+ }
+ continue
+ }
+ #
+ # select Austron LORAN TDATA records
+ # see summary for decode; note that signal quality log is simply
+ # copied to output
+ #
+ else if (NF >= 7 && $6 == "TDATA") {
+ tdata_count++
+ for (i = 7; i < NF; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m += $i
+ tdata_m++
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w += $i
+ tdata_w++
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x += $i
+ tdata_x++
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y += $i
+ tdata_y++
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z += $i
+ tdata_z++
+ }
+ }
+ continue
+ }
+ #
+ # select Austron ITF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ITF" && $12 >= 100) {
+ itf_count++
+ if ($9 > 200e-9 || $9 < -200e-9)
+ itf_200++
+ else if ($9 > 100e-9 || $9 < -100e-9)
+ itf_100++
+ itf_mean += $9
+ itf_rms += $9 * $9
+ itf_var += $10 * $10
+ continue
+ }
+ #
+ # select Austron ETF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ETF" && $13 >= 100) {
+ etf_count++
+ if ($6 > etf_max)
+ etf_max = $6
+ else if ($6 < etf_min)
+ etf_min = $6
+ etf_mean += $6
+ etf_rms += $6 * $6
+ etf_var += $9 * $9
+ continue
+ }
+ #
+ # select Austron TRSTAT records
+ # see summary for decode
+ #
+ else if (NF >= 5 && $5 == "TRSTAT") {
+ trstat_count++
+ j = 0
+ for (i = 6; i <= NF; i++)
+ if ($i == "T")
+ j++
+ trstat_sat[j]++
+ continue
+ }
+ #
+ # select Austron ID;OPT;VER records
+ #
+ # config GPS 2201A TTY1 TC1 LORAN IN OUT1 B.00 B.00 28-Apr-93
+ #
+ # GPS 2201A receiver model
+ # TTY1 rs232 moduel
+ # TC1 IRIG module
+ # LORAN LORAN assist module
+ # IN input module
+ # OUT1 output module
+ # B.00 B.00 firmware revision
+ # 28-Apr-9 firmware date3
+ #
+ else if (NF >= 5 && $5 == "ID;OPT;VER") {
+ id_count++
+ id_temp = ""
+ for (i = 6; i <= NF; i++)
+ id_temp = id_temp " " $i
+ if (id_string != id_temp)
+ printf "config%s\n", id_temp
+ id_string = id_temp
+ continue
+ }
+ #
+ # select Austron POS;PPS;PPSOFF records
+ #
+ # position +39:40:48.425 -075:45:02.392 +74.09 Stored UTC 0 200 0
+ #
+ # +39:40:48.425 position north latitude
+ # -075:45:02.392 position east longitude
+ # +74.09 elevation (meters)
+ # Stored position is stored
+ # UTC time is relative to UTC
+ # 0 200 0 PPS offsets
+ #
+ else if (NF >= 5 && $5 == "POS;PPS;PPSOFF") {
+ pos_count++
+ pos_temp = ""
+ for (i = 6; i <= NF; i++)
+ pos_temp = pos_temp " " $i
+ if (pos_string != pos_temp)
+ printf "position%s\n", pos_temp
+ pos_string = pos_temp
+ continue
+ }
+ #
+ # select Austron OSC;ET;TEMP records
+ #
+ # loop 1121 Software Control Locked
+ #
+ # 1121 oscillator type
+ # Software Control loop is under software control
+ # Locked loop is locked
+ #
+ else if (NF >= 5 && $5 == "OSC;ET;TEMP") {
+ osc_count++
+ osc_temp = $6 " " $7 " " $8 " " $9
+ if (osc_status != osc_temp)
+ printf "loop %s\n", osc_temp
+ osc_status = osc_temp
+ if ($10 > osc_vmax)
+ osc_vmax = $10
+ if ($10 < osc_vmin)
+ osc_vmin = $10
+ if ($11 > osc_tmax)
+ osc_tmax = $11
+ if ($11 < osc_tmin)
+ osc_tmin = $11
+ continue
+ }
+ #
+ # select Austron UTC records
+ # these ain't ready yet
+ #
+ else if (NF >= 5 && $5 == "UTC") {
+ utc_count++
+ utc_temp = ""
+ for (i = 6; i <= NF; i++)
+ utc_temp = utc_temp " " $i
+ if (utc_string != utc_temp)
+# printf "utc%s\n", utc_temp
+ utc_string = utc_temp
+ continue
+ }
+} END {
+#
+# PST/Traconex WWV summary data
+#
+ if (wwv_wwv + wwv_wwvh > 0)
+ printf "wwv %d, wwvh %d, err %d, MHz (2.5) %d, (5) %d, (10) %d, (15) %d, (20) %d\n", wwv_wwv, wwv_wwvh, wwv_sync, wwv_2.5, wwv_5, wwv_10, wwv_15, wwv_20
+#
+# Arbiter 1088 summary data
+#
+# gps record count
+# err error count
+# sats(0-6) satellites tracked
+# mean 1 PPS mean (us)
+# rms 1 PPS rms error (us)
+# var 1 PPS Allan variance
+#
+ if (arb_count > 0) {
+ printf "gps %d, err %d, sats(0-6) %d %d %d %d %d %d %d", arb_count, arb_sync, arb_0, arb_1, arb_2, arb_3, arb_4, arb_5, arb_6
+ if (arbn > 1) {
+ arb_mean /= arbn
+ arb_rms = sqrt(arb_rms / arbn - arb_mean * arb_mean)
+ arb_var = sqrt(arb_var / (2 * (arbn - 1)))
+ printf ", mean %.2f, rms %.2f, var %.2e\n", arb_mean, arb_rms, arb_var * 1e-6
+ } else {
+ printf "\n"
+ }
+ }
+#
+# ensemble summary data
+#
+# ensemble record count
+# badgps gps data unavailable
+# badloran loran data unavailable
+# rms ensemble rms error (ns)
+# >200 ensemble error >200 ns
+# >100 100 ns < ensemble error < 200 ns
+#
+ if (ensemble_count > 0) {
+ ensemble_mean /= ensemble_count
+ ensemble_rms = sqrt(ensemble_rms / ensemble_count - ensemble_mean * ensemble_mean) * 1e9
+ printf "ensemble %d, badgps %d, badloran %d, rms %.1f, >200 %d, >100 %d\n", ensemble_count, ensemble_badgps, ensemble_badloran, ensemble_rms, ensemble_200, ensemble_100
+ }
+#
+# wwvb summary data
+#
+# wwvb record count
+# ? unsynchronized
+# >1 error > 1 ms
+# >10 error > 10 ms
+# >100 error > 100 ms
+# >500 error > 500 ms
+#
+ if (wwvb_count > 0)
+ printf "wwvb %d, ? %d, >1 %d, >10 %d, >100 %d, >500 %d\n", wwvb_count, wwvb_x, wwvb_a, wwvb_b, wwvb_c, wwvb_d
+#
+# irig summary data
+#
+# irig record count
+# err error count
+#
+ if (irig_count > 0)
+ printf "irig %d, err %d\n", irig_count, irig_error
+#
+# tdata summary data
+#
+# tdata record count
+# m M master OK-count, mean level (dB)
+# w W slave OK-count, mean level (dB)
+# x X slave OK-count, mean level (dB)
+# y Y slave OK-count, mean level (dB)
+# z Z slave OK-count, mean level (dB)
+#
+ if (tdata_count > 0 ) {
+ if (tdata_m > 0)
+ m /= tdata_count
+ if (tdata_x > 0)
+ w /= tdata_count
+ if (tdata_x > 0)
+ x /= tdata_count
+ if (tdata_y > 0)
+ y /= tdata_count
+ if (tdata_z > 0)
+ z /= tdata_count
+ printf "tdata %d, m %d %.1f, w %d %.1f, x %d %.1f, y %d %.1f, z %d %.1f\n", tdata_count, tdata_m, m, tdata_w, w, tdata_x, x, tdata_y, y, tdata_z, z
+ }
+#
+# itf summary data
+#
+# itf record count
+# rms itf rms error (ns)
+# >200 itf error > 200 ns
+# >100 itf error > 100 ns
+# var Allan variance
+#
+ if (itf_count > 1) {
+ itf_mean /= itf_count
+ itf_rms = sqrt(itf_rms / itf_count - itf_mean * itf_mean) * 1e9
+ itf_var = sqrt(itf_var / (2 * (itf_count - 1)))
+ printf "itf %d, rms %.1f, >200 %d, >100 %d, var %.2e\n", itf_count, itf_rms, itf_200, itf_100, itf_var
+ }
+#
+# etf summary data
+#
+# etf record count
+# mean etf mean (ns)
+# rms etf rms error (ns)
+# max etf maximum (ns)
+# min etf minimum (ns)
+# var Allan variance
+#
+ if (etf_count > 0) {
+ etf_mean /= etf_count
+ etf_rms = sqrt(etf_rms / etf_count - etf_mean * etf_mean)
+ etf_var = sqrt(etf_var / (2 * (etf_count - 1)))
+ printf "etf %d, mean %.1f, rms %.1f, max %d, min %d, var %.2e\n", etf_count, etf_mean, etf_rms, etf_max, etf_min, etf_var
+ }
+#
+# trstat summary data
+#
+# trstat record count
+# sat histogram of tracked satellites (0 - 7)
+#
+ if (trstat_count > 0)
+ printf "trstat %d, sat %d %d %d %d %d %d %d %d\n", trstat_count, trstat_sat[0], trstat_sat[1], trstat_sat[2], trstat_sat[2], trstat_sat[3], trstat_sat[4], trstat_sat[5], trstat_sat[6], trstat_sat[7]
+#
+# osc summary data
+#
+# osc record count
+# control control midrange (V) +/- deviation (mV)
+# temp oven temperature midrange +/- deviation (deg C)
+#
+ if (osc_count > 0)
+ printf "osc %d, control %.3f+/-%.3f, temp %.1f+/-%.2f\n", osc_count, (osc_vmax + osc_vmin) / 2, (osc_vmax - osc_vmin) / 2 * 1e3, (osc_tmax + osc_tmin) / 2, (osc_tmax - osc_tmin) / 2
+}
diff --git a/contrib/ntp/scripts/stats/dupe.awk b/contrib/ntp/scripts/stats/dupe.awk
new file mode 100755
index 0000000..317c2a4
--- /dev/null
+++ b/contrib/ntp/scripts/stats/dupe.awk
@@ -0,0 +1,8 @@
+#
+# delete duplicate lines
+#
+{
+ if (old != $0)
+ printf "%s\n", $0
+ old = $0
+}
diff --git a/contrib/ntp/scripts/stats/ensemble.S b/contrib/ntp/scripts/stats/ensemble.S
new file mode 100755
index 0000000..32a4dba
--- /dev/null
+++ b/contrib/ntp/scripts/stats/ensemble.S
@@ -0,0 +1,5 @@
+ensemble <- scan(file1, list(day=0, sec=0, gps=0, gpsw=0, loran=0, loranw=0, ensemble=0, std=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck = 0.03, mar = c(2, 2, 1, 1))
+plot(ensemble$sec, ensemble$ensemble, type="l", xlab=paste("MJD", ensemble$day, "Time (s)"), ylab="Ensemble Offset (ns)", ylim=c(-400, 400))
diff --git a/contrib/ntp/scripts/stats/ensemble.awk b/contrib/ntp/scripts/stats/ensemble.awk
new file mode 100755
index 0000000..136b33d
--- /dev/null
+++ b/contrib/ntp/scripts/stats/ensemble.awk
@@ -0,0 +1,17 @@
+# program to produce loran ensemble statistics from clockstats files
+#
+# usage: awk -f ensemble.awk clockstats
+#
+# format of input record (time values in seconds)
+# 49165 8.628 127.127.10.1 93:178:00:00:07.241 LORAN ENSEMBLE
+# -6.43E-08 +5.02E-08 .091 +5.98E-08 +1.59E-08 .909 +4.85E-08 +3.52E-08
+#
+# format of output record (time values in nanoseconds)
+# MJD sec GPS wgt LORAN wgt avg sigma
+# 49165 8.628 -64.3 0.091 59.8 0.909 48.5 35.2
+#
+# select LORAN ENSEMBLE records with valid format and weights
+{
+ if (NF >= 14 && $6 == "ENSEMBLE" && $9 > 0 && $12 > 0)
+ printf "%5s %9.3f %7.1f %6.3f %7.1f %6.3f %7.1f %7.1f\n", $1, $2, $7*1e9, $9, $10*1e9, $12, $13*1e9, $14*1e9
+}
diff --git a/contrib/ntp/scripts/stats/etf.S b/contrib/ntp/scripts/stats/etf.S
new file mode 100755
index 0000000..9b9c68b
--- /dev/null
+++ b/contrib/ntp/scripts/stats/etf.S
@@ -0,0 +1,15 @@
+options(digits=4)
+file2 <- "etf_summary"
+etf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+r <- lsfit(etf$sec, etf$offset)
+count<-length(etf$sec)
+mean<-r$coef[[1]]
+std<-sqrt(var(r$residuals))
+slope<-r$coef[[2]] * 1000
+cat("\n", file=file2 , append=TRUE, fill=FALSE, sep="")
+cat(file1, "\n", file=file2, append=TRUE, fill=FALSE, sep="")
+cat("etf1 ", count, ", T ", mean, " ns, R ", slope, " ps/s, std ", std, " us\n", file=file2, append=TRUE, fill=FALSE, sep="")
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(etf$sec, etf$offset, type="l", xlab=paste("MJD", etf$day, "Time (s)"), ylab="External Offset (ns)", ylim=c(-400, 400))
diff --git a/contrib/ntp/scripts/stats/etf.awk b/contrib/ntp/scripts/stats/etf.awk
new file mode 100755
index 0000000..8e6e334
--- /dev/null
+++ b/contrib/ntp/scripts/stats/etf.awk
@@ -0,0 +1,19 @@
+# program to produce external time/frequence statistics from clockstats files
+#
+# usage: awk -f etf.awk clockstats
+#
+# format of input record
+# 49165 40.473 127.127.10.1 93:178:00:00:39.238 ETF
+# +175.0 +176.8 2.0 +3.729E-11 +1.000E-10 +3.511E-11 4.005E-13 500
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49165 40.473 175.0 3.729e-11
+#
+# select ETF records with valid format
+{
+ if (NF >= 9 && $5 == "ETF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $6, $9
+ }
+}
+
diff --git a/contrib/ntp/scripts/stats/itf.S b/contrib/ntp/scripts/stats/itf.S
new file mode 100755
index 0000000..56c8c8d
--- /dev/null
+++ b/contrib/ntp/scripts/stats/itf.S
@@ -0,0 +1,5 @@
+itf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(itf$sec, itf$offset, type="l", xlab=paste("MJD", itf$day, "Time (s)"), ylab="Internal Offset (ns)", ylim=c(-400, 400))
diff --git a/contrib/ntp/scripts/stats/itf.awk b/contrib/ntp/scripts/stats/itf.awk
new file mode 100755
index 0000000..2b21c5b
--- /dev/null
+++ b/contrib/ntp/scripts/stats/itf.awk
@@ -0,0 +1,19 @@
+# program to produce intewrnal time/frequence statistics from clockstats files
+#
+# usage: awk -f itf.awk clockstats
+#
+# format of input record
+# 49227 67.846 127.127.10.1 93:240:00:00:51.816 ITF
+# COCO 0 +2.0579E-07 -3.1037E-08 -7.7723E-11 +6.5455E-10 500.00 4.962819
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49227 67.846 +2.0579E-07 -7.7723E-11
+#
+# select ITF records with valid format
+{
+ if (NF >= 10 && $5 == "ITF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $8 * 1e9, $10
+ }
+}
+
diff --git a/contrib/ntp/scripts/stats/loop.S b/contrib/ntp/scripts/stats/loop.S
new file mode 100755
index 0000000..8e564b6
--- /dev/null
+++ b/contrib/ntp/scripts/stats/loop.S
@@ -0,0 +1,7 @@
+options(digits=4)
+loop <- scan(file1, list(day=0, sec=0, offset=0, freq=0, tc=0))
+loop$offset <- loop$offset * 1e6
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(loop$sec, loop$offset, type="l", xlab=paste("MJD", loop$day, "Time (s)"), ylab="PLL Offset (us)", ylim=c(-400, 400))
diff --git a/contrib/ntp/scripts/stats/loop.awk b/contrib/ntp/scripts/stats/loop.awk
new file mode 100755
index 0000000..bac90db
--- /dev/null
+++ b/contrib/ntp/scripts/stats/loop.awk
@@ -0,0 +1,45 @@
+# awk program to scan loopstats files and report errors/statistics
+#
+# usage: awk -f loop.awk loopstats
+#
+# format of loopstats record
+# MJD sec time (s) freq (ppm) poll
+# 49235 3.943 0.000016 22.4716 6
+#
+# format of output dataset (time values in milliseconds, freq in ppm)
+# loopstats.19960706
+# loop 1180, 0+/-11.0, rms 2.3, freq -24.45+/-0.045, var 0.019
+#
+BEGIN {
+ loop_tmax = loop_fmax = -1e9
+ loop_tmin = loop_fmin = 1e9
+}
+#
+# scan all records in file
+#
+{
+ if (NF >= 5) {
+ loop_count++
+ if ($3 > loop_tmax)
+ loop_tmax = $3
+ if ($3 < loop_tmin)
+ loop_tmin = $3
+ if ($4 > loop_fmax)
+ loop_fmax = $4
+ if ($4 < loop_fmin)
+ loop_fmin = $4
+ loop_time += $3
+ loop_time_rms += $3 * $3
+ loop_freq += $4
+ loop_freq_rms += $4 * $4
+ }
+} END {
+ if (loop_count > 0) {
+ loop_time /= loop_count
+ loop_time_rms = sqrt(loop_time_rms / loop_count - loop_time * loop_time)
+ loop_freq /= loop_count
+ loop_freq_rms = sqrt(loop_freq_rms / loop_count - loop_freq * loop_freq)
+ printf "loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n", loop_count, (loop_tmax + loop_tmin) / 2 * 1e6, (loop_tmax - loop_tmin) / 2 * 1e6, loop_time_rms * 1e6, (loop_fmax + loop_fmin) / 2, (loop_fmax - loop_fmin) / 2, loop_freq_rms
+ }
+}
+
diff --git a/contrib/ntp/scripts/stats/loop_summary b/contrib/ntp/scripts/stats/loop_summary
new file mode 100755
index 0000000..35479ec
--- /dev/null
+++ b/contrib/ntp/scripts/stats/loop_summary
@@ -0,0 +1,2 @@
+
+loopstats.[1-9]
diff --git a/contrib/ntp/scripts/stats/peer.awk b/contrib/ntp/scripts/stats/peer.awk
new file mode 100755
index 0000000..5fe260e
--- /dev/null
+++ b/contrib/ntp/scripts/stats/peer.awk
@@ -0,0 +1,68 @@
+# awk program to scan peerstats files and report errors/statistics
+#
+# usage: awk -f peer.awk peerstats
+#
+# format of peerstats record
+# MJD sec ident stat offset (s) delay (s) disp (s)
+# 49235 11.632 128.4.2.7 f414 -0.000041 0.21910 0.00084
+#
+# format of output dataset (time values in milliseconds)
+# peerstats.19960706
+# ident cnt mean rms max delay dist disp
+# ==========================================================================
+# 140.173.112.2 85 -0.509 1.345 4.606 80.417 49.260 1.092
+# 128.4.1.20 1364 0.058 0.364 4.465 3.712 10.540 1.101
+# 140.173.16.1 1415 -0.172 0.185 1.736 3.145 5.020 0.312
+#...
+#
+BEGIN {
+ n = 0
+ MAXDISTANCE = 1.0
+}
+#
+# scan all records in file
+#
+# we toss out all distances greater than one second on the assumption the
+# peer is in initial acquisition
+#
+{
+ if (NF >= 7 && ($7 + $6 / 2) < MAXDISTANCE) {
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($3 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $3
+ peer_tmax[i] = peer_dist[i] = -1e9
+ peer_tmin[i] = 1e9
+ n++
+ }
+ peer_count[i]++
+ if ($5 > peer_tmax[i])
+ peer_tmax[i] = $5
+ if ($5 < peer_tmin[i])
+ peer_tmin[i] = $5
+ dist = $7 + $6 / 2
+ if (dist > peer_dist[i])
+ peer_dist[i] = dist
+ peer_time[i] += $5
+ peer_time_rms[i] += $5 * $5
+ peer_delay[i] += $6
+ peer_disp[i] += $7
+ }
+} END {
+ printf " ident cnt mean rms max delay dist disp\n"
+ printf "==========================================================================\n"
+ for (i = 0; i < n; i++) {
+ peer_time[i] /= peer_count[i]
+ peer_time_rms[i] = sqrt(peer_time_rms[i] / peer_count[i] - peer_time[i] * peer_time[i])
+ peer_delay[i] /= peer_count[i]
+ peer_disp[i] /= peer_count[i]
+ peer_tmax[i] = peer_tmax[i] - peer_time[i]
+ peer_tmin[i] = peer_time[i] - peer_tmin[i]
+ if (peer_tmin[i] > peer_tmax[i])
+ peer_tmax[i] = peer_tmin[i]
+ printf "%-15s%5d%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", peer_ident[i], peer_count[i], peer_time[i] * 1e3, peer_time_rms[i] * 1e3, peer_tmax[i] * 1e3, peer_delay[i] * 1e3, peer_dist[i] * 1e3, peer_disp[i] * 1e3
+ }
+}
diff --git a/contrib/ntp/scripts/stats/psummary.awk b/contrib/ntp/scripts/stats/psummary.awk
new file mode 100755
index 0000000..ec06b0c
--- /dev/null
+++ b/contrib/ntp/scripts/stats/psummary.awk
@@ -0,0 +1,82 @@
+# program to scan peer_summary file and produce summary of daily summaries
+#
+# usage: awk -f psummary.awk peer_summary
+#
+# format of input records
+# peerstats.19960706
+# ident cnt mean rms max delay dist disp
+# ==========================================================================
+# 140.173.112.2 85 -0.509 1.345 4.606 80.417 49.260 1.092
+# 128.4.1.20 1364 0.058 0.364 4.465 3.712 10.540 1.101
+# ...
+#
+# format of output records (actual data from rackety.udel.edu)
+# host days mean rms max >1 >5 >10 >50
+# ==================================================================
+# 127.127.22.1 1090 0.001 0.401 99.800 19 14 13 10
+# 127.0.0.1 1188 0.060 1.622 105.004 78 65 51 32
+# 127.127.4.1 586 0.000 0.000 0.000 0 0 0 0
+# 140.173.64.1 975 -0.010 2.552 257.595 399 192 114 8
+# 128.175.1.3 1121 0.447 8.637 204.123 479 460 397 147
+# 140.173.16.1 1106 0.027 1.014 267.857 242 38 31 23
+# 128.4.1.4 1119 0.023 1.037 267.748 223 41 34 23
+# 128.4.1.2 850 1.202 1.654 267.704 196 53 45 34
+# 128.4.1.20 1101 0.088 1.139 268.322 430 111 83 16
+# 140.173.32.1 979 -0.949 2.344 257.671 396 217 136 7
+# 140.173.112.2 1066 0.040 2.111 152.969 442 315 152 16
+# 140.173.80.1 1059 0.019 1.858 87.690 438 348 150 9
+# 140.173.96.1 1015 0.110 2.007 266.744 399 314 170 17
+# 140.173.128.1 1103 -0.002 2.600 257.672 465 262 132 13
+# 140.222.135.1 347 -4.626 8.804 196.394 135 135 134 95
+# 140.173.128.2 1081 -0.046 2.967 261.448 463 342 172 17
+# 140.222.141.1 354 0.820 8.809 195.333 142 141 139 100
+# 140.173.144.2 1058 -0.107 2.805 270.498 448 341 163 17
+# 140.222.134.1 354 -0.056 8.479 172.458 142 141 141 100
+# 140.222.144.1 415 -1.456 9.964 191.684 161 161 161 123
+# 140.222.136.1 234 0.902 7.707 182.431 62 62 62 48
+# 128.175.1.1 774 0.890 4.838 266.799 358 291 200 83
+# 127.127.10.1 1086 -0.002 1.462 231.128 240 239 60 57
+# 140.173.48.2 576 0.016 4.092 350.512 213 126 88 16
+# 128.4.1.11 3 0.000 0.000 0.000 0 0 0 0
+# 128.4.1.26 386 -1.363 20.251 341.284 164 164 161 132
+#
+# select table beginning with "ident"
+{
+ if (NF < 8 || $1 == "ident")
+ continue
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($1 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $1
+ n++
+ }
+ peer_count[i]++
+ if (($7 - $6 / 2) < 400) {
+ peer_count[i]++
+ peer_mean[i] += $3
+ peer_var[i] += $4 * $4
+ if ($5 > peer_max[i])
+ peer_max[i] = $5
+ if ($5 > 1)
+ peer_1[i]++
+ if ($5 > 5)
+ peer_2[i]++
+ if ($5 > 10)
+ peer_3[i]++
+ if ($5 > 50)
+ peer_4[i]++
+ }
+} END {
+ printf " host days mean rms max >1 >5 >10 >50\n"
+ printf "==================================================================\n"
+ for (i = 0; i < n; i++) {
+ if (peer_count[i] <= 0)
+ continue
+ peer_mean[i] /= peer_count[i]
+ peer_var[i] = sqrt(peer_var[i] / peer_count[i])
+ printf "%-15s%4d%10.3f%10.3f%10.3f%4d%4d%4d%4d\n", peer_ident[i], peer_count[i], peer_mean[i], peer_var[i], peer_max[i], peer_1[i], peer_2[i], peer_3[i], peer_4[i]
+ }
+}
diff --git a/contrib/ntp/scripts/stats/summary.sh b/contrib/ntp/scripts/stats/summary.sh
new file mode 100755
index 0000000..655c4be
--- /dev/null
+++ b/contrib/ntp/scripts/stats/summary.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# Script to summarize ipeerstats, loopstats and clockstats files
+#
+# This script can be run from a cron job once per day, week or month. It
+# runs the file-specific summary script and appends the summary data to
+# designated files.
+#
+DATE=`date +19%y%m%d`
+S=/usr/local/bin/S
+SIN=S.in
+SOUT=S.out
+LOOP=loop_summary
+PEER=peer_summary
+CLOCK=clock_summary
+
+rm -f $SIN $SOUT
+
+#
+# Summarize loopstats files
+#
+for f in loopstats.[12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]; do
+ d=`echo $f | cut -f2 -d.`
+ if [ -f $f ] && [ $DATE != $d ]; then
+ echo " " >>$LOOP
+ echo $f >>$LOOP
+ awk -f loop.awk $f >>$LOOP
+ if [ -f $S ]; then
+ echo "file1<-"\"${f}\" >>$SIN
+ echo "source("\""loop.S"\"")" >>$SIN
+ echo "unix("\""rm ${f}"\"")" >>$SIN
+ else
+ rm -f $f
+ fi
+ fi
+done
+
+#
+# Summarize peerstats files
+#
+for f in peerstats.199[4-9][0-1][0-9][0-3][0-9]; do
+ d=`echo $f | cut -f2 -d.`
+ if [ -f $f ] && [ $DATE != $d ]; then
+ echo " " >>$PEER
+ echo $f >>$PEER
+ awk -f peer.awk $f >>$PEER
+ rm -f $f
+ fi
+done
+
+#
+# Summarize clockstats files
+#
+for f in clockstats.199[4-9][0-1][0-9][0-3][0-9]; do
+ d=`echo $f | cut -f2 -d.`
+ if [ -f $f ] && [ $DATE != $d ]; then
+ echo " " >>$CLOCK
+ echo $f >>$CLOCK
+ awk -f clock.awk $f >>$CLOCK
+ if [ -f /dev/gps[0-9] ]; then
+ awk -f itf.awk $f >itf.$d
+ awk -f etf.awk $f >etf.$d
+ awk -f ensemble.awk $f >ensemble.$d
+ awk -f tdata.awk $f >tdata.$d
+ fi
+ rm -f $f
+ fi
+done
+
+#
+# Process clockstat files with S and generate PostScript plots
+#
+for f in itf etf ensemble tdata; do
+ for d in ${f}.199[4-9][0-1][0-9][0-3][0-9]; do
+ if [ -f $d ]; then
+ if [ -f $S ]; then
+ echo "file1<-"\"${d}\" >>$SIN
+ echo "source("\"${f}.S\"")" >>$SIN
+ echo "unix("\""rm ${d}"\"")" >>$SIN
+ else
+ rm -f $d
+ fi
+ fi
+ done
+done
+if [ -f $SIN ]; then
+ $S BATCH $SIN $SOUT
+fi
diff --git a/contrib/ntp/scripts/stats/tdata.S b/contrib/ntp/scripts/stats/tdata.S
new file mode 100755
index 0000000..f360a24
--- /dev/null
+++ b/contrib/ntp/scripts/stats/tdata.S
@@ -0,0 +1,5 @@
+tdata <- scan(file1, list(day=0, sec=0, m=0, w=0, x=0, y=0, z=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(tdata$sec, tdata$m, type="l", xlab=paste("MJD", tdata$day, "Time (s)"), ylab="LORAN-M SNR (dB)")
diff --git a/contrib/ntp/scripts/stats/tdata.awk b/contrib/ntp/scripts/stats/tdata.awk
new file mode 100755
index 0000000..5be9c04
--- /dev/null
+++ b/contrib/ntp/scripts/stats/tdata.awk
@@ -0,0 +1,45 @@
+# program to produce loran tdata statistics from clockstats files
+#
+# usage: awk -f tdata.awk clockstats
+#
+# format of input record (missing replaced by -40.0)
+# 49228 36.852 127.127.10.1 93:241:00:00:20.812 LORAN TDATA
+# M OK 0 0 1169.14 -7.4 3.16E-07 .424
+# W CV 0 0 3329.30 -16.4 1.81E-06
+# X OK 0 0 1737.19 -10.5 3.44E-07 .358
+# Y OK 0 0 2182.07 -9.0 4.41E-07 .218
+#
+# format of output record (time in nanoseconds, signal values in dB)
+# MJD sec time M W X Y Z
+# 49228 36.852 175.0 -7.4 -16.4 -10.5 -9.0
+#
+# select LORAN TDATA records with valid format
+{
+ if (NF >= 7 && $6 == "TDATA") {
+ m = w = x = y = z = -40.0
+ for (i = 7; i < NF - 5; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m = $i
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w = $i
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x = $i
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y = $i
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z = $i
+ }
+ }
+ printf "%5s %9.3f %6.1f %6.1f %6.1f %6.1f %6.1f\n", $1, $2, m, w, x, y, z
+ }
+}
+
diff --git a/contrib/ntp/scripts/summary.pl b/contrib/ntp/scripts/summary.pl
new file mode 100644
index 0000000..c9deda9
--- /dev/null
+++ b/contrib/ntp/scripts/summary.pl
@@ -0,0 +1,357 @@
+#!/usr/bin/perl -w
+# $Id: summary.pl,v 1.1.1.1 1999/05/26 00:48:25 stenn Exp $
+# Perl version of (summary.sh, loop.awk, peer.awk):
+# Create summaries from xntpd's loop and peer statistics.
+#
+# Copyright (c) 1997, Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 5.003; # "never tested with any other version of Perl"
+use strict;
+
+use Getopt::Long;
+
+my $statsdir = ".";
+my $skip_time_steps = 3600.0; # ignore time offsets larger that this
+my $startdate = "19700101"; # first data file to use
+my $enddate=`date -u +%Y%m%d`; chomp $enddate; --$enddate;
+my $peer_dist_limit = 400.0;
+
+my %options = ("directory=s" => \$statsdir,
+ "skip-time-steps:f" => \$skip_time_steps,
+ "start-date=s" => \$startdate,
+ "end-date=s" => \$enddate,
+ "peer-dist-limit=f" => \$peer_dist_limit);
+
+if ( !GetOptions(%options) )
+{
+ print STDERR "valid options for $0 are:\n";
+ my $opt;
+ foreach $opt (sort(keys %options)) {
+ print STDERR "\t--$opt ";
+ if ( ref($options{$opt}) eq "ARRAY" ) {
+ print STDERR "(" . join (" ", @{$options{$opt}}) . ")\n";
+ } else {
+ print STDERR "(${$options{$opt}})\n";
+ }
+ }
+ print STDERR "\n";
+ die;
+}
+
+die "$statsdir: no such directory" unless (-d $statsdir);
+die "$skip_time_steps: skip-time-steps must be positive"
+ unless ($skip_time_steps >= 0.0);
+die "$startdate: invalid start date"
+ unless ($startdate =~ m/.*([12]\d{3}[01]\d[0-3]\d)$/);
+die "$enddate: invalid end date"
+ unless ($enddate =~ m/.*([12]\d{3}[01]\d[0-3]\d)$/);
+
+$skip_time_steps = 0.128 if ($skip_time_steps == 0);
+
+sub min
+{
+ my ($result, @rest) = @_;
+ map { $result = $_ if ($_ < $result) } @rest;
+ return($result);
+}
+
+sub max
+{
+ my ($result, @rest) = @_;
+ map { $result = $_ if ($_ > $result) } @rest;
+ return($result);
+}
+
+# calculate mean, range, and standard deviation for offset and frequency
+sub do_loop
+{
+ my ($directory, $fname, $out_file) = @_;
+ print "$directory/$fname\n";
+ open INPUT, "$directory/$fname";
+ open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
+ print OUTPUT "$fname\n";
+ my ($loop_tmax, $loop_fmax) = (-1e9, -1e9);
+ my ($loop_tmin, $loop_fmin) = (1e9, 1e9);
+ my ($loop_time_rms, $loop_freq_rms) = (0, 0);
+ my $loop_count = 0;
+ my $loop_time = 0;
+ my $loop_freq = 0;
+ my ($freq, $offs);
+ my @Fld;
+ while (<INPUT>) {
+ chop; # strip record separator
+ @Fld = split;
+ next if ($#Fld < 4);
+# 50529 74356.259 -0.000112 16.1230 8
+ if ($Fld[2] > $skip_time_steps || $Fld[2] < -$skip_time_steps) {
+ warn "ignoring loop offset $Fld[2] (file $fname, line $.)\n";
+ next
+ }
+ $loop_count++;
+ ($offs, $freq) = ($Fld[2], $Fld[3]);
+ $loop_tmax = max($loop_tmax, $offs);
+ $loop_tmin = min($loop_tmin, $offs);
+ $loop_fmax = max($loop_fmax, $freq);
+ $loop_fmin = min($loop_fmin, $freq);
+ $loop_time += $offs;
+ $loop_time_rms += $offs * $offs;
+ $loop_freq += $freq;
+ $loop_freq_rms += $freq * $freq;
+ }
+ close INPUT;
+ if ($loop_count > 1) {
+ $loop_time /= $loop_count;
+ $loop_time_rms = $loop_time_rms / $loop_count - $loop_time * $loop_time;
+ if ($loop_time_rms < 0) {
+ warn "loop_time_rms: $loop_time_rms < 0";
+ $loop_time_rms = 0;
+ }
+ $loop_time_rms = sqrt($loop_time_rms);
+ $loop_freq /= $loop_count;
+ $loop_freq_rms = $loop_freq_rms / $loop_count - $loop_freq * $loop_freq;
+ if ($loop_freq_rms < 0) {
+ warn "loop_freq_rms: $loop_freq_rms < 0";
+ $loop_freq_rms = 0;
+ }
+ $loop_freq_rms = sqrt($loop_freq_rms);
+ printf OUTPUT
+ ("loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n",
+ $loop_count, ($loop_tmax + $loop_tmin) / 2 * 1e6,
+ ($loop_tmax - $loop_tmin) / 2 * 1e6, $loop_time_rms * 1e6,
+ ($loop_fmax + $loop_fmin) / 2, ($loop_fmax - $loop_fmin) / 2,
+ $loop_freq_rms);
+ }
+ else {
+ warn "no valid lines in $directory/$fname";
+ }
+ close OUTPUT
+}
+
+# calculate mean, standard deviation, maximum offset, mean dispersion,
+# and maximum distance for each peer
+sub do_peer
+{
+ my ($directory, $fname, $out_file) = @_;
+ print "$directory/$fname\n";
+ open INPUT, "$directory/$fname";
+ open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
+ print OUTPUT "$fname\n";
+# we toss out all distances greater than one second on the assumption the
+# peer is in initial acquisition
+ my ($n, $MAXDISTANCE) = (0, 1.0);
+ my %peer_time;
+ my %peer_time_rms;
+ my %peer_count;
+ my %peer_delay;
+ my %peer_disp;
+ my %peer_dist;
+ my %peer_ident;
+ my %peer_tmin;
+ my %peer_tmax;
+ my @Fld;
+ my ($i, $j);
+ my ($dist, $offs);
+ while (<INPUT>) {
+ chop; # strip record separator
+ @Fld = split;
+ next if ($#Fld < 6);
+# 50529 83316.249 127.127.8.1 9674 0.008628 0.00000 0.00700
+ $dist = $Fld[6] + $Fld[5] / 2;
+ next if ($dist > $MAXDISTANCE);
+ $offs = $Fld[4];
+ if ($offs > $skip_time_steps || $offs < -$skip_time_steps) {
+ warn "ignoring peer offset $offs (file $fname, line $.)\n";
+ next
+ }
+ $i = $n;
+ for ($j = 0; $j < $n; $j++) {
+ if ($Fld[2] eq $peer_ident{$j}) {
+ $i = $j; # peer found
+ last;
+ }
+ }
+ if ($i == $n) { # add new peer
+ $peer_ident{$i} = $Fld[2];
+ $peer_tmax{$i} = $peer_dist{$i} = -1e9;
+ $peer_tmin{$i} = 1e9;
+ $peer_time{$i} = $peer_time_rms{$i} = 0;
+ $peer_delay{$i} = $peer_disp{$i} = 0;
+ $peer_count{$i} = 0;
+ $n++;
+ }
+ $peer_count{$i}++;
+ $peer_tmax{$i} = max($peer_tmax{$i}, $offs);
+ $peer_tmin{$i} = min($peer_tmin{$i}, $offs);
+ $peer_dist{$i} = max($peer_dist{$i}, $dist);
+ $peer_time{$i} += $offs;
+ $peer_time_rms{$i} += $offs * $offs;
+ $peer_delay{$i} += $Fld[5];
+ $peer_disp{$i} += $Fld[6];
+ }
+ close INPUT;
+ print OUTPUT
+" ident cnt mean rms max delay dist disp\n";
+ print OUTPUT
+"==========================================================================\n";
+ my @lines = ();
+ for ($i = 0; $i < $n; $i++) {
+ next if $peer_count{$i} < 2;
+ $peer_time{$i} /= $peer_count{$i};
+ $peer_time_rms{$i} = sqrt($peer_time_rms{$i} / $peer_count{$i} -
+ $peer_time{$i} * $peer_time{$i});
+ $peer_delay{$i} /= $peer_count{$i};
+ $peer_disp{$i} /= $peer_count{$i};
+ $peer_tmax{$i} = $peer_tmax{$i} - $peer_time{$i};
+ $peer_tmin{$i} = $peer_time{$i} - $peer_tmin{$i};
+ if ($peer_tmin{$i} > $peer_tmax{$i}) { # can this happen at all?
+ $peer_tmax{$i} = $peer_tmin{$i};
+ }
+ push @lines, sprintf
+ "%-15s %4d %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f\n",
+ $peer_ident{$i}, $peer_count{$i}, $peer_time{$i} * 1e3,
+ $peer_time_rms{$i} * 1e3, $peer_tmax{$i} * 1e3,
+ $peer_delay{$i} * 1e3, $peer_dist{$i} * 1e3, $peer_disp{$i} * 1e3;
+ }
+ print OUTPUT sort @lines;
+ close OUTPUT;
+}
+
+sub do_clock
+{
+ my ($directory, $fname, $out_file) = @_;
+ print "$directory/$fname\n";
+ open INPUT, "$directory/$fname";
+ open OUTPUT, ">>$out_file" or die "can't open $out_file: $!";
+ print OUTPUT "$fname\n";
+ close INPUT;
+ close OUTPUT;
+}
+
+sub peer_summary
+{
+ my $in_file = shift;
+ my ($i, $j, $n);
+ my (%peer_ident, %peer_count, %peer_mean, %peer_var, %peer_max);
+ my (%peer_1, %peer_2, %peer_3, %peer_4);
+ my $dist;
+ my $max;
+ open INPUT, "<$in_file" or die "can't open $in_file: $!";
+ my @Fld;
+ $n = 0;
+ while (<INPUT>) {
+ chop; # strip record separator
+ @Fld = split;
+ next if ($#Fld < 7 || $Fld[0] eq 'ident');
+ $i = $n;
+ for ($j = 0; $j < $n; $j++) {
+ if ($Fld[0] eq $peer_ident{$j}) {
+ $i = $j;
+ last; # peer found
+ }
+ }
+ if ($i == $n) { # add new peer
+ $peer_count{$i} = $peer_mean{$i} = $peer_var{$i} = 0;
+ $peer_max{$i} = 0;
+ $peer_1{$i} = $peer_2{$i} = $peer_3{$i} = $peer_4{$i} = 0;
+ $peer_ident{$i} = $Fld[0];
+ ++$n;
+ }
+ $dist = $Fld[6] - $Fld[5] / 2;
+ if ($dist < $peer_dist_limit) {
+ $peer_count{$i}++;
+ $peer_mean{$i} += $Fld[2];
+ $peer_var{$i} += $Fld[3] * $Fld[3];
+ $max = $Fld[4];
+ $peer_max{$i} = max($peer_max{$i}, $max);
+ if ($max > 1) {
+ $peer_1{$i}++;
+ if ($max > 5) {
+ $peer_2{$i}++;
+ if ($max > 10) {
+ $peer_3{$i}++;
+ if ($max > 50) {
+ $peer_4{$i}++;
+ }
+ }
+ }
+ }
+ }
+ else {
+ warn "dist exceeds limit: $dist (file $in_file, line $.)\n";
+ }
+ }
+ close INPUT;
+ my @lines = ();
+ print
+ " host days mean rms max >1 >5 >10 >50\n";
+ print
+ "==================================================================\n";
+ for ($i = 0; $i < $n; $i++) {
+ next if ($peer_count{$i} < 2);
+ $peer_mean{$i} /= $peer_count{$i};
+ $peer_var{$i} = sqrt($peer_var{$i} / $peer_count{$i} -
+ $peer_mean{$i} * $peer_mean{$i});
+ push @lines, sprintf
+ "%-15s %3d %9.3f% 9.3f %9.3f %3d %3d %3d %3d\n",
+ $peer_ident{$i}, $peer_count{$i}, $peer_mean{$i}, $peer_var{$i},
+ $peer_max{$i}, $peer_1{$i}, $peer_2{$i}, $peer_3{$i}, $peer_4{$i};
+ }
+ print sort @lines;
+}
+
+my $loop_summary="/tmp/loop_summary";
+my $peer_summary="/tmp/peer_summary";
+my $clock_summary="/tmp/clock_summary";
+my (@loopfiles, @peerfiles, @clockfiles);
+
+print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n";
+
+opendir SDIR, $statsdir or die "directory ${statsdir}: $!";
+rewinddir SDIR;
+@loopfiles=sort grep /loop.*[12]\d{3}[01]\d[0-3]\d/, readdir SDIR;
+rewinddir SDIR;
+@peerfiles=sort grep /peer.*[12]\d{3}[01]\d[0-3]\d/, readdir SDIR;
+rewinddir SDIR;
+@clockfiles=sort grep /clock.*[12]\d{3}[01]\d[0-3]\d/, readdir SDIR;
+closedir SDIR;
+
+map { unlink $_ if -f $_ } ($loop_summary, $peer_summary, $clock_summary);
+
+my $date;
+map {
+ $date = $_; $date =~ s/.*([12]\d{3}[01]\d[0-3]\d)$/$1/;
+ if ($date ge $startdate && $date le $enddate) {
+ do_loop $statsdir, $_, $loop_summary;
+ }
+} @loopfiles;
+
+map {
+ $date = $_; $date =~ s/.*([12]\d{3}[01]\d[0-3]\d)$/$1/;
+ if ($date ge $startdate && $date le $enddate) {
+ do_peer $statsdir, $_, $peer_summary;
+ }
+} @peerfiles;
+
+map {
+ $date = $_; $date =~ s/.*([12]\d{3}[01]\d[0-3]\d)$/$1/;
+ if ($date ge $startdate && $date le $enddate) {
+ do_clock $statsdir, $_, $clock_summary;
+ }
+} @clockfiles;
+
+print STDERR "Creating peer summary with limit $peer_dist_limit\n";
+peer_summary $peer_summary if (-f $peer_summary);
diff --git a/contrib/ntp/scripts/support/README b/contrib/ntp/scripts/support/README
new file mode 100644
index 0000000..812965b
--- /dev/null
+++ b/contrib/ntp/scripts/support/README
@@ -0,0 +1,73 @@
+The bin and etc directories contain several scripts (sh and perl) that
+should ease startup and configuration of NTP sites.
+
+ bin/monl is a monitoring script that prints out new, current and
+ old customers of an NTP timeserver when monitoring is
+ in effect.
+ monl has following options:
+ -i <regexp> (regular expression matchin IP addres to be ignored
+ -d <directory> where the current state is kept (default /tmp)
+ -v debug output
+ -n do not translate IP addresses into hostnames
+ <host> host to be analyzed
+
+ monl uses xntpdc for information gathering and is thus
+ limited to the NTP version xntpdc is compiled for.
+
+ bin/mvstats moves compresses and removes statistics files (useful mainly
+ for reference servers
+
+ etc/install creates the locally needed directories for NTP (if not residung in /etc)
+
+ etc/rc starts up daemon with configuration file and key file
+ etc/cron cron called monitor statistic (uses bin/monl)
+ etc/crontab crontab prototype for reference time servers
+ etc/setup sh script sourced by the other scripts for variable setup
+
+YOU MUST EDIT THESE FILES TO REFLECT YOUR LOCAL SETUP !
+
+READ THIS BEFORE USING THE STARTUP SCRIPTS
+
+The startupscript etc/rc has been written for Suns and HPs. They are not
+guaranteed to work elsewhere. Following assumptions have been made:
+
+ All NTP related files reside in ONE directory having following structure:
+
+ bin/* - all executables (daemon, control, date, scripts)
+ etc/* - startup scripts and cron scripts
+ conf/* - NTP configuration files
+
+The variable NTPROOT (etc/rc, etc/install) must be edited to reflect
+the NTP directory (e.g. /usr/local/NTP)
+
+NTP config files are located via Suns arch command and have the name
+conf/`arch`.`arch -k`.
+These are the default configurations (usually clients). If a file with the name
+conf/`arch`.`arch -k`.`hostname` is present this file will be preferred (Reference host,
+gateway). If the arch command is not available no-arch is used. The arch command
+is usually a shell script which echoes a string unique the the current machine
+architecture.
+
+The tickadj command has its own conf/tickconf file which is used to set host
+specific tickadj values. The line with DEFAULT specifies the default tickadj
+parameters, all other lines consists of <hostname> <hostid>
+<tickadj parameters>. These lines need only be entered if the specified host
+needs parameters different from the default parameters.
+
+Reference clock support is provided for DCF77. If you need to initialize
+certain things for reference clock support (e.g. loading STREAMS modules),
+you need to edit etc/rc.
+
+The current config files of Erlangen are included in the conf directory.
+They are just for reference, but might help you a bit in setting up a
+synchronisation network.
+
+The advantage of keeping all config files centralized is the easier
+administration.
+
+We replicate the NTP directory via NFS and rdist.
+
+When you have set up the local config files (YOUR OWN!) you can call
+<NTPROOT>/etc/rc for daemon startup.
+
+For more information: time@informatik.uni-erlangen.de
diff --git a/contrib/ntp/scripts/support/bin/monl b/contrib/ntp/scripts/support/bin/monl
new file mode 100644
index 0000000..f0c48db
--- /dev/null
+++ b/contrib/ntp/scripts/support/bin/monl
@@ -0,0 +1,213 @@
+#!/local/bin/perl
+
+%service = ( 0, "unspec",
+ 1, "Active",
+ 2, "Passive",
+ 3, "Client",
+ 4, "Server",
+ 5, "Broadcast",
+ 6, "Control",
+ 7, "Private" );
+%nc = ();
+@ignpat = ();
+$noname = 0;
+$verbose = 0;
+$retries = 5;
+$lastkey = 0;
+
+sub timedelta {
+ local($tm, $days, $h, $m, $s);
+
+ $tm = @_[$[];
+ $days = 0;
+ $days = sprintf("%dd+", $days) if $days = int($tm / (60*60*24));
+ $days = "" unless $days;
+ $tm = $tm % (60*60*24);
+ $h = int($tm / (60*60));
+ $tm = $tm % (60*60);
+ $m = int($tm / 60);
+ $s = $tm % 60;
+
+ return sprintf("%s%02d:%02d:%02d", $days, $h, $m, $s);
+}
+
+sub listentry {
+ local($host, $mode) = split("$;" , @_[$[]);
+ local($count, $version, $firsttime) = split("$;" , $_[$[+1]);
+ local($name);
+
+ if (grep($host =~ m/$_/, @ignpat))
+ {
+ print "ignored $host ...\n" if $verbose;
+ return;
+ }
+
+ return if ! $count;
+
+ if (defined($nc{$host}))
+ {
+ $name = $nc{$host};
+ }
+ else
+ {
+ if ($noname)
+ {
+ $nc{$host} = $name = $host;
+ }
+ else
+ {
+ $name = (gethostbyaddr(pack("C4", split(/\./, $host)), 2))[$[];
+ $nc{$host} = $name = $host if ! defined($name);
+ }
+ }
+
+ printf ($fmt, ($lastkey eq $host) ? "" : $name, $service{$mode}, $count, $version, &timedelta($firsttime), $firsttime / $count);
+
+ if (@_[$[+2])
+ {
+ $hostcnt++ if $lastkey ne $host;
+ $packcnt += $count;
+ $maxtime = $firsttime if $firsttime > $maxtime;
+ }
+
+ $lastkey = $host;
+}
+
+while ($ARGV[$[] =~ /^-[nvid]$/)
+ {
+ if ($ARGV[$[] eq "-i")
+ {
+ shift;
+ push(@ignpat, shift) unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-d")
+ {
+ shift;
+ $dir = shift unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-n")
+ {
+ shift;
+ $noname = 1;
+ }
+ elsif ($ARGV[$[] eq "-v")
+ {
+ shift;
+ $verbose = 1;
+ }
+ }
+
+$dir = "/tmp" unless defined($dir);
+$gone = 60*60*48;
+$fmt = "%48s %10s %7d %7d %13s %14.3f\n";
+$sfmt = "%48s %10s %7s %7s %13s %14s\n";
+@lbl = ("Host", "Mode", "Count", "Version", "Time active", "Packetinterval");
+
+if (!defined($ARGV[$[]))
+ {
+ $hostname = `hostname`;
+ chop($hostname);
+ unshift(@ARGV, $hostname);
+ }
+
+foreach $hostname (@ARGV)
+ {
+ $dbmfile = $dir . "/monlstats-" . $hostname;
+ $monl = "xntpdc -c 'hostnames no' -c monl $hostname | tail +3 |";
+ $hostcnt = 0;
+ $packcnt = 0;
+ $maxtime = 0;
+ %Seen = ();
+ %New = ();
+ %Old = ();
+
+ print "Monitor Status of $hostname\n\n";
+
+ $cnt = $retries;
+ do
+ {
+ open(MONL, $monl) || die("$monl failed $!");
+ @monlout = <MONL>;
+ close(MONL);
+ } while (! @monlout && $cnt--);
+
+ if (! @monlout)
+ {
+ print "not available.\n";
+ next;
+ }
+
+ dbmopen(Clients, $dbmfile, 0644) || die("dbmopen(.., $dbmfile, ...): $!");
+
+ foreach (@monlout)
+ {
+ chop;
+ split;
+ ($host, $count, $mode, $version, $lasttime, $firsttime) =
+ (@_[$[, $[+2 .. $[+4, $#_-1,$#_]);
+
+ $Seen{$host, $mode} = 1;
+
+ if (!defined($Clients{$host, $mode}))
+ {
+ if ($lasttime <= $gone)
+ {
+ ## got a new one
+ $Clients{$host, $mode} = $New{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ else
+ {
+ ## throw out the old ones
+ if ($lasttime > $gone)
+ {
+ $Old{$host, $mode} = $Clients{$host, $mode};
+ delete $Clients{$host, $mode};
+ }
+ else
+ {
+ $Clients{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ }
+
+ grep(($Seen{$_} || ($Old{$_} = delete $Clients{$_})), keys(%Clients));
+
+ if (grep(($tmp = $_ , !grep($tmp =~ m/$_/, @ignpat)), keys(%New)))
+ {
+ print "New customers\n";
+ print "-------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $New{$_}, 1), sort(keys(%New)) );
+ print "\n";
+ }
+
+
+ if (grep((!defined($New{$_}) && ($tmp = $_, !grep($tmp =~ m/$_/, @ignpat))), keys(%Clients)))
+ {
+ print "Current customers\n";
+ print "-----------------\n";
+ printf $sfmt, @lbl;
+ grep( defined($New{$_}) || &listentry($_, $Clients{$_}, 1) , sort(keys(%Clients)) );
+ print "\n";
+ }
+
+ if (grep(($tmp = $_, !grep($tmp =~ m/$_/, @ignpat)), keys(%Old)))
+ {
+ print "Discarded customers\n";
+ print "-------------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $Old{$_}, 0) , sort(keys(%Old)) );
+ print "\n";
+ }
+
+ dbmclose(Clients);
+
+ print "\nSummary:\n";
+ print "--------\n";
+ printf("Elapsed time: %13s\n", &timedelta($maxtime));
+ printf(" Hosts: %13d\n", $hostcnt);
+ printf(" Packets: %13d\n", $packcnt);
+ printf(" Rate: %13.2f\n", $packcnt / $maxtime) if $maxtime;
+ print "\n";
+ }
diff --git a/contrib/ntp/scripts/support/bin/mvstats b/contrib/ntp/scripts/support/bin/mvstats
new file mode 100644
index 0000000..e33dc792
--- /dev/null
+++ b/contrib/ntp/scripts/support/bin/mvstats
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# mvstats,v 3.1 1993/07/06 01:10:24 jbj Exp
+#
+# mvstats is called by cron for keeping the log files together
+# usually only used on reference hosts
+#
+# Files reside in /var/NTP
+# Files older than 2 days will be compressed,
+# Files older than 64 days will be removed.
+#
+# mvstats,v
+# Revision 3.1 1993/07/06 01:10:24 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:24 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+cd /var/NTP
+find . ! -name '*.Z' -mtime +2 -exec compress -f {} \;
+find . -mtime +64 -exec rm -f {} \;
diff --git a/contrib/ntp/scripts/support/conf/hp300.hp300 b/contrib/ntp/scripts/support/conf/hp300.hp300
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/hp300.hp300
diff --git a/contrib/ntp/scripts/support/conf/hp700.hp700 b/contrib/ntp/scripts/support/conf/hp700.hp700
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/hp700.hp700
diff --git a/contrib/ntp/scripts/support/conf/hp700.hp700.faui47 b/contrib/ntp/scripts/support/conf/hp700.hp700.faui47
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/hp700.hp700.faui47
diff --git a/contrib/ntp/scripts/support/conf/hp800.hp800 b/contrib/ntp/scripts/support/conf/hp800.hp800
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/hp800.hp800
diff --git a/contrib/ntp/scripts/support/conf/ntp.conf b/contrib/ntp/scripts/support/conf/ntp.conf
new file mode 100644
index 0000000..2d6bb85
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/ntp.conf
@@ -0,0 +1,3 @@
+#
+# put your default configuration (e.g. broadcastclient) in here
+#
diff --git a/contrib/ntp/scripts/support/conf/sun3.sun3 b/contrib/ntp/scripts/support/conf/sun3.sun3
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun3.sun3
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4.faui01 b/contrib/ntp/scripts/support/conf/sun4.sun4.faui01
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4.faui01
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4.faui10 b/contrib/ntp/scripts/support/conf/sun4.sun4.faui10
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4.faui10
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4.faui45 b/contrib/ntp/scripts/support/conf/sun4.sun4.faui45
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4.faui45
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4c b/contrib/ntp/scripts/support/conf/sun4.sun4c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4c
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4c.Lucifer b/contrib/ntp/scripts/support/conf/sun4.sun4c.Lucifer
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4c.Lucifer
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4m b/contrib/ntp/scripts/support/conf/sun4.sun4m
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4m
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4m.faui42 b/contrib/ntp/scripts/support/conf/sun4.sun4m.faui42
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4m.faui42
diff --git a/contrib/ntp/scripts/support/conf/sun4.sun4m.faui45m b/contrib/ntp/scripts/support/conf/sun4.sun4m.faui45m
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/sun4.sun4m.faui45m
diff --git a/contrib/ntp/scripts/support/conf/tickconf b/contrib/ntp/scripts/support/conf/tickconf
new file mode 100644
index 0000000..b17dbe8
--- /dev/null
+++ b/contrib/ntp/scripts/support/conf/tickconf
@@ -0,0 +1,19 @@
+DEFAULT -A -p -s -q
+Lucifer 55406cfa -a 1 -p -s -q -t 10001
+faui45 24000f9b -a 1 -p -s -q
+faui10 2440213c -a 1 -p -s -q
+faui1b 54001418 -A -p -s -q -t 10001
+faui4p 5100344d -A -p -s -q -t 9999
+faui02g 1200be20 -A -p -s -q -t 9999
+faui02e 1200bbab -A -p -s -q -t 9999
+faui02f 1200bedb -A -p -s -q -t 9999
+faui03b 1200b92b -A -p -s -q -t 9999
+faui45m 726001ac -A -p -s -q -t 10001
+faui45o 72600272 -A -p -s -q -t 10001
+faui45p 7260028f -A -p -s -q -t 10001
+faui45r 72400cc7 -A -p -s -q -t 10001
+faui45s 726045be -A -p -s -q -t 10001
+faui45v 72604487 -A -p -s -q -t 10001
+faui45x 726044eb -A -p -s -q -t 10001
+faui45y 7260476d -A -p -s -q -t 10001
+faui45z 726045a1 -A -p -s -q -t 10001
diff --git a/contrib/ntp/scripts/support/etc/cron b/contrib/ntp/scripts/support/etc/cron
new file mode 100644
index 0000000..07ed189
--- /dev/null
+++ b/contrib/ntp/scripts/support/etc/cron
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# cron,v 3.1 1993/07/06 01:10:50 jbj Exp
+#
+# called by cron for statistics gathering
+#
+# cron,v
+# Revision 3.1 1993/07/06 01:10:50 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:18 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+PATH="${PATH}:/local/NTP/bin"
+export PATH
+monl -d /local/NTP/monitor -i '127\.0\.0\.1' faui10 faui45 lucifer rackety.udel.edu
diff --git a/contrib/ntp/scripts/support/etc/crontab b/contrib/ntp/scripts/support/etc/crontab
new file mode 100644
index 0000000..2b2d19c
--- /dev/null
+++ b/contrib/ntp/scripts/support/etc/crontab
@@ -0,0 +1,8 @@
+#
+# NTP statistics periodic cleanup - REFERENCE SERVER ONLY
+#
+#55 23 * * * sh /local/NTP/etc/mvstats
+#
+# gather NTP client statistics - REFERENCE SERVER ONLY
+#
+0 8,18 * * * /local/NTP/etc/cron 2>/dev/null | /usr/ucb/mail -s "NTP statistics" time@informatik.uni-erlangen.de
diff --git a/contrib/ntp/scripts/support/etc/install b/contrib/ntp/scripts/support/etc/install
new file mode 100644
index 0000000..169a7e5
--- /dev/null
+++ b/contrib/ntp/scripts/support/etc/install
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# install,v 3.1 1993/07/06 01:10:53 jbj Exp
+#
+# install,v
+# Revision 3.1 1993/07/06 01:10:53 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:21 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/06/18 14:50:08 kardel
+# Initial revision
+#
+#
+NTPROOT=/local/NTP # SITE SPECIFIC: where NTP resides
+#
+# where the local NTP state files reside (xntp.drift) ussualle /etc
+# this directory must not be shared as machine dependent data ist stored there
+#
+NTPDIR="/+private/local/NTP"
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+Mkdir() {
+ p=""
+ IFS="/"
+ set -- $@
+ IFS='
+'
+ for pnc do
+ if [ ! -d "$p/$pnc" ]; then
+ ECHO -n "creating directory $p/$pnc"
+ if mkdir "$p/$pnc"; then
+ ECHO ""
+ else
+ ECHO " - FAILED"
+ break;
+ fi
+ fi
+ p="$p/$pnc"
+ done
+}
+
+if [ ! -d "$NTPDIR" ]; then
+ ECHO "installing NTP private data area ($NTPDIR)"
+ if Mkdir "$NTPDIR"; then
+ chmod 755 "$NTPDIR"
+ ECHO "$NTPDIR created."
+ fi
+else
+ ECHO "NTP already installed."
+ if [ -f "$NTPDIR/xntp.drift" ]; then
+ ECHO "currently saved drift value:" `cat "$NTPDIR/xntp.drift"`
+ fi
+fi
+
diff --git a/contrib/ntp/scripts/support/etc/rc b/contrib/ntp/scripts/support/etc/rc
new file mode 100644
index 0000000..ef8834a
--- /dev/null
+++ b/contrib/ntp/scripts/support/etc/rc
@@ -0,0 +1,198 @@
+#!/bin/sh
+# NTP time synchronisation
+#
+# /src/NTP/REPOSITORY/v3/supportscripts/etc/rc,v 1.11 1993/07/09 13:17:00 kardel Exp
+#
+# rc,v
+# Revision 1.11 1993/07/09 13:17:00 kardel
+# local NTPROOT
+#
+# Revision 1.10 1993/07/09 11:37:29 kardel
+# Initial restructured version + GPS support
+#
+# Revision 1.9 1993/06/23 14:10:36 kardel
+# June 21st reconcilation
+#
+# Revision 1.7 1993/06/02 12:04:43 kardel
+# May 28th reconcilation & clenaup
+#
+#
+# non reference clock hosts will try to do an ntpdate on NTPSERVERS
+#
+NTPSERVERS="ntps1-0 ntps1-1 ntps2-0 ntps2-1"
+NTPROOT=/local/NTP
+
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+msg=""
+#
+# default configuration files are named $NTPROOT/conf/<ARCH>.<KARCH>
+#
+CF=$NTPROOT/conf/$ARCH.$KARCH # default configuration file
+#
+# Host specific config file (reference clocks) have the hostname tagged on
+#
+CFH="$CF"."$HOSTNAME" # specific configuration file
+#
+# where to find the tickadj command
+#
+KFIX=$NTPROOT/bin/tickadj # kernel variable fix
+#
+# where to find special tickadj parameters
+#
+TC=$NTPROOT/conf/tickconf # special tickadj parameters
+#
+# where to find the keys file (if not found $KEY.dumb will be used)
+#
+KEY=$NTPROOT/conf/ntp.keys # private key file
+#
+# the daemon
+#
+XD=$NTPROOT/bin/xntpd # NTP daemon
+#
+# HP adjtimed
+#
+ADJTIMED=$NTPROOT/bin/adjtimed # HP special (adjtime() emulation)
+#
+# ntpdate command
+#
+NTPDATE=$NTPROOT/bin/ntpdate
+
+#
+# secondary timed support
+# The word "TIMED" must be in the config file for timed to start
+# Note that this times is a special version which does not ever set or
+# adjust the time. Ask time@informatik.uni-erlangen.de for patches
+#
+TIMED=$NTPROOT/bin/timed # timed (Berkeley) secondary time service
+ # here used in a *HARMLESS* version
+ # to provide time to "inferior" systems
+#
+# ISREFHOST is a command that returns exit status 0 for a reference host
+# Site specific: sample for dcf77 is given
+#
+ISREFHOST="[ -f $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -a -f /dev/refclock-0 ]"
+#
+# SETUP_REFCLOCK
+#
+# what to do in order to set up a local reference clock
+# usually this will load a STREAMS module or initialize other things
+# needed
+#
+SETUP_REFCLOCK() {
+ if modstat | grep -s 'PARSE'; then
+ ECHO "loadable PARSER STREAMS module already loaded."
+ else
+ ECHO "attempting to load PARSER STREAMS module..."
+ MDLFILE="/tmp/mdl.$$"
+ if modload $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -o $MDLFILE 2>&1; then
+ modstat
+ else
+ echo WARNING: load FAILED
+ fi | LOG
+ rm -f $MDLFILE
+ unset MDLFILE
+ fi
+}
+
+kargs() {
+ MATCH=NO
+ HOSTID="`(hostid) 2>/dev/null || echo 000000`"
+ if [ -r "$TC" ]; then
+ exec 0< "$TC"
+ while [ "$MATCH" != "YES" ] && read HOST ID PARAM; do
+ if [ "$HOST" = "DEFAULT" ]; then
+ DEFAULT="$ID $PARAM"
+ else
+ if [ "$ID" = "$HOSTID" -o "$HOST" = "$HOSTNAME" ]; then
+ echo "$PARAM"
+ MATCH=YES
+ fi
+ fi
+ done
+ if [ "$MATCH" != "YES" ]; then
+ if [ -z "$DEFAULT" ]; then
+ echo "-A -p -s -q";
+ else
+ echo "$DEFAULT";
+ fi
+ fi
+ else
+ echo "-A -p -s -q";
+ fi
+}
+
+if [ -x $XD ]; then
+ if [ -x "$ADJTIMED" ]; then
+ $ADJTIMED && ECHO "adjusttimesupport: adjtimed."
+ fi
+ #
+ # WARNING: check ps command first, or you might kill things you don't want to
+ #
+ PID="`(ps -efa 2>/dev/null || ps auxww 2>/dev/null || echo "") | grep xntp | grep -v grep | awk '{ print $2 }'`"
+
+ if [ ! -z "$PID" ]; then
+ ECHO "killing old NTP daemon (PID=$PID)"
+ #
+ # enable this after checking for correctness
+ # kill $PID
+ ECHO "should do a kill $PID, if this is the right PID - check rc script"
+ fi
+ #
+ # try an ntpdate when timeservers are configured
+ #
+ if [ ! -z "$NTPSERVERS" -a -x $NTPDATE ]; then
+ ECHO "NTP initial time setting"
+ $NTPDATE -v $NTPSERVERS | LOG
+ fi
+ #
+ # look for reference clock equipment
+ #
+ if $ISREFHOST; then
+ ECHO "REFERENCE CLOCK SUPPORT (initializing...)"
+ SETUP_REFCLOCK
+ fi
+
+ if [ -r "$CFH" ]; then
+ CF="$CFH"
+ else
+ if [ ! -r "$KEY" ]; then
+ KEY="$KEY.dumb"
+ fi
+ fi
+
+ ECHO "NTP configuration file: $CF"
+ ECHO -n "time daemon startup:"
+
+ if [ -r "$CF" ]; then
+ if [ -x "$KFIX" ]; then
+ KARGS="`kargs`"
+ if [ ! -z "$KARGS" ]; then
+ $KFIX $KARGS && ECHO -n "tickadj $KARGS"
+ fi
+ fi
+ $XD -c "$CF" -k "$KEY" && ECHO -n ' xntpd'
+ if [ -x "$TIMED" ] && grep -s TIMED "$CF"; then
+ $TIMED -M -N && ECHO -n ' timed'
+ fi
+ else
+ msg="configuration file ($CF) not present."
+ fi
+else
+ msg="daemon binary ($XD) not present."
+fi
+ECHO "."
+
+if [ "$msg" ]; then
+ NLECHO "WARNING: NO NTP time sychronisation: $msg"
+fi
diff --git a/contrib/ntp/scripts/support/etc/setup b/contrib/ntp/scripts/support/etc/setup
new file mode 100644
index 0000000..d4ea75e
--- /dev/null
+++ b/contrib/ntp/scripts/support/etc/setup
@@ -0,0 +1,72 @@
+#
+# setup,v 3.1 1993/07/06 01:10:55 jbj Exp
+#
+# /bin/sh sourced file for environment setup
+# expects NTPROOT variable initialized
+#
+# if not set it will be initialized to /usr/local/NTP
+#
+# setup,v
+# Revision 3.1 1993/07/06 01:10:55 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:25 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/12/10 10:14:46 kardel
+# Initial revision
+#
+#
+NTPROOT=${NTPROOT-/usr/local/NTP}
+
+#
+# we so use our own echos, as we somes want to substitute them with a
+# file logging version durin the /etc/rc.local phase
+#
+set `type ECHO`
+
+PATH="${PATH}:$NTPROOT/bin"
+export PATH
+
+if [ "$2" = "is" ]; then
+ :
+else
+ #
+ # find out the way echos work (Rest of rc thinks BSD echo)
+ #
+ ECHOREP="`echo -n x`"
+ if [ "$ECHOREP" = "-n x" ]; then
+ ECHO () {
+ if [ "$1" = "-n" ]; then
+ shift
+ echo "$@\c"
+ else
+ echo "$@"
+ fi
+ }
+ #ECHO "System V style echo"
+ else
+ ECHO () {
+ echo "$@"
+ }
+ #ECHO "BSD style echo"
+ fi
+
+ NLECHO () {
+ echo "$@"
+ }
+
+ LOG () {
+ while read _line; do
+ ECHO "$_line"
+ done
+ }
+ #
+ # carefully find out some configuration Variables
+ #
+ ARCH="`(arch) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $6; }') 2>/dev/null || echo 'no-arch'`"
+ KARCH="`(arch -k) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $5 }') || echo 'no-arch'`"
+ HOSTNAME="`(hostname) 2>/dev/null || uname -n`"
+fi
+
OpenPOWER on IntegriCloud