From: Harlan Stenn Date: Fri, 29 Nov 2013 06:12:37 +0000 (+0000) Subject: Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup X-Git-Tag: NTP_4_2_7P402~8^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5b67b91ab14dda726f7df31192351d841288cf8;p=thirdparty%2Fntp.git Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup bk: 529830555QlZI0fUjPAgBBlcoj8kcA --- diff --git a/ChangeLog b/ChangeLog index 3fe035d41..d35d801ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +* Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup. (4.2.7p390) 2013/09/26 Released by Harlan Stenn * [Bug 2482] Cleanup of droproot and jail support for Solaris. (4.2.7p389) 2013/09/24 Released by Harlan Stenn diff --git a/Makefile.am b/Makefile.am index 3a6102eb7..97ac28a01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,9 +80,9 @@ COPYRIGHT-please: $(srcdir)/COPYRIGHT Rather than determine our $(srcdir) from sntp/Makefile.am \ COPYRIGHT-please serves as a fixed target. -$(srcdir)/.checkChangeLog: $(srcdir)/ChangeLog $(srcdir)/scripts/checkChangeLog +$(srcdir)/.checkChangeLog: $(srcdir)/ChangeLog $(srcdir)/scripts/build/checkChangeLog cd $(srcdir) && \ - ./scripts/checkChangeLog + ./scripts/build/checkChangeLog sntp/built-sources-only: FRC.sntp @cd sntp && $(MAKE) $(AM_MAKEFLAGS) built-sources-only @@ -113,7 +113,7 @@ CommitLog: FRC.CommitLog cd $(srcdir) \ && $(PATH_TEST) -e CommitLog \ -a SCCS/s.ChangeSet -ot CommitLog \ - || scripts/genCommitLog + || scripts/build/genCommitLog libtool: $(LIBTOOL_DEPS) ./config.status --recheck diff --git a/bootstrap b/bootstrap index 71ecb88d3..92625d68d 100755 --- a/bootstrap +++ b/bootstrap @@ -29,7 +29,8 @@ set -e -(cd sntp && ../scripts/genver) || { echo scripts/genver failed ; exit 1; } +(cd sntp && ../scripts/build/genver) || { + echo scripts/build/genver failed ; exit 1; } # autoreconf says: # The environment variables AUTOCONF, AUTOHEADER, AUTOMAKE, ACLOCAL, diff --git a/configure.ac b/configure.ac index 4297760cc..ee5d0f1d8 100644 --- a/configure.ac +++ b/configure.ac @@ -4237,21 +4237,40 @@ AC_CONFIG_FILES([ntpq/Makefile]) AC_CONFIG_FILES([ntpsnmpd/Makefile]) AC_CONFIG_FILES([parseutil/Makefile]) AC_CONFIG_FILES([scripts/Makefile]) -AC_CONFIG_FILES([scripts/calc_tickadj], [chmod +x scripts/calc_tickadj]) -AC_CONFIG_FILES([scripts/checktime], [chmod +x scripts/checktime]) -AC_CONFIG_FILES([scripts/freq_adj], [chmod +x scripts/freq_adj]) -AC_CONFIG_FILES([scripts/html2man], [chmod +x scripts/html2man]) -AC_CONFIG_FILES([scripts/mkver], [chmod +x scripts/mkver]) -AC_CONFIG_FILES([scripts/ntp-wait], [chmod +x scripts/ntp-wait]) -AC_CONFIG_FILES([scripts/ntpsweep], [chmod +x scripts/ntpsweep]) -AC_CONFIG_FILES([scripts/ntptrace], [chmod +x scripts/ntptrace]) +AC_CONFIG_FILES([scripts/lib/Makefile]) +AC_CONFIG_FILES([scripts/calc_tickadj/calc_tickadj], [chmod +x scripts/calc_tickadj/calc_tickadj]) +AC_CONFIG_FILES([scripts/calc_tickadj/Makefile]) +AC_CONFIG_FILES([scripts/build/mkver], [chmod +x scripts/build/mkver]) +AC_CONFIG_FILES([scripts/ntp-wait/ntp-wait], [chmod +x scripts/ntp-wait/ntp-wait]) +AC_CONFIG_FILES([scripts/ntp-wait/Makefile]) +AC_CONFIG_FILES([scripts/ntpsweep/ntpsweep], [chmod +x scripts/ntpsweep/ntpsweep]) +AC_CONFIG_FILES([scripts/ntpsweep/Makefile]) +AC_CONFIG_FILES([scripts/ntptrace/ntptrace], [chmod +x scripts/ntptrace/ntptrace]) +AC_CONFIG_FILES([scripts/ntptrace/Makefile]) AC_CONFIG_FILES([scripts/ntpver], [chmod +x scripts/ntpver]) AC_CONFIG_FILES([scripts/plot_summary], [chmod +x scripts/plot_summary]) AC_CONFIG_FILES([scripts/summary], [chmod +x scripts/summary]) +AC_CONFIG_FILES([scripts/build/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_CONFIG_FILES([tests/libntp/Makefile]) AC_CONFIG_FILES([util/Makefile]) +perllibdir="${datadir}/ntp/lib" +AC_DEFINE_DIR([PERLLIBDIR], [perllibdir], [data dir]) + +calc_tickadj_opts="scripts/calc_tickadj/calc_tickadj-opts" +AC_SUBST_FILE([calc_tickadj_opts]) +ntp_wait_opts="scripts/ntp-wait/ntp-wait-opts" +AC_SUBST_FILE([ntp_wait_opts]) +ntpsweep_opts="scripts/ntpsweep/ntpsweep-opts" +AC_SUBST_FILE([ntpsweep_opts]) +ntptrace_opts="scripts/ntptrace/ntptrace-opts" +AC_SUBST_FILE([ntptrace_opts]) +summary_opts="scripts/summary-opts" +AC_SUBST_FILE([summary_opts]) +plot_summary_opts="scripts/plot_summary-opts" +AC_SUBST_FILE([plot_summary_opts]) + AC_CONFIG_SUBDIRS([sntp]) AC_OUTPUT diff --git a/ntpd/Makefile.am b/ntpd/Makefile.am index f97e49b54..f16e256c6 100644 --- a/ntpd/Makefile.am +++ b/ntpd/Makefile.am @@ -400,7 +400,7 @@ $(srcdir)/invoke-ntpd.menu: $(srcdir)/invoke-ntpd.texi $(srcdir)/invoke-ntpd.texi: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpd-opts.def - $(top_srcdir)/scripts/check--help $@ + $(top_srcdir)/scripts/build/check--help $@ $(PROGRAMS): $(LDADD) @@ -421,7 +421,7 @@ $(top_srcdir)/sntp/scm-rev: cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev version.c: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/sntp/scm-rev - env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpd + env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpd version.o: version.c env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o diff --git a/ntpdate/Makefile.am b/ntpdate/Makefile.am index 3b38d3eb0..774cf2978 100644 --- a/ntpdate/Makefile.am +++ b/ntpdate/Makefile.am @@ -29,7 +29,7 @@ $(top_srcdir)/sntp/scm-rev: cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev version.c: $(ntpdate_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev - env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpdate + env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpdate version.o: version.c env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o diff --git a/ntpdc/Makefile.am b/ntpdc/Makefile.am index a08438298..116a6cf4b 100644 --- a/ntpdc/Makefile.am +++ b/ntpdc/Makefile.am @@ -113,7 +113,7 @@ $(srcdir)/invoke-ntpdc.menu: $(srcdir)/invoke-ntpdc.texi $(srcdir)/invoke-ntpdc.texi: $(srcdir)/ntpdc-opts.def $(std_def_list) $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpdc-opts.def - $(top_srcdir)/scripts/check--help $@ + $(top_srcdir)/scripts/build/check--help $@ $(srcdir)/ntpdc.html: $(srcdir)/invoke-ntpdc.menu $(srcdir)/invoke-ntpdc.texi $(srcdir)/ntpdc.texi $(top_srcdir)/sntp/include/version.texi cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntpdc.html ntpdc.texi || true ) @@ -134,7 +134,7 @@ $(top_srcdir)/sntp/scm-rev: cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev version.c: $(ntpdc_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev - env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpdc + env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpdc version.o: version.c env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o diff --git a/ntpq/Makefile.am b/ntpq/Makefile.am index da460a1cc..9fac3f5df 100644 --- a/ntpq/Makefile.am +++ b/ntpq/Makefile.am @@ -106,7 +106,7 @@ $(srcdir)/invoke-ntpq.menu: $(srcdir)/invoke-ntpq.texi $(srcdir)/invoke-ntpq.texi: $(srcdir)/ntpq-opts.def $(std_def_list) $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpq-opts.def - $(top_srcdir)/scripts/check--help $@ + $(top_srcdir)/scripts/build/check--help $@ $(srcdir)/ntpq.html: $(srcdir)/ntpq.texi $(top_srcdir)/sntp/include/version.texi cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpq.html ntpq.texi || true ) @@ -119,7 +119,7 @@ $(top_srcdir)/sntp/scm-rev: cd ../sntp && $(MAKE) $(AM_MAKEFLAGS) check-scm-rev version.c: $(ntpq_OBJECTS) ../libntp/libntp.a Makefile $(top_srcdir)/sntp/scm-rev - env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/mkver ntpq + env CSET=`cat $(top_srcdir)/sntp/scm-rev` $(top_builddir)/scripts/build/mkver ntpq version.o: version.c env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o diff --git a/ntpsnmpd/Makefile.am b/ntpsnmpd/Makefile.am index 016824bb7..dcaf5f0b0 100644 --- a/ntpsnmpd/Makefile.am +++ b/ntpsnmpd/Makefile.am @@ -105,7 +105,7 @@ $(srcdir)/invoke-ntpsnmpd.menu: $(srcdir)/invoke-ntpsnmpd.texi $(srcdir)/invoke-ntpsnmpd.texi: $(srcdir)/ntpsnmpd-opts.def $(std_def_list) $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpsnmpd-opts.def - $(top_srcdir)/scripts/check--help $@ + $(top_srcdir)/scripts/build/check--help $@ $(srcdir)/ntpsnmpd.html: $(srcdir)/ntpsnmpd.texi $(top_srcdir)/sntp/include/version.texi cd $(srcdir) && ( makeinfo --force --html --no-split -o ntpsnmpd.html ntpsnmpd.texi || true ) diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 8b629292f..3834f0678 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,8 +1,9 @@ +SUBDIRS = build calc_tickadj ntp-wait ntpsweep ntptrace lib + NULL= man1_MANS= man8_MANS= -man_MANS= ntp-wait.$(NTP_WAIT_MS) DISTCLEANFILES= config.log $(man_MANS) @@ -17,97 +18,35 @@ std_def_list = \ $(top_srcdir)/sntp/include/version.def \ $(NULL) -EXTRA_SCRIPTS = ntp-wait ntptrace -bin_SCRIPTS = $(NTP_WAIT_DB) $(NTPTRACE_DB) -libexec_SCRIPTS = $(NTP_WAIT_DL) $(NTPTRACE_DL) -sbin_SCRIPTS = $(NTP_WAIT_DS) $(NTPTRACE_DS) - -html_DATA= \ - $(srcdir)/ntp-wait.html \ - $(NULL) - noinst_SCRIPTS = \ - calc_tickadj \ - checktime \ - freq_adj \ - html2man \ - mkver \ - ntpsweep \ ntpver \ plot_summary \ summary \ $(NULL) -noinst_DATA= \ - $(srcdir)/invoke-ntp-wait.menu \ - $(srcdir)/invoke-ntp-wait.texi \ - $(srcdir)/ntp-wait.man.in \ - $(srcdir)/ntp-wait.mdoc.in \ - $(NULL) - install-data-local: install-html EXTRA_DIST = \ - check--help \ - checkChangeLog \ - fixautomakedepsmagic \ - genCommitLog \ - genver \ - hpadjtime.sh \ - invoke-ntp-wait.menu \ - invoke-ntp-wait.texi \ + deprecated \ monitoring \ - ntp-close \ - ntp-groper \ - ntp-restart \ - ntp-status \ - ntp-wait.1ntp-waitman \ - ntp-wait.1ntp-waitmdoc \ - ntp-wait.man.in \ - ntp-wait.mdoc.in \ - ntp-wait.html \ - ntp-wait.texi \ - ntp-wait-opts.def \ - rc1 \ - rc2 \ + rc \ stats \ - UpdatePoint \ - VersionName \ $(NULL) -### - -$(srcdir)/ntp-wait.1ntp-waitman: $(srcdir)/ntp-wait-opts.def $(std_def_list) - $(run_ag) -DMAN_SECTION=1ntp-waitman -Tagman-cmd.tpl ntp-wait-opts.def - -$(srcdir)/ntp-wait.man.in: $(srcdir)/ntp-wait.1ntp-waitman $(top_srcdir)/sntp/scripts/mansec2subst.sed - sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitman > $(srcdir)/ntp-wait.man.in+ - mv $(srcdir)/ntp-wait.man.in+ $(srcdir)/ntp-wait.man.in - -### - -$(srcdir)/ntp-wait.1ntp-waitmdoc: $(srcdir)/ntp-wait-opts.def $(std_def_list) - $(run_ag) -DMAN_SECTION=1ntp-waitmdoc -Tagmdoc-cmd.tpl ntp-wait-opts.def +OPTTPL = ../sntp/ag-tpl/perlopt.tpl -$(srcdir)/ntp-wait.mdoc.in: $(srcdir)/ntp-wait.1ntp-waitmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed - sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitmdoc > $(srcdir)/ntp-wait.mdoc.in+ - mv $(srcdir)/ntp-wait.mdoc.in+ $(srcdir)/ntp-wait.mdoc.in +## -### +plot_summary: plot_summary-opts -ntp-wait.$(NTP_WAIT_MS): $(srcdir)/ntp-wait.$(MANTAGFMT).in $(top_builddir)/config.status - $(top_builddir)/config.status --file=ntp-wait.$(NTP_WAIT_MS)+:$(srcdir)/ntp-wait.$(MANTAGFMT).in - mv ntp-wait.$(NTP_WAIT_MS)+ ntp-wait.$(NTP_WAIT_MS) +plot_summary-opts: plot_summary-opts.def $(OPTTPL) + $(run_ag) plot_summary-opts.def -### +## -$(srcdir)/invoke-ntp-wait.menu: $(srcdir)/invoke-ntp-wait.texi - @: do-nothing action to avoid default SCCS get, .menu built with .texi +summary: summary-opts -$(srcdir)/invoke-ntp-wait.texi: $(srcdir)/ntp-wait-opts.def $(std_def_list) - $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntp-wait-opts.def - -$(top_srcdir)/../scripts/check--help $@ +summary-opts: summary-opts.def $(OPTTPL) + $(run_ag) summary-opts.def -$(srcdir)/ntp-wait.html: $(srcdir)/invoke-ntp-wait.menu $(srcdir)/invoke-ntp-wait.texi $(srcdir)/ntp-wait.texi $(top_srcdir)/sntp/include/version.texi - cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntp-wait.html ntp-wait.texi || true ) diff --git a/scripts/README b/scripts/README index 673ddb449..9f70a6362 100644 --- a/scripts/README +++ b/scripts/README @@ -6,38 +6,26 @@ files in the parent directory for directions on how to use these files. calc_tickadj Calculates "optimal" value for tick given ntp.drift file -freq_adj Calculates and optionally sets the clock frequency - based on ntp.drift . For FreeBSD systems. - -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-close find public stratum 2 servers that don't respond - -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 +rc start/stop scripts for NTP ntp-wait Blocks until ntpd is in state 4 (synchronized). - Hopefully useful at boot time, to delay the boot sequence + Useful at boot time, to delay the boot sequence until after "ntpd -g" has set the time. ntpsweep prints per host given in the NTP stratum level, the clock offset in seconds, the daemon version, the operating system and the processor. -ntpver What version of the NTP daemon is running? +ntptrace Trace ntp peers of a server up to stratum 1. 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 +summary Generate summary files out of stat files produced by NTP + daemon. + +plot_summary Plot summaries generated by summary script. diff --git a/scripts/build/Makefile.am b/scripts/build/Makefile.am new file mode 100755 index 000000000..cf0a44a25 --- /dev/null +++ b/scripts/build/Makefile.am @@ -0,0 +1,13 @@ +run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" \ + autogen -L ../sntp/include -L ../sntp/ag-tpl + +noinst_SCRIPTS = mkver + +EXTRA_DIST = \ + check--help \ + checkChangeLog \ + fixautomakedepsmagic \ + genCommitLog \ + genver \ + UpdatePoint \ + VersionName diff --git a/scripts/UpdatePoint b/scripts/build/UpdatePoint similarity index 98% rename from scripts/UpdatePoint rename to scripts/build/UpdatePoint index 309cdccb6..e3c5957dc 100755 --- a/scripts/UpdatePoint +++ b/scripts/build/UpdatePoint @@ -306,7 +306,7 @@ case "$newrcpoint" in esac printf "Previous version: " -scripts/VersionName +scripts/build/VersionName # apply packageinfo.sh changes @@ -321,11 +321,11 @@ case "$test" in 0) mv packageinfo.sh+ packageinfo.sh printf "Updated version: " - scripts/VersionName + scripts/build/VersionName ;; *) printf "Next version would be: " - scripts/VersionName -p ./packageinfo.sh+ + scripts/build/VersionName -p ./packageinfo.sh+ rm packageinfo.sh+ ;; esac diff --git a/scripts/VersionName b/scripts/build/VersionName similarity index 100% rename from scripts/VersionName rename to scripts/build/VersionName diff --git a/scripts/addChangeLogTag b/scripts/build/addChangeLogTag similarity index 91% rename from scripts/addChangeLogTag rename to scripts/build/addChangeLogTag index 0687ae8e9..abbeae138 100755 --- a/scripts/addChangeLogTag +++ b/scripts/build/addChangeLogTag @@ -2,7 +2,7 @@ set -e -clt=`./scripts/genChangeLogTag` +clt=`./scripts/build/genChangeLogTag` first=`line < ChangeLog` case "$first" in diff --git a/scripts/check--help b/scripts/build/check--help similarity index 100% rename from scripts/check--help rename to scripts/build/check--help diff --git a/scripts/checkChangeLog b/scripts/build/checkChangeLog similarity index 100% rename from scripts/checkChangeLog rename to scripts/build/checkChangeLog diff --git a/scripts/fixautomakedepsmagic b/scripts/build/fixautomakedepsmagic old mode 100644 new mode 100755 similarity index 100% rename from scripts/fixautomakedepsmagic rename to scripts/build/fixautomakedepsmagic diff --git a/scripts/genChangeLogTag b/scripts/build/genChangeLogTag similarity index 72% rename from scripts/genChangeLogTag rename to scripts/build/genChangeLogTag index c5a1e2f77..177a141c2 100755 --- a/scripts/genChangeLogTag +++ b/scripts/build/genChangeLogTag @@ -1,6 +1,6 @@ #! /bin/sh -dversion=`./scripts/VersionName` +dversion=`./scripts/build/VersionName` tag=`date +"(${dversion}) %Y/%m/%d Released by Harlan Stenn "` echo $tag diff --git a/scripts/genCommitLog b/scripts/build/genCommitLog similarity index 100% rename from scripts/genCommitLog rename to scripts/build/genCommitLog diff --git a/scripts/genver b/scripts/build/genver similarity index 96% rename from scripts/genver rename to scripts/build/genver index 2b3d16210..c07324392 100755 --- a/scripts/genver +++ b/scripts/build/genver @@ -35,7 +35,7 @@ set -e . ../packageinfo.sh -dversion=`../scripts/VersionName -p ../packageinfo.sh` +dversion=`../scripts/build/VersionName -p ../packageinfo.sh` set +e diff --git a/scripts/mkver.in b/scripts/build/mkver.in similarity index 100% rename from scripts/mkver.in rename to scripts/build/mkver.in diff --git a/scripts/calc_tickadj.in b/scripts/calc_tickadj.in deleted file mode 100644 index 32eae1296..000000000 --- a/scripts/calc_tickadj.in +++ /dev/null @@ -1,38 +0,0 @@ -#! @PATH_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 ($_=) - { - ($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/scripts/calc_tickadj/Makefile.am b/scripts/calc_tickadj/Makefile.am new file mode 100644 index 000000000..fbdea5741 --- /dev/null +++ b/scripts/calc_tickadj/Makefile.am @@ -0,0 +1,79 @@ +run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" \ + autogen -L ../../sntp/include -L ../../sntp/ag-tpl \ + --writable + +EXTRA_DIST = \ + $(srcdir)/calc_tickadj.in \ + $(srcdir)/calc_tickadj \ + $(srcdir)/calc_tickadj.1calc_tickadjman \ + $(srcdir)/calc_tickadj.1calc_tickadjmdoc \ + $(srcdir)/calc_tickadj.texi \ + $(srcdir)/calc_tickadj.html \ + $(srcdir)/calc_tickadj.1 \ + $(srcdir)/calc_tickadj-opts.def \ + $(srcdir)/calc_tickadj-opts \ + $(srcdir)/invoke-calc_tickadj.texi \ + $(srcdir)/invoke-calc_tickadj.menu + +noinst_SCRIPTS = calc_tickadj + +noinst_DATA = \ + $(srcdir)/calc_tickadj.in \ + $(srcdir)/calc_tickadj.1calc_tickadjman \ + $(srcdir)/calc_tickadj.1calc_tickadjmdoc \ + $(srcdir)/calc_tickadj.texi \ + $(srcdir)/calc_tickadj.html \ + $(srcdir)/calc_tickadj.1 \ + $(srcdir)/calc_tickadj-opts.def \ + $(srcdir)/calc_tickadj-opts \ + $(srcdir)/invoke-calc_tickadj.texi \ + $(srcdir)/invoke-calc_tickadj.menu + +calc_tickadj: $(srcdir)/calc_tickadj-opts + +$(srcdir)/calc_tickadj-opts: $(srcdir)/calc_tickadj-opts.def $(srcdir)/../../sntp/ag-tpl/perlopt.tpl + $(run_ag) calc_tickadj-opts.def + +clean-local: extra-clean + +extra-clean: + rm calc_tickadj + rm *.html *man *mdoc *.1 invoke-calc_tickadj.* calc_tickadj.*.in + +### Nroff + +$(srcdir)/calc_tickadj.1calc_tickadjman: $(srcdir)/calc_tickadj-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1calc_tickadjman -Tagman-cmd.tpl calc_tickadj-opts.def + +$(srcdir)/calc_tickadj.man.in: $(srcdir)/calc_tickadj.1calc_tickadjman $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/calc_tickadj.1calc_tickadjman > $(srcdir)/calc_tickadj.man.in+ + mv $(srcdir)/calc_tickadj.man.in+ $(srcdir)/calc_tickadj.man.in + +### Mdoc + +$(srcdir)/calc_tickadj.1calc_tickadjmdoc: $(srcdir)/calc_tickadj-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1calc_tickadjmdoc -Tagmdoc-cmd.tpl calc_tickadj-opts.def + +$(srcdir)/calc_tickadj.mdoc.in: $(srcdir)/calc_tickadj.1calc_tickadjmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/calc_tickadj.1calc_tickadjmdoc > $(srcdir)/calc_tickadj.mdoc.in+ + mv $(srcdir)/calc_tickadj.mdoc.in+ $(srcdir)/calc_tickadj.mdoc.in + +### Manpage + +$(srcdir)/calc_tickadj.1: $(srcdir)/calc_tickadj.$(MANTAGFMT).in $(top_builddir)/config.status + $(top_builddir)/config.status --file=calc_tickadj.1+:$(srcdir)/calc_tickadj.$(MANTAGFMT).in + mv calc_tickadj.1+ calc_tickadj.1 + +### Texinfo + +$(srcdir)/invoke-calc_tickadj.menu: $(srcdir)/invoke-calc_tickadj.texi + @: do-nothing action to avoid default SCCS get, .menu built with .texi + +$(srcdir)/invoke-calc_tickadj.texi: calc_tickadj $(srcdir)/calc_tickadj-opts.def $(std_def_list) + $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section calc_tickadj-opts.def + $(top_srcdir)/scripts/build/check--help $@ + +### HTML + +$(srcdir)/calc_tickadj.html: $(srcdir)/invoke-calc_tickadj.menu $(srcdir)/invoke-calc_tickadj.texi $(srcdir)/calc_tickadj.texi + cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o calc_tickadj.html calc_tickadj.texi || true ) diff --git a/scripts/calc_tickadj/calc_tickadj-opts b/scripts/calc_tickadj/calc_tickadj-opts new file mode 100644 index 000000000..c547a8eec --- /dev/null +++ b/scripts/calc_tickadj/calc_tickadj-opts @@ -0,0 +1,60 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'drift-file' => '/etc/ntp/drift', + 'tick' => '', + 'help' => '', 'more-help' => '' + }; + my $argument = ''; + my $ret = GetOptionsFromArray($args, $opts, ( + 'drift-file|d=s', 'tick|t=i', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +calc_tickadj - Calculates "optimal" value for tick given ntp drift file. +USAGE: calc_tickadj [ - [] | --[{=| }] ]... + + -d, --drift-file=str Ntp drift file to use + -t, --tick=num Tick value of this host + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/calc_tickadj/calc_tickadj-opts.def b/scripts/calc_tickadj/calc_tickadj-opts.def new file mode 100644 index 000000000..5c965b24e --- /dev/null +++ b/scripts/calc_tickadj/calc_tickadj-opts.def @@ -0,0 +1,55 @@ +AutoGen Definitions perlopt; +#include autogen-version.def + +prog-name = calc_tickadj; +prog-title = 'Calculates "optimal" value for tick given ntp drift file.'; +long-opts; +gnu-usage; + +flag = { + name = drift-file; + value = d; + arg-type = string; + arg-default = '/etc/ntp/drift'; + descrip = 'Ntp drift file to use'; + doc = 'Use the specified drift file for calculations'; +}; + +flag = { + name = tick; + value = t; + arg-type = number; + descrip = 'Tick value of this host'; + doc = 'The current tick which to adjustment will be calculated'; +}; + +doc-section = { + ds-type = 'DESCRIPTION'; + ds-format = 'texi'; + ds-text = <<- _EndOfDoc +The @code{calc_tickadj} script uses provided ntp drift file to generate optimal +tick value. Generally, ntpd can do better job if the drift value is the +smallest possible number. + +The example output of +@example +$ ./calc_tickadj +81.699 (drift) +9999 usec; 9999779 nsec +$ cat /etc/ntp/drift +-23.159 +@end example + +means the following. If tick on that box is 10,000, by making the value 9999 +we'll shift the box from its current drift of -23.159 to a drift of 81.699, and +in doing so we'll speed the clock up a little every second instead of slowing +the clock down a little. + +If 'tick' on that box is 10,000,000 then by setting it to 9999779 the drift +value will be somewhere around 0.0. + +@code{calc_tickadj} tries to determine the the tick value by using +@code{tickadj} program from ntp package. If this doesn't work you can specify +current tick manually on command line. + _EndOfDoc; +}; diff --git a/scripts/calc_tickadj/calc_tickadj.in b/scripts/calc_tickadj/calc_tickadj.in new file mode 100644 index 000000000..38b826eb2 --- /dev/null +++ b/scripts/calc_tickadj/calc_tickadj.in @@ -0,0 +1,59 @@ +#! @PATH_PERL@ -w +# +# drift of 104.8576 -> +1 tick. Base of 10000 ticks. +# +# 970306 HMS Deal with nanoseconds. Fix sign of adjustments. +package calc_tickadj; +use strict; + +exit run(@ARGV) unless caller; + +sub run { + my $opts; + if (!processOptions(\@_, $opts)) { + usage(1); + }; + my $drift_file = $opts->{'drift-file'}; + my $tick = $opts->{'tick'}; + + if (!$tick) { + my ($fl) = `tickadj`; + if (defined $fl && $fl =~ /(?:KERNEL|PRESET)?\s*tick\s+=\s+(\d+)/) { + $tick = $1; + } + else { + die "Could not get tick value, try manually with -t/--tick\n"; + } + } + + # Drift file is in PPM where Milion is actually 2**20 + my $cvt = (2 ** 20) / $tick; + my $drift = 0.; + + open my $dfh, $drift_file or die "Could not open $drift_file: $!\n"; + + $drift = <$dfh>; + + close $dfh; + die "Invalid drift file value <$drift>" if $drift !~ /[+-]?\d+\.?[0-9]+/; + + while ($drift < 0) { + $drift += $cvt; + $tick--; + } + + while ($drift > $cvt) { + $drift -= $cvt; + $tick++; + } + + printf "%.3f (drift)\n", $drift; + printf "%d usec; %d nsec\n", $tick, ($tick + ($drift/$cvt)) * 1000; + + return 0; +} + +@calc_tickadj_opts@ + +1; +__END__ diff --git a/scripts/calc_tickadj/calc_tickadj.texi b/scripts/calc_tickadj/calc_tickadj.texi new file mode 100644 index 000000000..a12a626c6 --- /dev/null +++ b/scripts/calc_tickadj/calc_tickadj.texi @@ -0,0 +1,37 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename calc_tickadj.info +@settitle calc_tickadj User's Manual +@include ../..//sntp/include/version.texi +@paragraphindent 2 +@c %**end of header + +@ifinfo +This file documents the use of @code{calc_tickadj}, a program from the NTP +Project, that is used calculate optimal tick value based on given drift file. +@end ifinfo + +@direntry +* calc_tickadj: (calc_tickadj). Calculate optimal tick value from a drift file. +@end direntry + +@titlepage +@title calc_tickadj User's Manual +@subtitle calc_tickadj, version @value{VERSION}, @value{UPDATED} +@c @author Max @email{foo@ntp.org} +@end titlepage + +@node Top, calc_tickadj Description, (dir), (dir) +@top calc_tickadj User's Manual + +This document describes the use of the NTP Project's @code{calc_tickadj} program. +This document applies to version @value{VERSION} of @code{calc_tickadj}. + +@shortcontents + +@menu +* calc_tickadj Description:: Description +* calc_tickadj Invocation:: Invoking calc_tickadj +@end menu + +@include invoke-calc_tickadj.texi diff --git a/scripts/checktime.in b/scripts/checktime.in deleted file mode 100644 index 2fe8f711b..000000000 --- a/scripts/checktime.in +++ /dev/null @@ -1,79 +0,0 @@ -#! @PATH_PERL@ -#! @PATH_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 -# 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=) { # 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/scripts/freq_adj.in b/scripts/deprecated/freq_adj.in similarity index 100% rename from scripts/freq_adj.in rename to scripts/deprecated/freq_adj.in diff --git a/scripts/hpadjtime.sh b/scripts/deprecated/hpadjtime.sh similarity index 100% rename from scripts/hpadjtime.sh rename to scripts/deprecated/hpadjtime.sh diff --git a/scripts/html2man.in b/scripts/deprecated/html2man.in similarity index 100% rename from scripts/html2man.in rename to scripts/deprecated/html2man.in diff --git a/scripts/ntp-close b/scripts/deprecated/ntp-close similarity index 100% rename from scripts/ntp-close rename to scripts/deprecated/ntp-close diff --git a/scripts/ntp-groper b/scripts/deprecated/ntp-groper similarity index 100% rename from scripts/ntp-groper rename to scripts/deprecated/ntp-groper diff --git a/scripts/ntp-restart b/scripts/deprecated/ntp-restart similarity index 100% rename from scripts/ntp-restart rename to scripts/deprecated/ntp-restart diff --git a/scripts/ntp-status b/scripts/deprecated/ntp-status similarity index 100% rename from scripts/ntp-status rename to scripts/deprecated/ntp-status diff --git a/scripts/lib/Makefile.am b/scripts/lib/Makefile.am new file mode 100644 index 000000000..f57bc3406 --- /dev/null +++ b/scripts/lib/Makefile.am @@ -0,0 +1,5 @@ +perllibdir = @PERLLIBDIR@ + +nobase_perllib_DATA = NTP/Util.pm + +EXTRA_DIST = ${perllib_DATA} diff --git a/scripts/lib/NTP/Mode6/Packet.pm b/scripts/lib/NTP/Mode6/Packet.pm new file mode 100644 index 000000000..4b154cd18 --- /dev/null +++ b/scripts/lib/NTP/Mode6/Packet.pm @@ -0,0 +1,222 @@ +package NTP::Mode6::Packet; +use strict; +use warnings; +use Carp; +use Exporter qw(import); +our @EXPORT_OK = qw(OP_UNSPEC OP_READSTAT OP_READVAR OP_WRITEVAR OP_READCLOCK + OP_WRITECLOCK OP_SETTRAP OP_ASYNCMSG OP_CONFIGURE OP_SAVECONFIG + OP_READ_MRU OP_READ_ORDLIST_A OP_REQ_NONCE OP_UNSETTRAP); +our %EXPORT_TAGS = (const => \@EXPORT_OK); + +use constant { + OP_UNSPEC => 0, # unspeciffied + OP_READSTAT => 1, # read status + OP_READVAR => 2, # read variables + OP_WRITEVAR => 3, # write variables + OP_READCLOCK => 4, # read clock variables + OP_WRITECLOCK => 5, # write clock variables + OP_SETTRAP => 6, # set trap address + OP_ASYNCMSG => 7, # asynchronous message + OP_CONFIGURE => 8, # runtime configuration + OP_SAVECONFIG => 9, # save config to file + OP_READ_MRU => 10, # retrieve MRU (mrulist) + OP_READ_ORDLIST_A => 11, # ordered list req. auth. + OP_REQ_NONCE => 12, # request a client nonce + OP_UNSETTRAP => 31, # unset trap +}; + +use constant { + SST_TS_UNSPEC => 0, # unspec + SST_TS_ATOM => 1, # pps + SST_TS_LF => 2, # lf radio + SST_TS_HF => 3, # hf radio + SST_TS_UHF => 4, # uhf radio + SST_TS_LOCAL => 5, # local + SST_TS_NTP => 6, # ntp + SST_TS_UDPTIME => 7, # other + SST_TS_WRSTWTCH => 8, # wristwatch + SST_TS_TELEPHONE => 9, # telephone +}; + +use constant CTL_HEADER_LENGTH => 12; + +sub new { + my ($class, %opts) = @_; + my $self = { + version => defined $opts{version} ? $opts{version} : 2, + leap => defined $opts{leap} ? $opts{leap} : 0, + sequence => defined $opts{sequence} ? $opts{sequence} : 0, + status => defined $opts{status} ? $opts{status} : 0, + assoc_id => defined $opts{assoc_id} ? $opts{assoc_id} : 0, + offset => defined $opts{offset} ? $opts{offset} : 0, + error_bit => defined $opts{is_error} ? $opts{is_error} : 0, + response_bit => defined $opts{is_response} ? $opts{is_response} : 0, + more_bit => defined $opts{more} ? $opts{more} : 0, + opcode => defined $opts{opcode} ? $opts{opcode} : 0, + }; + my $ret = bless $self, $class; + if (defined $opts{data}) { + $self->data($opts{data}); + } + else { + $self->data(''); + } + return $ret; +} + +sub version { + my ($self, $version) = @_; + $self->{version} = $version if defined $version; + return $self->{version}; +} + +sub leap { + my ($self, $leap) = @_; + $self->{leap} = 1 if $leap; + return $self->{leap}; +} + +sub assoc_id { + my ($self, $assoc_id) = @_; + $self->{assoc_id} = $assoc_id if defined $assoc_id; + return $self->{assoc_id}; +} + +sub is_response { + my ($self, $is_response) = @_; + $self->{response_bit} = 1 if defined $is_response; + return $self->{response_bit}; +} + +sub is_command { + my ($self, $is_command) = @_; + $self->{response_bit} = 0 if $is_command; + return !$self->{response_bit}; +} + +sub is_error { + my ($self, $is_error) = @_; + $self->{error_bit} = $is_error if defined $is_error; + return $self->{error_bit}; +} + +sub more { + my ($self, $is_more) = @_; + $self->{more_bit} = $is_more if defined $is_more; + return $self->{more_bit}; +} + +sub opcode { + my ($self, $opcode) = @_; + $self->{opcode} = $opcode if defined $opcode; + return $self->{opcode}; +} + +sub sequence { + my ($self, $sequence) = @_; + $self->{sequence} = $sequence if defined $sequence; + return $self->{sequence}; +} + +sub status { + my ($self, $status) = @_; + $self->{status} = $status if defined $status; + return $self->{status}; +} + +sub offset { + my ($self, $offset) = @_; + $self->{offset} = $offset if defined $offset; + return $self->{offset}; +} + +sub data_length { + my $self = shift; + return $self->{count}; +} + +sub data { + my ($self, $data) = @_; + if (defined $data) { + # TODO: prevent passing unicode? + $self->{count} = length $data; + $self->{data} = $data; + } + return $self->{data}; +} + +sub encode { + my $self = shift; + + my $li_vn_mode = 0; + $li_vn_mode = ($self->leap() & 7) << 3; + $li_vn_mode |= ($self->version() & 7) << 3; + $li_vn_mode |= 6; + + my $r_m_e_op = 0; + $r_m_e_op |= 0x80 if $self->is_response; + $r_m_e_op |= 0x40 if $self->is_error; + $r_m_e_op |= 0x20 if $self->more; + $r_m_e_op |= $self->opcode; + + # Align to 32-bit boundary + my $padding = 0; + while (($self->data_length()+CTL_HEADER_LENGTH+$padding) & 3) { + $padding++; + } + + my $msg = pack "CCnnnnnA*C$padding", $li_vn_mode, $r_m_e_op, + $self->sequence, $self->status, $self->assoc_id, $self->offset, + $self->data_length, $self->data, 0 x $padding; + return $msg; +} + +sub decode { + my ($self, $msg) = @_; + + my @res = unpack 'CCnnnnnA*', $msg; + + my $li_vn_mode = shift @res; + my $mode = 3; + $mode = $li_vn_mode & 0x7; + croak 'Not an NTP Mode 6 packet' if $mode != 6; + $self->version(($li_vn_mode >> 3) & 0x7); + $self->leap(($li_vn_mode >> 6) & 0x3); + + my $r_m_e_op = shift @res; + $r_m_e_op & 0x80 ? + $self->is_response(1) : + $self->is_command(1); + $self->is_error(1) if $r_m_e_op & 0x40; + $self->more(1) if $r_m_e_op & 0x20; + $self->opcode($r_m_e_op & 0x1f); + + $self->sequence(shift @res); + $self->status(shift @res); + $self->assoc_id(shift @res); + $self->offset(shift @res); + + my $count = shift @res; + $self->data(shift @res); + croak "count($count) != recieved data length(".$self->data_length.")" + if $self->data_length != $count; +} + +sub eq { + my ($self, $ex) = @_; + + croak 'Not a NTP::Mode6::Packet object' + if ref $ex ne 'NTP::Mode6::Packet'; + + #TODO: move diag out of this method + my @diag; + for (grep { $_ !~ /^(data|count)$/ } keys %$self) { + push @diag, "$_: $self->{$_} | $ex->{$_}" + if $self->{ $_ } != $ex->{ $_ }; + } + push @diag, "data: $self->{data} | $ex->{data}" + if $self->{data} ne $ex->{data}; + return join "\n", @diag; +} + +1; diff --git a/scripts/lib/NTP/Util.pm b/scripts/lib/NTP/Util.pm new file mode 100644 index 000000000..31fa57514 --- /dev/null +++ b/scripts/lib/NTP/Util.pm @@ -0,0 +1,136 @@ +package NTP::Util; +use strict; +use warnings; +use Exporter 'import'; +use Carp; + +our @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line); + +my $ntpq_path = 'ntpq'; +my $sntp_path = 'sntp'; + +our $IP_AGNOSTIC; + +BEGIN { + require Socket; + if ($Socket::VERSION >= 1.94) { + Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET)); + $IP_AGNOSTIC = 1; + } + else { + Socket->import(qw(inet_aton SOCK_RAW AF_INET)); + } +} + +my %obsolete_vars = ( + phase => 'offset', + rootdispersion => 'rootdisp', +); + +sub ntp_read_vars { + my ($peer, $vars, $host) = @_; + my $do_all = !@$vars; + my %out_vars = map {; $_ => undef } @$vars; + + $out_vars{status_line} = {} if $do_all; + + my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'"; + $cmd .= " $host" if defined $host; + $cmd .= " |"; + + open my $fh, $cmd or croak "Could not start ntpq: $!"; + + while (<$fh>) { + return undef if /Connection refused/; + + if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) { + $out_vars{status_line}{status} = $1; + $out_vars{status_line}{leap} = $2; + $out_vars{status_line}{sync} = $3; + } + + while (/(\w+)=([^,]+),?\s/g) { + my ($var, $val) = ($1, $2); + $val =~ s/^"([^"]+)"$/$1/; + $var = $obsolete_vars{$var} if exists $obsolete_vars{$var}; + if ($do_all) { + $out_vars{$var} = $val + } + else { + $out_vars{$var} = $val if exists $out_vars{$var}; + } + } + } + + close $fh or croak "running ntpq failed: $! (exit status $?)"; + return \%out_vars; +} + +sub do_dns { + my ($host) = @_; + + if ($IP_AGNOSTIC) { + my ($err, $res); + + ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW}); + die "getaddrinfo failed: $err\n" if $err; + + ($err, $res) = getnameinfo($res->{addr}, 0); + die "getnameinfo failed: $err\n" if $err; + + return $res; + } + # Too old perl, do only ipv4 + elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) { + return gethostbyaddr inet_aton($host), AF_INET; + } + else { + return; + } +} + +sub ntp_peers { + my ($host) = @_; + + my $cmd = "$ntpq_path -np $host |"; + + open my $fh, $cmd or croak "Could not start ntpq: $!"; + + <$fh> for 1 .. 2; + + my @columns = qw(remote refid st t when poll reach delay offset jitter); + my @peers; + while (<$fh>) { + if (/(?:[\w\.\*-]+\s*){10}/) { + my $col = 0; + push @peers, { map {; $columns[ $col++ ] => $_ } split /(?<=.)\s+/ }; + } + else { + #TODO return error (but not needed anywhere now) + warn "ERROR: $_"; + } + } + + close $fh or croak "running ntpq failed: $! (exit status $?)"; + return \@peers; +} + +# TODO: we don't need this but it would be nice to have all the line parsed +sub ntp_sntp_line { + my ($host) = @_; + + my $cmd = "$sntp_path $host |"; + open my $fh, $cmd or croak "Could not start sntp: $!"; + + my ($offset, $stratum); + while (<$fh>) { + next if !/^\d{4}-\d\d-\d\d/; + chomp; + my @output = split / /; + + $offset = $output[3]; + ($stratum = pop @output) =~ s/s(\d{1,2})/$1/; + } + close $fh or croak "running sntp failed: $! (exit status $?)"; + return ($offset, $stratum); +} diff --git a/scripts/ntp-wait.in b/scripts/ntp-wait.in deleted file mode 100644 index 069365b10..000000000 --- a/scripts/ntp-wait.in +++ /dev/null @@ -1,54 +0,0 @@ -#! @PATH_PERL@ -w - -die "perl5 needed\n" unless ($] > 5); - -use Getopt::Std; - -$opt_n = 1000; # How many tries before we give up? (10 min+) -$opt_s = 6; # Seconds to sleep between tries (6s = 10/min) -$opt_v = 0; # Be verbose? - -getopts('n:s:v'); - -$cmd = 'ntpq -c "rv 0"'; - -$| = 1; # Autoflush output. - -print "Waiting for ntpd to synchronize... " if ($opt_v); -for ($i = 0; $i < $opt_n; ++$i) { - open(Q, $cmd." 2>&1 |") || die "Can't start ntpq: $!"; - while() { - chomp; - # the first line should be similar to: - # associd=0 status=0645 leap_none, sync_ntp, ... - if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/i) { - my $status = $1; - my $leap = $2; - my $sync = $3; - # print $_; - # print "status <$status>, leap <$leap>, sync <$sync>\n"; - last if ($leap =~ /(sync|leap)_alarm/); - if ($leap =~ /leap_(none|((add|del)_sec))/) { - # We could check $sync here to make sure we like the source... - print "\bOK!\n" if ($opt_v); - exit 0; - } - print "\bUnexpected 'leap' status <$leap>\n"; - exit 1; - } - - if (/Connection refused/) { - print "\bntpd is not running!\n" if ($opt_v); - exit 1; - } - - # Otherwise, we have a bigger problem. - print "\bUnexpected first line <$_>\n"; - exit 1; - } - close(Q); - print "\b".substr("*+:.", $i % 4, 1) if ($opt_v); - sleep($opt_s); -} -print "\bNo!\nntpd did not synchronize.\n" if ($opt_v); -exit 1; diff --git a/scripts/ntp-wait.mdoc.in b/scripts/ntp-wait.mdoc.in deleted file mode 100644 index 42910b01f..000000000 --- a/scripts/ntp-wait.mdoc.in +++ /dev/null @@ -1,121 +0,0 @@ -.Dd September 26 2013 -.Dt NTP_WAIT @NTP_WAIT_MS@ User Commands -.Os SunOS 5.10 -.\" EDIT THIS FILE WITH CAUTION (ntp-wait-opts.mdoc) -.\" -.\" It has been AutoGen-ed September 26, 2013 at 11:31:57 AM by AutoGen 5.18.1pre5 -.\" From the definitions ntp-wait-opts.def -.\" and the template file agmdoc-cmd.tpl -.Sh NAME -.Nm ntp-wait -.Nd Wait for ntpd to stabilize the system clock -.Sh SYNOPSIS -.Nm -.\" Mixture of short (flag) options and long options -.Op Fl flags -.Op Fl flag Ar value -.Op Fl \-option\-name Ar value -.Pp -All arguments must be options. -.Pp -.Sh DESCRIPTION -.Nm -will send at most -.Ar num\-tries -queries to -.Xr ntpd 8 , -sleeping for -.Ar secs\-between\-tries -after each status return that says -.Xr ntpd 8 -has not yet produced a synchronized and stable system clock. -.Pp -.Nm -will do this quietly, unless the -.Fl v -flag is provided. -.Sh "OPTIONS" -.Bl -tag -.It \-n " \fInum\-tries\fP, " \-\- "=" \fInum\-tries\fP -Number of times to check ntpd. -This option takes an integer number as its argument. -The default \fInum\-tries\fP for this option is: -.ti +4 - 100 -.sp -The maximum number of times we will check \fBntpd\fP to see if -it has been able to synchronize and stabilize the system clock. -.It \-s " \fIsecs\-between\-tries\fP, " \-\- "=" \fIsecs\-between\-tries\fP -How long to sleep between tries. -This option takes an integer number as its argument. -The default \fIsecs\-between\-tries\fP for this option is: -.ti +4 - 6 -.sp -We will sleep for \fIsecs\-between\-tries\fP after each query -of \fBntpd\fP that returns "the time is not yet stable". -.It \-v ", " \-\- -Be verbose. -.sp -By default, \fBntp\-wait\fP is silent. -With this option, \fBntp\-wait\fP will provide status information. -.It \-? , " \-\-help" -Display usage information and exit. -.It \-! , " \-\-more\-help" -Pass the extended usage information through a pager. -.It \-\-version "[={\fIv|c|n\fP}]" -Output version of program and exit. The default mode is `v', a simple -version. The `c' mode will print copyright information and `n' will -print the full copyright notice. -.El -.Sh "OPTION PRESETS" -Any option that is not marked as \fInot presettable\fP may be preset -by loading values from environment variables named: -.nf - \fBNTP_WAIT_\fP or \fBNTP_WAIT\fP -.fi -.ad -cvt_prog='/usr/local/gnu/share/autogen/texi2mdoc' -cvt_prog=`cd \`dirname "$cvt_prog"\` >/dev/null && pwd - `/`basename "$cvt_prog"` -cd $tmp_dir -test \-x "$cvt_prog" || die "'$cvt_prog' is not executable" -{ - list='synopsis description options option\-presets' - for f in $list ; do cat $f ; echo ; done - rm \-f $list name - list='implementation\-notes environment files examples exit\-status errors - compatibility see\-also conforming\-to history authors copyright bugs - notes' - for f in $list ; do cat $f ; echo ; done > .end\-doc - rm \-f $list - list=`ls \-1 *`' .end\-doc' - for f in $list ; do cat $f ; echo ; done - rm \-f $list -} 1>.doc 2>/dev/null -sed \-f .cmds .doc | /usr/local/gnu/bin/grep \-E \-v '^[ ]*$' | $cvt_prog -.Sh "ENVIRONMENT" -See \fBOPTION PRESETS\fP for configuration environment variables. -.Sh "EXIT STATUS" -One of the following exit values will be returned: -.Bl -tag -.It 0 " (EXIT_SUCCESS)" -Successful program execution. -.It 1 " (EXIT_FAILURE)" -The operation failed or the command syntax was not valid. -.It 70 " (EX_SOFTWARE)" -libopts had an internal operational error. Please report -it to autogen\-users@lists.sourceforge.net. Thank you. -.El -.Sh AUTHORS -.An "Harlan Stenn" -.Sh "COPYRIGHT" -Copyright (C) 1970\-2013 The University of Delaware all rights reserved. -This program is released under the terms of the NTP license, . -.Sh "BUGS" -Please send bug reports to: http://bugs.ntp.org, bugs@ntp.org -.Sh NOTES -This document corresponds to version 4.2.7p390 of NTP. -.Pp -This manual page was \fIAutoGen\fP\-erated from the \fBntp\-wait\fP -option definitions. diff --git a/scripts/ntp-wait/Makefile.am b/scripts/ntp-wait/Makefile.am new file mode 100644 index 000000000..b8f48aefa --- /dev/null +++ b/scripts/ntp-wait/Makefile.am @@ -0,0 +1,87 @@ +man_MANS= ntp-wait.$(NTP_WAIT_MS) + +bin_SCRIPTS = $(NTP_WAIT_DB) +libexec_SCRIPTS = $(NTP_WAIT_DL) +sbin_SCRIPTS = $(NTP_WAIT_DS) + +run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" \ + autogen -L ../../sntp/include -L ../../sntp/ag-tpl \ + --writable + +EXTRA_SCRIPTS = ntp-wait + +EXTRA_DIST = \ + $(srcdir)/ntp-wait.in \ + $(srcdir)/ntp-wait \ + $(srcdir)/ntp-wait.1ntp-waitman \ + $(srcdir)/ntp-wait.1ntp-waitmdoc \ + $(srcdir)/ntp-wait.texi \ + $(srcdir)/ntp-wait.html \ + $(srcdir)/ntp-wait.1 \ + $(srcdir)/ntp-wait-opts.def \ + $(srcdir)/ntp-wait-opts \ + $(srcdir)/invoke-ntp-wait.texi \ + $(srcdir)/invoke-ntp-wait.menu + +html_DATA = $(srcdir)/ntp-wait.html + +noinst_DATA = \ + $(srcdir)/ntp-wait.in \ + $(srcdir)/ntp-wait.1ntp-waitman \ + $(srcdir)/ntp-wait.1ntp-waitmdoc \ + $(srcdir)/ntp-wait.texi \ + $(srcdir)/ntp-wait.html \ + $(srcdir)/ntp-wait.1 \ + $(srcdir)/ntp-wait-opts.def \ + $(srcdir)/ntp-wait-opts \ + $(srcdir)/invoke-ntp-wait.texi \ + $(srcdir)/invoke-ntp-wait.menu + +ntp-wait: $(srcdir)/ntp-wait-opts + +$(srcdir)/ntp-wait-opts: $(srcdir)/ntp-wait-opts.def ../../sntp/ag-tpl/perlopt.tpl + $(run_ag) ntp-wait-opts.def + +clean-local: extra-clean + +extra-clean: + rm ntp-wait + rm *.html *man *mdoc *.1 invoke-ntp-wait.* ntp-wait.man.in + +### Nroff + +$(srcdir)/ntp-wait.1ntp-waitman: $(srcdir)/ntp-wait-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1ntp-waitman -Tagman-cmd.tpl ntp-wait-opts.def + +$(srcdir)/ntp-wait.man.in: $(srcdir)/ntp-wait.1ntp-waitman $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitman > $(srcdir)/ntp-wait.man.in+ + mv $(srcdir)/ntp-wait.man.in+ $(srcdir)/ntp-wait.man.in + +### Mdoc + +$(srcdir)/ntp-wait.1ntp-waitmdoc: $(srcdir)/ntp-wait-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1ntp-waitmdoc -Tagmdoc-cmd.tpl ntp-wait-opts.def + +$(srcdir)/ntp-wait.mdoc.in: $(srcdir)/ntp-wait.1ntp-waitmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntp-wait.1ntp-waitmdoc > $(srcdir)/ntp-wait.mdoc.in+ + mv $(srcdir)/ntp-wait.mdoc.in+ $(srcdir)/ntp-wait.mdoc.in + +### Manpage + +$(srcdir)/ntp-wait.1: $(srcdir)/ntp-wait.$(MANTAGFMT).in $(top_builddir)/config.status + $(top_builddir)/config.status --file=ntp-wait.1+:$(srcdir)/ntp-wait.$(MANTAGFMT).in + mv ntp-wait.1+ ntp-wait.1 + +### Texinfo + +$(srcdir)/invoke-ntp-wait.menu: $(srcdir)/invoke-ntp-wait.texi + @: do-nothing action to avoid default SCCS get, .menu built with .texi + +$(srcdir)/invoke-ntp-wait.texi: ntp-wait $(srcdir)/ntp-wait-opts.def $(std_def_list) + $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntp-wait-opts.def + $(top_srcdir)/scripts/build/check--help $@ + +### HTML + +$(srcdir)/ntp-wait.html: $(srcdir)/invoke-ntp-wait.menu $(srcdir)/invoke-ntp-wait.texi $(srcdir)/ntp-wait.texi $(top_srcdir)/sntp/include/version.texi + cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntp-wait.html ntp-wait.texi || true ) diff --git a/scripts/invoke-ntp-wait.menu b/scripts/ntp-wait/invoke-ntp-wait.menu similarity index 100% rename from scripts/invoke-ntp-wait.menu rename to scripts/ntp-wait/invoke-ntp-wait.menu diff --git a/scripts/invoke-ntp-wait.texi b/scripts/ntp-wait/invoke-ntp-wait.texi similarity index 100% rename from scripts/invoke-ntp-wait.texi rename to scripts/ntp-wait/invoke-ntp-wait.texi diff --git a/scripts/ntp-wait/ntp-wait-opts b/scripts/ntp-wait/ntp-wait-opts new file mode 100644 index 000000000..49428d2b9 --- /dev/null +++ b/scripts/ntp-wait/ntp-wait-opts @@ -0,0 +1,62 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'tries' => '100', + 'sleep' => '6', + 'verbose' => '', + 'help' => '', 'more-help' => '' + }; + my $argument = ''; + my $ret = GetOptionsFromArray($args, $opts, ( + 'tries|n=i', 'sleep|s=i', 'verbose|v', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +ntp-wait - Wait for ntpd to stabilize the system clock +USAGE: ntp-wait [ - [] | --[{=| }] ]... + + -n, --tries=num Number of times to check ntpd + -s, --sleep=num How long to sleep between tries + -v, --verbose Be verbose + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/ntp-wait-opts.def b/scripts/ntp-wait/ntp-wait-opts.def similarity index 51% rename from scripts/ntp-wait-opts.def rename to scripts/ntp-wait/ntp-wait-opts.def index 72aaaa698..e42f9f58b 100644 --- a/scripts/ntp-wait-opts.def +++ b/scripts/ntp-wait/ntp-wait-opts.def @@ -1,53 +1,51 @@ /* -*- Mode: Text -*- */ +autogen definitions perlopt; -autogen definitions options; - -#include copyright.def -// #include homerc.def #include autogen-version.def prog-name = "ntp-wait"; prog-title = "Wait for ntpd to stabilize the system clock"; package = ntp; +long-opts; +gnu-usage; test-main; -// #include version.def - flag = { - value = n; - arg-type = number; - arg-name = "num-tries"; - arg-default = 100; - descrip = "Number of times to check ntpd"; - doc = <<- _EndOfDoc_ - The maximum number of times we will check @code{ntpd} to see if - it has been able to synchronize and stabilize the system clock. + name = tries; + value = n; + arg-type = number; + arg-default = 100; + descrip = "Number of times to check ntpd"; + doc = <<- _EndOfDoc_ + The maximum number of times we will check @code{ntpd} to see if + it has been able to synchronize and stabilize the system clock. _EndOfDoc_; }; flag = { - value = s; - arg-type = number; - arg-name = "secs-between-tries"; - arg-default = 6; - descrip = "How long to sleep between tries"; - doc = <<- _EndOfDoc_ - We will sleep for @file{secs-between-tries} after each query - of @code{ntpd} that returns "the time is not yet stable". + name = sleep; + value = s; + arg-type = number; + arg-name = "secs-between-tries"; + arg-default = 6; + descrip = "How long to sleep between tries"; + doc = <<- _EndOfDoc_ + We will sleep for @file{secs-between-tries} after each query + of @code{ntpd} that returns "the time is not yet stable". _EndOfDoc_; }; flag = { - value = v; - descrip = "Be verbose"; - doc = <<- _EndOfDoc_ - By default, @code{ntp-wait} is silent. - With this option, @code{ntp-wait} will provide status information. + name = verbose; + value = v; + descrip = "Be verbose"; + doc = <<- _EndOfDoc_ + By default, @code{ntp-wait} is silent. + With this option, @code{ntp-wait} will provide status information. _EndOfDoc_; }; -/* explain: Additional information whenever the usage routine is invoked */ explain = <<- _END_EXPLAIN _END_EXPLAIN; @@ -70,6 +68,9 @@ has not yet produced a synchronized and stable system clock. will do this quietly, unless the .Fl v flag is provided. +This can be useful at boot time, to delay the boot sequence until after +.Ar ntpd -g +has set the time. _END_PROG_MDOC_DESCRIP; }; @@ -94,7 +95,7 @@ doc-section = { ds-type = 'NOTES'; ds-format = 'mdoc'; ds-text = <<- _END_MDOC_NOTES -This document corresponds to version #VERSION# of NTP. +This document corresponds to version @VERSION@ of NTP. _END_MDOC_NOTES; }; diff --git a/scripts/ntp-wait.1ntp-waitman b/scripts/ntp-wait/ntp-wait.1ntp-waitman similarity index 100% rename from scripts/ntp-wait.1ntp-waitman rename to scripts/ntp-wait/ntp-wait.1ntp-waitman diff --git a/scripts/ntp-wait.1ntp-waitmdoc b/scripts/ntp-wait/ntp-wait.1ntp-waitmdoc similarity index 100% rename from scripts/ntp-wait.1ntp-waitmdoc rename to scripts/ntp-wait/ntp-wait.1ntp-waitmdoc diff --git a/scripts/ntp-wait.html b/scripts/ntp-wait/ntp-wait.html similarity index 100% rename from scripts/ntp-wait.html rename to scripts/ntp-wait/ntp-wait.html diff --git a/scripts/ntp-wait/ntp-wait.in b/scripts/ntp-wait/ntp-wait.in new file mode 100644 index 000000000..d856b4761 --- /dev/null +++ b/scripts/ntp-wait/ntp-wait.in @@ -0,0 +1,65 @@ +#! @PATH_PERL@ +package ntp_wait; +use 5.006_000; +use strict; +use warnings; +use lib "@PERLLIBDIR@"; +use NTP::Util qw(ntp_read_vars); + +exit run(@ARGV) unless caller; + +sub run { + my $opts; + if (!processOptions(\@_, $opts)) { + usage(1); + }; + + my $tries = $opts->{tries}; # How many tries before we give up? (10 min+) + my $sleep = $opts->{sleep}; # Seconds to sleep between tries (6s = 10/min) + my $verbose = $opts->{verbose}; # Be verbose? + + # Autoflush stdout + $| = 1; + + print "Waiting for ntpd to synchronize... " if $verbose; + + for my $i (1 .. $tries) { + my $info = ntp_read_vars(0, []); + + if (!defined $info) { + print "\bntpd is not running!\n" if $verbose; + return 1; + } + + if (!exists $info->{status_line}{leap}) { + print "\bLeap status not avalaible\n"; + return 1; + } + + my $leap = $info->{status_line}{leap}; + my $sync = $info->{status_line}{sync}; + + if ($leap =~ /(sync|leap)_alarm/) { + print "\b".(substr "*+:.", $i % 4, 1) if $verbose; + sleep $sleep if $i < $tries; + next; + } + + if ($leap =~ /leap_(none|((add|del)_sec))/) { + # We could check $sync here to make sure we like the source... + print "\bOK!\n" if $verbose; + return 0; + } + + print "\bUnexpected 'leap' status <$leap>\n"; + return 1; + } + + print "\bNo!\nntpd did not synchronize.\n" if $verbose; + return 1; +} + +@ntp_wait_opts@ + +1; +__END__ diff --git a/scripts/ntp-wait.man.in b/scripts/ntp-wait/ntp-wait.man.in similarity index 100% rename from scripts/ntp-wait.man.in rename to scripts/ntp-wait/ntp-wait.man.in diff --git a/scripts/ntp-wait/ntp-wait.mdoc.in b/scripts/ntp-wait/ntp-wait.mdoc.in new file mode 100644 index 000000000..ad07ce569 --- /dev/null +++ b/scripts/ntp-wait/ntp-wait.mdoc.in @@ -0,0 +1,91 @@ +.Dd October 2 2013 +.Dt NTP_WAIT @NTP_WAIT_MS@ User Commands +.Os FreeBSD 6.4-STABLE +.\" EDIT THIS FILE WITH CAUTION (ntp-wait-opts.mdoc) +.\" +.\" It has been AutoGen-ed October 2, 2013 at 09:50:12 PM by AutoGen 5.18.1pre5 +.\" From the definitions ntp-wait-opts.def +.\" and the template file agmdoc-cmd.tpl +.Sh NAME +.Nm ntp-wait +.Nd Wait for ntpd to stabilize the system clock +.Sh SYNOPSIS +.Nm +.\" Mixture of short (flag) options and long options +.Op Fl flags +.Op Fl flag Op Ar value +.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc +.Pp +All arguments must be options. +.Pp +.Sh DESCRIPTION +.Nm +will send at most +.Ar num\-tries +queries to +.Xr ntpd 8 , +sleeping for +.Ar secs\-between\-tries +after each status return that says +.Xr ntpd 8 +has not yet produced a synchronized and stable system clock. +.Pp +.Nm +will do this quietly, unless the +.Fl v +flag is provided. +This can be useful at boot time, to delay the boot sequence until after +.Ar ntpd \-g +has set the time. +.Sh "OPTIONS" +.Bl -tag +.It Fl n Ar number , Fl \-tries Ns = Ns Ar number +Number of times to check ntpd. +This option takes an integer number as its argument. +The default +.Ar number +for this option is: +.ti +4 + 100 +.sp + The maximum number of times we will check \fBntpd\fP to see if + it has been able to synchronize and stabilize the system clock. +.It Fl s Ar secs\-between\-tries , Fl \-sleep Ns = Ns Ar secs\-between\-tries +How long to sleep between tries. +This option takes an integer number as its argument. +The default +.Ar secs\-between\-tries +for this option is: +.ti +4 + 6 +.sp + We will sleep for \fIsecs\-between\-tries\fP after each query + of \fBntpd\fP that returns "the time is not yet stable". +.It Fl v , Fl \-verbose +Be verbose. +.sp + By default, \fBntp\-wait\fP is silent. + With this option, \fBntp\-wait\fP will provide status information. +.It Fl \&? , Fl \-help +Display usage information and exit. +.It Fl \&! , Fl \-more\-help +Pass the extended usage information through a pager. +.El +.Sh "EXIT STATUS" +One of the following exit values will be returned: +.Bl -tag +.It 0 " (EXIT_SUCCESS)" +Successful program execution. +.It 1 " (EXIT_FAILURE)" +The operation failed or the command syntax was not valid. +.It 70 " (EX_SOFTWARE)" +libopts had an internal operational error. Please report +it to autogen\-users@lists.sourceforge.net. Thank you. +.El +.Sh AUTHORS +.An "Harlan Stenn" +.Sh NOTES +This document corresponds to version @VERSION@ of NTP. +.Pp +This manual page was \fIAutoGen\fP\-erated from the \fBntp\-wait\fP +option definitions. diff --git a/scripts/ntp-wait.texi b/scripts/ntp-wait/ntp-wait.texi similarity index 79% rename from scripts/ntp-wait.texi rename to scripts/ntp-wait/ntp-wait.texi index df758bd82..208639b3d 100644 --- a/scripts/ntp-wait.texi +++ b/scripts/ntp-wait/ntp-wait.texi @@ -2,7 +2,7 @@ @c %**start of header @setfilename ntp-wait.info @settitle Ntp-wait User's Manual -@include ../sntp/include/version.texi +@include ../../sntp/include/version.texi @paragraphindent 2 @c %**end of header @@ -28,7 +28,6 @@ synchronize and stabilize the time on the current host. @c @vskip 0pt plus 1filll @node Top, ntp-wait Description, (dir), (dir) -@top Simple Network Time Protocol User Manual This document describes the use of the NTP Project's @code{ntp-wait} program. @@ -50,12 +49,4 @@ This document applies to version @value{VERSION} of @code{ntp-wait}. * ntp-wait Invocation:: Invoking ntp-wait @end menu -@node ntp-wait Description -@comment node-name, next, previous, up -@section Description - -The @code{ntp-wait} program blocks until @code{ntpd} is in synchronized state. -This can be useful at boot time, to delay the boot sequence until after -@code{ntpd -g} has set the time. - @include invoke-ntp-wait.texi diff --git a/scripts/ntpsweep.in b/scripts/ntpsweep.in deleted file mode 100644 index 650d2c5ec..000000000 --- a/scripts/ntpsweep.in +++ /dev/null @@ -1,300 +0,0 @@ -#! @PATH_PERL@ -w -# -# $Id$ -# -# DISCLAIMER -# -# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V. -# -# Permission to use, copy, modify and distribute this software and its -# documentation for any purpose and without fee is hereby granted, -# provided that the above copyright notice appears in all copies and -# that both the copyright notice and this permission notice appear in -# supporting documentation. This software is supported as is and without -# any express or implied warranties, including, without limitation, the -# implied warranties of merchantability and fitness for a particular -# purpose. The name Origin B.V. must not be used to endorse or promote -# products derived from this software without prior written permission. -# -# Hans Lambermont - -require 5.0; # But actually tested on 5.004 ;) -use Getopt::Long; # GetOptions() -use strict; - -my $version = 1.3; -(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%; - -# Hardcoded paths/program names -my $sntp = "sntp"; -my $ntpq = "ntpq"; - -# no STDOUT buffering -$| = 1; - -my ($help, $single_host, $showpeers, $maxlevel, $strip, $askversion); -my $res = GetOptions("help!" => \$help, - "host=s" => \$single_host, - "peers!" => \$showpeers, - "maxlevel=s" => \$maxlevel, - "strip=s" => \$strip, - "version!" => \$askversion); - -if ($askversion) { - print("$version\n"); - exit 0; -} - -if ($help || ((@ARGV != 1) && !$single_host)) { - warn <|--maxlevel |--version] \\ - |[--host ] - -Description: - $program prints per host given in the NTP stratum level, the - clock offset in seconds, the daemon version, the operating system and - the processor. Optionally recursing through all peers. - -Options: ---help - Print this short help text and exit. ---version - Print version ($version) and exit. - - Specify hosts file. File format is one hostname or ip number per line. - Lines beginning with # are considered as comment. ---host - Speficy a single host, bypassing the need for a hosts file. ---peers - Recursively list all peers a host synchronizes to. - An '= ' before a peer means a loop. Recursion stops here. ---maxlevel - Traverse peers up to this level (4 is a reasonable number). ---strip - Strip from hostnames. - -Examples: - $program myhosts.txt --strip .foo.com - $program --host some.host --peers --maxlevel 4 -EOF - exit 1; -} - -my $hostsfile = shift; -my (@hosts, @known_hosts); -my (%known_host_info, %known_host_peers); - -sub read_hosts() -{ - local *HOSTS; - open (HOSTS, $hostsfile) || - die "$program: FATAL: unable to read $hostsfile: $!\n"; - while () { - next if /^\s*(#|$)/; # comment/empty - chomp; - push(@hosts, $_); - } - close(HOSTS); -} - -# translate IP to hostname if possible -sub ip2name { - my($ip) = @_; - my($addr, $name, $aliases, $addrtype, $length, @addrs); - $addr = pack('C4', split(/\./, $ip)); - ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr($addr, 2); - if ($name) { - # return lower case name - return("\L$name"); - } else { - return($ip); - } -} - -# item_in_list($item, @list): returns 1 if $item is in @list, 0 if not -sub item_in_list { - my($item, @list) = @_; - my($i); - foreach $i (@list) { - return 1 if ($item eq $i); - } - return 0; -} - -sub scan_host($;$;$) { - my($host, $level, @trace) = @_; - my $stratum = 0; - my $offset = 0; - my $daemonversion = ""; - my $system = ""; - my $processor = ""; - my @peers; - my $known_host = 0; - - if (&item_in_list($host, @known_hosts)) { - $known_host = 1; - } else { - # sntp part - open my $sntp_pipe, "$sntp $host 2>/dev/null |" - or die "Cannot open $sntp pipe: $1"; - - while (<$sntp_pipe>) { - next if !/^\d{4}-\d\d-\d\d/; - my @output = split / /; - - $offset = $output[3]; - ($stratum = pop @output) =~ s/s(\d{1,2})/$1/; - } - close $sntp_pipe; - - # got answers ? If so, go on. - if ($stratum) { - # ntpq part - my $ntpqparams = "-c 'rv 0 processor,system,daemon_version'"; - open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") || - die "Cannot open ntpq pipe: $!\n"; - while () { - /daemon_version="(.*)"/ && do { - $daemonversion = $1; - }; - /system="([^"]*)"/ && do { - $system = $1; - }; - /processor="([^"]*)"/ && do { - $processor = $1; - }; - } - close(NTPQ); - - # Shorten daemon_version string. - $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//; - $daemonversion =~ s/version=//; - $daemonversion =~ s/(x|)ntpd //; - $daemonversion =~ s/(\(|\))//g; - $daemonversion =~ s/beta/b/; - $daemonversion =~ s/multicast/mc/; - - # Shorten system string - $system =~ s/UNIX\///; - $system =~ s/RELEASE/r/; - $system =~ s/CURRENT/c/; - - # Shorten processor string - $processor =~ s/unknown//; - } - - # got answers ? If so, go on. - if ($daemonversion) { - # ntpq again, find out the peers this time - if ($showpeers) { - my $ntpqparams = "-pn"; - open(NTPQ, "$ntpq $ntpqparams $host 2>/dev/null |") || - die "Cannot open ntpq pipe: $!\n"; - while () { - /^No association ID's returned$/ && do { - last; - }; - /^ remote/ && do { - next; - }; - /^==/ && do { - next; - }; - /^( |x|\.|-|\+|#|\*|o)([^ ]+)/ && do { - push(@peers, ip2name($2)); - next; - }; - print "ERROR: $_"; - } - close(NTPQ); - } - } - - # Add scanned host to known_hosts array - push(@known_hosts, $host); - if ($stratum) { - $known_host_info{$host} = sprintf("%2d %9.3f %-11s %-12s %s", - $stratum, $offset, substr($daemonversion,0,11), - substr($system,0,12), substr($processor,0,9)); - } else { - # Stratum level 0 is consider invalid - $known_host_info{$host} = sprintf(" ?"); - } - $known_host_peers{$host} = [@peers]; - } - - if ($stratum || $known_host) { # Valid or known host - my $printhost = ' ' x $level . $host; - # Shorten host string - if ($strip) { - $printhost =~ s/$strip//; - } - # append number of peers in brackets if requested and valid - if ($showpeers && ($known_host_info{$host} ne " ?")) { - $printhost .= " (" . @{$known_host_peers{$host}} . ")"; - } - # Finally print complete host line - printf("%-32s %s\n", - substr($printhost,0,32), $known_host_info{$host}); - if ($showpeers && (eval($maxlevel ? $level < $maxlevel : 1))) { - my $peer; - push(@trace, $host); - # Loop through peers - foreach $peer (@{$known_host_peers{$host}}) { - if (&item_in_list($peer, @trace)) { - # we've detected a loop ! - $printhost = ' ' x ($level + 1) . "= " . $peer; - # Shorten host string - if ($strip) { - $printhost =~ s/$strip//; - } - printf("%-32s %s\n", - substr($printhost,0,32)); - } else { - if (substr($peer,0,3) ne "127") { - &scan_host($peer, $level + 1, @trace); - } - } - } - } - } else { # We did not get answers from this host - my $printhost = ' ' x $level . $host; - # Shorten host string - if ($strip) { - $printhost =~ s/$strip//; - } - printf("%-32s ?\n", substr($printhost,0,32)); - } -} - -sub scan_hosts() -{ - my $host; - for $host (@hosts) { - my @trace; - push(@trace, $host); - scan_host($host, 0, @trace); - } -} - -# Main program - -if ($single_host) { - push(@hosts, $single_host); -} else { - &read_hosts($hostsfile); -} - -# Print header -print < $(srcdir)/ntpsweep.man.in+ + mv $(srcdir)/ntpsweep.man.in+ $(srcdir)/ntpsweep.man.in + +### Mdoc + +$(srcdir)/ntpsweep.1ntpsweepmdoc: $(srcdir)/ntpsweep-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1ntpsweepmdoc -Tagmdoc-cmd.tpl ntpsweep-opts.def + +$(srcdir)/ntpsweep.mdoc.in: $(srcdir)/ntpsweep.1ntpsweepmdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntpsweep.1ntpsweepmdoc > $(srcdir)/ntpsweep.mdoc.in+ + mv $(srcdir)/ntpsweep.mdoc.in+ $(srcdir)/ntpsweep.mdoc.in + +### Manpage + +$(srcdir)/ntpsweep.1: $(srcdir)/ntpsweep.$(MANTAGFMT).in $(top_builddir)/config.status + $(top_builddir)/config.status --file=ntpsweep.1+:$(srcdir)/ntpsweep.$(MANTAGFMT).in + mv ntpsweep.1+ ntpsweep.1 + +### Texinfo + +$(srcdir)/invoke-ntpsweep.menu: $(srcdir)/invoke-ntpsweep.texi + @: do-nothing action to avoid default SCCS get, .menu built with .texi + +$(srcdir)/invoke-ntpsweep.texi: ntpsweep $(srcdir)/ntpsweep-opts.def $(std_def_list) + $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntpsweep-opts.def + $(top_srcdir)/scripts/build/check--help $@ + +### HTML + +$(srcdir)/ntpsweep.html: $(srcdir)/invoke-ntpsweep.menu $(srcdir)/invoke-ntpsweep.texi $(srcdir)/ntpsweep.texi $(top_srcdir)/sntp/include/version.texi + cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntpsweep.html ntpsweep.texi || true ) diff --git a/scripts/ntpsweep/ntpsweep-opts b/scripts/ntpsweep/ntpsweep-opts new file mode 100644 index 000000000..91dcbd79c --- /dev/null +++ b/scripts/ntpsweep/ntpsweep-opts @@ -0,0 +1,72 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'host-list' => [], + 'peers' => '', + 'maxlevel' => '', + 'strip' => '', + 'host' => '', + 'help' => '', 'more-help' => '' + }; + my $argument = '[hostfile]'; + my $ret = GetOptionsFromArray($args, $opts, ( + 'host-list|l=s', 'peers|p', 'maxlevel|m=i', + 'strip|s=s', 'host|h=s', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +ntpsweep - Print various informations about given ntp servers +USAGE: ntpsweep [ - [] | --[{=| }] ]... [hostfile] + + -l, --host-list=str Host to execute actions on + - may appear multiple times + -p, --peers Recursively list all peers a host synchronizes to + -m, --maxlevel=num Traverse peers up to this level (4 is a reasonable number) + -s, --strip=str Strip this string from hostnames + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + + if ($argument && $argument =~ /^[^\[]/ && !@$args) { + print STDERR "Not enough arguments supplied (See --help/-?)\n"; + exit 1; + } + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/ntpsweep/ntpsweep-opts.def b/scripts/ntpsweep/ntpsweep-opts.def new file mode 100644 index 000000000..50dd537c9 --- /dev/null +++ b/scripts/ntpsweep/ntpsweep-opts.def @@ -0,0 +1,63 @@ +/* -*- Mode: Text -*- */ +autogen definitions perlopt; +#include autogen-version.def + +prog-name = 'ntpsweep'; +prog-title = 'Print various informations about given ntp servers'; +package = ntp; +argument = '[hostfile]'; +long-opts; +gnu-usage; + +flag = { + name = host-list; + value = l; + arg-type = string; + max = NOLIMIT; + stack-arg; + descrip = 'Host to execute actions on'; + doc = <<- _EndOfDoc_ + Use this option to specify the host on which this script operates. + May appear multiple times. + _EndOfDoc_; +}; + +flag = { + name = peers; + value = p; + descrip = 'Recursively list all peers a host synchronizes to'; +}; + +flag = { + name = maxlevel; + value = m; + arg-type = number; + descrip = 'Traverse peers up to this level (4 is a reasonable number)'; +}; + +flag = { + name = strip; + value = s; + arg-type = string; + descrip = 'Strip this string from hostnames'; +}; + +/* Deprecated options */ +flag = { + name = host; + value = h; + arg-type = string; + descrip = 'Specify a single host'; + deprecated; +}; + +doc-section = { + ds-type = 'DESCRIPTION'; + ds-format = 'mdoc'; + ds-text = <<- _END_PROG_MDOC_DESCRIP +.Nm +prints per host the NTP stratum level, the clock offset in seconds, the daemon +version, the operating system and the processor. Optionally recursing through +all peers. + _END_PROG_MDOC_DESCRIP; +}; diff --git a/scripts/ntpsweep/ntpsweep.in b/scripts/ntpsweep/ntpsweep.in new file mode 100644 index 000000000..54e13e997 --- /dev/null +++ b/scripts/ntpsweep/ntpsweep.in @@ -0,0 +1,203 @@ +#! @PATH_PERL@ -w +# +# $Id$ +# +# DISCLAIMER +# +# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V. +# +# Permission to use, copy, modify and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appears in all copies and +# that both the copyright notice and this permission notice appear in +# supporting documentation. This software is supported as is and without +# any express or implied warranties, including, without limitation, the +# implied warranties of merchantability and fitness for a particular +# purpose. The name Origin B.V. must not be used to endorse or promote +# products derived from this software without prior written permission. +# +# Hans Lambermont +package ntpsweep; +use 5.006_000; +use strict; +use lib "@PERLLIBDIR@"; +use NTP::Util qw(do_dns ntp_read_vars ntp_peers ntp_sntp_line); + +(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%; +my ($showpeers, $maxlevel, $strip); +my (%known_host_info, %known_host_peers); + +exit run(@ARGV) unless caller; + +sub run { + my $opts; + if (!processOptions(\@_, $opts) || + (((@_ != 1) && !$opts->{host} && !@{$opts->{'host-list'}}))) { + usage(1); + }; + + # no STDOUT buffering + $| = 1; + ($showpeers, $maxlevel, $strip) = + ($opts->{peers}, $opts->{maxlevel}, $opts->{strip}); + + my $hostsfile = shift; + + # Main program + + my @hosts; + + if ($opts->{host}) { + push @hosts, $opts->{host}; + } + else { + @hosts = read_hosts($hostsfile) if $hostsfile; + push @hosts, @{$opts->{'host-list'}}; + } + + # Print header + print < 1); + } +} + +sub read_hosts { + my ($hostsfile) = @_; + my @hosts; + + open my $hosts, $hostsfile + or die "$program: FATAL: unable to read $hostsfile: $!\n"; + + while (<$hosts>) { + next if /^\s*(#|$)/; # comment/empty + chomp; + push @hosts, $_; + } + + close $hosts; + return @hosts; +} + +sub scan_host { + my ($host, $level, %trace) = @_; + my $stratum = 0; + my $offset = 0; + my $daemonversion = ""; + my $system = ""; + my $processor = ""; + my @peers; + my $known_host = 0; + + if (exists $known_host_info{$host}) { + $known_host = 1; + } + else { + ($offset, $stratum) = ntp_sntp_line($host); + + # got answers ? If so, go on. + if ($stratum) { + my $vars = ntp_read_vars(0, [qw(processor system daemon_version)], $host) || {}; + $daemonversion = $vars->{daemon_version}; + $system = $vars->{system}; + $processor = $vars->{processor}; + + # Shorten daemon_version string. + $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//; + $daemonversion =~ s/version=//; + $daemonversion =~ s/(x|)ntpd //; + $daemonversion =~ s/(\(|\))//g; + $daemonversion =~ s/beta/b/; + $daemonversion =~ s/multicast/mc/; + + # Shorten system string + $system =~ s/UNIX\///; + $system =~ s/RELEASE/r/; + $system =~ s/CURRENT/c/; + + # Shorten processor string + $processor =~ s/unknown//; + } + + # got answers ? If so, go on. + if ($daemonversion) { + if ($showpeers) { + my @peers_tmp = ntp_peers($host); + for (@peers_tmp) { + $_->{remote} =~ s/^(?: |x|\.|-|\+|#|\*|o)([^ ]+)/$1/; + push @peers, $_->{remote}; + } + } + } + + # Add scanned host to known_hosts array + #push @known_hosts, $host; + if ($stratum) { + $known_host_info{$host} = sprintf "%2d %9.3f %-11s %-12s %s", + $stratum, $offset, (substr $daemonversion, 0, 11), + (substr $system, 0, 12), (substr $processor, 0, 9); + } + else { + # Stratum level 0 is consider invalid + $known_host_info{$host} = " ?"; + } + $known_host_peers{$host} = [@peers]; + } + + if ($stratum || $known_host) { # Valid or known host + my $printhost = ' ' x $level . (do_dns($host) || $host); + # Shorten host string + if ($strip) { + $printhost =~ s/$strip//; + } + # append number of peers in brackets if requested and valid + if ($showpeers && ($known_host_info{$host} ne " ?")) { + $printhost .= " (" . @{$known_host_peers{$host}} . ")"; + } + # Finally print complete host line + printf "%-32s %s\n", + (substr $printhost, 0, 32), $known_host_info{$host}; + if ($showpeers && ($maxlevel ? $level < $maxlevel : 1)) { + $trace{$host} = 1; + # Loop through peers + foreach my $peer (@{$known_host_peers{$host}}) { + if (exists $trace{$peer}) { + # we've detected a loop ! + $printhost = ' ' x ($level + 1) . "= " . $peer; + # Shorten host string + $printhost =~ s/$strip// if $strip; + printf "%-32s\n", substr $printhost, 0, 32; + } else { + if ((substr $peer, 0, 3) ne "127") { + scan_host($peer, $level + 1, %trace); + } + } + } + } + } + else { # We did not get answers from this host + my $printhost = ' ' x $level . (do_dns($host) || $host); + $printhost =~ s/$strip// if $strip; + printf "%-32s ?\n", substr $printhost, 0, 32; + } +} + +@ntpsweep_opts@ + +1; +__END__ diff --git a/scripts/ntpsweep/ntpsweep.mdoc.in b/scripts/ntpsweep/ntpsweep.mdoc.in new file mode 100644 index 000000000..f6f176689 --- /dev/null +++ b/scripts/ntpsweep/ntpsweep.mdoc.in @@ -0,0 +1,71 @@ +.Dd October 2 2013 +.Dt NTPSWEEP 1ntpsweepmdoc User Commands +.Os FreeBSD 6.4-STABLE +.\" EDIT THIS FILE WITH CAUTION (ntpsweep-opts.mdoc) +.\" +.\" It has been AutoGen-ed October 2, 2013 at 09:50:21 PM by AutoGen 5.18.1pre5 +.\" From the definitions ntpsweep-opts.def +.\" and the template file agmdoc-cmd.tpl +.Sh NAME +.Nm ntpsweep +.Nd Print various informations about given ntp servers +.Sh SYNOPSIS +.Nm +.\" Mixture of short (flag) options and long options +.Op Fl flags +.Op Fl flag Op Ar value +.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc +[hostfile] +.Pp +.Sh DESCRIPTION +.Nm +prints per host the NTP stratum level, the clock offset in seconds, the daemon +version, the operating system and the processor. Optionally recursing through +all peers. +.Sh "OPTIONS" +.Bl -tag +.It Fl l Ar string , Fl \-host\-list Ns = Ns Ar string +Host to execute actions on. +This option may appear an unlimited number of times. +.sp + Use this option to specify the host on which this script operates. + May appear multiple times. +.It Fl p , Fl \-peers +Recursively list all peers a host synchronizes to. +.sp +This option has not been fully documented. +.It Fl m Ar number , Fl \-maxlevel Ns = Ns Ar number +Traverse peers up to this level (4 is a reasonable number). +This option takes an integer number as its argument. +.sp +This option has not been fully documented. +.It Fl s Ar string , Fl \-strip Ns = Ns Ar string +Strip this string from hostnames. +.sp +This option has not been fully documented. +.It Fl h Ar string , Fl \-host Ns = Ns Ar string +Specify a single host. +.sp +This option has not been fully documented. +.sp +.B +NOTE: THIS OPTION IS DEPRECATED +.It Fl \&? , Fl \-help +Display usage information and exit. +.It Fl \&! , Fl \-more\-help +Pass the extended usage information through a pager. +.El +.Sh "EXIT STATUS" +One of the following exit values will be returned: +.Bl -tag +.It 0 " (EXIT_SUCCESS)" +Successful program execution. +.It 1 " (EXIT_FAILURE)" +The operation failed or the command syntax was not valid. +.It 70 " (EX_SOFTWARE)" +libopts had an internal operational error. Please report +it to autogen\-users@lists.sourceforge.net. Thank you. +.El +.Sh "NOTES" +This manual page was \fIAutoGen\fP\-erated from the \fBntpsweep\fP +option definitions. diff --git a/scripts/ntpsweep/ntpsweep.texi b/scripts/ntpsweep/ntpsweep.texi new file mode 100644 index 000000000..e408065a6 --- /dev/null +++ b/scripts/ntpsweep/ntpsweep.texi @@ -0,0 +1,43 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename ntpsweep.info +@settitle ntpsweep User's Manual +@include ../../sntp/include/version.texi +@paragraphindent 2 +@c %**end of header + +@ifinfo +This file documents the use of @code{ntpsweep}, +a program from +the NTP Project, +that is used to wait until @code{ntpd} has been able to +synchronize and stabilize the time on the current host. +@end ifinfo + +@direntry +* ntpsweep: (ntpsweep). Wait for ntpd to synchronize and stabilize the system clock. +@end direntry + +@titlepage +@title ntpsweep User's Manual +@subtitle ntpsweep, version @value{VERSION}, @value{UPDATED} +@c @author Max @email{foo@ntp.org} +@end titlepage + +@c @page +@c @vskip 0pt plus 1filll + +@node Top, ntpsweep Description, (dir), (dir) + +This document describes the use of the NTP Project's @code{ntpsweep} program. + +This document applies to version @value{VERSION} of @code{ntpsweep}. + +@shortcontents + +@menu +* ntpsweep Description:: Description +* ntpsweep Invocation:: Invoking ntpsweep +@end menu + +@include invoke-ntpsweep.texi diff --git a/scripts/ntptrace.in b/scripts/ntptrace.in deleted file mode 100755 index e54ca2bce..000000000 --- a/scripts/ntptrace.in +++ /dev/null @@ -1,72 +0,0 @@ -#! @PATH_PERL@ -w - -# John Hay -- John.Hay@icomtek.csir.co.za / jhay@FreeBSD.org - -use Socket; -use Getopt::Std; -use vars qw($opt_n $opt_m); - -$ntpq = "ntpq"; - -$Getopt::Std::STANDARD_HELP_VERSION=1; -getopts('nm:'); - -$dodns = 1; -$dodns = 0 if (defined($opt_n)); - -$max_hosts = (defined($opt_m) ? $opt_m : 99); -$max_hosts = 0 if ( $max_hosts !~ /^\d+$/ ); -$nb_host = 1; - -$host = shift; -$host ||= "127.0.0.1"; - -for (;;) { - $nb_host++; - $rootdelay = 0; - $rootdispersion = 0; - $stratum = 255; - $cmd = "$ntpq -n -c rv $host"; - open(PH, $cmd . "|") || die "failed to start command $cmd: $!"; - while () { - $stratum = $1 if (/stratum=(\d+)/); - $peer = $1 if (/peer=(\d+)/); - # Very old servers report phase and not offset. - $offset = $1 if (/(?:offset|phase)=([^\s,]+)/); - $rootdelay = $1 if (/rootdelay=([^\s,]+)/); - # firstly - rootdispersion, now - rootdisp - $rootdispersion = $1 if (/(?:rootdisp|rootdispersion)=([^\s,]+)/); - $refid = $1 if (/refid=([^\s,]+)/); - } - close(PH) || die "$cmd failed"; - last if ($stratum == 255); - $offset /= 1000; - $syncdistance = ($rootdispersion + ($rootdelay / 2)) / 1000; - $dhost = $host; - # Only do lookups of IPv4 addresses. The standard lookup functions - # of perl only do IPv4 and I don't know if we should require extras. - if ($dodns && $host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) { - $iaddr = inet_aton($host); - $name = (gethostbyaddr($iaddr, AF_INET))[0]; - $dhost = $name if (defined($name)); - } - printf("%s: stratum %d, offset %f, synch distance %f", - $dhost, $stratum, $offset, $syncdistance); - printf(", refid '%s'", $refid) if ($stratum == 1); - printf("\n"); - last if ($stratum == 0 || $stratum == 1 || $stratum == 16); - last if ($refid =~ /^127\.127\.\d{1,3}\.\d{1,3}$/); - last if ($nb_host > $max_hosts); - - $cmd = "$ntpq -n -c \"rv $peer\" $host"; - open(PH, $cmd . "|") || die "failed to start command $cmd: $!"; - $thost = ""; - while () { - $thost = $1, last if (/srcadr=(\S+),/); - } - close(PH) || die "$cmd failed"; - last if ($thost eq ""); - last if ($thost =~ /^127\.127\.\d{1,3}\.\d{1,3}$/); - $host = $thost; -} - diff --git a/scripts/ntptrace/Makefile.am b/scripts/ntptrace/Makefile.am new file mode 100644 index 000000000..91cc97ba9 --- /dev/null +++ b/scripts/ntptrace/Makefile.am @@ -0,0 +1,86 @@ +man_MANS= ntptrace.$(NTPTRACE_MS) + +bin_SCRIPTS = $(NTPTRACE_DB) +libexec_SCRIPTS = $(NTPTRACE_DL) +sbin_SCRIPTS = $(NTPTRACE_DS) + +run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" \ + autogen -L ../../sntp/include -L ../../sntp/ag-tpl \ + --writable + +EXTRA_SCRIPTS = ntptrace + +EXTRA_DIST = \ + $(srcdir)/ntptrace.in \ + $(srcdir)/ntptrace.1ntptraceman \ + $(srcdir)/ntptrace.1ntptracemdoc \ + $(srcdir)/ntptrace.texi \ + $(srcdir)/ntptrace.html \ + $(srcdir)/ntptrace.1 \ + $(srcdir)/ntptrace-opts.def \ + $(srcdir)/ntptrace-opts \ + $(srcdir)/invoke-ntptrace.texi \ + $(srcdir)/invoke-ntptrace.menu + +html_DATA = $(srcdir)/ntptrace.html + +noinst_DATA = \ + $(srcdir)/ntptrace.in \ + $(srcdir)/ntptrace.1ntptraceman \ + $(srcdir)/ntptrace.1ntptracemdoc \ + $(srcdir)/ntptrace.texi \ + $(srcdir)/ntptrace.html \ + $(srcdir)/ntptrace.1 \ + $(srcdir)/ntptrace-opts.def \ + $(srcdir)/ntptrace-opts \ + $(srcdir)/invoke-ntptrace.texi \ + $(srcdir)/invoke-ntptrace.menu + +ntptrace: $(srcdir)/ntptrace-opts + +$(srcdir)/ntptrace-opts: $(srcdir)/ntptrace-opts.def ../../sntp/ag-tpl/perlopt.tpl + $(run_ag) ntptrace-opts.def + +clean-local: extra-clean + +extra-clean: + rm ntptrace + rm *.html *man *mdoc *.1 invoke-ntptrace.* ntptrace.man.in + +### Nroff + +$(srcdir)/ntptrace.1ntptraceman: $(srcdir)/ntptrace-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1ntptraceman -Tagman-cmd.tpl ntptrace-opts.def + +$(srcdir)/ntptrace.man.in: $(srcdir)/ntptrace.1ntptraceman $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntptrace.1ntptraceman > $(srcdir)/ntptrace.man.in+ + mv $(srcdir)/ntptrace.man.in+ $(srcdir)/ntptrace.man.in + +### Mdoc + +$(srcdir)/ntptrace.1ntptracemdoc: $(srcdir)/ntptrace-opts.def $(std_def_list) + $(run_ag) -DMAN_SECTION=1ntptracemdoc -Tagmdoc-cmd.tpl ntptrace-opts.def + +$(srcdir)/ntptrace.mdoc.in: $(srcdir)/ntptrace.1ntptracemdoc $(top_srcdir)/sntp/scripts/mansec2subst.sed + sed -f $(top_srcdir)/sntp/scripts/mansec2subst.sed $(srcdir)/ntptrace.1ntptracemdoc > $(srcdir)/ntptrace.mdoc.in+ + mv $(srcdir)/ntptrace.mdoc.in+ $(srcdir)/ntptrace.mdoc.in + +### Manpage + +$(srcdir)/ntptrace.1: $(srcdir)/ntptrace.$(MANTAGFMT).in $(top_builddir)/config.status + $(top_builddir)/config.status --file=ntptrace.1+:$(srcdir)/ntptrace.$(MANTAGFMT).in + mv ntptrace.1+ ntptrace.1 + +### Texinfo + +$(srcdir)/invoke-ntptrace.menu: $(srcdir)/invoke-ntptrace.texi + @: do-nothing action to avoid default SCCS get, .menu built with .texi + +$(srcdir)/invoke-ntptrace.texi: ntptrace $(srcdir)/ntptrace-opts.def $(std_def_list) + $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section ntptrace-opts.def + $(top_srcdir)/scripts/build/check--help $@ + +### HTML + +$(srcdir)/ntptrace.html: $(srcdir)/invoke-ntptrace.menu $(srcdir)/invoke-ntptrace.texi $(srcdir)/ntptrace.texi $(top_srcdir)/sntp/include/version.texi + cd $(srcdir) && ( makeinfo --force --html --no-split -I ../sntp -o ntptrace.html ntptrace.texi || true ) diff --git a/scripts/ntptrace/ntptrace-opts b/scripts/ntptrace/ntptrace-opts new file mode 100644 index 000000000..27edbdc20 --- /dev/null +++ b/scripts/ntptrace/ntptrace-opts @@ -0,0 +1,67 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'numeric' => '', + 'max-hosts' => '99', + 'host' => '127.0.0.1', + 'help' => '', 'more-help' => '' + }; + my $argument = '[host]'; + my $ret = GetOptionsFromArray($args, $opts, ( + 'numeric|n', 'max-hosts|m=i', 'host|r=s', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +ntptrace - Trace peers of an NTP server +USAGE: ntptrace [ - [] | --[{=| }] ]... [host] + + -n, --numeric Print IP addresses instead of hostnames + -m, --max-hosts=num Maximum number of peers to trace + -r, --host=str Single remote host + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + + if ($argument && $argument =~ /^[^\[]/ && !@$args) { + print STDERR "Not enough arguments supplied (See --help/-?)\n"; + exit 1; + } + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/ntptrace/ntptrace-opts.def b/scripts/ntptrace/ntptrace-opts.def new file mode 100644 index 000000000..5b0e585fb --- /dev/null +++ b/scripts/ntptrace/ntptrace-opts.def @@ -0,0 +1,66 @@ +/* -*- Mode: Text -*- */ +autogen definitions perlopt; + +//#include copyright.def +#include autogen-version.def + +prog-name = 'ntptrace'; +prog-title = 'Trace peers of an NTP server'; +argument = '[host]'; +long-opts; +gnu-usage; + +flag = { + name = numeric; + value = n; + descrip = 'Print IP addresses instead of hostnames'; + doc = <<- _EndOfDoc_ + Output hosts as dotted-quad numeric format rather than converting to + the canonical host names. + _EndOfDoc_; +}; + +flag = { + name = max-hosts; + value = m; + arg-type = number; + arg-default = 99; + descrip = 'Maximum number of peers to trace'; +}; + +flag = { + name = host; + value = r; + arg-type = string; + arg-default = '127.0.0.1'; + descrip = 'Single remote host'; +}; + +doc-section = { + ds-type = 'DESCRIPTION'; + ds-format = 'texi'; + ds-text = <<- _END_PROG_MDOC_DESCRIP +@code{ntptrace} is a perl script that uses the ntpq utility program to follow +the chain of NTP servers from a given host back to the primary time source. For +ntptrace to work properly, each of these servers must implement the NTP Control +and Monitoring Protocol specified in RFC 1305 and enable NTP Mode 6 packets. + +If given no arguments, ntptrace starts with localhost. Here is an example of +the output from ntptrace: + +@example +% ntptrace localhost: stratum 4, offset 0.0019529, synch distance 0.144135 +server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784 usndh.edu: +stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB' +@end example + +On each line, the fields are (left to right): the host name, the host stratum, +the time offset between that host and the local host (as measured by +@code{ntptrace}; this is why it is not always zero for "localhost"), the host +synchronization distance, and (only for stratum-1 servers) the reference clock +ID. All times are given in seconds. Note that the stratum is the server hop +count to the primary source, while the synchronization distance is the +estimated error relative to the primary source. These terms are precisely +defined in RFC-1305. + _END_PROG_MDOC_DESCRIP; +}; diff --git a/scripts/ntptrace/ntptrace.in b/scripts/ntptrace/ntptrace.in new file mode 100755 index 000000000..dfb7c4868 --- /dev/null +++ b/scripts/ntptrace/ntptrace.in @@ -0,0 +1,79 @@ +#! @PATH_PERL@ -w +# John Hay -- John.Hay@icomtek.csir.co.za / jhay@FreeBSD.org +package ntptrace; +use 5.006_000; +use strict; +use lib "@PERLLIBDIR@"; +use NTP::Util qw(ntp_read_vars do_dns); + +exit run(@ARGV) unless caller; + +sub run { + my $opts; + if (!processOptions(\@_, $opts)) { + usage(1); + }; + + my $dodns = $opts->{numeric} ? 0 : 1; + my $max_hosts = $opts->{'max-hosts'}; + my $host = shift || $opts->{host}; + my $nb_host = 0; + + for (;;) { + $nb_host++; + + my %info = get_info($host); + last if not %info; + + my $dhost = $host; + if ($dodns) { + my $name = do_dns($host); + $dhost = $name if defined $name; + } + + printf "%s: stratum %d, offset %f, synch distance %f", + $dhost, $info{stratum}, $info{offset}, $info{syncdistance}; + printf ", refid '%s'", $info{refid} if $info{stratum} == 1; + print "\n"; + + last if $info{stratum} == 0 || $info{stratum} == 1 || + $info{stratum} == 16; + last if $info{refid} =~ /^127\.127\.\d{1,3}\.\d{1,3}$/; + last if $nb_host == $max_hosts; + + my $next_host = get_next_host($info{peer}, $host); + last if $next_host eq ''; + last if $next_host =~ /^127\.127\.\d{1,3}\.\d{1,3}$/; + + $host = $next_host; + } + return 0; +} + +sub get_info { + my ($host) = @_; + my ($rootdelay, $rootdisp, $info) = (0, 0); + + $info = ntp_read_vars(0, [], $host); + return if not defined $info; + return if not exists $info->{stratum}; + + $info->{offset} /= 1000; + $info->{syncdistance} = ($info->{rootdisp} + ($info->{rootdelay} / 2)) / 1000; + + return %$info; +} + + +sub get_next_host { + my ($peer, $host) = @_; + + my $info = ntp_read_vars($peer, [qw(srcadr)], $host); + return if not defined $info; + return $info->{srcadr}; +} + +@ntptrace_opts@ + +1; +__END__ diff --git a/scripts/ntptrace/ntptrace.mdoc.in b/scripts/ntptrace/ntptrace.mdoc.in new file mode 100644 index 000000000..63908a7c6 --- /dev/null +++ b/scripts/ntptrace/ntptrace.mdoc.in @@ -0,0 +1,87 @@ +.Dd October 2 2013 +.Dt NTPTRACE @NTPTRACE_MS@ User Commands +.Os FreeBSD 6.4-STABLE +.\" EDIT THIS FILE WITH CAUTION (ntptrace-opts.mdoc) +.\" +.\" It has been AutoGen-ed October 2, 2013 at 09:50:29 PM by AutoGen 5.18.1pre5 +.\" From the definitions ntptrace-opts.def +.\" and the template file agmdoc-cmd.tpl +.Sh NAME +.Nm ntptrace +.Nd Trace peers of an NTP server +.Sh SYNOPSIS +.Nm +.\" Mixture of short (flag) options and long options +.Op Fl flags +.Op Fl flag Op Ar value +.Op Fl \-option\-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc +[host] +.Pp +.Sh DESCRIPTION +\fBntptrace\fP is a perl script that uses the ntpq utility program to follow +the chain of NTP servers from a given host back to the primary time source. For +ntptrace to work properly, each of these servers must implement the NTP Control +and Monitoring Protocol specified in RFC 1305 and enable NTP Mode 6 packets. +.sp +If given no arguments, ntptrace starts with localhost. Here is an example of +the output from ntptrace: +.sp +.Bd -literal -offset indent +% ntptrace localhost: stratum 4, offset 0.0019529, synch distance 0.144135 +server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784 usndh.edu: +stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB' +.Ed +.sp +On each line, the fields are (left to right): the host name, the host stratum, +the time offset between that host and the local host (as measured by +\fBntptrace\fP; this is why it is not always zero for "localhost"), the host +synchronization distance, and (only for stratum\-1 servers) the reference clock +ID. All times are given in seconds. Note that the stratum is the server hop +count to the primary source, while the synchronization distance is the +estimated error relative to the primary source. These terms are precisely +defined in RFC\-1305. +.Sh "OPTIONS" +.Bl -tag +.It Fl n , Fl \-numeric +Print IP addresses instead of hostnames. +.sp +Output hosts as dotted\-quad numeric format rather than converting to +the canonical host names. +.It Fl m Ar number , Fl \-max\-hosts Ns = Ns Ar number +Maximum number of peers to trace. +This option takes an integer number as its argument. +The default +.Ar number +for this option is: +.ti +4 + 99 +.sp +This option has not been fully documented. +.It Fl r Ar string , Fl \-host Ns = Ns Ar string +Single remote host. +The default +.Ar string +for this option is: +.ti +4 + 127.0.0.1 +.sp +This option has not been fully documented. +.It Fl \&? , Fl \-help +Display usage information and exit. +.It Fl \&! , Fl \-more\-help +Pass the extended usage information through a pager. +.El +.Sh "EXIT STATUS" +One of the following exit values will be returned: +.Bl -tag +.It 0 " (EXIT_SUCCESS)" +Successful program execution. +.It 1 " (EXIT_FAILURE)" +The operation failed or the command syntax was not valid. +.It 70 " (EX_SOFTWARE)" +libopts had an internal operational error. Please report +it to autogen\-users@lists.sourceforge.net. Thank you. +.El +.Sh "NOTES" +This manual page was \fIAutoGen\fP\-erated from the \fBntptrace\fP +option definitions. diff --git a/scripts/ntptrace/ntptrace.texi b/scripts/ntptrace/ntptrace.texi new file mode 100644 index 000000000..b1851f2a8 --- /dev/null +++ b/scripts/ntptrace/ntptrace.texi @@ -0,0 +1,40 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename ntptrace.info +@settitle Ntptrace User's Manual +@include ../../sntp/include/version.texi +@paragraphindent 2 +@c %**end of header + +@ifinfo +This file documents the use of @code{ntptrace}, +a program from the NTP Project +@end ifinfo + +@direntry +* ntptrace: (ntptrace). Trace the ntp server to the primary time source. +@end direntry + +@titlepage +@title ntptrace User's Manual +@subtitle ntptrace, version @value{VERSION}, @value{UPDATED} +@c @author Max @email{foo@ntp.org} +@end titlepage + +@c @page +@c @vskip 0pt plus 1filll + +@node Top, ntptrace Description, (dir), (dir) +@top Simple Network Time Protocol User Manual + +This document describes the use of the NTP Project's @code{ntptrace} program. +This document applies to version @value{VERSION} of @code{ntptrace}. + +@shortcontents + +@menu +* ntptrace Description:: Description +* ntptrace Invocation:: Invoking ntptrace +@end menu + +@include invoke-ntptrace.texi diff --git a/scripts/plot_summary-opts b/scripts/plot_summary-opts new file mode 100644 index 000000000..1dd327c77 --- /dev/null +++ b/scripts/plot_summary-opts @@ -0,0 +1,73 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'directory' => '/tmp', + 'identifier' => '', + 'offset-limit' => '0.128', + 'peer' => [], + 'plot-term' => '', + 'output-file' => '', + 'dont-wait' => '', + 'help' => '', 'more-help' => '' + }; + my $argument = ''; + my $ret = GetOptionsFromArray($args, $opts, ( + 'directory=s', 'identifier=s', 'offset-limit=f', + 'peer=s', 'plot-term=s', 'output-file=s', + 'dont-wait', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +plot_summary - plot statistics generated by summary script +USAGE: plot_summary [ - [] | --[{=| }] ]... + + , --directory=str Where the summary files are + , --identifier=str Origin of the data + , --offset-limit=float Limit of absolute offset + , --peer=str Peers to generate plots for + - may appear multiple times + , --plot-term=str Gnuplot terminal + , --output-file=str Output file + , --dont-wait Don't wait for keystroke between plots + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/plot_summary-opts.def b/scripts/plot_summary-opts.def new file mode 100644 index 000000000..7b52115f1 --- /dev/null +++ b/scripts/plot_summary-opts.def @@ -0,0 +1,78 @@ +/* -*- Mode: Text -*- */ +autogen definitions perlopt; +#include autogen-version.def +prog-name = 'plot_summary'; +prog-title = 'plot statistics generated by summary script'; +long-opts; +gnu-usage; + +flag = { + name = directory; + arg-type = string; + arg-default = '/tmp'; + descrip = 'Where the summary files are'; + doc = <<- _EndOfDoc_ + The directory where the @code{plot_summary} will search for the + *_summary files generated by @code{summary} script. + _EndOfDoc_; +}; + +flag = { + name = identifier; + arg-type = string; + descrip = 'Origin of the data'; + doc = <<- _EndOfDoc_ + Where does the plotted data come from, default to string "host" plus + current hostname + _EndOfDoc_; +}; + +flag = { + name = offset-limit; + arg-type = string; + arg-name = float; + arg-default = "0.128"; + descrip = 'Limit of absolute offset'; + doc = <<- _EndOfDoc_ + _EndOfDoc_; +}; + +flag = { + name = peer; + arg-type = string; + stack-arg; + max = NOLIMIT; + descrip = 'Peers to generate plots for'; + doc = <<- _EndOfDoc_ + By default the peer_summary plots are not generated. Use this option to + specify list of peers if you want to generate plots for them. + _EndOfDoc_; +}; + +flag = { + name = plot-term; + arg-type = string; + descrip = 'Gnuplot terminal'; + doc = <<- _EndOfDoc_ + This is string is passed directly to the @code{gnuplot set terminal} + command. Default is @code{x11} if @code{DISPLAY} is set and + @code{dumb} is it's not'. See output from @code(gnuplot -e "set + terminal") for the list of avalaible options. + _EndOfDoc_; +}; + +flag = { + name = output-file; + arg-type = str; + descrip = 'Output file'; + doc = <<- _EndOfDoc_ + Output file for @code{gnuplot}, default to stdout. + _EndOfDoc_; +}; + +flag = { + name = dont-wait; + descrip = "Don't wait for keystroke between plots"; + doc = <<- _EndOfDoc_ + _EndOfDoc_; +}; diff --git a/scripts/plot_summary.in b/scripts/plot_summary.in index 3b46a0401..3401b0d0b 100644 --- a/scripts/plot_summary.in +++ b/scripts/plot_summary.in @@ -19,53 +19,48 @@ # 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" +package plot_summary; +use 5.006_000; use strict; - use Time::Local; -use Getopt::Long; -# parse command line -my $summary_dir = "/tmp"; -my $identifier = "host " . `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 ($identifier, $offset_limit, $gnuplot_terminal, $wait_after_plot, + $output_file, $output_file_number); -my %options = ("directory|input-directory=s" => \$summary_dir, - "identifier=s" => \$identifier, - "offset-limit=f" => \$offset_limit, - "output-file=s" => \$output_file, - "peer=s@" => \@peer_list, - "plot-term|gnuplot-term=s" => \$gnuplot_terminal, - "wait-after-plot!" => \$wait_after_plot, - ); +exit run(@ARGV) unless caller; -if ( !GetOptions(%options) ) -{ - print STDERR "valid options for $0 are:\n"; - my $opt; - foreach $opt (sort(keys %options)) { - print STDERR "\t--$opt\t(default is "; - if ( ref($options{$opt}) eq "ARRAY" ) { - print STDERR join(", ", map { "'$_'" } @{$options{$opt}}); - } else { - print STDERR "'${$options{$opt}}'"; - } - print STDERR ")\n"; +sub run { + my $opts; + if (!processOptions(\@_, $opts)) { + usage(1); } - print STDERR "\n"; - die; -} -chomp $identifier; -die "illegal offset-limit: $offset_limit" unless $offset_limit > 0.0; -$offset_limit *= 1e6; # scale to microseconds + $identifier = $opts->{'identifier'}; + if (!$identifier) { + $identifier = "host".`hostname`; + chomp $identifier; + } + $offset_limit = $opts->{'offset-limit'}; + $output_file = $opts->{'output-file'}; + $output_file_number = 1; + $gnuplot_terminal = $opts->{'plot-terminal'} + || ( $ENV{DISPLAY} ? "x11" : "dumb" ); + $wait_after_plot = !$opts->{'dont-wait'}; + + die "illegal offset-limit: $offset_limit" unless $offset_limit > 0.0; + $offset_limit *= 1e6; # scale to microseconds + + my $summary_dir = $opts->{'directory'}; + + my $loop_summary ="$summary_dir/loop_summary"; + my $peer_summary ="$summary_dir/peer_summary"; + my $clock_summary="$summary_dir/clock_summary"; + + my @peer_list = @{$opts->{'peer'}}; + + do_loop($loop_summary); + do_peer($peer_summary, $_) for @peer_list; +} # return the smallest value in the given list sub min @@ -176,7 +171,7 @@ sub do_loop "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 style data yerrorbars\n"; print "set multiplot\n"; print "set size 1, 0.5\n"; print "set lmargin 8\n"; @@ -224,7 +219,7 @@ sub do_loop print "set xlabel\n"; print "set ylabel \"[us]\"\n"; print "set origin 0, 0.5\n"; - print "set data style linespoints\n"; + print "set style data linespoints\n"; print "set multiplot\n"; print "plot $ylimit \"$out_file\" using 1:6 title \"Offset\", "; print "\"$out_file\" using 1:6 smooth bezier " . @@ -296,7 +291,7 @@ sub do_peer 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 style data 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 " . @@ -328,10 +323,7 @@ sub do_peer unlink $out_file; } +@plot_summary_opts@ -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; +1; +__END__ diff --git a/scripts/rc/README b/scripts/rc/README new file mode 100644 index 000000000..f5b82bcbc --- /dev/null +++ b/scripts/rc/README @@ -0,0 +1,13 @@ +This directory contains some example rc scripts for ntpd. + +In general, ntpd should be started as soon as possible in the boot process. If +any services require stable system clock, the ntpwait script should be run +before them as late as possible. + +The rc.d contains scripts for systems using rc.d init system (originated in +NetBSD). If a service requires stable system time, indicate it with TIMESYNC +dependency and set ntpwait_enable to YES. + +For SysV init systems, you'll have to create links as /etc/rc2.d/S20ntpd and +/etc/rc2.d/S80ntpwait yourself. (The numbers are just examples, try to give +ntpd as much time as possible to synchronize before running ntpwait). diff --git a/scripts/rc/ntpd b/scripts/rc/ntpd new file mode 100644 index 000000000..9896247e7 --- /dev/null +++ b/scripts/rc/ntpd @@ -0,0 +1,88 @@ +#!/bin/sh + +NTPD=/usr/sbin/ntpd +PIDFILE=/var/run/ntpd.pid +USER=ntp +GROUP=ntp +NTPD_OPTS="-g -u $USER:$GROUP -p $PIDFILE" + +ntpd_start() { + if [ -r $PIDFILE ]; then + echo "ntpd seems to be already running under pid `cat $PIDFILE`." + echo "Delete $PIDFILE if this is not the case."; + return 1; + fi + echo -n "Starting NTP daemon... " + + $NTPD $NTPD_OPTS + + # You can't always rely on the ntpd exit code, see Bug #2420 + # case "$?" in + # 0) echo "OK!" + # return 0;; + # *) echo "FAILED!" + # return 1;; + # esac + + sleep 1 + + if ps -Ao args|grep -q "^$NTPD $NTPD_OPTS"; then + echo "OK!" + return 0 + else + echo "FAILED!" + [ -e $PIDFILE ] && rm $PIDFILE + return 1 + fi +} + +ntpd_stop() { + if [ ! -r $PIDFILE ]; then + echo "ntpd doesn't seem to be running, cannot read the pid file." + return 1; + fi + echo -n "Stopping NTP daemon..."; + PID=`cat $PIDFILE` + + if kill -TERM $PID 2> /dev/null;then + # Give ntp 15 seconds to exit + for i in `seq 1 15`; do + if [ -n "`ps -p $PID|grep -v PID`" ]; then + echo -n . + sleep 1 + else + echo " OK!" + rm $PIDFILE + return 0 + fi + done + fi + + echo " FAILED! ntpd is still running"; + return 1 +} + +ntpd_status() { + if [ -r $PIDFILE ]; then + echo "NTP daemon is running as `cat $PIDFILE`" + else + echo "NTP daemon is not running" + fi +} + +case "$1" in + 'start') + ntpd_start + ;; + 'stop') + ntpd_stop + ;; + 'restart') + ntpd_stop && ntpd_start + ;; + 'status') + ntpd_status + ;; + *) + echo "Usage: $0 (start|stop|restart|status)" +esac diff --git a/scripts/rc/ntpwait b/scripts/rc/ntpwait new file mode 100644 index 000000000..2542b2add --- /dev/null +++ b/scripts/rc/ntpwait @@ -0,0 +1,15 @@ +#!/bin/sh + +NTPWAIT=/usr/sbin/ntpwait + +ntpwait_start() { + $NTPWAIT -v +} + +case "$1" in + 'start') + ntpwait_start + ;; + *) + echo "Usage: $0 (start)" +esac diff --git a/scripts/rc/rc.d/TIMESYNC b/scripts/rc/rc.d/TIMESYNC new file mode 100644 index 000000000..a8c074fd4 --- /dev/null +++ b/scripts/rc/rc.d/TIMESYNC @@ -0,0 +1,8 @@ +#!/bin/sh + +# PROVIDE: TIMESYNC +# REQUIRE: LOGIN ntpwait + +# This depedency ensures that all services which require stable system clock +# are run after ntpd is synchronized. It's run as late as possible, if you need +# stable clock before login use BEFORE: LOGIN diff --git a/scripts/rc/rc.d/ntpd b/scripts/rc/rc.d/ntpd new file mode 100644 index 000000000..ea33458bc --- /dev/null +++ b/scripts/rc/rc.d/ntpd @@ -0,0 +1,32 @@ +#!/bin/sh + +# PROVIDE: ntpd +# REQUIRE: syslogd cleanvar devfs +# BEFORE: SERVERS + +. /etc/rc.subr + +name="ntpd" +rcvar="ntpd_enable" +command="/usr/sbin/${name}" +pidfile="/var/run/${name}.pid" +start_precmd="ntpd_precmd" + +load_rc_config $name + +ntpd_precmd() +{ + rc_flags="-c ${ntpd_config} ${ntpd_flags}" + + if checkyesno ntpd_sync_on_start; then + rc_flags="-g $rc_flags" + fi + + if [ -z "$ntpd_chrootdir" ]; then + return 0; + fi + + rc_flags="-u ntpd:ntpd -i ${ntpd_chrootdir} $rc_flags" +} + +run_rc_command "$1" diff --git a/scripts/rc/rc.d/ntpwait b/scripts/rc/rc.d/ntpwait new file mode 100644 index 000000000..891d0dbd6 --- /dev/null +++ b/scripts/rc/rc.d/ntpwait @@ -0,0 +1,21 @@ +#!/bin/sh +# This script, when run, runs ntp-wait if ntpd is enabled. + +# PROVIDE: ntpwait + +. /etc/rc.subr + +name="ntpwait" +rcvar="ntpwait_enable" +start_cmd="ntpwait_start" +ntp_wait="/usr/sbin/ntp-wait" + +load_rc_config "$name" + +ntpwait_start() { + if checkyesno ntpd_enable; then + $ntp_wait -v + fi +} + +run_rc_command "$1" diff --git a/scripts/rc1/postinstall b/scripts/rc1/postinstall index d84b8c517..e69de29bb 100644 --- a/scripts/rc1/postinstall +++ b/scripts/rc1/postinstall @@ -1,2 +0,0 @@ -#!/bin/sh -/etc/init.d/xntp start diff --git a/scripts/rc1/preinstall b/scripts/rc1/preinstall index aa18639c2..e69de29bb 100644 --- a/scripts/rc1/preinstall +++ b/scripts/rc1/preinstall @@ -1,6 +0,0 @@ -#!/bin/sh -if [ -x /etc/init.d/xntp ] -then - /etc/init.d/xntp stop -fi -exit 0 diff --git a/scripts/rc1/preremove b/scripts/rc1/preremove index b870151a2..e69de29bb 100644 --- a/scripts/rc1/preremove +++ b/scripts/rc1/preremove @@ -1,4 +0,0 @@ -#!/bin/sh -/etc/init.d/xntp stop - -exit 0 diff --git a/scripts/rc1/prototype b/scripts/rc1/prototype index 3de20b076..e69de29bb 100644 --- a/scripts/rc1/prototype +++ b/scripts/rc1/prototype @@ -1,19 +0,0 @@ -!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/scripts/rc1/xntp b/scripts/rc1/xntp index 227b943aa..e69de29bb 100644 --- a/scripts/rc1/xntp +++ b/scripts/rc1/xntp @@ -1,29 +0,0 @@ -#!/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/scripts/rc2/local.ntpd b/scripts/rc2/local.ntpd index ba53e05ad..e69de29bb 100644 --- a/scripts/rc2/local.ntpd +++ b/scripts/rc2/local.ntpd @@ -1,64 +0,0 @@ -#! /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 () - { - chomp; - next unless (/$qt/); - print "Got <$_>\n"; - if (/^\s*(\d+)\s+/) - { - push @pids, $1; - } - } - close(PS); - return @pids; - } diff --git a/scripts/summary-opts b/scripts/summary-opts new file mode 100644 index 000000000..2cbc5cc80 --- /dev/null +++ b/scripts/summary-opts @@ -0,0 +1,69 @@ + +# DO NOT EDIT THE FOLLOWING +# +# It's auto generated option handling code + +use Getopt::Long qw(GetOptionsFromArray); +Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always)); + +my $usage; + +sub usage { + my ($ret) = @_; + print STDERR $usage; + exit $ret; +} + +sub paged_usage { + my ($ret) = @_; + my $pager = $ENV{PAGER} || '(less || more)'; + + open STDOUT, "| $pager" or die "Can't fork a pager: $!"; + print $usage; + + exit $ret; +} + +sub processOptions { + my $args = shift; + + my $opts = { + 'directory' => '/var/log/ntp', + 'end-date' => '', + 'output-directory' => '/tmp', + 'peer-dist-limit' => '400', + 'skip-time-steps' => '3600', + 'start-date' => '19700101', + 'help' => '', 'more-help' => '' + }; + my $argument = ''; + my $ret = GetOptionsFromArray($args, $opts, ( + 'directory=s', 'end-date=i', 'output-directory=s', + 'peer-dist-limit=f', 'skip-time-steps=f', 'start-date=i', + 'help|?', 'more-help')); + + $usage = <<'USAGE'; +summary - compute various stastics from NTP stat files +USAGE: summary [ - [] | --[{=| }] ]... + + , --directory=str Directory containing stat files + , --end-date=num End date + , --output-directory=str Output directory + , --peer-dist-limit=float Peer dist limit + , --skip-time-steps=float Ignore time offsets larger that this + , --start-date=num Start date + -?, --help Display usage information and exit + , --more-help Pass the extended usage information through a pager + +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character. +USAGE + + usage(0) if $opts->{'help'}; + paged_usage(0) if $opts->{'more-help'}; + $_[0] = $opts; + return $ret; +} + +END { close STDOUT }; + diff --git a/scripts/summary-opts.def b/scripts/summary-opts.def new file mode 100644 index 000000000..21bc9223b --- /dev/null +++ b/scripts/summary-opts.def @@ -0,0 +1,77 @@ +/* -*- Mode: Text -*- */ +autogen definitions perlopt; +#include autogen-version.def +prog-name = 'summary'; +prog-title = 'compute various stastics from NTP stat files'; +long-opts; +gnu-usage; + +flag = { + name = directory; + arg-type = string; + arg-default = '/var/log/ntp'; + descrip = 'Directory containing stat files'; + doc = <<- _EndOfDoc_ + The directory where @code{ntpd} will search for .stat files generated + by @code{ntpd}. + _EndOfDoc_; +}; + +flag = { + name = end-date; + arg-type = number; + descrip = 'End date'; + doc = <<- _EndOfDoc_ + Process all files with the date suffix less or equal to value of this + option. Defaults to today minus one day (Use @code{date -u +%Y%m%d}) + to get the timestamp. + _EndOfDoc_; +}; + +flag = { + name = output-directory; + arg-type = str; + arg-default = '/tmp'; + descrip = 'Output directory'; + doc = <<- _EndOfDoc_ + The output directory @code{summary} will write all output files to. + _EndOfDoc_; +}; + +flag = { + name = peer-dist-limit; + arg-type = string; + arg-name = float; + arg-default = 400; + descrip = 'Peer dist limit'; + doc = <<- _EndOfDoc_ + _EndOfDoc_; +}; + +flag = { + name = skip-time-steps; + arg-type = string; + arg-name = float; + arg-default = 3600; + descrip = 'Ignore time offsets larger that this'; + doc = <<- _EndOfDoc_ + _EndOfDoc_; +}; + +flag = { + name = start-date; + arg-type = num; + arg-default = 19700101; + descrip = 'Start date'; + doc = <<- _EndOfDoc_ + Process all files with the date suffix more or equal to value of + this option. Defaults to 197000101. + _EndOfDoc_; +}; + +doc-section = { + ds-type = 'DESCRIPTION'; + ds-format = 'texi'; + ds-text = <<- _EndOfDoc + _EndOfDoc; +}; diff --git a/scripts/summary.in b/scripts/summary.in index 515dfc6b5..a99f8df0e 100644 --- a/scripts/summary.in +++ b/scripts/summary.in @@ -18,55 +18,90 @@ # 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" +package summary; +use 5.006_000; use strict; -use Getopt::Long; +my ($log_date_pattern, $statsdir, $outputdir, $skip_time_steps, $startdate, + $enddate, $peer_dist_limit); -my $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d'; -my $statsdir = "/var/log/ntp"; # directory with input files -my $outputdir = "/tmp"; # directory for output files -my $skip_time_steps = 3600.0; # ignore time offsets larger that this -my $startdate = "19700101"; # first data file to use (YYYYMMDD) -my $enddate=`date -u +%Y%m%d`; chomp $enddate; --$enddate; -my $peer_dist_limit = 400.0; +exit run(@ARGV) unless caller; -my %options = ("directory|input-directory=s" => \$statsdir, - "output-directory=s" => \$outputdir, - "skip-time-steps:f" => \$skip_time_steps, - "start-date=s" => \$startdate, - "end-date=s" => \$enddate, - "peer-dist-limit=f" => \$peer_dist_limit); +sub run { + my $opts; + if (!processOptions(\@ARGV, $opts)) { + usage(1); + }; -if ( !GetOptions(%options) ) -{ - print STDERR "valid options for $0 are:\n"; - my $opt; - foreach $opt (sort(keys %options)) { - print STDERR "\t--$opt\t(default is "; - if ( ref($options{$opt}) eq "ARRAY" ) { - print STDERR join(", ", map { "'$_'" } @{$options{$opt}}); - } else { - print STDERR "'${$options{$opt}}'"; - } - print STDERR ")\n"; + $log_date_pattern = '[12]\d{3}[01]\d[0-3]\d'; + $statsdir = $opts->{directory}; + $outputdir = $opts->{'output-directory'}; + $skip_time_steps = $opts->{'skip-time-steps'}; + $startdate = $opts->{'start-date'}; + $enddate = $opts->{'end-date'}; + if (!$enddate){ + $enddate = `date -u +%Y%m%d`; + chomp $enddate; + --$enddate; } - print STDERR "\n"; - die; -} + $peer_dist_limit = $opts->{'peer-dist-limit'}; + + # check possibly current values of options + die "$statsdir: no such directory" unless (-d $statsdir); + die "$outputdir: no such directory" unless (-d $outputdir); + die "$skip_time_steps: skip-time-steps must be positive" + unless ($skip_time_steps >= 0.0); + die "$startdate: invalid start date|$`|$&|$'" + unless ($startdate =~ m/.*$log_date_pattern$/); + die "$enddate: invalid end date" + unless ($enddate =~ m/.*$log_date_pattern$/); + + $skip_time_steps = 0.128 if ($skip_time_steps == 0); + + my $loop_summary="$outputdir/loop_summary"; + my $peer_summary="$outputdir/peer_summary"; + my $clock_summary="$outputdir/clock_summary"; + my (@loopfiles, @peerfiles, @clockfiles); -# check possibly current values of options -die "$statsdir: no such directory" unless (-d $statsdir); -die "$outputdir: no such directory" unless (-d $outputdir); -die "$skip_time_steps: skip-time-steps must be positive" - unless ($skip_time_steps >= 0.0); -die "$startdate: invalid start date|$`|$&|$'" - unless ($startdate =~ m/.*$log_date_pattern$/); -die "$enddate: invalid end date" - unless ($enddate =~ m/.*$log_date_pattern$/); + print STDERR "Creating summaries from $statsdir ($startdate to $enddate)\n"; -$skip_time_steps = 0.128 if ($skip_time_steps == 0); + opendir SDIR, $statsdir or die "directory ${statsdir}: $!"; + rewinddir SDIR; + @loopfiles=sort grep /loop.*$log_date_pattern/, readdir SDIR; + rewinddir SDIR; + @peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR; + rewinddir SDIR; + @clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR; + closedir SDIR; + + # remove old summary files + for ($loop_summary, $peer_summary, $clock_summary) { unlink $_ if -f $_ }; + + my $date; + for (@loopfiles) { + $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; + if ($date ge $startdate && $date le $enddate) { + do_loop($statsdir, $_, $loop_summary); + } + } + + for (@peerfiles) { + $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; + if ($date ge $startdate && $date le $enddate) { + do_peer($statsdir, $_, $peer_summary); + } + } + + for (@clockfiles) { + $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; + if ($date ge $startdate && $date le $enddate) { + do_clock($statsdir, $_, $clock_summary); + } + } + + print STDERR "Creating peer summary with limit $peer_dist_limit\n"; + peer_summary($peer_summary) if (-f $peer_summary); +} sub min { @@ -328,46 +363,7 @@ sub peer_summary print sort @lines; } -my $loop_summary="$outputdir/loop_summary"; -my $peer_summary="$outputdir/peer_summary"; -my $clock_summary="$outputdir/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.*$log_date_pattern/, readdir SDIR; -rewinddir SDIR; -@peerfiles=sort grep /peer.*$log_date_pattern/, readdir SDIR; -rewinddir SDIR; -@clockfiles=sort grep /clock.*$log_date_pattern/, readdir SDIR; -closedir SDIR; - -# remove old summary files -map { unlink $_ if -f $_ } ($loop_summary, $peer_summary, $clock_summary); - -my $date; -map { - $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; - if ($date ge $startdate && $date le $enddate) { - do_loop $statsdir, $_, $loop_summary; - } -} @loopfiles; - -map { - $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; - if ($date ge $startdate && $date le $enddate) { - do_peer $statsdir, $_, $peer_summary; - } -} @peerfiles; - -map { - $date = $_; $date =~ s/.*($log_date_pattern)$/$1/; - if ($date ge $startdate && $date le $enddate) { - do_clock $statsdir, $_, $clock_summary; - } -} @clockfiles; +@summary_opts@ -print STDERR "Creating peer summary with limit $peer_dist_limit\n"; -peer_summary $peer_summary if (-f $peer_summary); +1; +__END__ diff --git a/scripts/t/1_common.t b/scripts/t/1_common.t new file mode 100644 index 000000000..9cd665c18 --- /dev/null +++ b/scripts/t/1_common.t @@ -0,0 +1,28 @@ +use strict; +use warnings; +use Test::More tests => 30; +use Test::Command; +use List::MoreUtils qw(any); + +my @script_list = qw( + calc_tickadj/calc_tickadj ntp-wait/ntp-wait ntpsweep/ntpsweep + ntptrace/ntptrace summary plot_summary +); + +for my $script (@script_list) { + fail("$script not a regular file") if (!-f $script); + pass("$script is a regular file"); + fail("$script not an executabe") if (!-x $script); + pass("$script is an executable"); + fail("$script is not readable") if (!-r $script); + pass("$script is readable"); + + TODO: { + #todo_skip "$script - Broken script/no perl", 2 + # if any { $script eq $_ } qw(freq_adj ntp-groper ntp-status plot_summary); + + require_ok("./$script"); + cmd_is("./$script --rubbish", '', qr/^Unknown option:/, 1, + "$script reports unkown option"); + } +} diff --git a/scripts/t/calc_tickadj.t b/scripts/t/calc_tickadj.t new file mode 100644 index 000000000..066fd390e --- /dev/null +++ b/scripts/t/calc_tickadj.t @@ -0,0 +1,90 @@ +use strict; +use warnings; +use Test::More tests => 21; +use Test::Util qw(check_help override); +use Test::Command; +use Test::Output; + +my $script_name = "calc_tickadj/calc_tickadj"; + +my @options = ( + [qw(drift-file=str d)], + [qw(tick=num t)] +); + +check_help($script_name, @options); + +require_ok("./$script_name"); + +BEGIN { + my ($tick, $drift, $drift_file, $open_fail); + + override('calc_tickadj', open => sub (*;@) { + return if $open_fail; + is($_[1], $drift_file) if defined $drift_file; + CORE::open $_[0], '<', \$drift; + }); + + override('calc_tickadj', readpipe => sub { + return $tick; + }); + + sub run_calc_tickadj { + my %opts = @_; + $open_fail = delete $opts{fail_open} || 0; + $drift = delete $opts{drift} || '0.00'; + $tick = delete $opts{tick} || ''; + $drift_file = delete $opts{drift_file}; + calc_tickadj::run(@{ delete $opts{opts} || [] }) + } +} + +my $ret; + +# Test tick detection +for ( + 'tick = 10000', + 'PRESET tick = 10000', + 'KERNEL tick = 10000', + ' tick = 10000', +) { + stdout_like( sub { $ret = run_calc_tickadj(tick => $_) }, qr/10000 usec; 10000000 nsec/ ); + ok(!$ret); +} + +# Test how the script copes if tickadj is not found/outputs invalid data +for (undef, "Invalid stuff") { + eval { + run_calc_tickadj(tick => $_); + } or do { + chomp $@; + is($@, 'Could not get tick value, try manually with -t/--tick') + }; + + # Still invalid data but pass tick as argument + stdout_like( sub { $ret = run_calc_tickadj(tick => $_, opts => [qw(-t 9999)]) }, qr/9999 usec; 9999000 nsec/); + ok(!$ret); +} + +# Test -d switch +stdout_like( + sub { $ret = run_calc_tickadj(drift_file => 'foobar', drift => '14.00', tick => 'tick = 10000', opts => [qw(-d foobar)]) }, + qr/14.000 \(drift\)/ +); +ok(!$ret); + +# Open on drift file fails +eval { + run_calc_tickadj(fail_open => 1, opts => [qw(-t 10000)]); +} or do { + chomp $@; + like($@, qr(Could not open)) +}; + +# Open succeeds but drift file is invalid +eval { + run_calc_tickadj(drift => 'NOTVALIDDRIFTVALUE', opts => [qw(-t 10000)]) +} or do { + chomp $@; + like($@, qr(Invalid drift file value )); +}; diff --git a/scripts/t/lib/Test/Command.pm b/scripts/t/lib/Test/Command.pm new file mode 100644 index 000000000..d2168205e --- /dev/null +++ b/scripts/t/lib/Test/Command.pm @@ -0,0 +1,261 @@ +package Test::Command; +use strict; +use warnings; +use Test::Builder; +use IPC::Open3; +use IO::Handle; +use Carp; +use base qw(Exporter); +our @EXPORT = qw(cmd_is start_command com_stdout_is com_stderr_is com_ret_is + com_slurp_stderr com_slurp_stdout wait_for_command); +our $VERSION = 0.1; + +my $Test = Test::Builder->new(); + +my ($pid, $writer_h, $stdout_h, $stderr_h); +my $in_command = 0; + +$SIG{CHLD} = sub { wait_for_command() if $in_command; }; + +sub start_command { + my (@cmd) = @_; + + croak "Specify a command to start_command" unless @cmd; + + ($pid, $writer_h, $stdout_h, $stderr_h) = + (0, IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); + + # Older IPC:Open3 just warns is there was an error + local $SIG{__WARN__} = sub { die @_ }; + + $in_command = 1; + $pid = open3($writer_h, $stdout_h, $stderr_h, @cmd); +} + +sub wait_for_command { + waitpid $pid, 0; + my $ret = $? >> 8; + if (com_slurp_stderr() =~ /^open3: (.+)$/) { + croak $1 + } + return $ret; +} + +sub com_ret_is { + my ($ex_ret, $descr) = @_; + + local $SIG{CHLD} = 'IGNORE'; + my $ret = wait_for_command; + $Test->ok(_test_ret($ret, $ex_ret), $descr); +} + +sub com_stdout_is { + my ($ex_stdout, $descr) = @_; + + my $stdout = com_slurp_stdout(); + $Test->ok(_test_string($stdout, $ex_stdout, 'STDOUT'), $descr); +} + +sub com_stderr_is { + my ($ex_stderr, $descr) = @_; + + my $stderr = com_slurp_stderr(); + $Test->ok(_test_string($stderr, $ex_stderr, 'STDERR'), $descr); +} + +{ + my $stderr; + sub com_slurp_stderr { + $stderr or $stderr = _slurp_fh($stderr_h); + } +} +{ + my $stdout; + sub com_slurp_stdout { + $stdout or $stdout = _slurp_fh($stdout_h); + } +} + +sub _test_one { + my ($s, $ex, $name) = @_; + my $diag = defined $ex && _test_string($s, $ex); + if ($diag) { + $Test->diag("$name doesn't match\n$diag\n"); + return 0; + } + return 1; +} + +sub cmd_is { + my ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr); + + if (ref $_[0] eq 'HASH') { + my %args = %{ $_[0] }; + ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr) = + @args{qw(run stdout stderr ret descr)}; + } + else { + ($cmd, $ex_stdout, $ex_stderr, $ex_ret, $descr) = @_; + } + + + local $SIG{CHLD} = 'DEFAULT'; + start_command($cmd); + + my $stdout = com_slurp_stdout; + my $stderr = $stderr_h ? com_slurp_stderr : ''; + my $ret = wait_for_command; + my $ok = 1; + + _test_string($stdout, $ex_stdout, 'STDOUT') or $ok = 0; + _test_string($stderr, $ex_stderr, 'STDERR') or $ok = 0; + _test_ret($ret, $ex_ret) or $ok = 0; + + $Test->ok($ok, $descr); +} + +sub _slurp_fh { + my $fh = shift; + do { local $/; <$fh> } +} + +sub _test_ret { + my ($got, $exp) = @_; + + return 1 if not defined $exp; + + if ($got != $exp) { + return $Test->diag(<diag(<($got))) { + return $Test->diag(<diag(< 1; + use Test::Command; + + cmd_is('ls', '.\n..', '', 0, "ls on empty directory"); + cmd_is({run => 'ls', stdout => '.\n..', ret => 0}); + + start_command('ls'); + ... + wait_for_command; + com_stdout_is('.\n..'); + +=head1 DESCRIPTION + +Test::Command provides a simple interface for testing stdout, stderr and return +value of an external command. + +=cut + +=head1 TESTS + +=over 4 + +=item B + + Runs command and tests for expected results. Pass undef in the parameter you + don't want to test for. + +=over 4 + +=item B - string of command tu run + +=item B - expected stdout + +=item B - expected stderr + +=item B - expected return value of a program + +=item B - diagnostic message printed if test failed + +=back + +=item B + +Start a command. Returns immediately and doesn't wait for command to finish. + +=item B + +Wait for command started with C to finish. Returns exit status +(return value of waipid) of the command. + +=item B + +=item B + +Slurp stdout/stderr of a process started with start_command and return it. + +=item B + +=item B + +Test stdout/stderr of the command started with start_command. This will block +until the process has closed its stdout or stderr + +=item B + +Test for expected return value of a command started with start_command. + +=back + +All parameters that represent expected value for stdout or stderr can be a +string, a compiled regexp or a code reference, which is passed the contents as +a first parameter and is expected to return true or false value. + +=head1 AUTHOR + +Oliver Kindernay + +=cut diff --git a/scripts/t/lib/Test/Mode6/Server.pm b/scripts/t/lib/Test/Mode6/Server.pm new file mode 100644 index 000000000..93c4c38e7 --- /dev/null +++ b/scripts/t/lib/Test/Mode6/Server.pm @@ -0,0 +1,66 @@ +package Test::Mode6::Server; +use strict; +use warnings; +use IO::Socket::INET; +use Test::Builder; +use NTP::Mode6::Packet; +use Carp; +use Exporter qw(import); +use Errno qw(EINTR); +use Devel::Hexdump qw(xd); +our @EXPORT = qw(start_mode6_server expect_mode6_command stop_mode6_server); + +my $socket; +my $Test = Test::Builder->new(); + +sub start_mode6_server { + $socket = IO::Socket::INET->new( + Proto => 'udp', + LocalPort => 123, + ) or croak "Could not create UDP socket on port 123: $!"; + $Test->diag("NTP control server started"); +} + +sub _diag_and_fail { + $Test->diag(shift); + $Test->ok(0, shift); + 0; +} + +sub expect_mode6_command { + my ($ex_packet, $respond, $descr) = @_; + + return _diag_and_fail('Not a CODE reference', $descr) + if ref $respond ne 'CODE'; + + my $remote_addr = $socket->recv(my $in_msg = '', 4096, 0); + if (!$remote_addr) { + _diag_and_fail("Error in recv: $!", $descr) if $! != EINTR; + return; + } + + my $recv_packet = NTP::Mode6::Packet->new(); + $recv_packet->decode($in_msg); + my $diag = $recv_packet->eq($ex_packet); + if ($diag) { + _diag_and_fail("Packets don't match\n$diag", $descr); + return; + } + + my $resp_packet = $respond->($recv_packet); + return _diag_and_fail('reponse sub didn\'t return NTP::Mode6::Packet object', $descr) + if ref $resp_packet ne 'NTP::Mode6::Packet'; + + my $out_msg = $resp_packet->encode; + $socket->send($out_msg, 0, $remote_addr) == length $out_msg + or return _diag_and_fail("Error sending response packet: $!", $descr); + + $Test->ok(1, $descr); +} + +sub stop_mode6_server { + close $socket; + $Test->diag("NTP control server stopped"); +} + +1; diff --git a/scripts/t/lib/Test/Util.pm b/scripts/t/lib/Test/Util.pm new file mode 100644 index 000000000..ea10a1101 --- /dev/null +++ b/scripts/t/lib/Test/Util.pm @@ -0,0 +1,167 @@ +package Test::Util; +use strict; +use warnings; +use Test::Command; +use Test::More; +use File::Basename qw(basename); +use Exporter qw(import); +use Carp qw(croak); +use Scalar::Util qw(set_prototype); +use Sub::Override; + +our @EXPORT_OK = qw(check_help override call_orig mock_ntp_util run_mocked); + +our $DIAG = 0; +sub cdiag { diag @_ if $DIAG } + +sub check_help { + my ($script_path, @options) = @_; + my $script_name = basename($script_path); + + cmd_is("$script_path -?", '', + sub { + my $help = shift; + my $i; + + cdiag "Got help '$help'"; + + $help =~ /^$script_name\ [^\n]+\nUSAGE:\ $script_name\ + \[\ -\ \[\]\ \|\ --\[{=\|\ }\]\ \] + /x or return 0; + + cdiag "Usage line OK"; + + for (@options, [qw(help ?)], [qw(more-help)]) { + my ($long, $short) = @$_; + my $o = $short ? "-$short, --$long" : " , --$long"; + + cdiag "Testing for $o\n"; + + return 0 if $help !~ /$o/; + } + + cdiag "Options OK"; + + $help =~ <<'END' or return 0; +Options are specified by doubled hyphens and their name or by a single +hyphen and the flag character.$ +END + cdiag "Bottom line OK"; + + return 1; + }, 0, "help's ok"); +} + +my $override = Sub::Override->new; + +{ + my (@vars, @peers, @hosts, @offset_stratums); + + sub mock_ntp_util { + my ($package, %mock) = @_; + + $override->replace($package.'::ntp_read_vars' => sub { + return undef if !@vars; + croak 'vars elements should be HASH refs' + if ref $vars[0] ne 'HASH'; + return +{ %{ shift @vars } }; + }) if exists $mock{read_vars}; + + $override->replace($package.'::ntp_peers' => sub { + return () if !@peers; + croak 'peer elements should be ARRAY refs' + if ref $peers[0] ne 'ARRAY'; + return @{ shift @peers } + }) if exists $mock{peers}; + + $override->replace($package.'::do_dns' => sub { + return shift @hosts + }) if exists $mock{dns}; + + $override->replace($package.'::ntp_sntp_line' => sub { + return () if !@offset_stratums; + croak 'offset_stratums elements should be ARRAY refs' + if ref $offset_stratums[0] ne 'ARRAY'; + return @{ shift @offset_stratums }; + }) if exists $mock{sntp_line}; + } + + sub run_mocked { + my %opts = @_; + + for (qw(vars peers hosts offset_stratums)) { + croak "$_ is not an ARRAY reference" if exists $opts{$_} && ref $opts{$_} ne 'ARRAY'; + } + croak "run is not a CODE reference" if ref $opts{run} ne 'CODE'; + + @vars = @{ $opts{vars} } if defined $opts{vars}; + @peers = @{ $opts{peers}} if defined $opts{peers}; + @hosts = @{ $opts{hosts}} if defined $opts{hosts}; + @offset_stratums = @{ $opts{offset_stratums} } if defined $opts{offset_stratums}; + $opts{run}->(); + } +} + +sub override { + my ($package, $subname, $code) = @_; + + croak 'not a CODE reference' if ref $code ne 'CODE'; + + my $proto = prototype $code; + my $override = set_prototype(sub { + my $caller = caller; + if ($caller eq $package) { + return $code->(@_); + } + else { + call_orig($package, $subname); + } + }, $proto); + + { + no strict 'refs'; + *{"CORE::GLOBAL::".${subname}} = $override; + } +} + +sub call_orig { + my ($pkg, $name) = (shift, shift); + # TODO: cache this? + _make_helper($pkg, $name)->(@_); +} + + +# Doing this right would require some hairy code and a lot more work (just look +# at Fatal.pm) Since we just want to override open sleep and few other builtins +# this is sufficient. +my %orig_helpers = ( + sleep => 'return CORE::sleep $_[0]', + open => ' + no strict q(refs); + if (@_ == 1) { + return CORE::open($_[0]); + } + elsif (@_ == 2) { + return CORE::open($_[0], $_[1]); + } + elsif (@_ >= 3) { + return CORE::open($_[0], $_[1], @_[2 .. $#_]); + } + ', +); + +# This a helper for the override sub, it generates sub that provides the +# original package context +sub _make_helper { + my ($pkg, $name) = @_; + my $sub = eval " + sub { + package $pkg; + $orig_helpers{$name} + } + "; + die "Eval failed for ${pkg}: $@" unless $sub; + return $sub; +} + +1; diff --git a/scripts/t/ntp-wait.t b/scripts/t/ntp-wait.t new file mode 100644 index 000000000..7a01b1242 --- /dev/null +++ b/scripts/t/ntp-wait.t @@ -0,0 +1,142 @@ +use strict; +use warnings; +use Test::More tests => 22; +use Test::Command; +use Test::Output; +use Test::Util qw(check_help mock_ntp_util run_mocked override); + +my $script_name = 'ntp-wait/ntp-wait'; + +require_ok($script_name); + +my @options = ( + [qw(tries=num n)], + [qw(sleep=num s)], + [qw(verbose v)], +); +check_help("./$script_name", @options); + +mock_ntp_util('ntp_wait', read_vars => 1); + +{ + my $sleep_for; + + BEGIN { + override('ntp_wait', sleep => sub { + is(shift, $sleep_for, 'sleep with the right value'); + }); + } + + sub run_ntp_wait { + my (%opts) = @_; + $sleep_for = delete $opts{sleep_for} || 0; + run_mocked(run => sub { ntp_wait::run(@{ delete $opts{opts} || [] }) }, %opts); + } +} + +my $ret; + +my %vars = ( + status_line => { + leap => 'leap_alarm', + } +); + +stdout_like( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) }, + qr/ntpd did not synchronize/, + 'failed to synchronize in time with leap_alarm' +); +is($ret, 1, 'fails when failed to synchronize with leap_alarm'); + +%vars = ( + status_line => { + leap => 'sync_alarm', + } +); + +stdout_like( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) }, + qr/ntpd did not synchronize/, + 'failed to synchronize in time with sync_alarm' +); +is($ret, 1, 'fails when failed to synchronize with sync_alarm'); + +stdout_like( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) }, + qr/ntpd did not synchronize/, + 'failed to synchronize in time with -v' +); +is($ret, 1, 'fails when failed to synchronize with -v'); + + +%vars = ( + status_line => { + leap => 'leap_bogus', + } +); + +stdout_like( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 2 -s 0 -v)]) }, + qr/Unexpected 'leap' status /, + 'prints debug information on invalid leap status' +); +is($ret, 1, 'fails on invalid leap status'); + + +stdout_like( + sub { $ret = run_ntp_wait(vars => [{}], opts => [qw(-n 2 -s 0 -v)]) }, + qr/Leap status not avalaible/, + 'print debug information when no leap returned' +); +is($ret, 1, 'fails if not leap returned'); + +%vars = ( + status_line => { + leap => 'leap_none', + } +); + +stdout_is( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0)]) }, + '', + 'prints nothing if OK' +); +is($ret, 0, 'succeeds if OK'); + +stdout_like( + sub { $ret = run_ntp_wait(vars => [\%vars], opts => [qw(-n 1 -s 0 -v)]) }, + qr/OK/, + 'prints OK if OK and -v' +); +is($ret, 0, 'succeeds if OK'); + +#Test the -n switch + +my @vars = ( + { + status_line => { + leap => 'leap_alarm', + } + }, + { + status_line => { + leap => 'leap_none', + } + }, +); +stdout_like( + sub { $ret = run_ntp_wait(vars => \@vars, opts => [qw(-n 2 -s 0 -v)]) }, + qr/OK/, + '-n switch works' +); +is($ret, 0, 'succeeds if OK the second time'); + +# Test the -s switch + +stdout_like( + sub { $ret = run_ntp_wait(vars => \@vars, opts => [qw(-n 2 -s 10 -v)], sleep_for => 10) }, + qr/OK/, + '-s switch works' +); +is($ret, 0, 'suceeds if OK and -s'); diff --git a/scripts/t/ntpsweep.t b/scripts/t/ntpsweep.t new file mode 100644 index 000000000..be974827b --- /dev/null +++ b/scripts/t/ntpsweep.t @@ -0,0 +1,117 @@ +use strict; +use warnings; +use Test::More; +use Test::Util qw(check_help override mock_ntp_util run_mocked); +use Test::Output; +use Sub::Override; + +my $script_name = "ntpsweep/ntpsweep"; + +my @options = ( + [qw(host-list=str l)], + [qw(peers p)], + [qw(maxlevel=num m)], + [qw(strip=str s)], +); + +require "./$script_name"; + +check_help("./$script_name", @options); + +mock_ntp_util('ntpsweep', read_vars => 1, peers => 1, sntp_line => 1, dns => 1); + +my $header = <<'HEADER'; +Host st offset(s) version system processor +--------------------------------+--+---------+-----------+------------+--------- +HEADER + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }) }, + $header."localhost ?\n", + 'no information about host' +); + +# Test deprecated -h switch +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-h localhost)) }) }, + $header."localhost ?\n", + '-h switch works' +); + +my @vars = ({ + processor => 'i686', + system => 'Linux/2.6.37.6-smp', + daemon_version => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)', +}); + +my @offset_stratum = (['0.00', 3]); + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }, offset_stratums => \@offset_stratum ) }, + $header.'localhost 3 0.000'.(' 'x26)."\n", + 'one host ok but no variables' +); + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost)) }, offset_stratums => \@offset_stratum, vars => \@vars ) }, + $header."localhost 3 0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n", + 'one host with ok variables' +); + +## Add one peer +my @peers = ([{ remote => 'pieskovisko.sk' }]); +push @offset_stratum, ['0.01', 2]; +push @vars, { + processor => 'i686', + system => 'Bungalanga OS', + daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)', +}; + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) }, + $header."localhost (1) 3 0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n" + ." pieskovisko.sk (0) 2 0.010 4.1.5p3\@1.2 Bungalanga O i686\n", + 'one peer' +); + +# Add another peer +push @peers, [{ remote => 'nic.nz' }]; +push @offset_stratum, ['0.04', 1]; +push @vars, { + processor => 'i686', + system => 'NIC OS', + daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)', +}; + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p -m 1)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) }, + $header."localhost (1) 3 0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n" + ." pieskovisko.sk (1) 2 0.010 4.1.5p3\@1.2 Bungalanga O i686\n", + 'one peer twice but -m 1' +); + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) }, + $header."localhost (1) 3 0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n" + ." pieskovisko.sk (1) 2 0.010 4.1.5p3\@1.2 Bungalanga O i686\n" + ." nic.nz (0) 1 0.040 4.1.5p3\@1.2 NIC OS i686\n", + 'one peer twice' +); + +push @peers, [{ remote => 'localhost' }]; +push @offset_stratum, ['0.04', 1]; +push @vars, { + processor => 'i686', + system => 'NIC OS', + daemon_version => 'ntpd 4.1.5p3@1.2-o Wed Jan 26 04:19:40 UTC 2011 (1)', +}; + +stdout_is( + sub { run_mocked(run => sub { ntpsweep::run(qw(-l localhost -p)) }, offset_stratums => \@offset_stratum, vars => \@vars, peers => \@peers) }, + $header."localhost (1) 3 0.000 4.2.6p3\@1.2 Linux/2.6.37 i686\n" + ." pieskovisko.sk (1) 2 0.010 4.1.5p3\@1.2 Bungalanga O i686\n" + ." nic.nz (1) 1 0.040 4.1.5p3\@1.2 NIC OS i686\n" + ." = localhost".(' 'x18)."\n", + 'loop detection' +); +done_testing; diff --git a/scripts/t/ntptrace.t b/scripts/t/ntptrace.t new file mode 100644 index 000000000..f471beb99 --- /dev/null +++ b/scripts/t/ntptrace.t @@ -0,0 +1,154 @@ +use strict; +use warnings; +use Test::More tests => 6; +use Test::Command; +use Test::Output; +use Test::Util qw(check_help mock_ntp_util run_mocked); +use Sub::Override; + +my $script_name = 'ntptrace/ntptrace'; + +my @options = ( + [qw(numeric n)], + [qw(max-hosts=num m)], + [qw(host=str r)], +); +check_help($script_name, @options); + +require "./$script_name"; + +mock_ntp_util('ntptrace', read_vars => 1, dns => 1); + +my @vars = ({ + version => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)', + processor => 'i686', + system => 'Linux/2.6.37.6-smp', + leap => '00', + stratum => '3', + precision => '-21', + rootdelay => '38.998', + rootdisp => '94.413', + refid => '92.240.244.202', + reftime => 'd5842335.00c8980f Sun Jul 7 2013 19:26:13.003', + clock => 'd5842501.ee752d06 Sun Jul 7 2013 19:33:53.931', + peer => '848', + tc => '9', + mintc => '3', + offset => '-23.168', + frequency => '-10.213', + sys_jitter => '14.724', + clk_jitter => '14.754', + clk_wander => '3.847', +}); + +stdout_is( + sub { run_mocked(run => sub { ntptrace::run() }, vars => [@vars,{}], hosts => [qw(localhost)]) }, + "localhost: stratum 3, offset -0.023168, synch distance 0.113912\n", + 'traces single host' +); + +stdout_is( + sub { run_mocked(run => sub { ntptrace::run(qw(-n)) }, vars => [@vars,{}]) }, + "127.0.0.1: stratum 3, offset -0.023168, synch distance 0.113912\n", + '-n switch works, localhost is default' +); + +#Add response to a rv peer +push @vars, { + srcadr => '1.2.3.4', + srcport => '123', + dstadr => '192.168.1.11', + dstport => '123', + leap => '00', + stratum => '2', + precision => '-22', + rootdelay => '5.142', + rootdisp => '30.884', + refid => '195.146.149.222', + reftime => 'd584299f.8904b565 Sun Jul 7 2013 19:53:35.535', + rec => 'd5842d6b.02efea46 Sun Jul 7 2013 20:09:47.011', + reach => '377', + unreach => '0', + hmode => '3', + pmode => '4', + hpoll => '9', + ppoll => '9', + headway => '0', + flash => '00 ok', + keyid => '0', + offset => '-24.318', + delay => '33.499', + dispersion => '20.588', + jitter => '39.916', + xleave => '0.031', + filtdelay => ' 42.35 93.96 235.47 33.50 34.31 33.90 37.25 34.97', + filtoffset => ' -21.46 4.17 77.27 -24.32 -23.48 -23.17 -21.27 -25.42', + filtdisp => ' 0.00 8.01 15.78 23.54 31.25 39.21 47.24 55.23', +}; + +# Add response from the peer +push @vars, { + version => 'ntpd 4.2.4p8@1.1612-o Tue Apr 19 07:08:29 UTC 2011 (1)', + processor => "i686", + system => "Linux/2.6.38-10-generic-pae", + leap => '00', + stratum => '1', + precision => '-20', + rootdelay => '0.000', + rootdisp => '0.348', + peer => '41683', + refid => 'CDMA', + reftime => 'd58443c5.cc475b06 Sun Jul 7 2013 19:45:09.797', + poll => '5', + clock => 'd58443cd.079e0276 Sun Jul 7 2013 19:45:17.029', + state => '4', + offset => '0.000', + frequency => '-21.795', + jitter => '0.001', + noise => '0.005', + stability => '0.000', + tai => '0', +}; + +stdout_is( + sub { run_mocked(run => sub { ntptrace::run() }, vars => \@vars, hosts => [qw(localhost kenny.oneemedia.com)]) }, + <<'END', +localhost: stratum 3, offset -0.023168, synch distance 0.113912 +kenny.oneemedia.com: stratum 1, offset 0.000000, synch distance 0.000348, refid 'CDMA' +END + 'traced a peer up to stratum 1' +); + +stdout_is( + sub { run_mocked(run => sub { ntptrace::run(qw(-m 1)) }, vars => \@vars, hosts => [qw(localhost)]) }, + "localhost: stratum 3, offset -0.023168, synch distance 0.113912\n", + '-m switch works' +); + +splice @vars, 0, 1, { + version => 'ntpd 4.2.6p3@1.2290-o Wed Jan 26 04:19:40 UTC 2011 (1)', + processor => "i686", + system => "Linux/2.6.37.6-smp", + leap => '00', + stratum => '3', + precision => '-21', + rootdelay => '38.998', + rootdisp => '94.413', + refid => '127.127.0.1', + reftime => 'd5842335.00c8980f Sun Jul 7 2013 19:26:13.003', + clock => 'd5842501.ee752d06 Sun Jul 7 2013 19:33:53.931', + peer => '848', + tc => '9', + mintc => '3', + offset => '-23.168', + frequency => '-10.213', + sys_jitter => '14.724', + clk_jitter => '14.754', + clk_wander => '3.847', +}; + +stdout_is( + sub { run_mocked(run => sub { ntptrace::run() }, vars => \@vars) }, + "127.0.0.1: stratum 3, offset -0.023168, synch distance 0.113912\n", + 'stopped on 127.127 refid' +); diff --git a/scripts/t/plot_summary.t b/scripts/t/plot_summary.t new file mode 100644 index 000000000..7941a1699 --- /dev/null +++ b/scripts/t/plot_summary.t @@ -0,0 +1,17 @@ +use strict; +use warnings; +use Test::More tests => 1; +use Test::Util qw(check_help); + +my $script_name = "plot_summary"; + +my @options = ( + [qw(directory=str)], + [qw(identifier=str)], + [qw(output-file=str)], + [qw(plot-term=str)], + [qw(dont-wait)], + [qw(offset-limit=float)], +); + +check_help("./$script_name", @options); diff --git a/scripts/t/summary.t b/scripts/t/summary.t new file mode 100644 index 000000000..516dc87b9 --- /dev/null +++ b/scripts/t/summary.t @@ -0,0 +1,17 @@ +use strict; +use warnings; +use Test::More tests => 1; +use Test::Util qw(check_help); + +my $script_name = "summary"; + +my @options = ( + [qw(directory=str)], + [qw(end-date=num)], + [qw(output-directory=str)], + [qw(peer-dist-limit=float)], + [qw(skip-time-steps=float)], + [qw(start-date=num)], +); + +check_help("./$script_name", @options); diff --git a/scripts/t/util.t b/scripts/t/util.t new file mode 100644 index 000000000..115408645 --- /dev/null +++ b/scripts/t/util.t @@ -0,0 +1,179 @@ +use strict; +use warnings; +use Test::More tests => 12; +use Test::Util qw(override); + +BEGIN { + my %test; + + override('NTP::Util', open => sub { + is($_[1], $test{open_str}, 'open with right string') + if defined $test{open_str}; + return if $test{fail_open}; + open $_[0], '<', \$test{output}; + }); + + sub test_read_vars { + my (%opts) = @_; + _parse_opts(%opts); + ntp_read_vars(@{ delete $opts{opts} || []}); + } + + sub test_peers { + my (%opts) = @_; + _parse_opts(%opts); + ntp_peers(@{ delete $opts{opts} || []}); + } + + sub test_sntp_line { + my (%opts) = @_; + _parse_opts(%opts); + ntp_sntp_line(@{ delete $opts{opts} || []}); + } + + sub _parse_opts { + my (%opts) = @_; + $test{fail_open} = delete $opts{fail_open} || 0; + $test{output} = delete $opts{output} || '' ; + $test{open_str} = delete $opts{open_str}; + } +} + +require_ok 'NTP::Util'; +NTP::Util->import(qw(ntp_read_vars ntp_peers ntp_sntp_line)); + +eval { + test_read_vars(opts => [0, []], fail_open => 1, + open_str => "ntpq -n -c 'rv 0 ' |"); +} or do { + like($@, qr/Could not start ntpq: /, 'ntp_read_vars dies on failed execution'); +}; + +is( + test_read_vars(opts => [0, []], output => 'Connection refused'), + undef, + 'returns undef when connection refused' +); + +is_deeply( + test_read_vars(opts => [0, [qw(var1 var2 var_3 rootdisp offset)]], + output => "var1=foo, var_3=\"bar\"\nphase=1.00, rootdispersion=4.00\n"), + { + var1 => 'foo', + var_3 => 'bar', + var2 => undef, + offset => '1.00', + rootdisp => '4.00', + }, + 'variables correctly parsed' +); + +is_deeply( + test_read_vars(opts => [0, []], + output => <<'VAR_END'), +associd=0 status=c012 leap_alarm, sync_unspec, 1 event, freq_set, +var1=foo, var_3="bar" +phase=1.00, rootdispersion=4.00 +VAR_END + { + var1 => 'foo', + var_3 => 'bar', + offset => '1.00', + rootdisp => '4.00', + + status_line => { + leap => 'leap_alarm', + sync => 'sync_unspec', + status => 'c012' + } + }, + 'variables correctly parsed' +); + +eval { + test_peers(opts => ['localhost'], fail_open => 1, + open_str => "ntpq -np localhost |"); +} or do { + like($@, qr/Could not start ntpq: /, 'ntp_peers dies on failed execution'); +}; + +is_deeply( + test_peers(opts => ['localhost'], + output => <<'PEER_END'), + remote refid st t when poll reach delay offset jitter +============================================================================== ++194.160.23.2 .GPS. 1 u 188 1024 377 0.290 0.051 0.058 +-217.31.205.226 195.113.144.201 2 u 227 1024 377 6.092 -0.372 0.064 + 147.231.19.43 .INIT. 16 u - 1024 0 0.000 0.000 4000.00 +*195.113.144.201 .GPS. 1 u 211 1024 377 5.817 0.023 0.054 +PEER_END + [ + { + 'when' => '188', + 'reach' => '377', + 'delay' => '0.290', + 'st' => '1', + 'remote' => '+194.160.23.2', + 'poll' => '1024', + 'jitter' => '0.058', + 'refid' => '.GPS.', + 't' => 'u', + 'offset' => '0.051' + }, + { + 'when' => '227', + 'reach' => '377', + 'delay' => '6.092', + 'st' => '2', + 'remote' => '-217.31.205.226', + 'poll' => '1024', + 'jitter' => '0.064', + 'refid' => '195.113.144.201', + 't' => 'u', + 'offset' => '-0.372' + }, + { + 'when' => '-', + 'reach' => '0', + 'delay' => '0.000', + 'st' => '16', + 'remote' => ' 147.231.19.43', + 'poll' => '1024', + 'jitter' => '4000.00', + 'refid' => '.INIT.', + 't' => 'u', + 'offset' => '0.000' + }, + { + 'when' => '211', + 'reach' => '377', + 'delay' => '5.817', + 'st' => '1', + 'remote' => '*195.113.144.201', + 'poll' => '1024', + 'jitter' => '0.054', + 'refid' => '.GPS.', + 't' => 'u', + 'offset' => '0.023' + } + ], + 'peers correctly parsed' +); + +eval { + test_sntp_line(opts => ['localhost'], fail_open => 1, + open_str => "sntp localhost |"); +} or do { + like($@, qr/Could not start sntp: /, 'ntp_sntp_line dies on failed execution'); +}; + +is_deeply( + [test_sntp_line(opts => ['localhost'], + output => <<'SNTP_END')], +sntp 4.2.7p379@1.2946-o Tue Aug 27 18:55:18 UTC 2013 (4) +Can't open KOD db file /var/db/ntp-kod for writing: Permission denied +2013-09-19 22:49:29.381062 (-0100) +0.000007 +/- 0.077138 localhost 127.0.0.1 s4 +SNTP_END + ['+0.000007', '4'], + 'stratum and offset parsed' +); diff --git a/sntp/Makefile.am b/sntp/Makefile.am index dcc78c077..bc0dcd411 100644 --- a/sntp/Makefile.am +++ b/sntp/Makefile.am @@ -176,7 +176,7 @@ $(PROGRAMS): version.o ## non-file "-levent_core". version.c: $(sntp_OBJECTS) ../libntp/libntp.a Makefile $(srcdir)/scm-rev - env CSET=`cat $(srcdir)/scm-rev` $(top_builddir)/../scripts/mkver sntp + env CSET=`cat $(srcdir)/scm-rev` $(top_builddir)/../scripts/build/mkver sntp version.o: version.c env CCACHE_DISABLE=1 $(COMPILE) -c version.c -o version.o @@ -212,15 +212,15 @@ $(srcdir)/scm-rev: $(srcdir)/m4/version.m4: $(srcdir)/../packageinfo.sh TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \ - ../scripts/genver m4/version.m4 + ../scripts/build/genver m4/version.m4 $(srcdir)/include/version.def: $(srcdir)/../packageinfo.sh TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \ - ../scripts/genver include/version.def + ../scripts/build/genver include/version.def $(srcdir)/include/version.texi: $(srcdir)/../packageinfo.sh TEMPDIR=`pwd` && export TEMPDIR && cd $(srcdir) && \ - ../scripts/genver include/version.texi + ../scripts/build/genver include/version.texi $(srcdir)/../COPYRIGHT: cd .. && $(MAKE) $(AM_MAKEFLAGS) COPYRIGHT-please @@ -276,7 +276,7 @@ $(srcdir)/invoke-sntp.menu: $(srcdir)/invoke-sntp.texi $(srcdir)/invoke-sntp.texi: $(srcdir)/sntp-opts.def $(std_def_list) $(run_ag) -Tagtexi-cmd.tpl -DLEVEL=section sntp-opts.def - $(top_srcdir)/../scripts/check--help $@ + $(top_srcdir)/../scripts/build/check--help $@ $(srcdir)/sntp.html: $(srcdir)/invoke-sntp.menu $(srcdir)/invoke-sntp.texi $(srcdir)/sntp.texi $(srcdir)/include/version.texi cd $(srcdir) && ( makeinfo --force --html --no-split -o sntp.html sntp.texi || true ) diff --git a/sntp/ag-tpl/Mdoc.pm b/sntp/ag-tpl/Mdoc.pm new file mode 100644 index 000000000..08db2fca2 --- /dev/null +++ b/sntp/ag-tpl/Mdoc.pm @@ -0,0 +1,515 @@ +=head1 NAME + +Mdoc - perl module to parse Mdoc macros + +=head1 SYNOPSIS + + use Mdoc qw(ns pp soff son stoggle mapwords); + +See mdoc2man and mdoc2texi for code examples. + +=head1 FUNCTIONS + +=over 4 + +=item def_macro( NAME, CODE, [ raw => 1, greedy => 1, concat_until => '.Xx' ] ) + +Define new macro. The CODE reference will be called by call_macro(). You can +have two distinct definitions for and inline macro and for a standalone macro +(i. e. 'Pa' and '.Pa'). + +The CODE reference is passed a list of arguments and is expected to return list +of strings and control characters (see C). + +By default the surrouding "" from arguments to macros are removed, use C +to disable this. + +Normaly CODE reference is passed all arguments up to next nested macro. Set +C to to pass everything up to the end of the line. + +If the concat_until is present, the line is concated until the .Xx macro is +found. For example the following macro definition + + def_macro('.Oo', gen_encloser(qw([ ]), concat_until => '.Oc' } + def_macro('.Cm', sub { mapwords {'($_)'} @_ } } + +and the following input + + .Oo + .Cm foo | + .Cm bar | + .Oc + +results in [(foo) | (bar)] + +=item get_macro( NAME ) + +Returns a hash reference like: + + { run => CODE, raw => [1|0], greedy => [1|0] } + +Where C is the CODE reference used to define macro called C + +=item parse_line( INPUT, OUTPUT_CODE, PREPROCESS_CODE ) + +Parse a line from the C filehandle. If a macro was detected it returns a +list (MACRO_NAME, @MACRO_ARGS), otherwise it calls the C, giving +caller a chance to modify line before printing it. If C is +defined it calls it prior to passing argument to a macro, giving caller a +chance to alter them. if EOF was reached undef is returned. + +=item call_macro( MACRO, ARGS, ... ) + +Call macro C with C. The CODE reference for macro C is +called and for all the nested macros. Every called macro returns a list which +is appended to return value and returned when all nested macros are processed. +Use to_string() to produce a printable string from the list. + +=item to_string ( LIST ) + +Processes C returned from call_macro() and returns formatted string. + +=item mapwords BLOCK ARRAY + +This is like perl's map only it calls BLOCK only on elements which are not +punctuation or control characters. + +=item space ( ['on'|'off] ) + +Turn spacing on or off. If called without argument it returns the current state. + +=item gen_encloser ( START, END ) + +Helper function for generating macros that enclose their arguments. + gen_encloser(qw({ })); +returns + sub { '{', ns, @_, ns, pp('}')} + +=item set_Bl_callback( CODE , DEFS ) + +This module implements the Bl/El macros for you. Using set_Bl_callback you can +provide a macro definition that should be executed on a .Bl call. + +=item set_El_callback( CODE , DEFS ) + +This module implements the Bl/El macros for you. Using set_El_callback you can +provide a macro definition that should be executed on a .El call. + +=item set_Re_callback( CODE ) + +The C is called after a Rs/Re block is done. With a hash reference as a +parameter, describing the reference. + +=back + +=head1 CONSTANTS + +=over 4 + +=item ns + +Indicate 'no space' between to members of the list. + +=item pp ( STRING ) + +The string is 'punctuation point'. It means that every punctuation +preceeding that element is put behind it. + +=item soff + +Turn spacing off. + +=item son + +Turn spacing on. + +=item stoggle + +Toogle spacing. + +=item hs + +Print space no matter spacing mode. + +=back + +=head1 TODO + +* The concat_until only works with standalone macros. This means that + .Po blah Pc +will hang until .Pc in encountered. + +* Provide default macros for Bd/Ed + +* The reference implementation is uncomplete + +=cut + +package Mdoc; +use strict; +use warnings; +use List::Util qw(reduce); +use Text::ParseWords qw(quotewords); +use Carp; +use Exporter qw(import); +our @EXPORT_OK = qw(ns pp soff son stoggle hs mapwords gen_encloser nl); + +use constant { + ns => ['nospace'], + soff => ['spaceoff'], + son => ['spaceon'], + stoggle => ['spacetoggle'], + hs => ['hardspace'], +}; + +sub pp { + my $c = shift; + return ['pp', $c ]; +} +sub gen_encloser { + my ($o, $c) = @_; + return sub { ($o, ns, @_, ns, pp($c)) }; +} + +sub mapwords(&@) { + my ($f, @l) = @_; + my @res; + for my $el (@l) { + local $_ = $el; + push @res, $el =~ /^(?:[,\.\{\}\(\):;\[\]\|])$/ || ref $el eq 'ARRAY' ? + $el : $f->(); + } + return @res; +} + +my %macros; + +############################################################################### + +# Default macro definitions start + +############################################################################### + +def_macro('Xo', sub { @_ }, concat_until => '.Xc'); + +def_macro('.Ns', sub {ns, @_}); +def_macro('Ns', sub {ns, @_}); + +{ + my %reference; + def_macro('.Rs', sub { () } ); + def_macro('.%A', sub { + if ($reference{authors}) { + $reference{authors} .= " and @_" + } + else { + $reference{authors} = "@_"; + } + return (); + }); + def_macro('.%T', sub { $reference{title} = "@_"; () } ); + def_macro('.%O', sub { $reference{optional} = "@_"; () } ); + + sub set_Re_callback { + my ($sub) = @_; + croak 'Not a CODE reference' if not ref $sub eq 'CODE'; + def_macro('.Re', sub { + my @ret = $sub->(\%reference); + %reference = (); @ret + }); + return; + } +} + +def_macro('.Bl', sub { die '.Bl - no list callback set' }); +def_macro('.It', sub { die '.It called outside of list context' }); +def_macro('.El', sub { die '.El requires .Bl first' }); + + +{ + my $elcb = sub { () }; + + sub set_El_callback { + my ($sub) = @_; + croak 'Not a CODE reference' if ref $sub ne 'CODE'; + $elcb = $sub; + return; + } + + sub set_Bl_callback { + my ($blcb, %defs) = @_; + croak 'Not a CODE reference' if ref $blcb ne 'CODE'; + def_macro('.Bl', sub { + + my $orig_it = get_macro('.It'); + my $orig_el = get_macro('.El'); + my $orig_bl = get_macro('.Bl'); + my $orig_elcb = $elcb; + + # Restore previous .It and .El on each .El + def_macro('.El', sub { + def_macro('.El', delete $orig_el->{run}, %$orig_el); + def_macro('.It', delete $orig_it->{run}, %$orig_it); + def_macro('.Bl', delete $orig_bl->{run}, %$orig_bl); + my @ret = $elcb->(@_); + $elcb = $orig_elcb; + @ret + }); + $blcb->(@_) + }, %defs); + return; + } +} + +def_macro('.Sm', sub { + my ($arg) = @_; + if (defined $arg) { + space($arg); + } else { + space() eq 'off' ? + space('on') : + space('off'); + } + () +} ); +def_macro('Sm', do { my $off; sub { + my ($arg) = @_; + if (defined $arg && $arg =~ /^(on|off)$/) { + shift; + if ($arg eq 'off') { soff, @_; } + elsif ($arg eq 'on') { son, @_; } + } + else { + stoggle, @_; + } +}} ); + +############################################################################### + +# Default macro definitions end + +############################################################################### + +sub def_macro { + croak 'Odd number of elements for hash argument <'.(scalar @_).'>' if @_%2; + my ($macro, $sub, %def) = @_; + croak 'Not a CODE reference' if ref $sub ne 'CODE'; + + $macros{ $macro } = { + run => $sub, + greedy => delete $def{greedy} || 0, + raw => delete $def{raw} || 0, + concat_until => delete $def{concat_until}, + }; + if ($macros{ $macro }{concat_until}) { + $macros{ $macros{ $macro }{concat_until} } = { run => sub { @_ } }; + $macros{ $macro }{greedy} = 1; + } + return; +} + +sub get_macro { + my ($macro) = @_; + croak "Macro <$macro> not defined" if not exists $macros{ $macro }; + +{ %{ $macros{ $macro } } } +} + +#TODO: document this +sub parse_opts { + my %args; + my $last; + for (@_) { + if ($_ =~ /^\\?-/) { + s/^\\?-//; + $args{$_} = 1; + $last = _unquote($_); + } + else { + $args{$last} = _unquote($_) if $last; + undef $last; + } + } + return %args; +} + +sub _is_control { + my ($el, $expected) = @_; + if (defined $expected) { + ref $el eq 'ARRAY' and $el->[0] eq $expected; + } + else { + ref $el eq 'ARRAY'; + } +} + +{ + my $sep = ' '; + + sub to_string { + if (@_ > 0) { + # Handle punctunation + my ($in_brace, @punct) = ''; + my @new = map { + if (/^([\[\(])$/) { + ($in_brace = $1) =~ tr/([/)]/; + $_, ns + } + elsif (/^([\)\]])$/ && $in_brace eq $1) { + $in_brace = ''; + ns, $_ + } + elsif ($_ =~ /^[,\.;:\?\!\)\]]$/) { + push @punct, ns, $_; + (); + } + elsif (_is_control($_, 'pp')) { + $_->[1] + } + elsif (_is_control($_)) { + $_ + } + else { + splice (@punct), $_; + } + } @_; + push @new, @punct; + + # Produce string out of an array dealing with the special control characters + # space('off') must but one character delayed + my ($no_space, $space_off) = 1; + my $res = ''; + while (defined(my $el = shift @new)) { + if (_is_control($el, 'hardspace')) { $no_space = 1; $res .= ' ' } + elsif (_is_control($el, 'nospace')) { $no_space = 1; } + elsif (_is_control($el, 'spaceoff')) { $space_off = 1; } + elsif (_is_control($el, 'spaceon')) { space('on'); } + elsif (_is_control($el, 'spacetoggle')) { space() eq 'on' ? + $space_off = 1 : + space('on') } + else { + if ($no_space) { + $no_space = 0; + $res .= "$el" + } + else { + $res .= "$sep$el" + } + + if ($space_off) { space('off'); $space_off = 0; } + } + } + $res + } + else { + ''; + } + } + + sub space { + my ($arg) = @_; + if (defined $arg && $arg =~ /^(on|off)$/) { + $sep = ' ' if $arg eq 'on'; + $sep = '' if $arg eq 'off'; + return; + } + else { + return $sep eq '' ? 'off' : 'on'; + } + } +} + +sub _unquote { + my @args = @_; + $_ =~ s/^"([^"]+)"$/$1/g for @args; + wantarray ? @args : $args[0]; +} + +sub call_macro { + my ($macro, @args) = @_; + my @ret; + + my @newargs; + my $i = 0; + + @args = _unquote(@args) if (!$macros{ $macro }{raw}); + + # Call any callable macros in the argument list + for (@args) { + if ($_ =~ /^[A-Z][a-z]+$/ && exists $macros{ $_ }) { + push @ret, call_macro($_, @args[$i+1 .. $#args]); + last; + } else { + if ($macros{ $macro }{greedy}) { + push @ret, $_; + } + else { + push @newargs, $_; + } + } + $i++; + } + + if ($macros{ $macro }{concat_until}) { + my ($n_macro, @n_args) = (''); + while (1) { + die "EOF was reached and no $macros{ $macro }{concat_until} found" + if not defined $n_macro; + ($n_macro, @n_args) = parse_line(undef, sub { push @ret, shift }); + if ($n_macro eq $macros{ $macro }{concat_until}) { + push @ret, call_macro($n_macro, @n_args); + last; + } + else { + $n_macro =~ s/^\.//; + push @ret, call_macro($n_macro, @n_args) if exists $macros{ $n_macro }; + } + } + } + + if ($macros{ $macro }{greedy}) { + #print "MACROG $macro (", (join ', ', @ret), ")\n"; + return $macros{ $macro }{run}->(@ret); + } + else { + #print "MACRO $macro (", (join ', ', @newargs), ")".(join ', ', @ret)."\n"; + return $macros{ $macro }{run}->(@newargs), @ret; + } +} + +{ + my ($in_fh, $out_sub, $preprocess_sub); + sub parse_line { + $in_fh = $_[0] if defined $_[0] || !defined $in_fh; + $out_sub = $_[1] if defined $_[1] || !defined $out_sub; + $preprocess_sub = $_[2] if defined $_[2] || !defined $preprocess_sub; + + croak 'out_sub not a CODE reference' + if not ref $out_sub eq 'CODE'; + croak 'preprocess_sub not a CODE reference' + if defined $preprocess_sub && not ref $preprocess_sub eq 'CODE'; + + while (my $line = <$in_fh>) { + chomp $line; + if ($line =~ /^\.[A-z][a-z0-9]+/ || $line =~ /^\.%[A-Z]/ || + $line =~ /^\.\\"/) + { + $line =~ s/ +/ /g; + my ($macro, @args) = quotewords(' ', 1, $line); + @args = grep { defined $_ } @args; + $preprocess_sub->(@args) if defined $preprocess_sub; + if ($macro && exists $macros{ $macro }) { + return ($macro, @args); + } else { + $out_sub->($line); + } + } + else { + $out_sub->($line); + } + } + return; + } +} + +1; +__END__ diff --git a/sntp/ag-tpl/agman-cmd.tpl b/sntp/ag-tpl/agman-cmd.tpl new file mode 100644 index 000000000..6d937e468 --- /dev/null +++ b/sntp/ag-tpl/agman-cmd.tpl @@ -0,0 +1,58 @@ +[+: -*- Mode: nroff -*- + + AutoGen5 template man + +## agman-cmd.tpl -- Template for command line man pages +## +## This file is part of AutoOpts, a companion to AutoGen. +## AutoOpts is free software. +## Copyright (C) 1992-2013 Bruce Korb - all rights reserved +## +## AutoOpts is available under any one of two licenses. The license +## in use must be one of these two and the choice is under the control +## of the user of the license. +## +## The GNU Lesser General Public License, version 3 or later +## See the files "COPYING.lgplv3" and "COPYING.gplv3" +## +## The Modified Berkeley Software Distribution License +## See the file "COPYING.mbsd" +## +## These files have the following sha256 sums: +## +## 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 +## 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 +## 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd + +# Produce a man page for section 1, 5 or 8 commands. +# Which is selected via: -DMAN_SECTION=n +# passed to the autogen invocation. "n" may have a suffix, if desired. +# +:+][+: + +(define head-line (lambda() + (sprintf ".TH %s %s \"%s\" \"%s\" \"%s\"\n.\\\"\n" + (get "prog-name") man-sect + (shell "date '+%d %b %Y'") package-text section-name) )) + +(define man-page #t) +(out-push-new) :+][+: + +INCLUDE "mdoc-synopsis.tlib" :+][+: +INCLUDE "cmd-doc.tlib" :+][+: + +INVOKE build-doc :+][+: + + (shell (string-append + "fn='" (find-file "mdoc2man") "'\n" + "test -f ${fn} || die mdoc2man not found from $PWD\n" + "${fn} <<\\_EndOfMdoc_ || die ${fn} failed in $PWD\n" + (out-pop #t) + "\n_EndOfMdoc_" )) + +:+][+: + +(out-move (string-append (get "prog-name") "." + man-sect)) :+][+: + +agman-cmd.tpl ends here :+] diff --git a/sntp/ag-tpl/agmdoc-cmd.tpl b/sntp/ag-tpl/agmdoc-cmd.tpl new file mode 100644 index 000000000..bb605fb79 --- /dev/null +++ b/sntp/ag-tpl/agmdoc-cmd.tpl @@ -0,0 +1,46 @@ +[+: -*- Mode: nroff -*- + + AutoGen5 template mdoc + +## agman-cmd.tpl -- Template for command line mdoc pages +## +## This file is part of AutoOpts, a companion to AutoGen. +## AutoOpts is free software. +## AutoOpts is Copyright (C) 1992-2013 by Bruce Korb - all rights reserved +## +## AutoOpts is available under any one of two licenses. The license +## in use must be one of these two and the choice is under the control +## of the user of the license. +## +## The GNU Lesser General Public License, version 3 or later +## See the files "COPYING.lgplv3" and "COPYING.gplv3" +## +## The Modified Berkeley Software Distribution License +## See the file "COPYING.mbsd" +## +## These files have the following sha256 sums: +## +## 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 +## 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 +## 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd + +# Produce a man page for section 1, 5 or 8 commands. +# Which is selected via: -DMAN_SECTION=n +# passed to the autogen invocation. "n" may have a suffix, if desired. +# +:+][+: + +(define head-line (lambda() (string-append + ".Dd " (shell "date '+%B %e %Y' | sed 's/ */ /g'") + "\n.Dt " UP-PROG-NAME " " man-sect " " section-name + "\n.Os " (shell "uname -sr") "\n") )) + +(define man-page #f) :+][+: + +INCLUDE "mdoc-synopsis.tlib" :+][+: +INCLUDE "cmd-doc.tlib" :+][+: +INVOKE build-doc :+][+: + +(out-move (string-append + (get "prog-name") "." man-sect)) :+][+: +agmdoc-cmd.tpl ends here :+] diff --git a/sntp/ag-tpl/cmd-doc.tlib b/sntp/ag-tpl/cmd-doc.tlib new file mode 100644 index 000000000..0f4c50c10 --- /dev/null +++ b/sntp/ag-tpl/cmd-doc.tlib @@ -0,0 +1,1079 @@ +[+: -*- Mode: nroff -*- + + AutoGen5 template man + +# cmd-doc.tlib -- Template for command line man/mdoc pages +# +# This file is part of AutoOpts, a companion to AutoGen. +# AutoOpts is free software. +# Copyright (C) 1992-2013 Bruce Korb - all rights reserved +# +# AutoOpts is available under any one of two licenses. The license +# in use must be one of these two and the choice is under the control +# of the user of the license. +# +# The GNU Lesser General Public License, version 3 or later +# See the files "COPYING.lgplv3" and "COPYING.gplv3" +# +# The Modified Berkeley Software Distribution License +# See the file "COPYING.mbsd" +# +# These files have the following sha256 sums: +# +# 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 +# 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 +# 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd + +# Produce a man page for section 1, 5, 6 or 8 commands. Which is +# selected via: -DMAN_SECTION=n. "n" may have a suffix, if desired. +# These sections have default section names that may be overridden +# with -DSECTIN_NAME=XX, also passed to the autogen invocation. +# +:+][+: + + ;;# START-BUILDTREE-ISMS + ;; + (shell "CLexe=${AGexe%/agen5/*}/columns/columns + test -x \"${CLexe}\" || { + CLexe=${AGexe%/autogen}/columns + test -x \"${CLexe}\" || die 'columns program is not findable' + }") + +:+][+: # END-BUILDTREE-ISMS + +(shell "CLexe=`echo ${AGexe} | sed 's@/autogen@/columns@'` + test -x \"${CLexe}\" || CLexe=`which columns`") + +# END-INSTALL-ONLY-CODE :+][+: + +(define down-prog-name (string-downcase! (get "prog-name"))) +(define UP-PROG-NAME (get-up-name "prog-name")) + +(define command-doc #t) +(define tmp-val (getenv "MAN_SECTION")) +(define man-sect (if (exist? "cmd-section") (get "cmd-section") "1")) +(define file-name "") +(define sect-name "") +(define macro-name "") +(define tmp-str "") +(define fname-line "") +(define use-flags (exist? "flag.value")) +(define named-mode (not (or use-flags (exist? "long-opts") ))) + +(if (defined? 'tmp-val) + (if (string? tmp-val) + (set! man-sect tmp-val))) + +(define section-name + (if (=* man-sect "1") "User Commands" + (if (=* man-sect "5") "File Formats" + (if (=* man-sect "6") "Games" + (if (=* man-sect "8") "System Management" + (error + "the agman-cmd template only produces section 1, 5, 6 and 8 man pages") +))))) +(set! tmp-val (getenv "SECTION_NAME")) +(if (defined? 'tmp-val) (if (string? tmp-val) + (set! section-name tmp-val) )) + +(define package-text "") +(define package+version (and (exist? "package") (exist? "version"))) + +(if (or (exist? "package") (exist? "version")) (begin + (set! package-text (string-append + (get "package") + (if package+version " (" "") + (get "version") + (if package+version ")" "") )) +) ) + +(define name-to-fname (lambda (nm) + (string-tr (string-downcase nm) " " "-") )) + +(define sect-line-fname (lambda () (begin + (out-push-new file-name) + (emit (string-append ".Sh \"" sect-name "\"\n")) + (string-append "mk-" macro-name) ))) + +(make-tmp-dir) + +(define home-rc-files (exist? "homerc")) +(define home-rc-text + "\nSee \\fBOPTION PRESETS\\fP for configuration files.") + +(define environ-init (exist? "environrc")) +(define environ-text + "\nSee \\fBOPTION PRESETS\\fP for configuration environment variables.") + +(emit (head-line)) +(dne ".\\\" ") :+] +.Sh NAME +.Nm [+: prog-name :+] +.Nd [+: prog-title :+] +[+: INCLUDE "tpl-config.tlib" :+][+:# + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" B U I L D D O C +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE build-doc :+][+: + +(if (not command-doc) (begin + (set! home-rc-files #f) + (set! home-rc-text "") +) ) :+][+: + +INVOKE doc-sections :+][+: +INVOKE ao-sections :+][+: +INVOKE assemble-sections :+][+: + +ENDDEF build-doc + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" A S S E M B L E S E C T I O N S +.\" +.\" Emit the files for each section that was provided, and do conversions +.\" +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE assemble-sections :+][+: + +(out-push-new) + +:+][+: +#.\" Insert the sections in the prescribed order +#.\" Ensure a newline between them all. We strip blank lines, +#.\" so extra blank lines get removed. +#:+] +cvt_prog='[+: + + (define target-form (if man-page "man" "mdoc")) + (define source-form (get "option-format" "texi")) + (define converter (string-append source-form "2" target-form )) + (set! tmp-str (find-file converter)) + + (if (not (defined? 'tmp-str)) + (error (string-append "cannot locate " converter))) + tmp-str +:+]' +cvt_prog=`cd \`dirname "$cvt_prog"\` >/dev/null && pwd + `/`basename "$cvt_prog"` +cd $tmp_dir +test -x "$cvt_prog" || die "'$cvt_prog' is not executable" +{ + list='synopsis description options option-presets' + for f in $list ; do cat $f ; echo ; done + rm -f $list name + list='implementation-notes environment files examples exit-status errors + compatibility see-also conforming-to history authors copyright bugs + notes' + for f in $list ; do cat $f ; echo ; done > .end-doc + rm -f $list + list=`ls -1 *`' .end-doc' + for f in $list ; do cat $f ; echo ; done + rm -f $list +} 1>.doc 2>/dev/null +[+: +IF (exist? "doc-sub") :+][+: + (out-push-new (string-append tmp-dir "/.cmds")) :+][+: + FOR doc-sub :+][+: + + IF (define field-name (get "sub-type" target-form)) + (~~ target-form field-name) :+][+: + + (set! field-name (get "sub-name")) + (define rep-string (string-append "<<" field-name ">>")) + (emit (string-substitute (get "sub-text") rep-string (get field-name))) + "\n" + + :+][+: ENDIF :+][+: + + ENDFOR doc-sub :+][+: + + (out-pop) + (define post-proc-cmd (string-append + (get "doc-sub-cmd" "sed -f %s %s") " | " + egrep-prog " -v '^[ ]*$' | $cvt_prog")) + (sprintf post-proc-cmd ".cmds" ".doc") + :+][+: +ELSE \:+] +[+:(. egrep-prog):+] -v '^[ ]*$' .doc | $cvt_prog[+: +ENDIF doc-sub exists :+][+: + +(shell (out-pop #t)) :+][+: + +ENDDEF assemble-sections + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" D O C S E C T I O N S +.\" +.\" Emit the files for each section that was provided. +.\" If multiple sections exist, they get glued together with ".Pp" +.\" between them. +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE doc-sections :+][+: + +FOR doc-section :+][+: + IF + (define sec-type (string-upcase (get "ds-type"))) + (define sec-name (name-to-fname sec-type)) + (define cvt-fn (find-file (string-append + (get "ds-format" "man") "2mdoc"))) + (if (not (defined? 'cvt-fn)) + (error (sprintf "Cannot locate converter for %s" + (get "ds-format" "man")))) + + (define sec-file (string-append tmp-dir "/" sec-name)) + (access? sec-file R_OK) :+][+: + (out-push-add sec-file) + (emit ".Pp\n") :+][+: + + ELSE :+][+: CASE + (out-push-new sec-file) + sec-type :+][+: + + == "" :+][+: (error "unnamed doc-section") :+][+: + *==* " " :+].Sh "[+: (. sec-type) :+]"[+: + * :+].Sh [+: (. sec-type) :+][+: + ESAC :+][+: + ENDIF :+] +[+: + (shell (string-append + "fn='" cvt-fn "'\n" + "test -f ${fn} || die ${fn} not found from $PWD\n" + "${fn} <<\\_EndOfDocSection_ || die ${fn} failed in $PWD\n" + (get "ds-text") + "\n_EndOfDocSection_" + )) :+][+: + + CASE (emit "\n") sec-type :+][+: + == FILES :+][+: + (if home-rc-files (emit home-rc-text)) + (set! home-rc-files #f) :+][+: + + == ENVIRONMENT :+][+: + (if environ-init (emit environ-text)) + (set! environ-init #f) :+][+: + ESAC :+][+: + + (out-pop) + :+][+: + +ENDFOR doc-section :+][+: + +ENDDEF doc-sections + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" A O S E C T I O N S +.\" +.\" Emit the files for the sections that these templates augment, +.\" replace or conditionally replace +.\" +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE ao-sections :+][+: + IF (. command-doc) :+][+: + INVOKE cond-section sec = "OPTIONS" mode = "replace" :+][+: + INVOKE cond-section sec = "EXIT STATUS" mode = "insert" :+][+: + + IF (or home-rc-files environ-init) :+][+: + INVOKE cond-section sec = "OPTION PRESETS" mode = "replace" :+][+: + + IF (. home-rc-files) :+][+: + INVOKE cond-section sec = "FILES" mode = "append" :+][+: + ENDIF :+][+: + + IF (. environ-init) :+][+: + INVOKE cond-section sec = "ENVIRONMENT" mode = "append" :+][+: + ENDIF :+][+: + ENDIF :+][+: + + ELSE section 5, not command :+][+: + INVOKE cond-section sec = "FILES" mode = "append" :+][+: + ENDIF section 5/not :+][+: + + INVOKE cond-section sec = "SYNOPSIS" mode = "alt" :+][+: + INVOKE cond-section sec = "DESCRIPTION" mode = "append" :+][+: + INVOKE cond-section sec = "AUTHORS" mode = "alt" :+][+: + INVOKE cond-section sec = "BUGS" mode = "append" :+][+: + INVOKE cond-section sec = "NOTES" mode = "append" :+][+: + +IF (exist? "copyright") :+][+: + INVOKE cond-section sec = "COPYRIGHT" mode = "alt" :+][+: +ENDIF :+][+: + +ENDDEF ao-sections + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" C O N D I T I O N A L S E C T I O N +.\" +.\" Figure out what to do for AutoOpts required sections, depending on "mode" +.\" In all cases, if the file does not exist, invoke the "mk" macro to create +.\" a new file. If it does exist, then: +.\" +.\" alt Alternate -- emit no text +.\" replace throw away any pre-existing file. +.\" append invoke the "append" macro to emit additional text +.\" insert save the current contents, replacing the .Sh line with .Pp. +.\" invoke the "mk" macro then emit the saved text +.\" +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE cond-section :+][+: + + IF + (set! sect-name (string-upcase! (string-substitute + (get "sec") "-" " " ))) + (set! macro-name (string-downcase! (string-substitute + sect-name " " "-" ))) + (set! file-name (string-append tmp-dir "/" macro-name)) + + (not (access? file-name R_OK)) :+][+: + + INVOKE (sect-line-fname) :+][+: + + ELSE file exists :+][+: + + CASE (get "mode") :+][+: + + == replace :+][+: + INVOKE (sect-line-fname) :+][+: + + == append :+][+: + (out-push-add file-name) :+][+: + INVOKE (string-append "append-" macro-name) :+][+: + + == insert :+][+: + (set! fname-line (shellf + "sed '1s/.Sh .*/.Pp/' %1$s ; rm -f %1$s" file-name)) :+][+: + INVOKE (sect-line-fname) :+][+: + + == alt :+][+: + (out-push-new) :+][+: + + * :+][+: + (error (sprintf "invalid section type: %s" (get "mode"))) + + :+][+: + ESAC :+][+: + + ENDIF file existence/non-existence :+][+: + (out-pop) :+][+: # All paths open out :+][+: +ENDDEF cond-section + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - D E S C R I P T I O N +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-description :+][+: + + (out-push-new) + (emit + (if (exist? "prog-man-descrip") + (stack-join "\n.Pp\n" "prog-man-descrip") + (if (exist? "detail") + (stack-join "\n.Pp\n" "detail") + "There is no description for this command." + ) ) ) + (shell "sed 's/^$/.sp/' <<\\_EODesc_\n" (out-pop #t) "\n_EODesc_") + + :+][+: + INVOKE append-description :+][+: + +ENDDEF mk-description + +.\" = = = = = = = = = = = = = = = = = = +.\" A P P E N D - D E S C R I P T I O N +.\" = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE append-description :+][+: + +IF (= (get "main.main-type") "for-each"):+][+: + + CASE main.handler-type :+][+: + ~* ^(name|file)|.*text \:+] +.Pp +This program will perform its function for every file named on the command +line or every file named in a list read from stdin. The arguments or input +names must be pre\-existing files. The input list may contain comments, +which[+: + + !E \:+] +.Pp +This program will perform its function for every command line argument +or every non\-comment line in a list read from stdin. +The input list comments[+: + + * :+][+: + (error "the 'for-each' main has in invalid handler-type.") :+][+: + ESAC \:+] + are blank lines or lines beginning with a '[+: + ?% comment-char "%s" "#" :+]' character. +[+: + +ENDIF - "main" is of "for-each" type :+][+: + +ENDDEF append-description + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - O P T I O N S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-options + +:+][+: + +(define opt-arg "") +(define dis-name "") +(define opt-name "") +(define optname-from "A-Z_^") +(define optname-to "a-z--") +(define cvt-cmd "") +(define formatted-doc (exist? "option-format")) + +(if formatted-doc (begin + (out-push-new) + (set! cvt-cmd (string-append (get "option-format") "2mdoc")) +) ) + +(if (exist? "preserve-case") + (begin + (set! optname-from "_^") + (set! optname-to "--") +) ) + +(define fix-optname (lambda (o_nm) (begin + (set! o_nm (string-tr o_nm optname-from optname-to)) + (set! o_nm (string-substitute o_nm "-" "\\-" )) + o_nm ))) + +(if (exist? "option-info") + (string-append ".Pp\n" (get "option-info") "\n") ) +\:+] +.Bl -tag[+: + +FOR flag :+][+: + IF (not (exist? "documentation")) :+][+: + IF (exist? "aliases") :+][+: + INVOKE emit-alias-opt :+][+: + ELSE :+][+: + INVOKE emit-flag-text :+][+: + ENDIF :+][+: + + ELSE :+] +.Ss "[+: (get "descrip" "") :+]"[+: +(set! tmp-str (get "documentation" "")) +(if (> (string-length tmp-str) 3) (string-append + "\n" tmp-str "\n" )) :+][+: + + ENDIF :+][+: +ENDFOR flag + +.\" = = = = = = = = = = = = = = = = = +.\" help option +.\" = = = = = = = = = = = = = = = = = + +:+] +.It [+: + (define tmp-val (get "help-value" "\\&?")) + (if (and use-flags (> (string-length tmp-val) 0)) + (string-append "Fl " tmp-val + (if (exist? "long-opts") " , Fl -help" "") ) + (string-append (if (exist? "long-opts") "Fl -" "") "help" ) + ) \:+] + +Display usage information and exit.[+:# + +.\" = = = = = = = = = = = = = = = = = +.\" more-help option +.\" = = = = = = = = = = = = = = = = = :+][+: + + IF (not (exist? "no-libopts")) :+] +.It [+: + (define tmp-val (get "more-help-value" "\\&!")) + (if (and use-flags (> (string-length tmp-val) 0)) + (string-append "Fl " tmp-val + (if (exist? "long-opts") " , Fl -more-help" "") ) + (string-append (if (exist? "long-opts") "Fl -" "") "more-help" ) + ) \:+] + +Pass the extended usage information through a pager.[+: + +ENDIF no no-libopts + +.\" = = = = = = = = = = = = = = = = = +.\" save and load configuration +.\" = = = = = = = = = = = = = = = = = :+][+: + +IF (exist? "homerc") :+] +.It [+: + + IF (not (exist? "disable-save")) :+][+: + + (define tmp-val (get "save-opts-value" ">")) + (if (and use-flags (> (string-length tmp-val) 0)) + (string-append "Fl " tmp-val " Oo Ar cfgfile Oc" + (if (exist? "long-opts") + " , Fl -save-opts Oo Ns = Ns Ar cfgfile Oc" ) "") + (string-append (if (exist? "long-opts") "Fl -" "") + "save-opts Oo Ns = Ns Ar cfgfile Oc" ) + ) \:+] + +Save the option state to \fIcfgfile\fP. The default is the \fIlast\fP +configuration file listed in the \fBOPTION PRESETS\fP section, below. +The command will exit after updating the config file. +.It [+: + ENDIF saving not disabled :+][+: + + (define tmp-val (get "load-opts-value" "<")) + (define tmp-str (if (exist? "long-opts") "Fl -" "")) + + (if (and use-flags (> (string-length tmp-val) 0)) + (string-append "Fl " tmp-val " Ar cfgfile" + (if (exist? "long-opts") + (string-append " , " tmp-str "load-opts Ns = Ns Ar cfgfile" + " , " tmp-str "no-load-opts" ) + "") ) + (string-append tmp-str "load-opts Ns = Ns Ar cfgfile , " + tmp-str "no-load-opts" ) + ) \:+] + +Load options from \fIcfgfile\fP. +The \fIno-load-opts\fP form will disable the loading +of earlier config/rc/ini files. \fI\-\-no-load-opts\fP is handled early, +out of order.[+: + +ENDIF (exist? "homerc") + +.\" = = = = = = = = = = = = = = = = = +.\" version +.\" = = = = = = = = = = = = = = = = = :+][+: + +IF (exist? "version") :+] +.It [+: + + (define tmp-val (get "version-value" "v")) + (if (and use-flags (> (string-length tmp-val) 0)) + (string-append "Fl " tmp-val " Op Brq Ar v|c|n" + (if (exist? "long-opts") + " Fl -version Op Brq Ar v|c|n" ) "") + (string-append (if (exist? "long-opts") "Fl -" "") + "version Op Brq Ar v|c|n" ) + ) \:+] + +Output version of program and exit. The default mode is `v', a simple +version. The `c' mode will print copyright information and `n' will +print the full copyright notice.[+: +ENDIF :+] +.El +[+: + +(if formatted-doc + (shell (string-append + "fn='" (find-file cvt-cmd) + "'\ntest -f ${fn} || die '" cvt-cmd " not found'\n" + "${fn} <<\\_EndOfMdoc_ || die ${fn} failed in $PWD\n" + (out-pop #t) + "\n_EndOfMdoc_" )) ) :+][+: + +ENDDEF mk-options + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - O P T I O N - P R E S E T S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-option-presets \:+] +Any option that is not marked as \fInot presettable\fP may be preset +by loading values from [+: + IF (. home-rc-files) + :+]configuration ("RC" or ".INI") file(s)[+: + IF (. environ-init) :+] and values from +[+: + ENDIF :+][+: + ENDIF :+][+: + IF (. environ-init) :+]environment variables named: +.nf + \fB[+:(. UP-PROG-NAME):+]_\fP or \fB[+:(. UP-PROG-NAME):+]\fP +.fi +.ad[+: + IF (. home-rc-files) :+] +The environmental presets take precedence (are processed later than) +the configuration files.[+: + ENDIF :+][+: + ELSE :+].[+: + ENDIF :+][+: + + CASE + (define rc-file + (get "rcfile" (string-append "." (get "prog-name") "rc")) ) + (count "homerc") :+][+: + + == "0" :+][+: + == "1" :+][+: + + CASE homerc :+][+: + ~~ '\.|\$HOME' :+] +The file "\fI[+: (string-append (get "homerc") "/" rc-file) +:+]\fP" will be used, if present.[+: + + == "" :+][+: + + * :+] +The \fIhomerc\fP file is "\fI[+:homerc:+]\fP", unless that is a directory. +In that case, the file "\fI[+: (. rc-file) :+]\fP" +is searched for within that directory.[+: + ESAC :+][+: + + * :+] +The \fIhomerc\fP files are [+: + FOR homerc ", " :+][+: + IF (last-for?) :+]and [+: + ENDIF :+]"\fI[+: homerc :+]\fP"[+: ENDFOR :+]. +If any of these are directories, then the file \fI[+: (. rc-file) :+]\fP +is searched for within those directories.[+: + ESAC :+][+: + +ENDDEF mk-option-presets + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - E X I T - S T A T U S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-exit-status \:+] +One of the following exit values will be returned: +.Bl -tag +[+: +(ag-fprintf 0 ".It 0 \" (EXIT_%s)\"\n%s\n" + (string->c-name! (string-upcase (get "exit-name[0]" "SUCCESS"))) + (get "exit-desc[0]" "Successful program execution.") ) + +(define need-ex-noinput (exist? "homerc")) +(define need-ex-software #t) + +(ag-fprintf 0 ".It 1 \" (EXIT_%s)\"\n%s\n" + (string->c-name! (string-upcase (get "exit-name[1]" "FAILURE"))) + (get "exit-desc[1]" + "The operation failed or the command syntax was not valid.")) :+][+: + +FOR exit-desc (for-from 2) :+][+: + (if (= (for-index) 66) + (set! need-ex-noinput #f) + (if (= (for-index) 70) + (set! need-ex-software #f) )) + + (set! tmp-str (get (sprintf "exit-name[%d]" (for-index)) "* unnamed *")) + (sprintf ".It %d \" (EXIT_%s)\"\n%s\n" + (for-index) + (string-upcase (string->c-name! tmp-str)) + (get "exit-desc" "")) :+][+: +ENDFOR exit-desc :+][+: +(if need-ex-noinput + (emit ".It 66 \" (EX_NOINPUT)\" +A specified configuration file could not be loaded.\n")) + +(if need-ex-software + (emit ".It 70 \" (EX_SOFTWARE)\" +libopts had an internal operational error. Please report +it to autogen-users@lists.sourceforge.net. Thank you.\n")) + +(if (> (string-length fname-line) 1) + (emit fname-line)) :+] +.El +[+: + +ENDDEF mk-exit-status + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - A U T H O R S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-authors :+][+: + + (define remove-authors #t) + + (set! tmp-val + (if (exist? "copyright.author") + (stack-join ",\n" "copyright.author") + (stack-join ",\n" "copyright.owner") )) + + (if (> (string-length tmp-val) 1) + (string-append tmp-val "\n") + (delete-file file-name)) + + :+][+: + +ENDDEF mk-authors + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - B U G S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-bugs :+][+: + + (set! tmp-val (get "copyright.eaddr" (get "eaddr"))) + (if (> (string-length tmp-val) 1) + (string-append "Please send bug reports to: " tmp-val "\n") + (delete-file file-name) ) + :+][+: + +ENDDEF mk-bugs :+][+: + +DEFINE append-bugs :+][+: + + (set! tmp-val (get "copyright.eaddr" (get "eaddr"))) + (if (> (string-length tmp-val) 1) + (string-append "\n.Pp\nPlease send bug reports to: " tmp-val "\n") ) + :+][+: + +ENDDEF append-bugs + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - C O P Y R I G H T (+ licensing) +.\" +.\" This section is guaranteed to be the last section in the man page +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-copyright \:+] +Copyright (C) [+: copyright.date :+] [+: + (get "copyright.owner" (get "copyright.author" (get "copyright.eaddr"))) + :+] all rights reserved. +[+: CASE (get "copyright.type") :+][+: + = note :+][+: (get "copyright.text") :+][+: + == '' :+]This program has an unspecified license.[+: + + * :+][+: + (string-append "This program is released under the terms of " + (license-name (get "copyright.type")) ".") :+][+: + + ESAC :+] +[+: +ENDDEF mk-copyright + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - N O T E S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-notes \:+] +This manual page was \fIAutoGen\fP-erated from the \fB[+: prog-name :+]\fP +option definitions. +[+: + +ENDDEF mk-notes + +.\" = = = = = APPEND TO IT: :+][+: + +DEFINE append-notes :+] +.Pp +This manual page was \fIAutoGen\fP-erated from the \fB[+: prog-name :+]\fP +option definitions.[+: + +ENDDEF append-notes + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - E N V I R O N M E N T +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-environment :+][+: + INVOKE append-environment :+][+: +ENDDEF mk-environment + +.\" = = = = = APPEND TO IT: :+][+: + +DEFINE append-environment :+] +[+:(. environ-text) :+][+: +ENDDEF append-environment + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" M K - F I L E S +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE mk-files :+][+: + INVOKE append-files :+][+: +ENDDEF mk-files + +.\" = = = = = APPEND TO IT: :+][+: + +DEFINE append-files :+] +[+:(. home-rc-text) :+][+: +ENDDEF append-files + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" E M I T A L I A S O P T +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE emit-alias-opt :+] +.It [+: + IF (exist? "value") :+][+: + IF (exist? "long-opts") \:+] + Fl [+:value:+] , Fl \-[+: name :+][+: + ELSE \:+] + Fl [+:value:+][+: + ENDIF (exist? "long-opts") :+][+: + + ELSE value does not exist -- named option only :+][+: + + IF (not (exist? "long-opts")) \:+] + [+: name :+][+: + ELSE \:+] + Fl \-[+: (get "name") :+][+: + ENDIF :+][+: + ENDIF :+] +This is an alias for the \fI--[+: aliases :+]\fR option.[+: + IF (exist? "deprecated") :+] +.sp +.B +NOTE: THIS OPTION IS DEPRECATED +[+: + ENDIF :+][+: +ENDDEF emit-alias-opt + +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +.\" E M I T F L A G T E X T +.\" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = :+][+: + +DEFINE emit-flag-text :+][+: + + (if (exist? "enable") + (set! opt-name (string-append (get "enable") "-" (get "name"))) + (set! opt-name (get "name")) ) + (if (exist? "disable") + (set! dis-name (string-append (get "disable") "-" (get "name"))) + (set! dis-name "") ) + + (set! opt-name (fix-optname opt-name)) + (if (> (string-length dis-name) 0) + (set! dis-name (fix-optname dis-name)) ) + + (if (not (exist? "arg-type")) + (set! opt-arg "") + (set! opt-arg (string-append "Ar " + (fix-optname (if (exist? "arg-name") + (get "arg-name") + (string-downcase! (get "arg-type")) )) + )) + ) + +:+] +.It [+: + IF (exist? "value") :+][+: + IF (exist? "long-opts") :+][+: + + # * * * * * * * * * * * * * * * * * * * * + * + * The option has a flag value (character) AND + * the program uses long options + * + \:+] + Fl [+:value:+][+: + IF (not (exist? "arg-type")) :+] , Fl -[+: + ELSE :+] [+:(. opt-arg):+] , Fl -[+: + ENDIF :+][+: (. opt-name) :+] [+: + IF (exist? "arg-type") :+][+: + ? arg-optional Oo Ns = Ns + :+] [+: (. opt-arg) :+] [+: + arg-optional Oc :+][+: + ENDIF :+][+: + IF (exist? "disable") :+] , Fl -[+:(. dis-name):+][+: + ENDIF :+][+: + + ELSE :+][+: + + # * * * * * * * * * * * * * * * * * * * * + * + * The option has a flag value (character) BUT + * the program does _NOT_ use long options + * + \:+] + Fl [+:value:+] [+: + IF (exist? "arg-type") :+][+: + arg-optional Oo :+] [+:(. opt-arg):+] [+: + arg-optional Oc :+] [+: + ENDIF " :+][+: + ENDIF (exist? "long-opts") :+][+: + + + ELSE value does not exist -- named option only :+][+: + + IF (not (exist? "long-opts")) :+][+: + + # * * * * * * * * * * * * * * * * * * * * + * + * The option does not have a flag value (character). + * The program does _NOT_ use long options either. + * Special magic: All arguments are named options. + * + \:+] + [+: (. opt-name) :+] [+: + IF (exist? "arg-type") :+] [+: + ? arg-optional ' Oo = Ns' ' Ns = Ns ' + :+] [+:(. opt-arg) :+] [+: + arg-optional Oc :+] [+: + ENDIF:+][+: + IF (exist? "disable") :+] , Fl -[+:(. dis-name):+][+: + ENDIF :+][+: + + + ELSE :+][+: + # * * * * * * * * * * * * * * * * * * * * + * + * The option does not have a flag value (character). + * The program, instead, only accepts long options. + * + \:+] + Fl -[+: (. opt-name) :+] [+: + + IF (exist? "arg-type") :+][+: + arg-optional Oo :+] Ns = Ns [+:(. opt-arg):+] [+: + arg-optional Oc :+][+: + ENDIF :+][+: + + IF (exist? "disable") + :+], " Fl \-[+:(. dis-name):+]"[+: + ENDIF :+][+: + ENDIF :+][+: + ENDIF :+] +[+: (get "descrip" "") :+].[+: + + IF (exist? "min") :+] +This option is required to appear.[+: + ENDIF :+][+: + + IF (exist? "max") :+] +This option may appear [+: + IF % max (= "%s" "NOLIMIT") + :+]an unlimited number of times[+:ELSE + :+]up to [+: max :+] times[+: + ENDIF:+].[+: + ENDIF:+][+: + + IF (exist? "disable") :+] +The \fI[+:(. dis-name):+]\fP form will [+: + IF (exist? "stack-arg") + :+]clear the list of option arguments[+: + ELSE :+]disable the option[+: + ENDIF :+].[+: + ENDIF:+][+: + + IF (exist? "enabled") :+] +This option is enabled by default.[+: + ENDIF :+][+: + + IF (exist? "no-preset") :+] +This option may not be preset with environment variables +or in initialization (rc) files.[+: + ENDIF :+][+: + + IF (and (exist? "default") named-mode) :+] +This option is the default option.[+: + ENDIF :+][+: + + IF (exist? "equivalence") :+] +This option is a member of the [+:equivalence:+] class of options.[+: + ENDIF :+][+: + + IF (exist? "flags-must") :+] +This option must appear in combination with the following options: +[+: FOR flags-must ", " :+][+:flags-must:+][+:ENDFOR:+].[+: + ENDIF :+][+: + + IF (exist? "flags-cant") :+] +This option must not appear in combination with any of the following options: +[+: FOR flags-cant ", " :+][+:flags-cant:+][+:ENDFOR:+].[+: + ENDIF :+][+: + + + IF (~* (get "arg-type") "key|set") :+] +This option takes a keyword as its argument[+: + + IF (=* (get "arg-type") "set") + +:+] list. Each entry turns on or off +membership bits. The bits are set by name or numeric value and cleared +by preceding the name or number with an exclamation character ('!'). +They can all be cleared with the magic name \fInone\fR and they can all be set +with +.IR all . +A single option will process a list of these values.[+: + + ELSE + +:+]. The argument sets an enumeration value that can +be tested by comparing them against the option value macro.[+: + + ENDIF + +:+] +The available keywords are: +.in +4 +.nf +.na +[+: (shellf "${CLexe} --indent='' --spread=1 -W50 <<_EOF_\n%s\n_EOF_" + (join "\n" (stack "keyword")) ) :+] +.fi +or their numeric equivalent. +.in -4[+: (if (exist? "arg-default") "\n.sp" ) :+][+: + + ELIF (=* (get "arg-type") "num") :+] +This option takes an integer number as its argument.[+: + + IF (exist? "arg-range") :+] +The value of +.[+:(. opt-arg):+] +is constrained to being: +.in +4 +.nf +.na[+:FOR arg_range ", or" :+] +[+: (shellf " +range='%s' + +case \"X${range}\" in +X'->'?* ) + echo \"less than or equal to\" ` + echo $range | sed 's/->//' ` ;; + +X?*'->' ) + echo \"greater than or equal to\" ` + echo $range | sed 's/->.*//' ` ;; + +X?*'->'?* ) + echo \"in the range \" ` + echo $range | sed 's/->/ through /' ` ;; + +X?* ) + echo exactly $range ;; + +X* ) echo $range is indeterminate +esac" + +(get "arg-range") ) +:+][+: + ENDFOR arg-range :+] +.fi +.in -4[+: + + ENDIF arg-range exists :+][+: + + ENDIF arg-type key/set/num :+][+: + + IF (exist? "arg-default") :+] +The default +.[+: (. opt-arg) :+] +for this option is: +.ti +4 + [+: (join " + " (stack "arg-default" )) :+][+: + ENDIF :+] +.sp +[+: + (if (exist? "doc") (string-substitute (get "doc" "") "\n\n" "\n.sp\n") + "This option has not been fully documented." ) :+][+: + IF (exist? "deprecated") :+] +.sp +.B +NOTE: THIS OPTION IS DEPRECATED +[+: + ENDIF :+][+: + +ENDDEF emit-flag-text + +.\" cmd-doc.tlib ends here \:+] diff --git a/sntp/ag-tpl/mdoc-synopsis.tlib b/sntp/ag-tpl/mdoc-synopsis.tlib new file mode 100644 index 000000000..35fe05bbd --- /dev/null +++ b/sntp/ag-tpl/mdoc-synopsis.tlib @@ -0,0 +1,92 @@ +[+: autogen5 template man :+][+: +DEFINE mk-synopsis :+][+: + (out-push-new file-name) \:+] +.Sh SYNOPSIS +.Nm[+: + + IF (. use-flags) :+][+: + IF (exist? "long-opts") :+] +.\" Mixture of short (flag) options and long options +.Op Fl flags +.Op Fl flag Op Ar value +.Op Fl \-option-name Ns Oo Oo Ns "=| " Oc Ns Ar value Oc +[+: ELSE no long options: :+] +.Op Fl flags +.Op Fl flag Op Ar value +[+: ENDIF + :+][+: + ELIF (exist? "long-opts") + :+] +.Op Fl \-option-name +.Op Fl \-option-name Ar value +[+: + + ELIF (not (exist? "argument")) :+] +.Op Ar option\-name Ar value +.Pp +All arguments are named options. +[+: + ENDIF :+][+: + + IF (exist? "argument") :+][+: + argument :+][+: + + IF (exist? "reorder-args") :+] +.Pp +Operands and options may be intermixed. They will be reordered. +[+: ENDIF :+][+: + + ELIF (or (exist? "long-opts") use-flags) + +:+] +.Pp +All arguments must be options. +[+: + + ENDIF :+][+: + + IF (exist? "main") :+][+: + CASE main.main-type :+][+: + == shell-process :+] +.Pp +This program will emit text that is expected to be evaluated by +a Bourne-compatible shell, thus digesting the options for the script.[+: + + == shell-parser :+] +.Pp +This program is designed to produce output suitable for inclusion +into a shell script that will parse the options described.[+: + + == for-each :+] +.Pp +The operands that this program operates on may be specified either +on the command line or read from standard input, one per line. +In that input, leading and trailing white space is stripped, +blank lines are ignored[+: + + IF (define comment-char (get "comment-char" "#")) + (> (string-length comment-char) 1) \:+] + and lines beginning with the character +.I [+: (substring comment-char 1 0):+] +are treated as comments[+: + ENDIF :+].[+: + + IF (exist? "interleaved") :+] +Options may be interleaved with operands both on the command +line and when operands are read from standard input.[+: + ENDIF interleaved + +:+] +Standard input may not be a terminal.[+: + + ESAC main-type :+][+: + ENDIF main exists :+] +.Pp +[+: + +FOR explain "\n.Pp\n" :+][+: + (get "explain" "") :+][+: +ENDFOR :+][+: + +(out-pop) :+][+: +ENDDEF mk-synopsis :+] diff --git a/sntp/ag-tpl/mdoc2man b/sntp/ag-tpl/mdoc2man new file mode 100755 index 000000000..a3041eed3 --- /dev/null +++ b/sntp/ag-tpl/mdoc2man @@ -0,0 +1,181 @@ +#!/usr/bin/perl +package mdoc2man; +use strict; +use warnings; +use File::Basename; +use lib dirname(__FILE__); +use Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser); + +######## +## Basic +######## + +Mdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1); +Mdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1); +Mdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } ); +Mdoc::def_macro( '.Nd', sub { "\\- @_" } ); + +# Macros that enclose things +Mdoc::def_macro( '.Brq', gen_encloser(qw({ })) , greedy => 1 ); +Mdoc::def_macro( '.Op' , gen_encloser(qw([ ])) , greedy => 1 ); +Mdoc::def_macro( '.Qq' , gen_encloser(qw(" ")) , greedy => 1 ); +Mdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 ); +Mdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq])) , greedy => 1 ); +Mdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq])) , greedy => 1 ); +Mdoc::def_macro( '.Pq' , gen_encloser(qw/( )/) , greedy => 1 ); +Mdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1); + +Mdoc::def_macro( 'Oo', sub { '[', @_ } ); +Mdoc::def_macro( 'Oc', sub { ']', @_ } ); + +Mdoc::def_macro( 'Po', sub { '(', @_} ); +Mdoc::def_macro( 'Pc', sub { ')', @_ } ); + +Mdoc::def_macro( 'Bro', sub { '{', ns, @_ } ); +Mdoc::def_macro( 'Brc', sub { '}', @_ } ); + +Mdoc::def_macro( '.Oo', gen_encloser(qw([ ])), concat_until => '.Oc' ); +Mdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' ); +Mdoc::def_macro( '.Po', gen_encloser(qw/( )/), concat_until => '.Pc' ); + +Mdoc::def_macro( '.Ev', sub { @_ } ); +Mdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 ); +Mdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } ); +Mdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\fR(".(shift).")\\f[]", @_ } ); +Mdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\fR()\\f[]" } ); +Mdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\fR()\\f[]" } ); +Mdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } ); +Mdoc::def_macro( '.Ux', sub { "UNIX", @_ } ); + +Mdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } ); +Mdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } ); +{ + my $name; + Mdoc::def_macro('.Nm', sub { + $name = shift if (!$name); + "\\f\\*[B-Font]$name\\fP", @_ + } ); +} + +######## +## lists +######## + +my %lists = ( + bullet => sub { + Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' }); + }, + + tag => sub { + my (%opts) = @_; + + my $width = ''; + + if (exists $opts{width}) { + $width = ' '.((length $opts{width})+1); + } + + if (exists $opts{compact}) { + my $dobrns = 0; + Mdoc::def_macro('.It', sub { + my @ret = (".TP$width\n.NOP", hs); + if ($dobrns) { + ".br\n.ns\n", ns, @ret, @_; + } + else { + $dobrns = 1; + @ret, @_; + } + }, raw => 1); + } + else { + Mdoc::def_macro('.It', sub { + ".TP$width\n.NOP", hs, @_ + }, raw => 1); + } + }, +); + +Mdoc::set_Bl_callback(do { my $nested = 0; sub { + my $type = shift; + my %opts = Mdoc::parse_opts(@_); + if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) { + + # Wrap nested lists with .RS and .RE + Mdoc::set_El_callback(sub { + return '.RE' if $nested-- > 1; + return '.PP'; + }); + + $lists{$1}->(%opts); + + if ($nested++) { + return ".RS"; + } + else { + return (); + } + } + else { + die "Invalid list type <$type>"; + } +}}, raw => 1); + +# don't bother with arguments for now and do what mdoc2man'.sh' did + +Mdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } ); +Mdoc::def_macro('.Ed', sub { ".in -4\n.fi" } ); + +Mdoc::set_Re_callback(sub { + my ($reference) = @_; + <<"REF"; +$reference->{authors}, +\\fI$reference->{title}\\fR, +$reference->{optional}\n.PP +REF +}); + +# Define all macros which have the same sub for inline and standalone macro +for (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) { + my $m = Mdoc::get_macro(".$_"); + Mdoc::def_macro($_, delete $m->{run}, %$m); +} + +sub print_line { + print shift."\n"; +} + +sub run { + print <<'DEFS'; +.de1 NOP +. it 1 an-trap +. if \\n[.$] \,\\$*\/ +.. +.ie t \ +.ds B-Font [CB] +.ds I-Font [CI] +.ds R-Font [CR] +.el \ +.ds B-Font B +.ds I-Font I +.ds R-Font R +DEFS + + while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) { + my @ret = Mdoc::call_macro($macro, @args); + print_line(Mdoc::to_string(@ret)) if @ret; + } + return 0; +} + +exit run(@ARGV) unless caller; + +1; +__END__ diff --git a/sntp/ag-tpl/mdoc2texi b/sntp/ag-tpl/mdoc2texi index d6aacf0e9..b5e96f5a1 100755 --- a/sntp/ag-tpl/mdoc2texi +++ b/sntp/ag-tpl/mdoc2texi @@ -1,1022 +1,150 @@ #! /usr/bin/perl - +package mdoc2texi; use strict; - -my ($line); -my ($bdEd); -my ($blCf,$blEl,@blEl,$blIt,@blIt); -my ($progName); -my (@words,$wc); -my ($extArg,$ns,$sm,$wantspace); -my ($noNl); -my ($ref,$refCount); -my (%anchor, $aCount); - -$aCount = 0; -$noNl = 0; -$ns = 0; # .Ns (no spaces from here to first parameter) -$sm = 1; # Spacing mode -$extArg = 0; # Extended Arguments - disable NLs. -$ref = 0; -$refCount = 0; - -### -# -# We don't know what order we'll see .Sx -# -# Whenever we find one, we look it up. -# If it doesn't exist we assign it an anchor name. -# Regardless, we "return" the anchor name, as we're either going -# to define the anchor point using '@anchor{anchor-1}' or reference -# the anchor using something like '@xref{anchor-1,,whatever}' -# -### - -while ($line = ) -{ - chomp $line; - - $wc = 0; - - if ($line =~ /^\./) - { - $line =~ s/^\.//; - @words = split(/\s+/, $line); - parseMacro(); - } - else - { - print $line; - } - - if ($noNl) - { - $noNl = 0; - } - elsif (!$extArg) - { - print "\n"; - } -} - -sub Anchor ($) -{ - my $string = shift; - - # Look up the provided string. - # If it's not there, bump $aCount and "add" the anchor. - # Return the anchor. - - if (!exists $anchor{$string}) - { - ++$aCount; - $anchor{$string} = "anchor-$aCount"; - } - - return $anchor{$string}; -} - -sub Handle_An -{ - # We should eventually support -nosplit and -split. - # Usage: .An ... - # .An "Joe Author" Joe Author - # .An "Joe Author" , Joe Author, - # .An "Joe Author" Aq user@site Joe Author - # .An "Joe Author" ) ) , Joe Author)), - - do { - parseQuote(\@words) if ($words[0] =~ /^"/); - print shift @words; # XXX: Spaces? - } while scalar(@words); - - # Anything else should be punctuation. - while ($_ = shift @words) - { - print; - } - - print "\@*"; -} - -sub Handle_Bd -{ - # Must end with a .Ed. - # Bd {-literal | -filled | -unfilled | -ragged | -centered} - # [-offset ] [-file ] [-compact] - - my ($bd); - - if ($words[0] eq '-literal') # Literal font display. - { - $bd = "\@verbatim"; - $bdEd = "\@end verbatim"; - } - elsif ($words[0] eq '-filled') # Filled display (R/L justified) - { - die "Handle_Bd: -filled not supported.\n"; - $bd = "\@table \@asis"; - $bdEd = "\@end table"; - } - elsif ($words[0] eq '-unfilled') # Display as typed. - { - die "Handle_Bd: -unfilled not supported.\n"; - $bd = "\@table \@asis"; - $bdEd = "\@end table"; - } - elsif ($words[0] eq '-ragged') # Left-justified only. - { - die "Handle_Bd: -ragged not supported.\n"; - $bd = "\@table \@asis"; - $bdEd = "\@end table"; - } - elsif ($words[0] eq '-centered') # Center each line. - { - die "Handle_Bd: -centered not supported.\n"; - $bd = "\@table \@asis"; - $bdEd = "\@end table"; - } - else - { - die "Handle_Bd: Unknown list type <$words[0]>\n"; - } - - shift @words; - - while ($_ = shift @words) - { - if (/^-file$/) - { - die "Handle_Bd: -file not supported.\n"; - } - elsif (/^-offset$/) - { - die "Handle_Bd: -offset not supported.\n"; - $_ = shift @words; - if (/^left$/) - { - } - elsif (/^center$/) - { - } - elsif (/^indent$/) - { - } - elsif (/^indent-two$/) - { - } - elsif (/^right$/) - { - } - else - { - die "Handle_Bd: Unexpected value for -offset: <$_>\n"; - } - } - else - { - die "Handle_Bd: unexpected argument: <$_>\n"; - } - } - print $bd; -} - -sub Handle_Bl -{ - # Must end with a .El. May be nested, including inside displays. - # - # .Bl {-hang | -ohang | -tag | -diag | -inset} [-width ] \ - # [-offset ] - # .Bl -column [-offset ] ... - # .Bl {-item | -enum [-nested] | -bullet | -hyphen | -dash} \ - # [-offset ] [-compact] - # "-offset indent" uses a standard indent. - # -compact suppresses insertion of vertical space before the list and - # between the list items. - my ($inMulti); - - # For nesting, save needed context: - # $blEl - # $blIt - unshift @blEl, $blEl; - unshift @blIt, $blIt; - - $blEl = "blEl - XXX!"; - $blIt = ""; # Would undef be easier? - - $inMulti = 0; - if ($words[0] eq '-hang') # hanging tags - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-ohang') # tag on its own line, no indent - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-tag') # like hang. - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-diag') # man (4) diagnostic list - inset - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-inset') # inset list - See the mdoc page. - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-column') # Multiple columns - { - print "\@multitable\n"; # XXX Set $wc to 0? - $blEl = "\@end multitable"; - $blCf = "\@columnfractions"; - $inMulti = 1; - } - elsif ($words[0] eq '-item') # Item with no list markers - { - print "\@table \@asis"; - $blEl = "\@end table"; - } - elsif ($words[0] eq '-enum') # Enumerated (numbered) list - { - print "\@itemize \@enumerate"; - $blEl = "\@end enumerate"; - } - elsif ($words[0] eq '-bullet') # Bullet list - { - print "\@itemize \@bullet"; - $blEl = "\@end itemize"; - } - elsif ($words[0] eq '-hyphen') # dash/hyphen list - { - # What would be better? Maybe a 2 column table... - print "\@itemize \@bullet"; - $blEl = "\@end itemize"; - $blIt = "-"; # @minus ? - } - elsif ($words[0] eq '-dash') # dash/hyphen list - { - # What would be better? Maybe a 2 column table... - print "\@itemize \@bullet"; - $blEl = "\@end itemize"; - $blIt = "-"; # @minus ? - } - else - { - die "Handle_Bl: Unknown list type <$words[0]>\n"; - } - - shift @words; - - while ($_ = shift @words) - { - if (/^-width$/) - { - # Maybe some day we will do something with the value... - parseQuote(\@words) if ($words[0] =~ /^"/); - shift @words; - } - elsif (/^-offset$/) - { - # Maybe some day we will do something with the value... - shift @words; - } - elsif (/^-compact$/) - { - # No argument expected - } - elsif ($inMulti) # -column width - { - # Maybe some day we will do something with the value... - $blCf .= " .2"; - } - else - { - die "Handle_Bl: unexpected argument: <$_>\n"; - } - } - - if ($blCf ne "") - { - print $blCf; # The \n below used to be here... - $blCf = ""; - } - - print "\n"; - $wc = 0; -} - -sub Handle_Comment -{ - # print STDERR "In Handle_Comment\n"; - while ($_ = shift @words) - { - } - - $noNl = 1; # No newline needed. -} - -sub Handle_It -{ - # .It Li "sntp ntpserver.somewhere" - - # print STDERR "Handle_It: looking at <", join(" ",@words), ">\n"; # XXX - # die "Handle_It: \$wc was $wc, not 0.\n" if ($wc); - - print "\@item"; - if ($blIt ne "") - { - print " $blIt"; - # Assert @words is empty? - } - else - { - do - { - # print STDERR "Handle_It: looking at <", join(" ",@words), ">\n"; # XXX - parseMacro(); - } while scalar(@words); - } -} - -sub Handle_D -{ - # .D1 Fl abcdefg -abcdefg<> # 1 line of indented text - # .Dl % ls \-l /etc % ls -l /etc # 1 indented literal text line - - if (/^D1$/) - { - print "\@example\n"; - $wc = 0; - parseMacro(); - print "\n\@end example"; - } - elsif (/^Dl$/) - { - print "\@example\n"; - while ($_ = shift @words) - { - s/\\//; - print "$_ "; - } - print "\n\@end example"; - } - else - { - die "Handle_D(): Unexpected mode: <$_>\n"; - } -} - -sub Handle_Ed -{ - print $bdEd; -} - -sub Handle_El -{ - print $blEl; - - $blIt = shift @blIt; - $blEl = shift @blEl; -} - -sub Handle_Em -{ - # Usage: .Em stuff - # .Em word word - # .Em or Ap ing or'ing - # - - print '@emph{'; - parseMacro(); # XXX: Might we get a leading space? - print "}"; - - # On the assumption that the rest is punctuation... - while ($_ = shift @words) - { - print; - } -} - -sub Handle_ArCmFlIcLi -{ - # .Ar wants an italic code font, texi uses @kbd for that. - # .Cm is .Fl but no '-'. - # Usage: .Fl ... - # - # .Fl - - # .Fl cfv -cfv - # .Fl cfv . -cfv. - # .Cm cfv . cfv. - # .Fl s v t -s -v -t - # .Fl - , --, - # .Fl xyz ) , -xyz), - # .Fl | - | - # .Ic "do while {...}" do while {...} - # .Li M1 M2 ; M1 M2>; - # - my ($dash, $didOne, $font, $fontE); - - $dash = (/^Fl$/) ? "-" : ""; - $font = (/^Ar$/) ? "\@kbd{" : "\@code{"; # } - $fontE = '}'; - $didOne = 0; - - do { - if ($words[0] eq '|') - { - print " " if $didOne && $sm && !$ns; - print $font, $dash, $fontE, ' ' if ($dash ne ""); - print "$words[0]"; - $ns = 0; - } - elsif ($words[0] eq '-') - { - print " " if $didOne && $sm && !$ns; - print $font, $dash, $words[0], $fontE; - $ns = 0; - } - elsif ($words[0] =~ /^"/) - { - print " " if $didOne && $sm && !$ns; - print $font; - print $dash if ($dash ne ""); # Do we need this? - parseQuote(\@words); - print $words[0]; - print $fontE; - $ns = 0; - } - elsif ($words[0] eq 'Ar') # Argument - { - $font = '@kbd{'; # } slanted tty - } - elsif ($words[0] eq 'Ic') # Interactive/internal command - { - $font = '@code{'; # } - } - elsif ($words[0] eq 'Xc') - { - $sm = 1; - } - elsif ($words[0] eq 'Xo') - { - $sm = 0; - } - elsif ($words[0] =~ /^[[:punct:]]$/) - { - print $words[0]; - } - else # Should be empty or a word - { - print " " if $didOne && $sm && !$ns; - print $font; - print $dash if ($dash ne ""); # Do we need this? - $words[0] =~ s/\\&//; - print $words[0]; - print $fontE; - $ns = 0; - } - shift @words; - $didOne = 1; - } while (scalar(@words) && $words[0] ne "Op"); -} - -sub Handle_Fn -{ - # Usage: .Fn [] ... - # .Fn getchar getchar() - # .Fn strlen ) , strlen()), - # .Fn align "char *pt" , alignchar *pt), - # - my ($didArg, $isOpen); - - print '@code{', $words[0], "}("; - $isOpen = 1; - shift; - - $didArg = 0; - while ($_ = shift @words) - { - if ($words[0] =~ /^"/) { - # assert $isOpen == 1 - if ($didArg) - { - print '@code{,}', (($sm) ? ' ' : ''); # Ignore $ns here - } - parseQuote(\@words); - print '@emph{', $words[0], "}"; - $didArg = 1; - $ns = 0; - } else { - print ")" if ($isOpen); - $isOpen = 0; - print $words[0]; - } - } -} - -sub Handle_Nm -{ - # Usage: .Nm [] ... - # - # .Nm groff_mdoc groff_mdoc - # .Nm \-mdoc -mdoc - # .Nm foo ) ) , foo)), - # .Nm : groff_mdoc: - # - if (!defined $progName) - { - if (defined $ENV{AG_DEF_PROG_NAME}) - { - $progName = $ENV{AG_DEF_PROG_NAME}; - } - else - { - $progName = "XXX Program Name"; - } - } - - if ($words[0] =~ /^[\\\w]/) - { - $progName = shift @words; - } - print '@code{', $progName, '}'; - - # Anything after this should be punctuation - - while ($_ = shift @words) - { - print; - } -} - -sub Handle_Ns -{ - # Usage: .Pf ... - # .Pa ntpkey_cert_ Ns Ar hostname - # - # Suppress whitespace between "here" and the first parameter - - $wc = 0; # This might be ok... -} - -sub Handle_Op -{ - # Usage: .Op [