summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd')
-rw-r--r--contrib/ntp/ntpd/Makefile.am108
-rw-r--r--contrib/ntp/ntpd/Makefile.in1063
-rw-r--r--contrib/ntp/ntpd/check_y2k.c627
-rw-r--r--contrib/ntp/ntpd/cmd_args.c232
-rw-r--r--contrib/ntp/ntpd/jupiter.h255
-rw-r--r--contrib/ntp/ntpd/ntp_config.c2579
-rw-r--r--contrib/ntp/ntpd/ntp_control.c3001
-rw-r--r--contrib/ntp/ntpd/ntp_crypto.c4184
-rw-r--r--contrib/ntp/ntpd/ntp_filegen.c576
-rw-r--r--contrib/ntp/ntpd/ntp_intres.c1139
-rw-r--r--contrib/ntp/ntpd/ntp_io.c3913
-rw-r--r--contrib/ntp/ntpd/ntp_loopfilter.c1058
-rw-r--r--contrib/ntp/ntpd/ntp_monitor.c358
-rw-r--r--contrib/ntp/ntpd/ntp_peer.c1051
-rw-r--r--contrib/ntp/ntpd/ntp_proto.c3450
-rw-r--r--contrib/ntp/ntpd/ntp_refclock.c1229
-rw-r--r--contrib/ntp/ntpd/ntp_request.c2888
-rw-r--r--contrib/ntp/ntpd/ntp_restrict.c607
-rw-r--r--contrib/ntp/ntpd/ntp_timer.c409
-rw-r--r--contrib/ntp/ntpd/ntp_util.c801
-rw-r--r--contrib/ntp/ntpd/ntpd-opts.c1021
-rw-r--r--contrib/ntp/ntpd/ntpd-opts.def13
-rw-r--r--contrib/ntp/ntpd/ntpd-opts.h351
-rw-r--r--contrib/ntp/ntpd/ntpd-opts.menu1
-rw-r--r--contrib/ntp/ntpd/ntpd-opts.texi489
-rw-r--r--contrib/ntp/ntpd/ntpd.1296
-rw-r--r--contrib/ntp/ntpd/ntpd.c1231
-rw-r--r--contrib/ntp/ntpd/ntpdbase-opts.def459
-rw-r--r--contrib/ntp/ntpd/ntpdsim-opts.c1262
-rw-r--r--contrib/ntp/ntpd/ntpdsim-opts.def15
-rw-r--r--contrib/ntp/ntpd/ntpdsim-opts.h422
-rw-r--r--contrib/ntp/ntpd/ntpdsim-opts.menu1
-rw-r--r--contrib/ntp/ntpd/ntpdsim-opts.texi509
-rw-r--r--contrib/ntp/ntpd/ntpdsim.1357
-rw-r--r--contrib/ntp/ntpd/ntpsim.c371
-rw-r--r--contrib/ntp/ntpd/ppsapi_timepps.h26
-rw-r--r--contrib/ntp/ntpd/refclock_acts.c931
-rw-r--r--contrib/ntp/ntpd/refclock_arbiter.c453
-rw-r--r--contrib/ntp/ntpd/refclock_arc.c1569
-rw-r--r--contrib/ntp/ntpd/refclock_as2201.c388
-rw-r--r--contrib/ntp/ntpd/refclock_atom.c517
-rw-r--r--contrib/ntp/ntpd/refclock_bancomm.c467
-rw-r--r--contrib/ntp/ntpd/refclock_chronolog.c341
-rw-r--r--contrib/ntp/ntpd/refclock_chu.c1687
-rw-r--r--contrib/ntp/ntpd/refclock_conf.c319
-rw-r--r--contrib/ntp/ntpd/refclock_datum.c877
-rw-r--r--contrib/ntp/ntpd/refclock_dumbclock.c385
-rw-r--r--contrib/ntp/ntpd/refclock_fg.c345
-rw-r--r--contrib/ntp/ntpd/refclock_gpsvme.c254
-rw-r--r--contrib/ntp/ntpd/refclock_heath.c451
-rw-r--r--contrib/ntp/ntpd/refclock_hopfpci.c265
-rw-r--r--contrib/ntp/ntpd/refclock_hopfser.c380
-rw-r--r--contrib/ntp/ntpd/refclock_hpgps.c619
-rw-r--r--contrib/ntp/ntpd/refclock_irig.c1048
-rw-r--r--contrib/ntp/ntpd/refclock_jjy.c1011
-rw-r--r--contrib/ntp/ntpd/refclock_jupiter.c1137
-rw-r--r--contrib/ntp/ntpd/refclock_leitch.c626
-rw-r--r--contrib/ntp/ntpd/refclock_local.c258
-rw-r--r--contrib/ntp/ntpd/refclock_msfees.c1462
-rw-r--r--contrib/ntp/ntpd/refclock_mx4200.c1652
-rw-r--r--contrib/ntp/ntpd/refclock_neoclock4x.c1113
-rw-r--r--contrib/ntp/ntpd/refclock_nmea.c762
-rw-r--r--contrib/ntp/ntpd/refclock_oncore.c3732
-rw-r--r--contrib/ntp/ntpd/refclock_palisade.c954
-rw-r--r--contrib/ntp/ntpd/refclock_palisade.h170
-rw-r--r--contrib/ntp/ntpd/refclock_parse.c6045
-rw-r--r--contrib/ntp/ntpd/refclock_pcf.c224
-rw-r--r--contrib/ntp/ntpd/refclock_pst.c322
-rw-r--r--contrib/ntp/ntpd/refclock_ripencc.c4866
-rw-r--r--contrib/ntp/ntpd/refclock_shm.c312
-rw-r--r--contrib/ntp/ntpd/refclock_tpro.c216
-rw-r--r--contrib/ntp/ntpd/refclock_trak.c359
-rw-r--r--contrib/ntp/ntpd/refclock_true.c873
-rw-r--r--contrib/ntp/ntpd/refclock_tt560.c274
-rw-r--r--contrib/ntp/ntpd/refclock_ulink.c589
-rw-r--r--contrib/ntp/ntpd/refclock_wwv.c2709
-rw-r--r--contrib/ntp/ntpd/refclock_wwvb.c459
-rw-r--r--contrib/ntp/ntpd/refclock_zyfer.c346
78 files changed, 78119 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/Makefile.am b/contrib/ntp/ntpd/Makefile.am
new file mode 100644
index 0000000..6896b45
--- /dev/null
+++ b/contrib/ntp/ntpd/Makefile.am
@@ -0,0 +1,108 @@
+AUTOMAKE_OPTIONS= ../util/ansi2knr
+
+bindir= ${exec_prefix}/${BINSUBDIR}
+
+bin_PROGRAMS= ntpd @MAKE_NTPDSIM@
+
+noinst_LIBRARIES= libntpd.a
+
+AM_CPPFLAGS= -I$(top_srcdir)/include -I../include $(LIBOPTS_CFLAGS)
+# LDADD might need RESLIB and ADJLIB.
+LDADD= version.o libntpd.a @LIBPARSE@
+
+BUILT_SOURCES= ntpd-opts.c ntpd-opts.h ntpd.1 ntpd-opts.texi ntpd-opts.menu
+man_MANS= ntpd.1
+
+BUILT_SOURCES+= ntpdsim-opts.c ntpdsim-opts.h ntpdsim.1 ntpdsim-opts.texi ntpdsim-opts.menu
+man_MANS+= ntpdsim.1
+
+# ntpd may need:
+# log10 refclock_wwv.o
+# sqrt ntp_control.o
+# floor refclock_wwv.o
+# which are (usually) provided by -lm.
+ntpd_LDADD = $(LDADD) -lm @LCRYPTO@ $(LIBOPTS_LDADD) ../libntp/libntp.a
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a -lm @LCRYPTO@ $(LIBOPTS_LDADD)
+ntpdsim_CFLAGS = $(CFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a
+DISTCLEANFILES = .version version.c
+EXTRA_DIST = ntpd-opts.def ntpdbase-opts.def ntpdsim-opts.def \
+ refclock_msfees.c \
+ refclock_trak.c \
+ $(BUILT_SOURCES)
+ETAGS_ARGS = Makefile.am
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k ntpdsim
+run_ag = cd $(srcdir) && autogen -L ../include --writable
+std_def_list= $(top_srcdir)/include/debug-opt.def \
+ $(top_srcdir)/include/autogen-version.def \
+ $(top_srcdir)/include/copyright.def \
+ $(top_srcdir)/include/version.def
+
+check-local: @MAKE_CHECK_Y2K@
+ test -z "@MAKE_CHECK_Y2K@" || ./@MAKE_CHECK_Y2K@
+
+# SIM: cmd_args.c ntp_config.c ntp_io.c ntpd.c + ntpsim.c (include/ntpsim.h)
+# ntp_resolver.c is presently unused...
+ntpd_SOURCES = cmd_args.c ntp_config.c ntp_io.c ntpd.c ntpd-opts.c ntpd-opts.h
+ntpdsim_SOURCES = $(ntpd_SOURCES) ntpsim.c ntpdsim-opts.c ntpdsim-opts.h
+libntpd_a_SOURCES = jupiter.h ntp_control.c \
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ ppsapi_timepps.h \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+ refclock_chu.c refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c refclock_hopfser.c \
+ refclock_hopfpci.c refclock_hpgps.c refclock_irig.c refclock_jjy.c \
+ refclock_jupiter.c refclock_leitch.c refclock_local.c \
+ refclock_mx4200.c refclock_neoclock4x.c \
+ refclock_nmea.c refclock_oncore.c refclock_palisade.c \
+ refclock_palisade.h refclock_parse.c \
+ refclock_pcf.c refclock_pst.c refclock_ripencc.c refclock_shm.c \
+ refclock_tpro.c refclock_true.c refclock_tt560.c \
+ refclock_ulink.c refclock_wwv.c refclock_wwvb.c \
+ refclock_zyfer.c
+
+$(srcdir)/ntpd-opts.h: $(srcdir)/ntpd-opts.c
+$(srcdir)/ntpd-opts.c: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) ntpd-opts.def
+
+$(srcdir)/ntpd.1: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Tagman1.tpl -bntpd ntpd-opts.def
+
+$(srcdir)/ntpd-opts.texi $(srcdir)/ntpd-opts.menu: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Taginfo.tpl -DLEVEL=section ntpd-opts.def
+
+$(srcdir)/ntpdsim-opts.h: $(srcdir)/ntpdsim-opts.c
+$(srcdir)/ntpdsim-opts.c: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) ntpdsim-opts.def
+
+$(srcdir)/ntpdsim.1: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) -Tagman1.tpl -bntpdsim ntpdsim-opts.def
+
+$(srcdir)/ntpdsim-opts.texi $(srcdir)/ntpdsim-opts.menu: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) -Taginfo.tpl -DLEVEL=section ntpdsim-opts.def
+
+$(PROGRAMS): $(LDADD)
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE) libntp.a
+
+../libntp/libntpsim.a:
+ cd ../libntp && $(MAKE) libntpsim.a
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE)
+
+$(top_srcdir)/version :
+ cd $(top_srcdir) && $(MAKE) version
+
+version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/version
+ env CSET=`cat $(top_srcdir)/version` $(top_builddir)/scripts/mkver ntpd
+ $(COMPILE) -c version.c
+
+include ../bincheck.mf
diff --git a/contrib/ntp/ntpd/Makefile.in b/contrib/ntp/ntpd/Makefile.in
new file mode 100644
index 0000000..ad07930
--- /dev/null
+++ b/contrib/ntp/ntpd/Makefile.in
@@ -0,0 +1,1063 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# we traditionally installed software in bindir, while it should have gone
+# in sbindir. Now that we offer a choice, look in the "other" installation
+# subdir to warn folks if there is another version there.
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+ANSI2KNR = ../util/ansi2knr
+bin_PROGRAMS = ntpd$(EXEEXT) @MAKE_NTPDSIM@
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k$(EXEEXT) ntpdsim$(EXEEXT)
+DIST_COMMON = $(srcdir)/../bincheck.mf $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+subdir = ntpd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/libopts/m4/libopts.m4 \
+ $(top_srcdir)/m4/define_dir.m4 \
+ $(top_srcdir)/m4/hs_ulong_const.m4 \
+ $(top_srcdir)/m4/os_cflags.m4 $(top_srcdir)/version.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libntpd_a_AR = $(AR) $(ARFLAGS)
+libntpd_a_LIBADD =
+am_libntpd_a_OBJECTS = ntp_control$U.$(OBJEXT) ntp_crypto$U.$(OBJEXT) \
+ ntp_filegen$U.$(OBJEXT) ntp_intres$U.$(OBJEXT) \
+ ntp_loopfilter$U.$(OBJEXT) ntp_monitor$U.$(OBJEXT) \
+ ntp_peer$U.$(OBJEXT) ntp_proto$U.$(OBJEXT) \
+ ntp_refclock$U.$(OBJEXT) ntp_request$U.$(OBJEXT) \
+ ntp_restrict$U.$(OBJEXT) ntp_timer$U.$(OBJEXT) \
+ ntp_util$U.$(OBJEXT) refclock_acts$U.$(OBJEXT) \
+ refclock_arbiter$U.$(OBJEXT) refclock_arc$U.$(OBJEXT) \
+ refclock_as2201$U.$(OBJEXT) refclock_atom$U.$(OBJEXT) \
+ refclock_bancomm$U.$(OBJEXT) refclock_chronolog$U.$(OBJEXT) \
+ refclock_chu$U.$(OBJEXT) refclock_conf$U.$(OBJEXT) \
+ refclock_datum$U.$(OBJEXT) refclock_dumbclock$U.$(OBJEXT) \
+ refclock_fg$U.$(OBJEXT) refclock_gpsvme$U.$(OBJEXT) \
+ refclock_heath$U.$(OBJEXT) refclock_hopfser$U.$(OBJEXT) \
+ refclock_hopfpci$U.$(OBJEXT) refclock_hpgps$U.$(OBJEXT) \
+ refclock_irig$U.$(OBJEXT) refclock_jjy$U.$(OBJEXT) \
+ refclock_jupiter$U.$(OBJEXT) refclock_leitch$U.$(OBJEXT) \
+ refclock_local$U.$(OBJEXT) refclock_mx4200$U.$(OBJEXT) \
+ refclock_neoclock4x$U.$(OBJEXT) refclock_nmea$U.$(OBJEXT) \
+ refclock_oncore$U.$(OBJEXT) refclock_palisade$U.$(OBJEXT) \
+ refclock_parse$U.$(OBJEXT) refclock_pcf$U.$(OBJEXT) \
+ refclock_pst$U.$(OBJEXT) refclock_ripencc$U.$(OBJEXT) \
+ refclock_shm$U.$(OBJEXT) refclock_tpro$U.$(OBJEXT) \
+ refclock_true$U.$(OBJEXT) refclock_tt560$U.$(OBJEXT) \
+ refclock_ulink$U.$(OBJEXT) refclock_wwv$U.$(OBJEXT) \
+ refclock_wwvb$U.$(OBJEXT) refclock_zyfer$U.$(OBJEXT)
+libntpd_a_OBJECTS = $(am_libntpd_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+check_y2k_SOURCES = check_y2k.c
+check_y2k_OBJECTS = check_y2k$U.$(OBJEXT)
+am__DEPENDENCIES_1 = version.o libntpd.a
+check_y2k_DEPENDENCIES = $(am__DEPENDENCIES_1) ../libntp/libntp.a
+am_ntpd_OBJECTS = cmd_args$U.$(OBJEXT) ntp_config$U.$(OBJEXT) \
+ ntp_io$U.$(OBJEXT) ntpd$U.$(OBJEXT) ntpd-opts$U.$(OBJEXT)
+ntpd_OBJECTS = $(am_ntpd_OBJECTS)
+am__DEPENDENCIES_2 =
+ntpd_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ ../libntp/libntp.a
+am__objects_1 = ntpdsim-cmd_args$U.$(OBJEXT) \
+ ntpdsim-ntp_config$U.$(OBJEXT) ntpdsim-ntp_io$U.$(OBJEXT) \
+ ntpdsim-ntpd$U.$(OBJEXT) ntpdsim-ntpd-opts$U.$(OBJEXT)
+am_ntpdsim_OBJECTS = $(am__objects_1) ntpdsim-ntpsim$U.$(OBJEXT) \
+ ntpdsim-ntpdsim-opts$U.$(OBJEXT)
+ntpdsim_OBJECTS = $(am_ntpdsim_OBJECTS)
+ntpdsim_DEPENDENCIES = $(am__DEPENDENCIES_1) ../libntp/libntpsim.a \
+ $(am__DEPENDENCIES_2)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libntpd_a_SOURCES) check_y2k.c $(ntpd_SOURCES) \
+ $(ntpdsim_SOURCES)
+DIST_SOURCES = $(libntpd_a_SOURCES) check_y2k.c $(ntpd_SOURCES) \
+ $(ntpdsim_SOURCES)
+man1dir = $(mandir)/man1
+NROFF = nroff
+MANS = $(man_MANS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+ARLIB_DIR = @ARLIB_DIR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BINSUBDIR = @BINSUBDIR@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHUTEST = @CHUTEST@
+CLKTEST = @CLKTEST@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DCFD = @DCFD@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EF_LIBS = @EF_LIBS@
+EF_PROGS = @EF_PROGS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_LIBOPTS_FALSE = @INSTALL_LIBOPTS_FALSE@
+INSTALL_LIBOPTS_TRUE = @INSTALL_LIBOPTS_TRUE@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LCRYPTO = @LCRYPTO@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBOPTS_CFLAGS = @LIBOPTS_CFLAGS@
+LIBOPTS_DIR = @LIBOPTS_DIR@
+LIBOPTS_LDADD = @LIBOPTS_LDADD@
+LIBPARSE = @LIBPARSE@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAKE_ADJTIMED = @MAKE_ADJTIMED@
+MAKE_CHECK_LAYOUT = @MAKE_CHECK_LAYOUT@
+MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@
+MAKE_LIBNTPSIM = @MAKE_LIBNTPSIM@
+MAKE_LIBPARSE = @MAKE_LIBPARSE@
+MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@
+MAKE_NTPDSIM = @MAKE_NTPDSIM@
+MAKE_NTPTIME = @MAKE_NTPTIME@
+MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@
+MAKE_TICKADJ = @MAKE_TICKADJ@
+MAKE_TIMETRIM = @MAKE_TIMETRIM@
+NEED_LIBOPTS_FALSE = @NEED_LIBOPTS_FALSE@
+NEED_LIBOPTS_TRUE = @NEED_LIBOPTS_TRUE@
+OBJEXT = @OBJEXT@
+OPENSSL = @OPENSSL@
+OPENSSL_INC = @OPENSSL_INC@
+OPENSSL_LIB = @OPENSSL_LIB@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PERL = @PATH_PERL@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PATH_SH = @PATH_SH@
+PROPDELAY = @PROPDELAY@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TESTDCF = @TESTDCF@
+U = @U@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = ${exec_prefix}/${BINSUBDIR}
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+AUTOMAKE_OPTIONS = ../util/ansi2knr
+noinst_LIBRARIES = libntpd.a
+AM_CPPFLAGS = -I$(top_srcdir)/include -I../include $(LIBOPTS_CFLAGS)
+# LDADD might need RESLIB and ADJLIB.
+LDADD = version.o libntpd.a @LIBPARSE@
+BUILT_SOURCES = ntpd-opts.c ntpd-opts.h ntpd.1 ntpd-opts.texi \
+ ntpd-opts.menu ntpdsim-opts.c ntpdsim-opts.h ntpdsim.1 \
+ ntpdsim-opts.texi ntpdsim-opts.menu
+man_MANS = ntpd.1 ntpdsim.1
+
+# ntpd may need:
+# log10 refclock_wwv.o
+# sqrt ntp_control.o
+# floor refclock_wwv.o
+# which are (usually) provided by -lm.
+ntpd_LDADD = $(LDADD) -lm @LCRYPTO@ $(LIBOPTS_LDADD) ../libntp/libntp.a
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a -lm @LCRYPTO@ $(LIBOPTS_LDADD)
+ntpdsim_CFLAGS = $(CFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a
+DISTCLEANFILES = .version version.c
+EXTRA_DIST = ntpd-opts.def ntpdbase-opts.def ntpdsim-opts.def \
+ refclock_msfees.c \
+ refclock_trak.c \
+ $(BUILT_SOURCES)
+
+ETAGS_ARGS = Makefile.am
+run_ag = cd $(srcdir) && autogen -L ../include --writable
+std_def_list = $(top_srcdir)/include/debug-opt.def \
+ $(top_srcdir)/include/autogen-version.def \
+ $(top_srcdir)/include/copyright.def \
+ $(top_srcdir)/include/version.def
+
+
+# SIM: cmd_args.c ntp_config.c ntp_io.c ntpd.c + ntpsim.c (include/ntpsim.h)
+# ntp_resolver.c is presently unused...
+ntpd_SOURCES = cmd_args.c ntp_config.c ntp_io.c ntpd.c ntpd-opts.c ntpd-opts.h
+ntpdsim_SOURCES = $(ntpd_SOURCES) ntpsim.c ntpdsim-opts.c ntpdsim-opts.h
+libntpd_a_SOURCES = jupiter.h ntp_control.c \
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ ppsapi_timepps.h \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+ refclock_chu.c refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c refclock_hopfser.c \
+ refclock_hopfpci.c refclock_hpgps.c refclock_irig.c refclock_jjy.c \
+ refclock_jupiter.c refclock_leitch.c refclock_local.c \
+ refclock_mx4200.c refclock_neoclock4x.c \
+ refclock_nmea.c refclock_oncore.c refclock_palisade.c \
+ refclock_palisade.h refclock_parse.c \
+ refclock_pcf.c refclock_pst.c refclock_ripencc.c refclock_shm.c \
+ refclock_tpro.c refclock_true.c refclock_tt560.c \
+ refclock_ulink.c refclock_wwv.c refclock_wwvb.c \
+ refclock_zyfer.c
+
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/../bincheck.mf $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ntpd/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign ntpd/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libntpd.a: $(libntpd_a_OBJECTS) $(libntpd_a_DEPENDENCIES)
+ -rm -f libntpd.a
+ $(libntpd_a_AR) libntpd.a $(libntpd_a_OBJECTS) $(libntpd_a_LIBADD)
+ $(RANLIB) libntpd.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+check_y2k$(EXEEXT): $(check_y2k_OBJECTS) $(check_y2k_DEPENDENCIES)
+ @rm -f check_y2k$(EXEEXT)
+ $(LINK) $(check_y2k_LDFLAGS) $(check_y2k_OBJECTS) $(check_y2k_LDADD) $(LIBS)
+ntpd$(EXEEXT): $(ntpd_OBJECTS) $(ntpd_DEPENDENCIES)
+ @rm -f ntpd$(EXEEXT)
+ $(LINK) $(ntpd_LDFLAGS) $(ntpd_OBJECTS) $(ntpd_LDADD) $(LIBS)
+ntpdsim$(EXEEXT): $(ntpdsim_OBJECTS) $(ntpdsim_DEPENDENCIES)
+ @rm -f ntpdsim$(EXEEXT)
+ $(LINK) $(ntpdsim_LDFLAGS) $(ntpdsim_OBJECTS) $(ntpdsim_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+../util/ansi2knr:
+ cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr
+
+mostlyclean-kr:
+ -test "$U" = "" || rm -f *_.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_y2k$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd_args$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_config$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_control$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_crypto$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_filegen$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_intres$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_io$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_loopfilter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_monitor$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_peer$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_proto$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_refclock$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_request$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_restrict$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_timer$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_util$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpd$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpd-opts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-cmd_args$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_config$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_io$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpd$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpd-opts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpsim$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_acts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arbiter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arc$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_as2201$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_atom$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_bancomm$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chronolog$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chu$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_conf$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_datum$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_dumbclock$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_fg$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_gpsvme$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_heath$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfpci$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfser$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hpgps$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_irig$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jjy$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jupiter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_leitch$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_local$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_mx4200$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_neoclock4x$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_nmea$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_oncore$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_palisade$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_parse$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pcf$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pst$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ripencc$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_shm$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tpro$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_true$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tt560$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ulink$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwv$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwvb$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_zyfer$U.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+ntpdsim-cmd_args$U.o: cmd_args$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-cmd_args$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" -c -o ntpdsim-cmd_args$U.o `test -f 'cmd_args$U.c' || echo '$(srcdir)/'`cmd_args$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" "$(DEPDIR)/ntpdsim-cmd_args$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cmd_args$U.c' object='ntpdsim-cmd_args$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-cmd_args$U.o `test -f 'cmd_args$U.c' || echo '$(srcdir)/'`cmd_args$U.c
+
+ntpdsim-cmd_args$U.obj: cmd_args$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-cmd_args$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" -c -o ntpdsim-cmd_args$U.obj `if test -f 'cmd_args$U.c'; then $(CYGPATH_W) 'cmd_args$U.c'; else $(CYGPATH_W) '$(srcdir)/cmd_args$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" "$(DEPDIR)/ntpdsim-cmd_args$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cmd_args$U.c' object='ntpdsim-cmd_args$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-cmd_args$U.obj `if test -f 'cmd_args$U.c'; then $(CYGPATH_W) 'cmd_args$U.c'; else $(CYGPATH_W) '$(srcdir)/cmd_args$U.c'; fi`
+
+ntpdsim-ntp_config$U.o: ntp_config$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" -c -o ntpdsim-ntp_config$U.o `test -f 'ntp_config$U.c' || echo '$(srcdir)/'`ntp_config$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_config$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config$U.c' object='ntpdsim-ntp_config$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config$U.o `test -f 'ntp_config$U.c' || echo '$(srcdir)/'`ntp_config$U.c
+
+ntpdsim-ntp_config$U.obj: ntp_config$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" -c -o ntpdsim-ntp_config$U.obj `if test -f 'ntp_config$U.c'; then $(CYGPATH_W) 'ntp_config$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_config$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config$U.c' object='ntpdsim-ntp_config$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config$U.obj `if test -f 'ntp_config$U.c'; then $(CYGPATH_W) 'ntp_config$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config$U.c'; fi`
+
+ntpdsim-ntp_io$U.o: ntp_io$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" -c -o ntpdsim-ntp_io$U.o `test -f 'ntp_io$U.c' || echo '$(srcdir)/'`ntp_io$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_io$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io$U.c' object='ntpdsim-ntp_io$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io$U.o `test -f 'ntp_io$U.c' || echo '$(srcdir)/'`ntp_io$U.c
+
+ntpdsim-ntp_io$U.obj: ntp_io$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" -c -o ntpdsim-ntp_io$U.obj `if test -f 'ntp_io$U.c'; then $(CYGPATH_W) 'ntp_io$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_io$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io$U.c' object='ntpdsim-ntp_io$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io$U.obj `if test -f 'ntp_io$U.c'; then $(CYGPATH_W) 'ntp_io$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io$U.c'; fi`
+
+ntpdsim-ntpd$U.o: ntpd$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" -c -o ntpdsim-ntpd$U.o `test -f 'ntpd$U.c' || echo '$(srcdir)/'`ntpd$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd$U.c' object='ntpdsim-ntpd$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd$U.o `test -f 'ntpd$U.c' || echo '$(srcdir)/'`ntpd$U.c
+
+ntpdsim-ntpd$U.obj: ntpd$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" -c -o ntpdsim-ntpd$U.obj `if test -f 'ntpd$U.c'; then $(CYGPATH_W) 'ntpd$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd$U.c' object='ntpdsim-ntpd$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd$U.obj `if test -f 'ntpd$U.c'; then $(CYGPATH_W) 'ntpd$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd$U.c'; fi`
+
+ntpdsim-ntpd-opts$U.o: ntpd-opts$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd-opts$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo" -c -o ntpdsim-ntpd-opts$U.o `test -f 'ntpd-opts$U.c' || echo '$(srcdir)/'`ntpd-opts$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd-opts$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd-opts$U.c' object='ntpdsim-ntpd-opts$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd-opts$U.o `test -f 'ntpd-opts$U.c' || echo '$(srcdir)/'`ntpd-opts$U.c
+
+ntpdsim-ntpd-opts$U.obj: ntpd-opts$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd-opts$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo" -c -o ntpdsim-ntpd-opts$U.obj `if test -f 'ntpd-opts$U.c'; then $(CYGPATH_W) 'ntpd-opts$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd-opts$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd-opts$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpd-opts$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd-opts$U.c' object='ntpdsim-ntpd-opts$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd-opts$U.obj `if test -f 'ntpd-opts$U.c'; then $(CYGPATH_W) 'ntpd-opts$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd-opts$U.c'; fi`
+
+ntpdsim-ntpsim$U.o: ntpsim$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" -c -o ntpdsim-ntpsim$U.o `test -f 'ntpsim$U.c' || echo '$(srcdir)/'`ntpsim$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" "$(DEPDIR)/ntpdsim-ntpsim$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim$U.c' object='ntpdsim-ntpsim$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim$U.o `test -f 'ntpsim$U.c' || echo '$(srcdir)/'`ntpsim$U.c
+
+ntpdsim-ntpsim$U.obj: ntpsim$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" -c -o ntpdsim-ntpsim$U.obj `if test -f 'ntpsim$U.c'; then $(CYGPATH_W) 'ntpsim$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" "$(DEPDIR)/ntpdsim-ntpsim$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim$U.c' object='ntpdsim-ntpsim$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim$U.obj `if test -f 'ntpsim$U.c'; then $(CYGPATH_W) 'ntpsim$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim$U.c'; fi`
+
+ntpdsim-ntpdsim-opts$U.o: ntpdsim-opts$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpdsim-opts$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo" -c -o ntpdsim-ntpdsim-opts$U.o `test -f 'ntpdsim-opts$U.c' || echo '$(srcdir)/'`ntpdsim-opts$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo" "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpdsim-opts$U.c' object='ntpdsim-ntpdsim-opts$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpdsim-opts$U.o `test -f 'ntpdsim-opts$U.c' || echo '$(srcdir)/'`ntpdsim-opts$U.c
+
+ntpdsim-ntpdsim-opts$U.obj: ntpdsim-opts$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpdsim-opts$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo" -c -o ntpdsim-ntpdsim-opts$U.obj `if test -f 'ntpdsim-opts$U.c'; then $(CYGPATH_W) 'ntpdsim-opts$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpdsim-opts$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo" "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Po"; else rm -f "$(DEPDIR)/ntpdsim-ntpdsim-opts$U.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpdsim-opts$U.c' object='ntpdsim-ntpdsim-opts$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpdsim-opts$U.obj `if test -f 'ntpdsim-opts$U.c'; then $(CYGPATH_W) 'ntpdsim-opts$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpdsim-opts$U.c'; fi`
+check_y2k_.c: check_y2k.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/check_y2k.c; then echo $(srcdir)/check_y2k.c; else echo check_y2k.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+cmd_args_.c: cmd_args.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/cmd_args.c; then echo $(srcdir)/cmd_args.c; else echo cmd_args.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_config_.c: ntp_config.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_config.c; then echo $(srcdir)/ntp_config.c; else echo ntp_config.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_control_.c: ntp_control.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_control.c; then echo $(srcdir)/ntp_control.c; else echo ntp_control.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_crypto_.c: ntp_crypto.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_crypto.c; then echo $(srcdir)/ntp_crypto.c; else echo ntp_crypto.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_filegen_.c: ntp_filegen.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_filegen.c; then echo $(srcdir)/ntp_filegen.c; else echo ntp_filegen.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_intres_.c: ntp_intres.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_intres.c; then echo $(srcdir)/ntp_intres.c; else echo ntp_intres.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_io_.c: ntp_io.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_io.c; then echo $(srcdir)/ntp_io.c; else echo ntp_io.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_loopfilter_.c: ntp_loopfilter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_loopfilter.c; then echo $(srcdir)/ntp_loopfilter.c; else echo ntp_loopfilter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_monitor_.c: ntp_monitor.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_monitor.c; then echo $(srcdir)/ntp_monitor.c; else echo ntp_monitor.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_peer_.c: ntp_peer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_peer.c; then echo $(srcdir)/ntp_peer.c; else echo ntp_peer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_proto_.c: ntp_proto.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_proto.c; then echo $(srcdir)/ntp_proto.c; else echo ntp_proto.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_refclock_.c: ntp_refclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_refclock.c; then echo $(srcdir)/ntp_refclock.c; else echo ntp_refclock.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_request_.c: ntp_request.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_request.c; then echo $(srcdir)/ntp_request.c; else echo ntp_request.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_restrict_.c: ntp_restrict.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_restrict.c; then echo $(srcdir)/ntp_restrict.c; else echo ntp_restrict.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_timer_.c: ntp_timer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_timer.c; then echo $(srcdir)/ntp_timer.c; else echo ntp_timer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_util_.c: ntp_util.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_util.c; then echo $(srcdir)/ntp_util.c; else echo ntp_util.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpd_.c: ntpd.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpd.c; then echo $(srcdir)/ntpd.c; else echo ntpd.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpd-opts_.c: ntpd-opts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpd-opts.c; then echo $(srcdir)/ntpd-opts.c; else echo ntpd-opts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpdsim-opts_.c: ntpdsim-opts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpdsim-opts.c; then echo $(srcdir)/ntpdsim-opts.c; else echo ntpdsim-opts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpsim_.c: ntpsim.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpsim.c; then echo $(srcdir)/ntpsim.c; else echo ntpsim.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_acts_.c: refclock_acts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_acts.c; then echo $(srcdir)/refclock_acts.c; else echo refclock_acts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_arbiter_.c: refclock_arbiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_arbiter.c; then echo $(srcdir)/refclock_arbiter.c; else echo refclock_arbiter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_arc_.c: refclock_arc.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_arc.c; then echo $(srcdir)/refclock_arc.c; else echo refclock_arc.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_as2201_.c: refclock_as2201.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_as2201.c; then echo $(srcdir)/refclock_as2201.c; else echo refclock_as2201.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_atom_.c: refclock_atom.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_atom.c; then echo $(srcdir)/refclock_atom.c; else echo refclock_atom.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_bancomm_.c: refclock_bancomm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_bancomm.c; then echo $(srcdir)/refclock_bancomm.c; else echo refclock_bancomm.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_chronolog_.c: refclock_chronolog.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_chronolog.c; then echo $(srcdir)/refclock_chronolog.c; else echo refclock_chronolog.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_chu_.c: refclock_chu.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_chu.c; then echo $(srcdir)/refclock_chu.c; else echo refclock_chu.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_conf_.c: refclock_conf.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_conf.c; then echo $(srcdir)/refclock_conf.c; else echo refclock_conf.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_datum_.c: refclock_datum.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_datum.c; then echo $(srcdir)/refclock_datum.c; else echo refclock_datum.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_dumbclock_.c: refclock_dumbclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_dumbclock.c; then echo $(srcdir)/refclock_dumbclock.c; else echo refclock_dumbclock.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_fg_.c: refclock_fg.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_fg.c; then echo $(srcdir)/refclock_fg.c; else echo refclock_fg.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_gpsvme_.c: refclock_gpsvme.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_gpsvme.c; then echo $(srcdir)/refclock_gpsvme.c; else echo refclock_gpsvme.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_heath_.c: refclock_heath.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_heath.c; then echo $(srcdir)/refclock_heath.c; else echo refclock_heath.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hopfpci_.c: refclock_hopfpci.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hopfpci.c; then echo $(srcdir)/refclock_hopfpci.c; else echo refclock_hopfpci.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hopfser_.c: refclock_hopfser.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hopfser.c; then echo $(srcdir)/refclock_hopfser.c; else echo refclock_hopfser.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hpgps_.c: refclock_hpgps.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hpgps.c; then echo $(srcdir)/refclock_hpgps.c; else echo refclock_hpgps.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_irig_.c: refclock_irig.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_irig.c; then echo $(srcdir)/refclock_irig.c; else echo refclock_irig.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_jjy_.c: refclock_jjy.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_jjy.c; then echo $(srcdir)/refclock_jjy.c; else echo refclock_jjy.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_jupiter_.c: refclock_jupiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_jupiter.c; then echo $(srcdir)/refclock_jupiter.c; else echo refclock_jupiter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_leitch_.c: refclock_leitch.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_leitch.c; then echo $(srcdir)/refclock_leitch.c; else echo refclock_leitch.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_local_.c: refclock_local.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_local.c; then echo $(srcdir)/refclock_local.c; else echo refclock_local.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_mx4200_.c: refclock_mx4200.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_mx4200.c; then echo $(srcdir)/refclock_mx4200.c; else echo refclock_mx4200.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_neoclock4x_.c: refclock_neoclock4x.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_neoclock4x.c; then echo $(srcdir)/refclock_neoclock4x.c; else echo refclock_neoclock4x.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_nmea_.c: refclock_nmea.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_nmea.c; then echo $(srcdir)/refclock_nmea.c; else echo refclock_nmea.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_oncore_.c: refclock_oncore.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_oncore.c; then echo $(srcdir)/refclock_oncore.c; else echo refclock_oncore.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_palisade_.c: refclock_palisade.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_palisade.c; then echo $(srcdir)/refclock_palisade.c; else echo refclock_palisade.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_parse_.c: refclock_parse.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_parse.c; then echo $(srcdir)/refclock_parse.c; else echo refclock_parse.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_pcf_.c: refclock_pcf.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_pcf.c; then echo $(srcdir)/refclock_pcf.c; else echo refclock_pcf.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_pst_.c: refclock_pst.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_pst.c; then echo $(srcdir)/refclock_pst.c; else echo refclock_pst.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_ripencc_.c: refclock_ripencc.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ripencc.c; then echo $(srcdir)/refclock_ripencc.c; else echo refclock_ripencc.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_shm_.c: refclock_shm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_shm.c; then echo $(srcdir)/refclock_shm.c; else echo refclock_shm.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_tpro_.c: refclock_tpro.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_tpro.c; then echo $(srcdir)/refclock_tpro.c; else echo refclock_tpro.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_true_.c: refclock_true.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_true.c; then echo $(srcdir)/refclock_true.c; else echo refclock_true.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_tt560_.c: refclock_tt560.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_tt560.c; then echo $(srcdir)/refclock_tt560.c; else echo refclock_tt560.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_ulink_.c: refclock_ulink.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ulink.c; then echo $(srcdir)/refclock_ulink.c; else echo refclock_ulink.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_wwv_.c: refclock_wwv.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_wwv.c; then echo $(srcdir)/refclock_wwv.c; else echo refclock_wwv.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_wwvb_.c: refclock_wwvb.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_wwvb.c; then echo $(srcdir)/refclock_wwvb.c; else echo refclock_wwvb.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_zyfer_.c: refclock_zyfer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_zyfer.c; then echo $(srcdir)/refclock_zyfer.c; else echo refclock_zyfer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+check_y2k_.$(OBJEXT) check_y2k_.lo cmd_args_.$(OBJEXT) cmd_args_.lo \
+ntp_config_.$(OBJEXT) ntp_config_.lo ntp_control_.$(OBJEXT) \
+ntp_control_.lo ntp_crypto_.$(OBJEXT) ntp_crypto_.lo \
+ntp_filegen_.$(OBJEXT) ntp_filegen_.lo ntp_intres_.$(OBJEXT) \
+ntp_intres_.lo ntp_io_.$(OBJEXT) ntp_io_.lo ntp_loopfilter_.$(OBJEXT) \
+ntp_loopfilter_.lo ntp_monitor_.$(OBJEXT) ntp_monitor_.lo \
+ntp_peer_.$(OBJEXT) ntp_peer_.lo ntp_proto_.$(OBJEXT) ntp_proto_.lo \
+ntp_refclock_.$(OBJEXT) ntp_refclock_.lo ntp_request_.$(OBJEXT) \
+ntp_request_.lo ntp_restrict_.$(OBJEXT) ntp_restrict_.lo \
+ntp_timer_.$(OBJEXT) ntp_timer_.lo ntp_util_.$(OBJEXT) ntp_util_.lo \
+ntpd_.$(OBJEXT) ntpd_.lo ntpd-opts_.$(OBJEXT) ntpd-opts_.lo \
+ntpdsim-opts_.$(OBJEXT) ntpdsim-opts_.lo ntpsim_.$(OBJEXT) ntpsim_.lo \
+refclock_acts_.$(OBJEXT) refclock_acts_.lo refclock_arbiter_.$(OBJEXT) \
+refclock_arbiter_.lo refclock_arc_.$(OBJEXT) refclock_arc_.lo \
+refclock_as2201_.$(OBJEXT) refclock_as2201_.lo \
+refclock_atom_.$(OBJEXT) refclock_atom_.lo refclock_bancomm_.$(OBJEXT) \
+refclock_bancomm_.lo refclock_chronolog_.$(OBJEXT) \
+refclock_chronolog_.lo refclock_chu_.$(OBJEXT) refclock_chu_.lo \
+refclock_conf_.$(OBJEXT) refclock_conf_.lo refclock_datum_.$(OBJEXT) \
+refclock_datum_.lo refclock_dumbclock_.$(OBJEXT) \
+refclock_dumbclock_.lo refclock_fg_.$(OBJEXT) refclock_fg_.lo \
+refclock_gpsvme_.$(OBJEXT) refclock_gpsvme_.lo \
+refclock_heath_.$(OBJEXT) refclock_heath_.lo \
+refclock_hopfpci_.$(OBJEXT) refclock_hopfpci_.lo \
+refclock_hopfser_.$(OBJEXT) refclock_hopfser_.lo \
+refclock_hpgps_.$(OBJEXT) refclock_hpgps_.lo refclock_irig_.$(OBJEXT) \
+refclock_irig_.lo refclock_jjy_.$(OBJEXT) refclock_jjy_.lo \
+refclock_jupiter_.$(OBJEXT) refclock_jupiter_.lo \
+refclock_leitch_.$(OBJEXT) refclock_leitch_.lo \
+refclock_local_.$(OBJEXT) refclock_local_.lo \
+refclock_mx4200_.$(OBJEXT) refclock_mx4200_.lo \
+refclock_neoclock4x_.$(OBJEXT) refclock_neoclock4x_.lo \
+refclock_nmea_.$(OBJEXT) refclock_nmea_.lo refclock_oncore_.$(OBJEXT) \
+refclock_oncore_.lo refclock_palisade_.$(OBJEXT) refclock_palisade_.lo \
+refclock_parse_.$(OBJEXT) refclock_parse_.lo refclock_pcf_.$(OBJEXT) \
+refclock_pcf_.lo refclock_pst_.$(OBJEXT) refclock_pst_.lo \
+refclock_ripencc_.$(OBJEXT) refclock_ripencc_.lo \
+refclock_shm_.$(OBJEXT) refclock_shm_.lo refclock_tpro_.$(OBJEXT) \
+refclock_tpro_.lo refclock_true_.$(OBJEXT) refclock_true_.lo \
+refclock_tt560_.$(OBJEXT) refclock_tt560_.lo refclock_ulink_.$(OBJEXT) \
+refclock_ulink_.lo refclock_wwv_.$(OBJEXT) refclock_wwv_.lo \
+refclock_wwvb_.$(OBJEXT) refclock_wwvb_.lo refclock_zyfer_.$(OBJEXT) \
+refclock_zyfer_.lo : $(ANSI2KNR)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+install-man1: $(man1_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(man1dir)"
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(mkdir_p) $(distdir)/..
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libtool clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-exec-am: install-binPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+
+install-info: install-info-am
+
+install-man: install-man1
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic mostlyclean-kr \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am uninstall-man
+
+uninstall-man: uninstall-man1
+
+.PHONY: CTAGS GTAGS all all-am check check-am check-local clean \
+ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-exec install-exec-am install-exec-hook \
+ install-info install-info-am install-man install-man1 \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-kr \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-info-am \
+ uninstall-man uninstall-man1
+
+
+check-local: @MAKE_CHECK_Y2K@
+ test -z "@MAKE_CHECK_Y2K@" || ./@MAKE_CHECK_Y2K@
+
+$(srcdir)/ntpd-opts.h: $(srcdir)/ntpd-opts.c
+$(srcdir)/ntpd-opts.c: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) ntpd-opts.def
+
+$(srcdir)/ntpd.1: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Tagman1.tpl -bntpd ntpd-opts.def
+
+$(srcdir)/ntpd-opts.texi $(srcdir)/ntpd-opts.menu: $(srcdir)/ntpd-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list)
+ $(run_ag) -Taginfo.tpl -DLEVEL=section ntpd-opts.def
+
+$(srcdir)/ntpdsim-opts.h: $(srcdir)/ntpdsim-opts.c
+$(srcdir)/ntpdsim-opts.c: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) ntpdsim-opts.def
+
+$(srcdir)/ntpdsim.1: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) -Tagman1.tpl -bntpdsim ntpdsim-opts.def
+
+$(srcdir)/ntpdsim-opts.texi $(srcdir)/ntpdsim-opts.menu: $(srcdir)/ntpdsim-opts.def $(srcdir)/ntpdbase-opts.def $(std_def_list) $(top_srcdir)/include/homerc.def
+ $(run_ag) -Taginfo.tpl -DLEVEL=section ntpdsim-opts.def
+
+$(PROGRAMS): $(LDADD)
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE) libntp.a
+
+../libntp/libntpsim.a:
+ cd ../libntp && $(MAKE) libntpsim.a
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE)
+
+$(top_srcdir)/version :
+ cd $(top_srcdir) && $(MAKE) version
+
+version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/version
+ env CSET=`cat $(top_srcdir)/version` $(top_builddir)/scripts/mkver ntpd
+ $(COMPILE) -c version.c
+
+install-exec-hook:
+ @case ${BINSUBDIR} in \
+ bin) ODIR=${exec_prefix}/sbin ;; \
+ sbin) ODIR=${exec_prefix}/bin ;; \
+ esac; \
+ test -z "${bin_PROGRAMS}${bin_SCRIPTS}" \
+ || for i in ${bin_PROGRAMS} ${bin_SCRIPTS} " "; do \
+ test ! -f $$ODIR/$$i || echo "*** $$i is also in $$ODIR!"; \
+ done
+
+#
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/ntp/ntpd/check_y2k.c b/contrib/ntp/ntpd/check_y2k.c
new file mode 100644
index 0000000..6b83115
--- /dev/null
+++ b/contrib/ntp/ntpd/check_y2k.c
@@ -0,0 +1,627 @@
+/* check_y2k.c -- test ntp code constructs for Y2K correctness Y2KFixes [*/
+
+ /*
+ Code invoked by `make check`. Not part of ntpd and not to be
+ installed.
+
+ On any code I even wonder about, I've cut and pasted the code
+ here and ran it as a test case just to be sure.
+
+ For code not in "ntpd" proper, we have tried to call most
+ repaired functions from herein to properly test them
+ (something never done before!). This has found several bugs,
+ not normal Y2K bugs, that will strike in Y2K so repair them
+ we did.
+
+ Program exits with 0 on success, 1 on Y2K failure (stdout messages).
+ Exit of 2 indicates internal logic bug detected OR failure of
+ what should be our correct formulas.
+
+ While "make check" should only check logic for source within that
+ specific directory, this check goes outside the scope of the local
+ directory. It's not a perfect world (besides, there is a lot of
+ interdependence here, and it really needs to be tested in
+ a controled order).
+ */
+
+/* { definitions lifted from ntpd.c to allow us to complie with
+ "#include ntp.h". I have not taken the time to reduce the clutter. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#ifndef SYS_WINNT
+# if !defined(VMS) /*wjm*/
+# include <sys/param.h>
+# endif /* VMS */
+# if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# endif /* HAVE_SYS_SIGNAL_H */
+# include <sys/signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# if !defined(VMS) /*wjm*/
+# include <sys/resource.h>
+# endif /* VMS */
+#else
+# include <signal.h>
+# include <process.h>
+# include <io.h>
+# include "../libntp/log.h"
+#endif /* SYS_WINNT */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+/* } end definitions lifted from ntpd.c */
+
+#include "ntp_calendar.h"
+#include "parse.h"
+
+#define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 )
+
+volatile int debug = 0; /* debugging requests for parse stuff */
+char const *progname = "check_y2k";
+
+long
+Days ( int Year ) /* return number of days since year "0" */
+{
+ long Return;
+ /* this is a known to be good algorithm */
+ Return = Year * 365; /* first aproximation to the value */
+ if ( Year >= 1 )
+ { /* see notes in libparse/parse.c if you want a PROPER
+ * **generic algorithm. */
+ Return += (Year+3) / 4; /* add in (too many) leap days */
+ Return -= (Year-1) / 100; /* reduce by (too many) centurys */
+ Return += (Year-1) / 400; /* get final answer */
+ }
+
+ return Return;
+}
+
+static int year0 = 1900; /* sarting year for NTP time */
+static int yearend; /* ending year we test for NTP time.
+ * 32-bit systems: through 2036, the
+ **year in which NTP time overflows.
+ * 64-bit systems: a reasonable upper
+ **limit (well, maybe somewhat beyond
+ **reasonable, but well before the
+ **max time, by which time the earth
+ **will be dead.) */
+static time_t Time;
+static struct tm LocalTime;
+
+#define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \
+ Warnings++; else Fatals++
+
+int
+main( void )
+{
+ int Fatals;
+ int Warnings;
+ int year;
+
+ Time = time( (time_t *)NULL )
+#ifdef TESTTIMEOFFSET
+ + test_time_offset
+#endif
+ ;
+ LocalTime = *localtime( &Time );
+
+ year = ( sizeof( u_long ) > 4 ) /* save max span using year as temp */
+ ? ( 400 * 3 ) /* three greater gregorian cycles */
+ : ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/
+ /* NOTE: will automacially expand test years on
+ * 64 bit machines.... this may cause some of the
+ * existing ntp logic to fail for years beyond
+ * 2036 (the current 32-bit limit). If all checks
+ * fail ONLY beyond year 2036 you may ignore such
+ * errors, at least for a decade or so. */
+ yearend = year0 + year;
+
+ puts( " internal self check" );
+ { /* verify our own logic used to verify repairs */
+ unsigned long days;
+
+ if ( year0 >= yearend )
+ {
+ fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d (span=%d)\n",
+ (int)year0, (int)yearend, (int)year );
+ exit(2);
+ }
+
+ {
+ int save_year;
+
+ save_year = LocalTime.tm_year; /* save current year */
+
+ year = 1980;
+ LocalTime.tm_year = year - 1900;
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATAL DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ year = 2100; /* test year > limit but CURRENT year < limit */
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Warnings == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): WARNING DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+ Fatals = Warnings = 0;
+ LocalTime.tm_year = year - 1900; /* everything > limit */
+ Error(1980); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATALS DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ LocalTime.tm_year = save_year;
+ }
+
+ days = 365+1; /* days in year 0 + 1 more day */
+ for ( year = 1; year <= 2500; year++ )
+ {
+ long Test;
+ Test = Days( year );
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ Test = julian0(year); /* compare with julian0() macro */
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ days += 365;
+ if ( isleap_4(year) ) days++;
+ }
+
+ if ( isleap_4(1999) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+ if ( !isleap_4(2000) )
+ {
+ fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" );
+ exit(2);
+ }
+ if ( isleap_4(2001) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+
+ if ( !isleap_tm(2000-1900) )
+ {
+ fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" );
+ exit(2);
+ }
+ }
+
+ Fatals = Warnings = 0;
+
+ puts( " include/ntp.h" );
+ { /* test our new isleap_*() #define "functions" */
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int IsLeapSw;
+
+ LeapSw = GoodLeap(year);
+ IsLeapSw = isleap_4(year);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+
+ IsLeapSw = isleap_tm(year-1900);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+ }
+ }
+
+ puts( " include/ntp_calendar.h" );
+ { /* I belive this is good, but just to be sure... */
+
+ /* we are testing this #define */
+#define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0)))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+
+ LeapSw = GoodLeap(year);
+
+ if ( !(!LeapSw) != !(!is_leapyear(year)) )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d *** ERROR\n", year, LeapSw );
+ break;
+ }
+ }
+ }
+
+
+ puts( " libparse/parse.c" );
+ {
+ long Days1970; /* days from 1900 to 1970 */
+
+ struct ParseTime /* womp up a test structure to all cut/paste code */
+ {
+ int year;
+ } Clock_Time, *clock_time;
+
+ clock_time = &Clock_Time;
+
+ /* first test this #define */
+#define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int DayCnt;
+
+ LeapSw = GoodLeap(year);
+ DayCnt = (int)days_per_year(year);
+
+ if ( ( LeapSw ? 366 : 365 ) != DayCnt )
+ {
+ Error(year);
+ fprintf( stdout,
+ " days_per_year() %4d %2d %3d *** ERROR\n",
+ year, LeapSw, DayCnt );
+ break;
+ }
+ }
+
+ /* test (what is now julian0) calculations */
+
+ Days1970 = Days( 1970 ); /* get days since 1970 using a known good */
+
+ for ( year = 1970; year < yearend; year++ )
+ {
+ unsigned long t;
+ long DaysYear ;
+
+ clock_time->year = year;
+
+ /* here is the code we are testing, cut and pasted out of the source */
+#if 0 /* old BUGGY code that has Y2K (and many other) failures */
+ /* ghealton: this logic FAILED with great frequency when run
+ * over a period of time, including for year 2000. True, it
+ * had more successes than failures, but that's not really good
+ * enough for critical time distribution software.
+ * It is so awful I wonder if it has had a history of failure
+ * and fixes? */
+ t = (clock_time->year - 1970) * 365;
+ t += (clock_time->year >> 2) - (1970 >> 2);
+ t -= clock_time->year / 100 - 1970 / 100;
+ t += clock_time->year / 400 - 1970 / 400;
+
+ /* (immediate feare of rounding errors on integer
+ * **divisions proved well founded) */
+
+#else
+ /* my replacement, based on Days() above */
+ t = julian0(year) - julian0(1970);
+#endif
+
+ /* compare result in t against trusted calculations */
+ DaysYear = Days( year ); /* get days to this year */
+ if ( t != DaysYear - Days1970 )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d 1970=%-8ld %4d=%-8ld %-3ld t=%-8ld *** ERROR ***\n",
+ year, (long)Days1970,
+ year,
+ (long)DaysYear,
+ (long)(DaysYear - Days1970),
+ (long)t );
+ }
+ }
+
+#if 1 /* { */
+ {
+ debug = 1; /* enable debugging */
+ for ( year = 1970; year < yearend; year++ )
+ { /* (limited by theory unix 2038 related bug lives by, but
+ * ends in yearend) */
+ clocktime_t ct;
+ time_t Observed;
+ time_t Expected;
+ u_long Flag;
+ unsigned long t;
+
+ ct.day = 1;
+ ct.month = 1;
+ ct.year = year;
+ ct.hour = ct.minute = ct.second = ct.usecond = 0;
+ ct.utcoffset = 0;
+ ct.utctime = 0;
+ ct.flags = 0;
+
+ Flag = 0;
+ Observed = parse_to_unixtime( &ct, &Flag );
+ if ( ct.year != year )
+ {
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n",
+ (int)year, (int)Flag, (int)ct.year );
+ Error(year);
+ break;
+ }
+ t = julian0(year) - julian0(1970); /* Julian day from 1970 */
+ Expected = t * 24 * 60 * 60;
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ if ( year >= YEAR_PIVOT+1900 )
+ {
+ /* check year % 100 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year % 100;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ /* check year - 1900 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year - 1900;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+
+ }
+ }
+#endif /* } */
+ }
+ }
+
+ puts( " libntp/caljulian.c" );
+ { /* test caljulian() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ ntp_time = julian0( year0 ); /* NTP starts in 1900-01-01 */
+#if DAY_NTP_STARTS == 693596
+ ntp_time -= 365; /* BIAS required for successful test */
+#endif
+ if ( DAY_NTP_STARTS != ntp_time )
+ {
+ Error(year);
+ fprintf( stdout,
+ "%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n",
+ (int)year0,
+ (long)DAY_NTP_STARTS, (long)ntp_time,
+ (long)DAY_NTP_STARTS - (long)ntp_time );
+ }
+
+ for ( ; year < yearend; year++ )
+ {
+
+ /* 01-01 for the current year */
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ caljulian( ntp_time, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ ntp_time += (31 + 28-1) * ( 24 * 60 * 60 ); /* advance to 02-28 */
+ caljulian( ntp_time, &ot ); /* convert Feb 28 */
+ if ( ot.year != year
+ || ot.month != 2
+ || ot.monthday != 28 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ {
+ int m; /* expected month */
+ int d; /* expected day */
+
+ m = isleap_4(year) ? 2 : 3;
+ d = isleap_4(year) ? 29 : 1;
+
+ ntp_time += ( 24 * 60 * 60 ); /* advance to the next day */
+ caljulian( ntp_time, &ot ); /* convert this day */
+ if ( ot.year != year
+ || ot.month != m
+ || ot.monthday != d )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year, m, d,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ }
+ }
+ }
+
+ puts( " libntp/caltontp.c" );
+ { /* test caltontp() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ for ( ; year < yearend; year++ )
+ {
+ u_long ObservedNtp;
+
+ /* 01-01 for the current year */
+ ot.year = year;
+ ot.month = ot.monthday = 1; /* unused, but set anyway JIC */
+ ot.yearday = 1; /* this is the magic value used by caltontp() */
+ ot.hour = ot.minute = ot.second = 0;
+
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ ObservedNtp = caltontp( &ot );
+ if ( ntp_time != ObservedNtp )
+ {
+ Error(year);
+ fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n",
+ (int)year,
+ (unsigned long)ntp_time, (unsigned long)ObservedNtp ,
+ (long)ntp_time - (long)ObservedNtp );
+
+ break;
+ }
+
+ /* now call caljulian as a type of failsafe supercheck */
+ caljulian( ObservedNtp, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ObservedNtp,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+ }
+ }
+
+ if ( Warnings > 0 )
+ fprintf( stdout, "%d WARNINGS\n", Warnings );
+ if ( Fatals > 0 )
+ fprintf( stdout, "%d FATAL ERRORS\n", Fatals );
+ return Fatals ? 1 : 0;
+}
+ /* Y2KFixes ] */
diff --git a/contrib/ntp/ntpd/cmd_args.c b/contrib/ntp/ntpd/cmd_args.c
new file mode 100644
index 0000000..760bced
--- /dev/null
+++ b/contrib/ntp/ntpd/cmd_args.c
@@ -0,0 +1,232 @@
+/*
+ * cmd_args.c = command-line argument processing
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_cmdargs.h"
+
+#ifdef SIM
+# include "ntpsim.h"
+# include "ntpdsim-opts.h"
+# define OPTSTRUCT ntpdsimOptions
+#else
+# include "ntpd-opts.h"
+# define OPTSTRUCT ntpdOptions
+#endif /* SIM */
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+extern char const *progname;
+extern const char *specific_interface;
+extern short default_ai_family;
+
+#ifdef HAVE_NETINFO
+extern int check_netinfo;
+#endif
+
+
+/*
+ * getCmdOpts - get command line options
+ */
+void
+getCmdOpts(
+ int argc,
+ char *argv[]
+ )
+{
+ extern const char *config_file;
+ int errflg;
+ tOptions *myOptions = &OPTSTRUCT;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+
+ switch (WHICH_IDX_IPV4) {
+ case INDEX_OPT_IPV4:
+ default_ai_family = AF_INET;
+ break;
+ case INDEX_OPT_IPV6:
+ default_ai_family = AF_INET6;
+ break;
+ default:
+ /* ai_fam_templ = ai_fam_default; */
+ break;
+ }
+
+ if (HAVE_OPT( AUTHREQ ))
+ proto_config(PROTO_AUTHENTICATE, 1, 0., NULL);
+
+ if (HAVE_OPT( AUTHNOREQ ))
+ proto_config(PROTO_AUTHENTICATE, 0, 0., NULL);
+
+ if (HAVE_OPT( BCASTSYNC ))
+ proto_config(PROTO_BROADCLIENT, 1, 0., NULL);
+
+ if (HAVE_OPT( CONFIGFILE )) {
+ config_file = OPT_ARG( CONFIGFILE );
+#ifdef HAVE_NETINFO
+ check_netinfo = 0;
+#endif
+ }
+
+ if (HAVE_OPT( DRIFTFILE ))
+ stats_config(STATS_FREQ_FILE, OPT_ARG( DRIFTFILE ));
+
+ if (HAVE_OPT( PANICGATE ))
+ allow_panic = TRUE;
+
+ if (HAVE_OPT( JAILDIR )) {
+#ifdef HAVE_DROPROOT
+ droproot = 1;
+ chrootdir = OPT_ARG( JAILDIR );
+#else
+ errflg++;
+#endif
+ }
+
+ if (HAVE_OPT( KEYFILE ))
+ getauthkeys(OPT_ARG( KEYFILE ));
+
+ if (HAVE_OPT( PIDFILE ))
+ stats_config(STATS_PID_FILE, OPT_ARG( PIDFILE ));
+
+ if (HAVE_OPT( QUIT ))
+ mode_ntpdate = TRUE;
+
+ if (HAVE_OPT( PROPAGATIONDELAY ))
+ do {
+ double tmp;
+ const char *my_ntp_optarg = OPT_ARG( PROPAGATIONDELAY );
+
+ if (sscanf(my_ntp_optarg, "%lf", &tmp) != 1) {
+ msyslog(LOG_ERR,
+ "command line broadcast delay value %s undecodable",
+ my_ntp_optarg);
+ } else {
+ proto_config(PROTO_BROADDELAY, 0, tmp, NULL);
+ }
+ } while (0);
+
+ if (HAVE_OPT( STATSDIR ))
+ stats_config(STATS_STATSDIR, OPT_ARG( STATSDIR ));
+
+ if (HAVE_OPT( TRUSTEDKEY )) {
+ int ct = STACKCT_OPT( TRUSTEDKEY );
+ const char** pp = STACKLST_OPT( TRUSTEDKEY );
+
+ do {
+ u_long tkey;
+ const char* p = *pp++;
+
+ tkey = (int)atol(p);
+ if (tkey == 0 || tkey > NTP_MAXKEY) {
+ msyslog(LOG_ERR,
+ "command line trusted key %s is invalid",
+ p);
+ } else {
+ authtrust(tkey, 1);
+ }
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( USER )) {
+#ifdef HAVE_DROPROOT
+ char *ntp_optarg = OPT_ARG( USER );
+
+ droproot = 1;
+ user = malloc(strlen(ntp_optarg) + 1);
+ if (user == NULL) {
+ errflg++;
+ } else {
+ (void)strncpy(user, ntp_optarg, strlen(ntp_optarg) + 1);
+ group = rindex(user, ':');
+ if (group)
+ *group++ = '\0'; /* get rid of the ':' */
+ }
+#else
+ errflg++;
+#endif
+ }
+
+ if (HAVE_OPT( VAR )) {
+ int ct = STACKCT_OPT( VAR );
+ const char** pp = STACKLST_OPT( VAR );
+
+ do {
+ const char* my_ntp_optarg = *pp++;
+
+ set_sys_var(my_ntp_optarg, strlen(my_ntp_optarg)+1,
+ (u_short) (RW));
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( DVAR )) {
+ int ct = STACKCT_OPT( DVAR );
+ const char** pp = STACKLST_OPT( DVAR );
+
+ do {
+ const char* my_ntp_optarg = *pp++;
+
+ set_sys_var(my_ntp_optarg, strlen(my_ntp_optarg)+1,
+ (u_short) (RW | DEF));
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( SLEW ))
+ clock_max = 600;
+
+ if (HAVE_OPT( UPDATEINTERVAL )) {
+ long val = OPT_VALUE_UPDATEINTERVAL;
+
+ if (val >= 0)
+ interface_interval = val;
+ else {
+ msyslog(LOG_ERR,
+ "command line interface update interval %ld must be greater or equal to 0",
+ val);
+ errflg++;
+ }
+ }
+#ifdef SIM
+ if (HAVE_OPT( SIMBROADCASTDELAY ))
+ sscanf(OPT_ARG( SIMBROADCASTDELAY ), "%lf", &ntp_node.bdly);
+
+ if (HAVE_OPT( PHASENOISE ))
+ sscanf(OPT_ARG( PHASENOISE ), "%lf", &ntp_node.snse);
+
+ if (HAVE_OPT( SIMSLEW ))
+ sscanf(OPT_ARG( SIMSLEW ), "%lf", &ntp_node.slew);
+
+ if (HAVE_OPT( SERVERTIME ))
+ sscanf(OPT_ARG( SERVERTIME ), "%lf", &ntp_node.clk_time);
+
+ if (HAVE_OPT( ENDSIMTIME ))
+ sscanf(OPT_ARG( ENDSIMTIME ), "%lf", &ntp_node.sim_time);
+
+ if (HAVE_OPT( FREQERR ))
+ sscanf(OPT_ARG( FREQERR ), "%lf", &ntp_node.ferr);
+
+ if (HAVE_OPT( WALKNOISE ))
+ sscanf(OPT_ARG( WALKNOISE ), "%lf", &ntp_node.fnse);
+
+ if (HAVE_OPT( NDELAY ))
+ sscanf(OPT_ARG( NDELAY ), "%lf", &ntp_node.ndly);
+
+ if (HAVE_OPT( PDELAY ))
+ sscanf(OPT_ARG( PDELAY ), "%lf", &ntp_node.pdly);
+
+#endif /* SIM */
+
+ if (errflg || argc) {
+ printf("argc is <%d>\n", argc);
+ optionUsage(myOptions, 2);
+ }
+ return;
+}
diff --git a/contrib/ntp/ntpd/jupiter.h b/contrib/ntp/ntpd/jupiter.h
new file mode 100644
index 0000000..ed80b0c
--- /dev/null
+++ b/contrib/ntp/ntpd/jupiter.h
@@ -0,0 +1,255 @@
+/* @(#) $Header$ (LBL) */
+
+/*
+ * Rockwell Jupiter GPS receiver definitions
+ *
+ * This is all based on the "Zodiac GPS Receiver Family Designer's
+ * Guide" (dated 12/96)
+ */
+
+#define JUPITER_SYNC 0x81ff /* sync word (book says 0xff81 !?!?) */
+#define JUPITER_ALL 0xffff /* disable all output messages */
+
+/* Output messages (sent by the Jupiter board) */
+#define JUPITER_O_GPOS 1000 /* geodetic position status */
+#define JUPITER_O_EPOS 1001 /* ECEF position status */
+#define JUPITER_O_CHAN 1002 /* channel summary */
+#define JUPITER_O_VIS 1003 /* visible satellites */
+#define JUPITER_O_DGPS 1005 /* differential GPS status */
+#define JUPITER_O_MEAS 1007 /* channel measurement */
+#define JUPITER_O_ID 1011 /* receiver id */
+#define JUPITER_O_USER 1012 /* user-settings output */
+#define JUPITER_O_TEST 1100 /* built-in test results */
+#define JUPITER_O_MARK 1102 /* measurement time mark */
+#define JUPITER_O_PULSE 1108 /* UTC time mark pulse output */
+#define JUPITER_O_PORT 1130 /* serial port com parameters in use */
+#define JUPITER_O_EUP 1135 /* EEPROM update */
+#define JUPITER_O_ESTAT 1136 /* EEPROM status */
+
+/* Input messages (sent to the Jupiter board) */
+#define JUPITER_I_PVTINIT 1200 /* geodetic position and velocity */
+#define JUPITER_I_USER 1210 /* user-defined datum */
+#define JUPITER_I_MAPSEL 1211 /* map datum select */
+#define JUPITER_I_ELEV 1212 /* satellite elevation mask control */
+#define JUPITER_I_CAND 1213 /* satellite candidate select */
+#define JUPITER_I_DGPS 1214 /* differential GPS control */
+#define JUPITER_I_COLD 1216 /* cold start control */
+#define JUPITER_I_VALID 1217 /* solution validity criteria */
+#define JUPITER_I_ALT 1219 /* user-entered altitude input */
+#define JUPITER_I_PLAT 1220 /* application platform control */
+#define JUPITER_I_NAV 1221 /* nav configuration */
+#define JUPITER_I_TEST 1300 /* preform built-in test command */
+#define JUPITER_I_RESTART 1303 /* restart command */
+#define JUPITER_I_PORT 1330 /* serial port com parameters */
+#define JUPITER_I_PROTO 1331 /* message protocol control */
+#define JUPITER_I_RDGPS 1351 /* raw DGPS RTCM SC-104 data */
+
+struct jheader {
+ u_short sync; /* (JUPITER_SYNC) */
+ u_short id; /* message id */
+ u_short len; /* number of data short wordss (w/o cksum) */
+ u_char reqid; /* JUPITER_REQID_MASK bits available as id */
+ u_char flags; /* flags */
+ u_short hsum; /* header cksum */
+};
+
+#define JUPITER_REQID_MASK 0x3f /* bits available as id */
+#define JUPITER_FLAG_NAK 0x01 /* negative acknowledgement */
+#define JUPITER_FLAG_ACK 0x02 /* acknowledgement */
+#define JUPITER_FLAG_REQUEST 0x04 /* request ACK or NAK */
+#define JUPITER_FLAG_QUERY 0x08 /* request one shot output message */
+#define JUPITER_FLAG_LOG 0x20 /* request periodic output message */
+#define JUPITER_FLAG_CONN 0x40 /* enable periodic message */
+#define JUPITER_FLAG_DISC 0x80 /* disable periodic message */
+
+#define JUPITER_H_FLAG_BITS \
+ "\020\1NAK\2ACK\3REQUEST\4QUERY\5MBZ\6LOG\7CONN\10DISC"
+
+/* Log request messages (data payload when using JUPITER_FLAG_LOG) */
+struct jrequest {
+ u_short trigger; /* if 0, trigger on time trigger on
+ update (e.g. new almanac) */
+ u_short interval; /* frequency in seconds */
+ u_short offset; /* offset into minute */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_GPOS (1000) */
+struct jgpos {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short navval; /* navigation soltuion validity */
+ u_short navtype; /* navigation solution type */
+ u_short nmeas; /* # of measurements used in solution */
+ u_short polar; /* if 1 then polar navigation */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short nsweek[2]; /* GPS nanoseconds into second */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short utcnsec[2]; /* 0 to 999999999 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short gsep; /* geoidal separation */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short mvar;
+ u_short climb;
+ u_short mapd;
+ u_short herr[2];
+ u_short verr[2];
+ u_short terr[2];
+ u_short hverr;
+ u_short bias[2];
+ u_short biassd[2];
+ u_short drift[2];
+ u_short driftsd[2];
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_GPOS_NAV_NOALT 0x01 /* altitude used */
+#define JUPITER_O_GPOS_NAV_NODGPS 0x02 /* no differential GPS */
+#define JUPITER_O_GPOS_NAV_NOSAT 0x04 /* not enough satellites */
+#define JUPITER_O_GPOS_NAV_MAXH 0x08 /* exceeded max EHPE */
+#define JUPITER_O_GPOS_NAV_MAXV 0x10 /* exceeded max EVPE */
+
+/* JUPITER_O_CHAN (1002) */
+struct jchan {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short gpsns[2]; /* GPS nanoseconds from epoch */
+ struct jchan2 {
+ u_short flags; /* flags */
+ u_short prn; /* satellite PRN */
+ u_short chan; /* channel number */
+ } sat[12];
+ u_short dsum;
+};
+
+/* JUPITER_O_VIS (1003) */
+struct jvis {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short gdop; /* best possible GDOP */
+ u_short pdop; /* best possible PDOP */
+ u_short hdop; /* best possible HDOP */
+ u_short vdop; /* best possible VDOP */
+ u_short tdop; /* best possible TDOP */
+ u_short nvis; /* number of visible satellites */
+ struct jvis2 {
+ u_short prn; /* satellite PRN */
+ u_short azi; /* satellite azimuth (radians) */
+ u_short elev; /* satellite elevation (radians) */
+ } sat[12];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_ID (1011) */
+struct jid {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ char chans[20]; /* number of channels (ascii) */
+ char vers[20]; /* software version (ascii) */
+ char date[20]; /* software date (ascii) */
+ char opts[20]; /* software options (ascii) */
+ char reserved[20];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_USER (1012) */
+struct juser {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short status; /* operatinoal status */
+ u_short coldtmo; /* cold start time-out */
+ u_short dgpstmo; /* DGPS correction time-out*/
+ u_short emask; /* elevation mask */
+ u_short selcand[2]; /* selected candidate */
+ u_short solflags; /* solution validity criteria */
+ u_short nsat; /* number of satellites in track */
+ u_short herr[2]; /* minimum expected horizontal error */
+ u_short verr[2]; /* minimum expected vertical error */
+ u_short platform; /* application platform */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_PULSE (1108) */
+struct jpulse {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short reserved[5];
+ u_short sweek[2]; /* GPS seconds into week */
+ short offs; /* GPS to UTC time offset (seconds) */
+ u_short offns[2]; /* GPS to UTC offset (nanoseconds) */
+ u_short flags; /* flags */
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_PULSE_VALID 0x1 /* time mark validity */
+#define JUPITER_O_PULSE_UTC 0x2 /* GPS/UTC sync */
+
+/* JUPITER_O_EUP (1135) */
+struct jeup {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_char dataid; /* data id */
+ u_char prn; /* satellite PRN */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_I_RESTART (1303) */
+struct jrestart {
+ u_short seq; /* sequence number */
+ u_short flags;
+ u_short dsum; /* checksum */
+};
+#define JUPITER_I_RESTART_INVRAM 0x01
+#define JUPITER_I_RESTART_INVEEPROM 0x02
+#define JUPITER_I_RESTART_INVRTC 0x04
+#define JUPITER_I_RESTART_COLD 0x80
+
+/* JUPITER_I_PVTINIT (1200) */
+struct jpvtinit {
+ u_short flags;
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short climb;
+ u_short dsum;
+};
+#define JUPITER_I_PVTINIT_FORCE 0x01
+#define JUPITER_I_PVTINIT_GPSVAL 0x02
+#define JUPITER_I_PVTINIT_UTCVAL 0x04
+#define JUPITER_I_PVTINIT_POSVAL 0x08
+#define JUPITER_I_PVTINIT_ALTVAL 0x10
+#define JUPITER_I_PVTINIT_SPDVAL 0x12
+#define JUPITER_I_PVTINIT_MAGVAL 0x14
+#define JUPITER_I_PVTINIT_CLIMBVAL 0x18
+
+/* JUPITER_I_PLAT (1220) */
+struct jplat {
+ u_short seq; /* sequence number */
+ u_short platform; /* application platform */
+ u_short dsum;
+};
+#define JUPITER_I_PLAT_DEFAULT 0 /* default dynamics */
+#define JUPITER_I_PLAT_LOW 2 /* pedestrian */
+#define JUPITER_I_PLAT_MED 5 /* land (e.g. automobile) */
+#define JUPITER_I_PLAT_HIGH 6 /* air */
diff --git a/contrib/ntp/ntpd/ntp_config.c b/contrib/ntp/ntpd/ntp_config.c
new file mode 100644
index 0000000..beb4c48
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_config.c
@@ -0,0 +1,2579 @@
+/*
+ * ntp_config.c - read and apply configuration information
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_NETINFO
+# include <netinfo/ni.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+#include <isc/net.h>
+#include <isc/result.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <signal.h>
+#ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+#endif
+#if !defined(VMS)
+# ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+# endif
+#endif /* VMS */
+
+#ifdef SYS_WINNT
+# include <io.h>
+static HANDLE ResolverThreadHandle = NULL;
+HANDLE ResolverEventHandle;
+#else
+int resolver_pipe_fd[2]; /* used to let the resolver process alert the parent process */
+#endif /* SYS_WINNT */
+
+/*
+ * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS
+ * so #include these later.
+ */
+
+#include "ntp_config.h"
+#include "ntp_cmdargs.h"
+
+extern int priority_done;
+
+/*
+ * These routines are used to read the configuration file at
+ * startup time. An entry in the file must fit on a single line.
+ * Entries are processed as multiple tokens separated by white space
+ * Lines are considered terminated when a '#' is encountered. Blank
+ * lines are ignored.
+ */
+/*
+ * Translation table - keywords to function index
+ */
+struct keyword {
+ const char *text;
+ int keytype;
+};
+
+/*
+ * Command keywords
+ */
+static struct keyword keywords[] = {
+ { "automax", CONFIG_AUTOMAX },
+ { "broadcast", CONFIG_BROADCAST },
+ { "broadcastclient", CONFIG_BROADCASTCLIENT },
+ { "broadcastdelay", CONFIG_BDELAY },
+ { "calldelay", CONFIG_CDELAY},
+#ifdef OPENSSL
+ { "crypto", CONFIG_CRYPTO },
+#endif /* OPENSSL */
+ { "controlkey", CONFIG_CONTROLKEY },
+ { "disable", CONFIG_DISABLE },
+ { "driftfile", CONFIG_DRIFTFILE },
+ { "enable", CONFIG_ENABLE },
+ { "end", CONFIG_END },
+ { "filegen", CONFIG_FILEGEN },
+ { "fudge", CONFIG_FUDGE },
+ { "includefile", CONFIG_INCLUDEFILE },
+ { "keys", CONFIG_KEYS },
+ { "keysdir", CONFIG_KEYSDIR },
+ { "logconfig", CONFIG_LOGCONFIG },
+ { "logfile", CONFIG_LOGFILE },
+ { "manycastclient", CONFIG_MANYCASTCLIENT },
+ { "manycastserver", CONFIG_MANYCASTSERVER },
+ { "multicastclient", CONFIG_MULTICASTCLIENT },
+ { "peer", CONFIG_PEER },
+ { "phone", CONFIG_PHONE },
+ { "pidfile", CONFIG_PIDFILE },
+ { "discard", CONFIG_DISCARD },
+ { "requestkey", CONFIG_REQUESTKEY },
+ { "restrict", CONFIG_RESTRICT },
+ { "revoke", CONFIG_REVOKE },
+ { "server", CONFIG_SERVER },
+ { "setvar", CONFIG_SETVAR },
+ { "statistics", CONFIG_STATISTICS },
+ { "statsdir", CONFIG_STATSDIR },
+ { "tick", CONFIG_ADJ },
+ { "tinker", CONFIG_TINKER },
+ { "tos", CONFIG_TOS },
+ { "trap", CONFIG_TRAP },
+ { "trustedkey", CONFIG_TRUSTEDKEY },
+ { "ttl", CONFIG_TTL },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "peer", "server", "broadcast" modifier keywords
+ */
+static struct keyword mod_keywords[] = {
+ { "autokey", CONF_MOD_SKEY },
+ { "burst", CONF_MOD_BURST },
+ { "iburst", CONF_MOD_IBURST },
+ { "key", CONF_MOD_KEY },
+ { "maxpoll", CONF_MOD_MAXPOLL },
+ { "minpoll", CONF_MOD_MINPOLL },
+ { "mode", CONF_MOD_MODE }, /* refclocks */
+ { "noselect", CONF_MOD_NOSELECT },
+ { "preempt", CONF_MOD_PREEMPT },
+ { "true", CONF_MOD_TRUE },
+ { "prefer", CONF_MOD_PREFER },
+ { "ttl", CONF_MOD_TTL }, /* NTP peers */
+ { "version", CONF_MOD_VERSION },
+ { "dynamic", CONF_MOD_DYNAMIC },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "restrict" modifier keywords
+ */
+static struct keyword res_keywords[] = {
+ { "ignore", CONF_RES_IGNORE },
+ { "limited", CONF_RES_LIMITED },
+ { "kod", CONF_RES_DEMOBILIZE },
+ { "lowpriotrap", CONF_RES_LPTRAP },
+ { "mask", CONF_RES_MASK },
+ { "nomodify", CONF_RES_NOMODIFY },
+ { "nopeer", CONF_RES_NOPEER },
+ { "noquery", CONF_RES_NOQUERY },
+ { "noserve", CONF_RES_NOSERVE },
+ { "notrap", CONF_RES_NOTRAP },
+ { "notrust", CONF_RES_NOTRUST },
+ { "ntpport", CONF_RES_NTPPORT },
+ { "version", CONF_RES_VERSION },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "trap" modifier keywords
+ */
+static struct keyword trap_keywords[] = {
+ { "port", CONF_TRAP_PORT },
+ { "interface", CONF_TRAP_INTERFACE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "fudge" modifier keywords
+ */
+static struct keyword fudge_keywords[] = {
+ { "flag1", CONF_FDG_FLAG1 },
+ { "flag2", CONF_FDG_FLAG2 },
+ { "flag3", CONF_FDG_FLAG3 },
+ { "flag4", CONF_FDG_FLAG4 },
+ { "refid", CONF_FDG_REFID }, /* this mapping should be cleaned up (endianness, \0) - kd 20041031 */
+ { "stratum", CONF_FDG_STRATUM },
+ { "time1", CONF_FDG_TIME1 },
+ { "time2", CONF_FDG_TIME2 },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "filegen" modifier keywords
+ */
+static struct keyword filegen_keywords[] = {
+ { "disable", CONF_FGEN_FLAG_DISABLE },
+ { "enable", CONF_FGEN_FLAG_ENABLE },
+ { "file", CONF_FGEN_FILE },
+ { "link", CONF_FGEN_FLAG_LINK },
+ { "nolink", CONF_FGEN_FLAG_NOLINK },
+ { "type", CONF_FGEN_TYPE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "type" modifier keywords
+ */
+static struct keyword fgen_types[] = {
+ { "age", FILEGEN_AGE },
+ { "day", FILEGEN_DAY },
+ { "month", FILEGEN_MONTH },
+ { "none", FILEGEN_NONE },
+ { "pid", FILEGEN_PID },
+ { "week", FILEGEN_WEEK },
+ { "year", FILEGEN_YEAR },
+ { "", CONFIG_UNKNOWN}
+};
+
+/*
+ * "enable", "disable" modifier keywords
+ */
+static struct keyword flags_keywords[] = {
+ { "auth", PROTO_AUTHENTICATE },
+ { "bclient", PROTO_BROADCLIENT },
+ { "calibrate", PROTO_CAL },
+ { "kernel", PROTO_KERNEL },
+ { "monitor", PROTO_MONITOR },
+ { "ntp", PROTO_NTP },
+ { "stats", PROTO_FILEGEN },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "discard" modifier keywords
+ */
+static struct keyword discard_keywords[] = {
+ { "average", CONF_DISCARD_AVERAGE },
+ { "minimum", CONF_DISCARD_MINIMUM },
+ { "monitor", CONF_DISCARD_MONITOR },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "tinker" modifier keywords
+ */
+static struct keyword tinker_keywords[] = {
+ { "step", CONF_CLOCK_MAX },
+ { "panic", CONF_CLOCK_PANIC },
+ { "dispersion", CONF_CLOCK_PHI },
+ { "stepout", CONF_CLOCK_MINSTEP },
+ { "allan", CONF_CLOCK_ALLAN },
+ { "huffpuff", CONF_CLOCK_HUFFPUFF },
+ { "freq", CONF_CLOCK_FREQ },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "tos" modifier keywords
+ */
+static struct keyword tos_keywords[] = {
+ { "minclock", CONF_TOS_MINCLOCK },
+ { "maxclock", CONF_TOS_MAXCLOCK },
+ { "minsane", CONF_TOS_MINSANE },
+ { "floor", CONF_TOS_FLOOR },
+ { "ceiling", CONF_TOS_CEILING },
+ { "cohort", CONF_TOS_COHORT },
+ { "mindist", CONF_TOS_MINDISP },
+ { "maxdist", CONF_TOS_MAXDIST },
+ { "maxhop", CONF_TOS_MAXHOP },
+ { "beacon", CONF_TOS_BEACON },
+ { "orphan", CONF_TOS_ORPHAN },
+ { "", CONFIG_UNKNOWN }
+};
+
+#ifdef OPENSSL
+/*
+ * "crypto" modifier keywords
+ */
+static struct keyword crypto_keywords[] = {
+ { "cert", CONF_CRYPTO_CERT },
+ { "gqpar", CONF_CRYPTO_GQPAR },
+ { "host", CONF_CRYPTO_RSA },
+ { "ident", CONF_CRYPTO_IDENT },
+ { "iffpar", CONF_CRYPTO_IFFPAR },
+ { "leap", CONF_CRYPTO_LEAP },
+ { "mvpar", CONF_CRYPTO_MVPAR },
+ { "pw", CONF_CRYPTO_PW },
+ { "randfile", CONF_CRYPTO_RAND },
+ { "sign", CONF_CRYPTO_SIGN },
+ { "", CONFIG_UNKNOWN }
+};
+#endif /* OPENSSL */
+
+/*
+ * Address type selection, IPv4 or IPv4.
+ * Used on various lines.
+ */
+static struct keyword addr_type[] = {
+ { "-4", CONF_ADDR_IPV4 },
+ { "-6", CONF_ADDR_IPV6 },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "logconfig" building blocks
+ */
+struct masks {
+ const char *name;
+ unsigned long mask;
+};
+
+static struct masks logcfg_class[] = {
+ { "clock", NLOG_OCLOCK },
+ { "peer", NLOG_OPEER },
+ { "sync", NLOG_OSYNC },
+ { "sys", NLOG_OSYS },
+ { (char *)0, 0 }
+};
+
+static struct masks logcfg_item[] = {
+ { "info", NLOG_INFO },
+ { "allinfo", NLOG_SYSINFO|NLOG_PEERINFO|NLOG_CLOCKINFO|NLOG_SYNCINFO },
+ { "events", NLOG_EVENT },
+ { "allevents", NLOG_SYSEVENT|NLOG_PEEREVENT|NLOG_CLOCKEVENT|NLOG_SYNCEVENT },
+ { "status", NLOG_STATUS },
+ { "allstatus", NLOG_SYSSTATUS|NLOG_PEERSTATUS|NLOG_CLOCKSTATUS|NLOG_SYNCSTATUS },
+ { "statistics", NLOG_STATIST },
+ { "allstatistics", NLOG_SYSSTATIST|NLOG_PEERSTATIST|NLOG_CLOCKSTATIST|NLOG_SYNCSTATIST },
+ { "allclock", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OCLOCK },
+ { "allpeer", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OPEER },
+ { "allsys", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYS },
+ { "allsync", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYNC },
+ { "all", NLOG_SYSMASK|NLOG_PEERMASK|NLOG_CLOCKMASK|NLOG_SYNCMASK },
+ { (char *)0, 0 }
+};
+
+/*
+ * Limits on things
+ */
+#define MAXTOKENS 20 /* 20 tokens on line */
+#define MAXLINE 1024 /* maximum length of line */
+#define MAXPHONE 10 /* maximum number of phone strings */
+#define MAXPPS 20 /* maximum length of PPS device string */
+#define MAXINCLUDELEVEL 5 /* maximum include file levels */
+
+/*
+ * Miscellaneous macros
+ */
+#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
+#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#define KEY_TYPE_MD5 4
+
+/*
+ * File descriptor used by the resolver save routines, and temporary file
+ * name.
+ */
+int call_resolver = 1; /* ntp-genkeys sets this to 0, for example */
+static FILE *res_fp;
+#ifndef SYS_WINNT
+static char res_file[20]; /* enough for /tmp/ntpXXXXXX\0 */
+#define RES_TEMPFILE "/tmp/ntpXXXXXX"
+#else
+static char res_file[MAX_PATH];
+#endif /* SYS_WINNT */
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+
+short default_ai_family = AF_UNSPEC; /* Default either IPv4 or IPv6 */
+char *sys_phone[MAXPHONE] = {NULL}; /* ACTS phone numbers */
+char *keysdir = NTP_KEYSDIR; /* crypto keys directory */
+#if defined(HAVE_SCHED_SETSCHEDULER)
+int config_priority_override = 0;
+int config_priority;
+#endif
+
+const char *config_file;
+#ifdef HAVE_NETINFO
+ struct netinfo_config_state *config_netinfo = NULL;
+ int check_netinfo = 1;
+#endif /* HAVE_NETINFO */
+#ifdef SYS_WINNT
+ char *alt_config_file;
+ LPTSTR temp;
+ char config_file_storage[MAX_PATH];
+ char alt_config_file_storage[MAX_PATH];
+#endif /* SYS_WINNT */
+
+#ifdef HAVE_NETINFO
+/*
+ * NetInfo configuration state
+ */
+struct netinfo_config_state {
+ void *domain; /* domain with config */
+ ni_id config_dir; /* ID config dir */
+ int prop_index; /* current property */
+ int val_index; /* current value */
+ char **val_list; /* value list */
+};
+#endif
+
+/*
+ * Function prototypes
+ */
+static unsigned long get_pfxmatch P((char **, struct masks *));
+static unsigned long get_match P((char *, struct masks *));
+static unsigned long get_logmask P((char *));
+#ifdef HAVE_NETINFO
+static struct netinfo_config_state *get_netinfo_config P((void));
+static void free_netinfo_config P((struct netinfo_config_state *));
+static int gettokens_netinfo P((struct netinfo_config_state *, char **, int *));
+#endif
+static int gettokens P((FILE *, char *, char **, int *));
+static int matchkey P((char *, struct keyword *, int));
+enum gnn_type {
+ t_UNK, /* Unknown */
+ t_REF, /* Refclock */
+ t_MSK /* Network Mask */
+ };
+static int getnetnum P((const char *, struct sockaddr_storage *, int,
+ enum gnn_type));
+static void save_resolve P((char *, int, int, int, int, u_int, int,
+ keyid_t, u_char *));
+static void do_resolve_internal P((void));
+static void abort_resolve P((void));
+#if !defined(VMS) && !defined(SYS_WINNT)
+static RETSIGTYPE catchchild P((int));
+#endif /* VMS */
+
+/*
+ * get_pfxmatch - find value for prefixmatch
+ * and update char * accordingly
+ */
+static unsigned long
+get_pfxmatch(
+ char ** s,
+ struct masks *m
+ )
+{
+ while (m->name) {
+ if (strncmp(*s, m->name, strlen(m->name)) == 0) {
+ *s += strlen(m->name);
+ return m->mask;
+ } else {
+ m++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get_match - find logmask value
+ */
+static unsigned long
+get_match(
+ char *s,
+ struct masks *m
+ )
+{
+ while (m->name) {
+ if (strcmp(s, m->name) == 0) {
+ return m->mask;
+ } else {
+ m++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get_logmask - build bitmask for ntp_syslogmask
+ */
+static unsigned long
+get_logmask(
+ char *s
+ )
+{
+ char *t;
+ unsigned long offset;
+ unsigned long mask;
+
+ t = s;
+ offset = get_pfxmatch(&t, logcfg_class);
+ mask = get_match(t, logcfg_item);
+
+ if (mask)
+ return mask << offset;
+ else
+ msyslog(LOG_ERR, "logconfig: illegal argument %s - ignored", s);
+
+ return 0;
+}
+
+
+/*
+ * getconfig - get command line options and read the configuration file
+ */
+void
+getconfig(
+ int argc,
+ char *argv[]
+ )
+{
+ register int i;
+ int c;
+ int errflg;
+ int status;
+ int istart;
+ int peerversion;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ long stratum;
+ unsigned long ul;
+ keyid_t peerkey;
+ u_char *peerkeystr;
+ u_long fudgeflag;
+ u_int peerflags;
+ int hmode;
+ struct sockaddr_storage peeraddr;
+ struct sockaddr_storage maskaddr;
+ FILE *fp[MAXINCLUDELEVEL+1];
+ FILE *includefile;
+ int includelevel = 0;
+ char line[MAXLINE];
+ char *(tokens[MAXTOKENS]);
+ int ntokens = 0;
+ int tok = CONFIG_UNKNOWN;
+ struct interface *localaddr;
+ struct refclockstat clock_stat;
+ FILEGEN *filegen;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+
+#ifndef SYS_WINNT
+ config_file = CONFIG_FILE;
+#else
+ temp = CONFIG_FILE;
+ if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n");
+ exit(1);
+ }
+ config_file = config_file_storage;
+
+ temp = ALT_CONFIG_FILE;
+ if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n");
+ exit(1);
+ }
+ alt_config_file = alt_config_file_storage;
+
+#endif /* SYS_WINNT */
+ res_fp = NULL;
+ ntp_syslogmask = NLOG_SYNCMASK; /* set more via logconfig */
+
+ /*
+ * install a non default variable with this daemon version
+ */
+ (void) sprintf(line, "daemon_version=\"%s\"", Version);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Say how we're setting the time of day
+ */
+ (void) sprintf(line, "settimeofday=\"%s\"", set_tod_using);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Initialize the loop.
+ */
+ loop_config(LOOP_DRIFTINIT, 0.);
+
+ getCmdOpts(argc, argv);
+
+ if (
+ (fp[0] = fopen(FindConfig(config_file), "r")) == NULL
+#ifdef HAVE_NETINFO
+ /* If there is no config_file, try NetInfo. */
+ && check_netinfo && !(config_netinfo = get_netinfo_config())
+#endif /* HAVE_NETINFO */
+ ) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file));
+#ifdef SYS_WINNT
+ /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
+
+ if ((fp[0] = fopen(FindConfig(alt_config_file), "r")) == NULL) {
+
+ /*
+ * Broadcast clients can sometimes run without
+ * a configuration file.
+ */
+
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file));
+ return;
+ }
+#else /* not SYS_WINNT */
+ return;
+#endif /* not SYS_WINNT */
+ }
+
+ for (;;) {
+ if (tok == CONFIG_END)
+ break;
+ if (fp[includelevel])
+ tok = gettokens(fp[includelevel], line, tokens, &ntokens);
+#ifdef HAVE_NETINFO
+ else
+ tok = gettokens_netinfo(config_netinfo, tokens, &ntokens);
+#endif /* HAVE_NETINFO */
+
+ if (tok == CONFIG_UNKNOWN) {
+ if (includelevel > 0) {
+ fclose(fp[includelevel--]);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ switch(tok) {
+ case CONFIG_PEER:
+ case CONFIG_SERVER:
+ case CONFIG_MANYCASTCLIENT:
+ case CONFIG_BROADCAST:
+ if (tok == CONFIG_PEER)
+ hmode = MODE_ACTIVE;
+ else if (tok == CONFIG_SERVER)
+ hmode = MODE_CLIENT;
+ else if (tok == CONFIG_MANYCASTCLIENT)
+ hmode = MODE_CLIENT;
+ else
+ hmode = MODE_BROADCAST;
+
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "No address for %s, line ignored",
+ tokens[0]);
+ break;
+ }
+
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ peeraddr.ss_family = default_ai_family;
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ status = getnetnum(tokens[istart], &peeraddr, 0, t_UNK);
+ if (status == -1)
+ break; /* Found IPv6 address */
+ if(status != 1) {
+ errflg = -1;
+ } else {
+ errflg = 0;
+
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ /*
+ * Shouldn't be able to specify multicast
+ * address for server/peer!
+ * and unicast address for manycastclient!
+ */
+ if (peeraddr.ss_family == AF_INET) {
+ if (((tok == CONFIG_SERVER) ||
+ (tok == CONFIG_PEER)) &&
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ if ((tok == CONFIG_MANYCASTCLIENT) &&
+ !IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ }
+ else if(peeraddr.ss_family == AF_INET6) {
+ if (((tok == CONFIG_SERVER) ||
+ (tok == CONFIG_PEER)) &&
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure in valid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ if ((tok == CONFIG_MANYCASTCLIENT) &&
+ !IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure in valid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ }
+ }
+ if (peeraddr.ss_family == AF_INET6 &&
+ isc_net_probeipv6() != ISC_R_SUCCESS)
+ break;
+
+ peerversion = NTP_VERSION;
+ minpoll = NTP_MINDPOLL;
+ maxpoll = NTP_MAXDPOLL;
+ peerkey = 0;
+ peerkeystr = (u_char *)"*";
+ peerflags = 0;
+ ttl = 0;
+ istart++;
+ for (i = istart; i < ntokens; i++)
+ switch (matchkey(tokens[i], mod_keywords, 1)) {
+ case CONF_MOD_VERSION:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "peer/server version requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if ((u_char)peerversion > NTP_VERSION
+ || (u_char)peerversion < NTP_OLDVERSION) {
+ msyslog(LOG_ERR,
+ "inappropriate version number %s, line ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_KEY:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "key: argument required");
+ errflg = 1;
+ break;
+ }
+ peerkey = (int)atol(tokens[++i]);
+ peerflags |= FLAG_AUTHENABLE;
+ break;
+
+ case CONF_MOD_MINPOLL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "minpoll: argument required");
+ errflg = 1;
+ break;
+ }
+ minpoll = atoi(tokens[++i]);
+ if (minpoll < NTP_MINPOLL) {
+ msyslog(LOG_INFO,
+ "minpoll: provided value (%d) is below minimum (%d)",
+ minpoll, NTP_MINPOLL);
+ minpoll = NTP_MINPOLL;
+ }
+ break;
+
+ case CONF_MOD_MAXPOLL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "maxpoll: argument required"
+ );
+ errflg = 1;
+ break;
+ }
+ maxpoll = atoi(tokens[++i]);
+ if (maxpoll > NTP_MAXPOLL) {
+ msyslog(LOG_INFO,
+ "maxpoll: provided value (%d) is above maximum (%d)",
+ maxpoll, NTP_MAXPOLL);
+ maxpoll = NTP_MAXPOLL;
+ }
+ break;
+
+ case CONF_MOD_PREFER:
+ peerflags |= FLAG_PREFER;
+ break;
+
+ case CONF_MOD_PREEMPT:
+ peerflags |= FLAG_PREEMPT;
+ break;
+
+ case CONF_MOD_NOSELECT:
+ peerflags |= FLAG_NOSELECT;
+ break;
+
+ case CONF_MOD_TRUE:
+ peerflags |= FLAG_TRUE;
+
+ case CONF_MOD_BURST:
+ peerflags |= FLAG_BURST;
+ break;
+
+ case CONF_MOD_IBURST:
+ peerflags |= FLAG_IBURST;
+ break;
+
+ case CONF_MOD_DYNAMIC:
+ msyslog(LOG_WARNING,
+ "Warning: the \"dynamic\" keyword has been obsoleted"
+ " and will be removed in the next release\n");
+ break;
+
+#ifdef OPENSSL
+ case CONF_MOD_SKEY:
+ peerflags |= FLAG_SKEY |
+ FLAG_AUTHENABLE;
+ break;
+#endif /* OPENSSL */
+
+ case CONF_MOD_TTL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "ttl: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ if (ttl >= MAX_TTL) {
+ msyslog(LOG_ERR,
+ "ttl: invalid argument");
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_MODE:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "mode: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = 1;
+ break;
+ }
+ if (minpoll > maxpoll) {
+ msyslog(LOG_ERR,
+ "config error: minpoll > maxpoll");
+ errflg = 1;
+ }
+ if (errflg == 0) {
+ if (peer_config(&peeraddr,
+ ANY_INTERFACE_CHOOSE(&peeraddr), hmode,
+ peerversion, minpoll, maxpoll, peerflags,
+ ttl, peerkey, peerkeystr) == 0) {
+ msyslog(LOG_ERR,
+ "configuration of %s failed",
+ stoa(&peeraddr));
+ }
+ } else if (errflg == -1) {
+ save_resolve(tokens[1], hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl,
+ peerkey, peerkeystr);
+ }
+ break;
+
+ case CONFIG_DRIFTFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_FREQ_FILE, tokens[1]);
+ else
+ stats_config(STATS_FREQ_FILE, (char *)0);
+ stats_write_period = stats_write_tolerance = 0;
+ if (ntokens >= 3)
+ stats_write_period = 60 * atol(tokens[2]);
+ if (stats_write_period <= 0)
+ stats_write_period = 3600;
+ if (ntokens >= 4) {
+ double ftemp;
+ sscanf(tokens[3], "%lf", &ftemp);
+ stats_write_tolerance = ftemp / 100;
+ }
+ break;
+
+ case CONFIG_PIDFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_PID_FILE, tokens[1]);
+ else
+ stats_config(STATS_PID_FILE, (char *)0);
+ break;
+
+ case CONFIG_END:
+ for ( i = 0; i <= includelevel; i++ ) {
+ fclose(fp[i]);
+ }
+ break;
+
+ case CONFIG_INCLUDEFILE:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR, "includefile needs one argument");
+ break;
+ }
+ if (includelevel >= MAXINCLUDELEVEL) {
+ fprintf(stderr, "getconfig: Maximum include file level exceeded.\n");
+ msyslog(LOG_INFO, "getconfig: Maximum include file level exceeded.");
+ break;
+ }
+ includefile = fopen(FindConfig(tokens[1]), "r");
+ if (includefile == NULL) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(tokens[1]));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(tokens[1]));
+ break;
+ }
+ fp[++includelevel] = includefile;
+ break;
+
+ case CONFIG_LOGFILE:
+ if (ntokens >= 2) {
+ FILE *new_file;
+
+ new_file = fopen(tokens[1], "a");
+ if (new_file != NULL) {
+ NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "logging to file %s", tokens[1]);
+ if (syslog_file != NULL &&
+ fileno(syslog_file) != fileno(new_file))
+ (void)fclose(syslog_file);
+
+ syslog_file = new_file;
+ syslogit = 0;
+ }
+ else
+ msyslog(LOG_ERR,
+ "Cannot open log file %s",
+ tokens[1]);
+ }
+ else
+ msyslog(LOG_ERR, "logfile needs one argument");
+ break;
+
+ case CONFIG_LOGCONFIG:
+ for (i = 1; i < ntokens; i++)
+ {
+ int add = 1;
+ int equals = 0;
+ char * s = &tokens[i][0];
+
+ switch (*s) {
+ case '+':
+ case '-':
+ case '=':
+ add = *s == '+';
+ equals = *s == '=';
+ s++;
+ break;
+
+ default:
+ break;
+ }
+ if (equals) {
+ ntp_syslogmask = get_logmask(s);
+ } else {
+ if (add) {
+ ntp_syslogmask |= get_logmask(s);
+ } else {
+ ntp_syslogmask &= ~get_logmask(s);
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("ntp_syslogmask = 0x%08lx (%s)\n", ntp_syslogmask, tokens[i]);
+#endif
+ }
+ break;
+
+ case CONFIG_BROADCASTCLIENT:
+ if (ntokens == 1) {
+ proto_config(PROTO_BROADCLIENT, 1, 0., NULL);
+ } else {
+ proto_config(PROTO_BROADCLIENT, 2, 0., NULL);
+ }
+ break;
+
+ case CONFIG_MULTICASTCLIENT:
+ case CONFIG_MANYCASTSERVER:
+ if (ntokens > 1) {
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ peeraddr.ss_family = default_ai_family;
+ switch (matchkey(tokens[istart],
+ addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+ /*
+ * Abuse maskaddr to store the prefered ip
+ * version.
+ */
+ memset((char *)&maskaddr, 0, sizeof(maskaddr));
+ maskaddr.ss_family = peeraddr.ss_family;
+
+ for (i = istart; i < ntokens; i++) {
+ memset((char *)&peeraddr, 0,
+ sizeof(peeraddr));
+ peeraddr.ss_family = maskaddr.ss_family;
+ if (getnetnum(tokens[i], &peeraddr, 1,
+ t_UNK) == 1)
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., &peeraddr);
+ }
+ } else
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., NULL);
+ if (tok == CONFIG_MULTICASTCLIENT)
+ proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL);
+ else if (tok == CONFIG_MANYCASTSERVER)
+ sys_manycastserver = 1;
+ break;
+
+ case CONFIG_KEYS:
+ if (ntokens >= 2) {
+ getauthkeys(tokens[1]);
+ }
+ break;
+
+ case CONFIG_KEYSDIR:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "Keys directory name required");
+ break;
+ }
+ keysdir = (char *)emalloc(strlen(tokens[1]) + 1);
+ strcpy(keysdir, tokens[1]);
+ break;
+
+ case CONFIG_TINKER:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+ double ftemp;
+
+ temp = matchkey(tokens[i++], tinker_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "tinker: missing argument");
+ errflg++;
+ break;
+ }
+ sscanf(tokens[i], "%lf", &ftemp);
+ switch(temp) {
+
+ case CONF_CLOCK_MAX:
+ loop_config(LOOP_MAX, ftemp);
+ break;
+
+ case CONF_CLOCK_PANIC:
+ loop_config(LOOP_PANIC, ftemp);
+ break;
+
+ case CONF_CLOCK_PHI:
+ loop_config(LOOP_PHI, ftemp);
+ break;
+
+ case CONF_CLOCK_MINSTEP:
+ loop_config(LOOP_MINSTEP, ftemp);
+ break;
+
+ case CONF_CLOCK_ALLAN:
+ loop_config(LOOP_ALLAN, ftemp);
+ break;
+
+ case CONF_CLOCK_HUFFPUFF:
+ loop_config(LOOP_HUFFPUFF, ftemp);
+ break;
+
+ case CONF_CLOCK_FREQ:
+ loop_config(LOOP_FREQ, ftemp);
+ break;
+ }
+ }
+ break;
+
+ case CONFIG_TOS:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+ double ftemp;
+
+ temp = matchkey(tokens[i++], tos_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "tos: missing argument");
+ errflg++;
+ break;
+ }
+ sscanf(tokens[i], "%lf", &ftemp);
+ switch(temp) {
+
+ case CONF_TOS_MINCLOCK:
+ proto_config(PROTO_MINCLOCK, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MAXCLOCK:
+ proto_config(PROTO_MAXCLOCK, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MINSANE:
+ proto_config(PROTO_MINSANE, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_FLOOR:
+ proto_config(PROTO_FLOOR, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_CEILING:
+ proto_config(PROTO_CEILING, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_COHORT:
+ proto_config(PROTO_COHORT, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MINDISP:
+ proto_config(PROTO_MINDISP, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MAXDIST:
+ proto_config(PROTO_MAXDIST, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MAXHOP:
+ proto_config(PROTO_MAXHOP, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_ORPHAN:
+ proto_config(PROTO_ORPHAN, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_BEACON:
+ proto_config(PROTO_BEACON, 0, ftemp, NULL);
+ break;
+ }
+ }
+ break;
+
+ case CONFIG_TTL:
+ for (i = 1; i < ntokens && i < MAX_TTL; i++) {
+ sys_ttl[i - 1] = (u_char) atoi(tokens[i]);
+ sys_ttlmax = i - 1;
+ }
+ break;
+
+ case CONFIG_DISCARD:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+
+ temp = matchkey(tokens[i++],
+ discard_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "discard: missing argument");
+ errflg++;
+ break;
+ }
+ switch(temp) {
+ case CONF_DISCARD_AVERAGE:
+ res_avg_interval = atoi(tokens[i]);
+ break;
+
+ case CONF_DISCARD_MINIMUM:
+ res_min_interval = atoi(tokens[i]);
+ break;
+
+ case CONF_DISCARD_MONITOR:
+ mon_age = atoi(tokens[i]);
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "discard: unknown keyword");
+ break;
+ }
+ }
+ break;
+
+#ifdef OPENSSL
+ case CONFIG_REVOKE:
+ if (ntokens >= 2)
+ sys_revoke = (u_char) max(atoi(tokens[1]), KEY_REVOKE);
+ break;
+
+ case CONFIG_AUTOMAX:
+ if (ntokens >= 2)
+ sys_automax = 1 << max(atoi(tokens[1]), 10);
+ break;
+
+ case CONFIG_CRYPTO:
+ if (ntokens == 1) {
+ crypto_config(CRYPTO_CONF_NONE, NULL);
+ break;
+ }
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+
+ temp = matchkey(tokens[i++],
+ crypto_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "crypto: missing argument");
+ errflg++;
+ break;
+ }
+ switch(temp) {
+
+ case CONF_CRYPTO_CERT:
+ crypto_config(CRYPTO_CONF_CERT,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_RSA:
+ crypto_config(CRYPTO_CONF_PRIV,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_IDENT:
+ crypto_config(CRYPTO_CONF_IDENT,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_IFFPAR:
+ crypto_config(CRYPTO_CONF_IFFPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_GQPAR:
+ crypto_config(CRYPTO_CONF_GQPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_MVPAR:
+ crypto_config(CRYPTO_CONF_MVPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_LEAP:
+ crypto_config(CRYPTO_CONF_LEAP,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_PW:
+ crypto_config(CRYPTO_CONF_PW,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_RAND:
+ crypto_config(CRYPTO_CONF_RAND,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_SIGN:
+ crypto_config(CRYPTO_CONF_SIGN,
+ tokens[i]);
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "crypto: unknown keyword");
+ break;
+ }
+ }
+ break;
+#endif /* OPENSSL */
+
+ case CONFIG_RESTRICT:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR, "restrict requires an address");
+ break;
+ }
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ peeraddr.ss_family = default_ai_family;
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ /*
+ * Assume default means an IPv4 address, except
+ * if forced by a -4 or -6.
+ */
+ if (STREQ(tokens[istart], "default")) {
+ if (peeraddr.ss_family == 0)
+ peeraddr.ss_family = AF_INET;
+ } else if (getnetnum(tokens[istart], &peeraddr, 1,
+ t_UNK) != 1)
+ break;
+
+ /*
+ * Use peerversion as flags, peerkey as mflags. Ick.
+ */
+ peerversion = 0;
+ peerkey = 0;
+ errflg = 0;
+ SET_HOSTMASK(&maskaddr, peeraddr.ss_family);
+ istart++;
+ for (i = istart; i < ntokens; i++) {
+ switch (matchkey(tokens[i], res_keywords, 1)) {
+ case CONF_RES_MASK:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "mask keyword needs argument");
+ errflg++;
+ break;
+ }
+ i++;
+ if (getnetnum(tokens[i], &maskaddr, 1,
+ t_MSK) != 1)
+ errflg++;
+ break;
+
+ case CONF_RES_IGNORE:
+ peerversion |= RES_IGNORE;
+ break;
+
+ case CONF_RES_NOSERVE:
+ peerversion |= RES_DONTSERVE;
+ break;
+
+ case CONF_RES_NOTRUST:
+ peerversion |= RES_DONTTRUST;
+ break;
+
+ case CONF_RES_NOQUERY:
+ peerversion |= RES_NOQUERY;
+ break;
+
+ case CONF_RES_NOMODIFY:
+ peerversion |= RES_NOMODIFY;
+ break;
+
+ case CONF_RES_NOPEER:
+ peerversion |= RES_NOPEER;
+ break;
+
+ case CONF_RES_NOTRAP:
+ peerversion |= RES_NOTRAP;
+ break;
+
+ case CONF_RES_LPTRAP:
+ peerversion |= RES_LPTRAP;
+ break;
+
+ case CONF_RES_NTPPORT:
+ peerkey |= RESM_NTPONLY;
+ break;
+
+ case CONF_RES_VERSION:
+ peerversion |= RES_VERSION;
+ break;
+
+ case CONF_RES_DEMOBILIZE:
+ peerversion |= RES_DEMOBILIZE;
+ break;
+
+ case CONF_RES_LIMITED:
+ peerversion |= RES_LIMITED;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+ }
+ if (SOCKNUL(&peeraddr))
+ ANYSOCK(&maskaddr);
+ if (!errflg)
+ hack_restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr,
+ (int)peerkey, peerversion);
+ break;
+
+ case CONFIG_BDELAY:
+ if (ntokens >= 2) {
+ double tmp;
+
+ if (sscanf(tokens[1], "%lf", &tmp) != 1) {
+ msyslog(LOG_ERR,
+ "broadcastdelay value %s undecodable",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_BROADDELAY, 0, tmp, NULL);
+ }
+ }
+ break;
+
+ case CONFIG_CDELAY:
+ if (ntokens >= 2) {
+ u_long ui;
+
+ if (sscanf(tokens[1], "%ld", &ui) != 1)
+ msyslog(LOG_ERR,
+ "illegal value - line ignored");
+ else
+ proto_config(PROTO_CALLDELAY, ui, 0, NULL);
+ }
+ break;
+
+ case CONFIG_TRUSTEDKEY:
+ for (i = 1; i < ntokens; i++) {
+ keyid_t tkey;
+
+ tkey = atol(tokens[i]);
+ if (tkey == 0) {
+ msyslog(LOG_ERR,
+ "trusted key %s unlikely",
+ tokens[i]);
+ } else {
+ authtrust(tkey, 1);
+ }
+ }
+ break;
+
+ case CONFIG_REQUESTKEY:
+ if (ntokens >= 2) {
+ if (!atouint(tokens[1], &ul)) {
+ msyslog(LOG_ERR,
+ "%s is undecodable as request key",
+ tokens[1]);
+ } else if (ul == 0) {
+ msyslog(LOG_ERR,
+ "%s makes a poor request keyid",
+ tokens[1]);
+ } else {
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "set info_auth_key to %08lx\n", ul);
+#endif
+ info_auth_keyid = (keyid_t)ul;
+ }
+ }
+ break;
+
+ case CONFIG_CONTROLKEY:
+ if (ntokens >= 2) {
+ keyid_t ckey;
+
+ ckey = atol(tokens[1]);
+ if (ckey == 0) {
+ msyslog(LOG_ERR,
+ "%s makes a poor control keyid",
+ tokens[1]);
+ } else {
+ ctl_auth_keyid = ckey;
+ }
+ }
+ break;
+
+ case CONFIG_TRAP:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no address for trap command, line ignored");
+ break;
+ }
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ peeraddr.ss_family = default_ai_family;
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ if (getnetnum(tokens[istart], &peeraddr, 1, t_UNK) != 1)
+ break;
+
+ /*
+ * Use peerversion for port number. Barf.
+ */
+ errflg = 0;
+ peerversion = 0;
+ localaddr = 0;
+ istart++;
+ for (i = istart; i < ntokens-1; i++)
+ switch (matchkey(tokens[i], trap_keywords, 1)) {
+ case CONF_TRAP_PORT:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "trap port requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if (peerversion <= 0
+ || peerversion > 32767) {
+ msyslog(LOG_ERR,
+ "invalid port number %s, trap ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_TRAP_INTERFACE:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "trap interface requires an argument");
+ errflg = 1;
+ break;
+ }
+
+ memset((char *)&maskaddr, 0,
+ sizeof(maskaddr));
+ maskaddr.ss_family = peeraddr.ss_family;
+ if (getnetnum(tokens[++i],
+ &maskaddr, 1, t_UNK) != 1) {
+ errflg = 1;
+ break;
+ }
+
+ localaddr = findinterface(&maskaddr);
+ if (localaddr == NULL) {
+ msyslog(LOG_ERR,
+ "can't find interface with address %s",
+ stoa(&maskaddr));
+ errflg = 1;
+ }
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+
+ if (!errflg) {
+ if (peerversion != 0)
+ ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons( (u_short) peerversion);
+ else
+ ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons(TRAPPORT);
+ if (localaddr == NULL)
+ localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ msyslog(LOG_ERR,
+ "can't set trap for %s, no resources",
+ stoa(&peeraddr));
+ }
+ break;
+
+ case CONFIG_FUDGE:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no address for fudge command, line ignored");
+ break;
+ }
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (getnetnum(tokens[1], &peeraddr, 1, t_REF) != 1)
+ break;
+
+ if (!ISREFCLOCKADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "%s is inappropriate address for the fudge command, line ignored",
+ stoa(&peeraddr));
+ break;
+ }
+
+ memset((void *)&clock_stat, 0, sizeof clock_stat);
+ fudgeflag = 0;
+ errflg = 0;
+ for (i = 2; i < ntokens-1; i++) {
+ switch (c = matchkey(tokens[i],
+ fudge_keywords, 1)) {
+ case CONF_FDG_TIME1:
+ if (sscanf(tokens[++i], "%lf",
+ &clock_stat.fudgetime1) != 1) {
+ msyslog(LOG_ERR,
+ "fudge %s time1 value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.haveflags |= CLK_HAVETIME1;
+ break;
+
+ case CONF_FDG_TIME2:
+ if (sscanf(tokens[++i], "%lf",
+ &clock_stat.fudgetime2) != 1) {
+ msyslog(LOG_ERR,
+ "fudge %s time2 value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.haveflags |= CLK_HAVETIME2;
+ break;
+
+
+ case CONF_FDG_STRATUM:
+ if (!atoint(tokens[++i], &stratum))
+ {
+ msyslog(LOG_ERR,
+ "fudge %s stratum value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.fudgeval1 = stratum;
+ clock_stat.haveflags |= CLK_HAVEVAL1;
+ break;
+
+ case CONF_FDG_REFID:
+ i++;
+ memcpy(&clock_stat.fudgeval2,
+ tokens[i], min(strlen(tokens[i]),
+ 4));
+ clock_stat.haveflags |= CLK_HAVEVAL2;
+ break;
+
+ case CONF_FDG_FLAG1:
+ case CONF_FDG_FLAG2:
+ case CONF_FDG_FLAG3:
+ case CONF_FDG_FLAG4:
+ if (!atouint(tokens[++i], &fudgeflag)
+ || fudgeflag > 1) {
+ msyslog(LOG_ERR,
+ "fudge %s flag value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ switch(c) {
+ case CONF_FDG_FLAG1:
+ c = CLK_FLAG1;
+ clock_stat.haveflags|=CLK_HAVEFLAG1;
+ break;
+ case CONF_FDG_FLAG2:
+ c = CLK_FLAG2;
+ clock_stat.haveflags|=CLK_HAVEFLAG2;
+ break;
+ case CONF_FDG_FLAG3:
+ c = CLK_FLAG3;
+ clock_stat.haveflags|=CLK_HAVEFLAG3;
+ break;
+ case CONF_FDG_FLAG4:
+ c = CLK_FLAG4;
+ clock_stat.haveflags|=CLK_HAVEFLAG4;
+ break;
+ }
+ if (fudgeflag == 0)
+ clock_stat.flags &= ~c;
+ else
+ clock_stat.flags |= c;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = -1;
+ break;
+ }
+ }
+
+#ifdef REFCLOCK
+ /*
+ * If reference clock support isn't defined the
+ * fudge line will still be accepted and syntax
+ * checked, but will essentially do nothing.
+ */
+ if (!errflg) {
+ refclock_control(&peeraddr, &clock_stat,
+ (struct refclockstat *)0);
+ }
+#endif
+ break;
+
+ case CONFIG_STATSDIR:
+ if (ntokens >= 2)
+ stats_config(STATS_STATSDIR,tokens[1]);
+ break;
+
+ case CONFIG_STATISTICS:
+ for (i = 1; i < ntokens; i++) {
+ filegen = filegen_get(tokens[i]);
+
+ if (filegen == NULL) {
+ msyslog(LOG_ERR,
+ "no statistics named %s available",
+ tokens[i]);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("enabling filegen for %s statistics \"%s%s\"\n",
+ tokens[i], filegen->prefix, filegen->basename);
+#endif
+ filegen->flag |= FGEN_FLAG_ENABLED;
+ }
+ break;
+
+ case CONFIG_FILEGEN:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no id for filegen command, line ignored");
+ break;
+ }
+
+ filegen = filegen_get(tokens[1]);
+ if (filegen == NULL) {
+ msyslog(LOG_ERR,
+ "unknown filegen \"%s\" ignored",
+ tokens[1]);
+ break;
+ }
+ /*
+ * peerversion is (ab)used for filegen file (index)
+ * peerkey is (ab)used for filegen type
+ * peerflags is (ab)used for filegen flags
+ */
+ peerversion = 0;
+ peerkey = filegen->type;
+ peerflags = filegen->flag;
+ errflg = 0;
+
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i],
+ filegen_keywords, 1)) {
+ case CONF_FGEN_FILE:
+ if (i >= ntokens - 1) {
+ msyslog(LOG_ERR,
+ "filegen %s file requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerversion = ++i;
+ break;
+ case CONF_FGEN_TYPE:
+ if (i >= ntokens -1) {
+ msyslog(LOG_ERR,
+ "filegen %s type requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerkey = matchkey(tokens[++i],
+ fgen_types, 1);
+ if (peerkey == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "filegen %s unknown type \"%s\"",
+ tokens[1], tokens[i]);
+ errflg = i;
+ break;
+ }
+ break;
+
+ case CONF_FGEN_FLAG_LINK:
+ peerflags |= FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_NOLINK:
+ peerflags &= ~FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_ENABLE:
+ peerflags |= FGEN_FLAG_ENABLED;
+ break;
+
+ case CONF_FGEN_FLAG_DISABLE:
+ peerflags &= ~FGEN_FLAG_ENABLED;
+ break;
+ }
+ }
+ if (!errflg)
+ filegen_config(filegen, tokens[peerversion],
+ (u_char)peerkey, (u_char)peerflags);
+ break;
+
+ case CONFIG_SETVAR:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no value for setvar command - line ignored");
+ } else {
+ set_sys_var(tokens[1], strlen(tokens[1])+1,
+ (u_short) (RW |
+ ((((ntokens > 2)
+ && !strcmp(tokens[2],
+ "default")))
+ ? DEF
+ : 0)));
+ }
+ break;
+
+ case CONFIG_ENABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords, 1);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "enable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 1, 0., NULL);
+ }
+ break;
+
+ case CONFIG_DISABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords, 1);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "disable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 0, 0., NULL);
+ }
+ break;
+
+ case CONFIG_PHONE:
+ for (i = 1; i < ntokens && i < MAXPHONE - 1; i++) {
+ sys_phone[i - 1] =
+ emalloc(strlen(tokens[i]) + 1);
+ strcpy(sys_phone[i - 1], tokens[i]);
+ }
+ sys_phone[i] = NULL;
+ break;
+
+ case CONFIG_ADJ: {
+ double ftemp;
+
+ sscanf(tokens[1], "%lf", &ftemp);
+ proto_config(PROTO_ADJ, 0, ftemp, NULL);
+ }
+ break;
+
+ }
+ }
+ if (fp[0])
+ (void)fclose(fp[0]);
+
+#ifdef HAVE_NETINFO
+ if (config_netinfo)
+ free_netinfo_config(config_netinfo);
+#endif /* HAVE_NETINFO */
+
+#if !defined(VMS) && !defined(SYS_VXWORKS)
+ /* find a keyid */
+ if (info_auth_keyid == 0)
+ req_keyid = 65535;
+ else
+ req_keyid = info_auth_keyid;
+
+ /* if doesn't exist, make up one at random */
+ if (!authhavekey(req_keyid)) {
+ char rankey[9];
+ int j;
+
+ for (i = 0; i < 8; i++)
+ for (j = 1; j < 100; ++j) {
+ rankey[i] = (char) (ntp_random() & 0xff);
+ if (rankey[i] != 0) break;
+ }
+ rankey[8] = 0;
+ authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey);
+ authtrust(req_keyid, 1);
+ if (!authhavekey(req_keyid)) {
+ msyslog(LOG_ERR, "getconfig: Couldn't generate a valid random key!");
+ /* HMS: Should this be fatal? */
+ }
+ }
+
+ /* save keyid so we will accept config requests with it */
+ info_auth_keyid = req_keyid;
+#endif /* !defined(VMS) && !defined(SYS_VXWORKS) */
+
+ if (res_fp != NULL) {
+ if (call_resolver) {
+ /*
+ * Need name resolution
+ */
+ do_resolve_internal();
+ }
+ }
+}
+
+
+#ifdef HAVE_NETINFO
+
+/*
+ * get_netinfo_config - find the nearest NetInfo domain with an ntp
+ * configuration and initialize the configuration state.
+ */
+static struct netinfo_config_state *
+get_netinfo_config()
+{
+ ni_status status;
+ void *domain;
+ ni_id config_dir;
+ struct netinfo_config_state *config;
+
+ if (ni_open(NULL, ".", &domain) != NI_OK) return NULL;
+
+ while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) {
+ void *next_domain;
+ if (ni_open(domain, "..", &next_domain) != NI_OK) {
+ ni_free(next_domain);
+ break;
+ }
+ ni_free(domain);
+ domain = next_domain;
+ }
+ if (status != NI_OK) {
+ ni_free(domain);
+ return NULL;
+ }
+
+ config = (struct netinfo_config_state *)malloc(sizeof(struct netinfo_config_state));
+ config->domain = domain;
+ config->config_dir = config_dir;
+ config->prop_index = 0;
+ config->val_index = 0;
+ config->val_list = NULL;
+
+ return config;
+}
+
+
+
+/*
+ * free_netinfo_config - release NetInfo configuration state
+ */
+static void
+free_netinfo_config(struct netinfo_config_state *config)
+{
+ ni_free(config->domain);
+ free(config);
+}
+
+
+
+/*
+ * gettokens_netinfo - return tokens from NetInfo
+ */
+static int
+gettokens_netinfo (
+ struct netinfo_config_state *config,
+ char **tokenlist,
+ int *ntokens
+ )
+{
+ int prop_index = config->prop_index;
+ int val_index = config->val_index;
+ char **val_list = config->val_list;
+
+ /*
+ * Iterate through each keyword and look for a property that matches it.
+ */
+ again:
+ if (!val_list) {
+ for (; prop_index < (sizeof(keywords)/sizeof(keywords[0])); prop_index++)
+ {
+ ni_namelist namelist;
+ struct keyword current_prop = keywords[prop_index];
+
+ /*
+ * For each value associated in the property, we're going to return
+ * a separate line. We squirrel away the values in the config state
+ * so the next time through, we don't need to do this lookup.
+ */
+ NI_INIT(&namelist);
+ if (ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist) == NI_OK) {
+ ni_index index;
+
+ /* Found the property, but it has no values */
+ if (namelist.ni_namelist_len == 0) continue;
+
+ if (! (val_list = config->val_list = (char**)malloc(sizeof(char*) * (namelist.ni_namelist_len + 1))))
+ { msyslog(LOG_ERR, "out of memory while configuring"); break; }
+
+ for (index = 0; index < namelist.ni_namelist_len; index++) {
+ char *value = namelist.ni_namelist_val[index];
+
+ if (! (val_list[index] = (char*)malloc(strlen(value)+1)))
+ { msyslog(LOG_ERR, "out of memory while configuring"); break; }
+
+ strcpy(val_list[index], value);
+ }
+ val_list[index] = NULL;
+
+ break;
+ }
+ ni_namelist_free(&namelist);
+ }
+ config->prop_index = prop_index;
+ }
+
+ /* No list; we're done here. */
+ if (!val_list) return CONFIG_UNKNOWN;
+
+ /*
+ * We have a list of values for the current property.
+ * Iterate through them and return each in order.
+ */
+ if (val_list[val_index])
+ {
+ int ntok = 1;
+ int quoted = 0;
+ char *tokens = val_list[val_index];
+
+ msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]);
+
+ (const char*)tokenlist[0] = keywords[prop_index].text;
+ for (ntok = 1; ntok < MAXTOKENS; ntok++) {
+ tokenlist[ntok] = tokens;
+ while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted))
+ quoted ^= (*tokens++ == '"');
+
+ if (ISEOL(*tokens)) {
+ *tokens = '\0';
+ break;
+ } else { /* must be space */
+ *tokens++ = '\0';
+ while (ISSPACE(*tokens)) tokens++;
+ if (ISEOL(*tokens)) break;
+ }
+ }
+
+ if (ntok == MAXTOKENS) {
+ /* HMS: chomp it to lose the EOL? */
+ msyslog(LOG_ERR,
+ "gettokens_netinfo: too many tokens. Ignoring: %s",
+ tokens);
+ } else {
+ *ntokens = ntok + 1;
+ }
+
+ config->val_index++; /* HMS: Should this be in the 'else'? */
+
+ return keywords[prop_index].keytype;
+ }
+
+ /* We're done with the current property. */
+ prop_index = ++config->prop_index;
+
+ /* Free val_list and reset counters. */
+ for (val_index = 0; val_list[val_index]; val_index++)
+ free(val_list[val_index]);
+ free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0;
+
+ goto again;
+}
+
+#endif /* HAVE_NETINFO */
+
+
+/*
+ * gettokens - read a line and return tokens
+ */
+static int
+gettokens (
+ FILE *fp,
+ char *line,
+ char **tokenlist,
+ int *ntokens
+ )
+{
+ register char *cp;
+ register int ntok;
+ register int quoted = 0;
+
+ /*
+ * Find start of first token
+ */
+ again:
+ while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
+ cp = line;
+ while (ISSPACE(*cp))
+ cp++;
+ if (!ISEOL(*cp))
+ break;
+ }
+ if (cp == NULL) {
+ *ntokens = 0;
+ return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */
+ }
+
+ /*
+ * Now separate out the tokens
+ */
+ for (ntok = 0; ntok < MAXTOKENS; ntok++) {
+ tokenlist[ntok] = cp;
+ while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
+ quoted ^= (*cp++ == '"');
+
+ if (ISEOL(*cp)) {
+ *cp = '\0';
+ break;
+ } else { /* must be space */
+ *cp++ = '\0';
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ }
+ }
+
+ /* Heiko: Remove leading and trailing quotes around tokens */
+ {
+ int i,j = 0;
+
+
+ for (i = 0; i < ntok; i++) {
+ /* Now check if the first char is a quote and remove that */
+ if ( tokenlist[ntok][0] == '"' )
+ tokenlist[ntok]++;
+
+ /* Now check the last char ... */
+ j = strlen(tokenlist[ntok])-1;
+ if ( tokenlist[ntok][j] == '"' )
+ tokenlist[ntok][j] = '\0';
+ }
+
+ }
+
+ if (ntok == MAXTOKENS) {
+ --ntok;
+ /* HMS: chomp it to lose the EOL? */
+ msyslog(LOG_ERR,
+ "gettokens: too many tokens on the line. Ignoring %s",
+ cp);
+ } else {
+ /*
+ * Return the match
+ */
+ *ntokens = ntok + 1;
+ ntok = matchkey(tokenlist[0], keywords, 1);
+ if (ntok == CONFIG_UNKNOWN)
+ goto again;
+ }
+
+ return ntok;
+}
+
+
+
+/*
+ * matchkey - match a keyword to a list
+ */
+static int
+matchkey(
+ register char *word,
+ register struct keyword *keys,
+ int complain
+ )
+{
+ for (;;) {
+ if (keys->keytype == CONFIG_UNKNOWN) {
+ if (complain)
+ msyslog(LOG_ERR,
+ "configure: keyword \"%s\" unknown, line ignored",
+ word);
+ return CONFIG_UNKNOWN;
+ }
+ if (STRSAME(word, keys->text))
+ return keys->keytype;
+ keys++;
+ }
+}
+
+
+/*
+ * getnetnum - return a net number (this is crude, but careful)
+ */
+static int
+getnetnum(
+ const char *num,
+ struct sockaddr_storage *addr,
+ int complain,
+ enum gnn_type a_type
+ )
+{
+ struct addrinfo hints;
+ struct addrinfo *ptr;
+ int retval;
+
+#if 0
+ printf("getnetnum: <%s> is a %s (%d)\n",
+ num,
+ (a_type == t_UNK)
+ ? "t_UNK"
+ : (a_type == t_REF)
+ ? "t_REF"
+ : (a_type == t_MSK)
+ ? "t_MSK"
+ : "???",
+ a_type);
+#endif
+
+ /* Get host address. Looking for UDP datagram connection */
+ memset(&hints, 0, sizeof (hints));
+ if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6)
+ hints.ai_family = addr->ss_family;
+ else
+ hints.ai_family = AF_UNSPEC;
+ /*
+ * If we don't have an IPv6 stack, just look up IPv4 addresses
+ */
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ hints.ai_family = AF_INET;
+
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if (a_type != t_UNK) {
+ hints.ai_flags = AI_NUMERICHOST;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("getnetnum: calling getaddrinfo(%s,...)\n", num);
+#endif
+ retval = getaddrinfo(num, "ntp", &hints, &ptr);
+ if (retval != 0 ||
+ (ptr->ai_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) {
+ if (complain)
+ msyslog(LOG_ERR,
+ "getaddrinfo: \"%s\" invalid host address, ignored",
+ num);
+#ifdef DEBUG
+ if (debug > 0)
+ printf(
+ "getaddrinfo: \"%s\" invalid host address%s.\n",
+ num, (complain)
+ ? ", ignored"
+ : "");
+#endif
+ if (retval == 0 &&
+ ptr->ai_family == AF_INET6 &&
+ isc_net_probeipv6() != ISC_R_SUCCESS)
+ {
+ return -1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ memcpy(addr, ptr->ai_addr, ptr->ai_addrlen);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("getnetnum given %s, got %s (%s/%d)\n",
+ num, stoa(addr),
+ (a_type == t_UNK)
+ ? "t_UNK"
+ : (a_type == t_REF)
+ ? "t_REF"
+ : (a_type == t_MSK)
+ ? "t_MSK"
+ : "???",
+ a_type);
+#endif
+ freeaddrinfo(ptr);
+ return 1;
+}
+
+
+#if !defined(VMS) && !defined(SYS_WINNT)
+/*
+ * catchchild - receive the resolver's exit status
+ */
+static RETSIGTYPE
+catchchild(
+ int sig
+ )
+{
+ /*
+ * We only start up one child, and if we're here
+ * it should have already exited. Hence the following
+ * shouldn't hang. If it does, please tell me.
+ */
+#if !defined (SYS_WINNT) && !defined(SYS_VXWORKS)
+ (void) wait(0);
+#endif /* SYS_WINNT && VXWORKS*/
+}
+#endif /* VMS */
+
+
+/*
+ * save_resolve - save configuration info into a file for later name resolution
+ */
+static void
+save_resolve(
+ char *name,
+ int mode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t keyid,
+ u_char *keystr
+ )
+{
+#ifndef SYS_VXWORKS
+ if (res_fp == NULL) {
+#ifndef SYS_WINNT
+ (void) strcpy(res_file, RES_TEMPFILE);
+#else
+ /* no /tmp directory under NT */
+ {
+ if(!(GetTempPath((DWORD)MAX_PATH, (LPTSTR)res_file))) {
+ msyslog(LOG_ERR, "cannot get pathname for temporary directory: %m");
+ return;
+ }
+ (void) strcat(res_file, "ntpdXXXXXX");
+ }
+#endif /* SYS_WINNT */
+#ifdef HAVE_MKSTEMP
+ {
+ int fd;
+
+ res_fp = NULL;
+ if ((fd = mkstemp(res_file)) != -1)
+ res_fp = fdopen(fd, "r+");
+ }
+#else
+ (void) mktemp(res_file);
+ res_fp = fopen(res_file, "w");
+#endif
+ if (res_fp == NULL) {
+ msyslog(LOG_ERR, "open failed for %s: %m", res_file);
+ return;
+ }
+ }
+#ifdef DEBUG
+ if (debug) {
+ printf("resolving %s\n", name);
+ }
+#endif
+
+ (void)fprintf(res_fp, "%s %d %d %d %d %d %d %u %s\n", name,
+ mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("config: %s %d %d %d %d %x %d %u %s\n", name, mode,
+ version, minpoll, maxpoll, flags, ttl, keyid, keystr);
+#endif
+
+#else /* SYS_VXWORKS */
+ /* save resolve info to a struct */
+#endif /* SYS_VXWORKS */
+}
+
+
+/*
+ * abort_resolve - terminate the resolver stuff and delete the file
+ */
+static void
+abort_resolve(void)
+{
+ /*
+ * In an ideal world we would might reread the file and
+ * log the hosts which aren't getting configured. Since
+ * this is too much work, however, just close and delete
+ * the temp file.
+ */
+ if (res_fp != NULL)
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+#ifndef SYS_VXWORKS /* we don't open the file to begin with */
+#if !defined(VMS)
+ (void) unlink(res_file);
+#else
+ (void) delete(res_file);
+#endif /* VMS */
+#endif /* SYS_VXWORKS */
+}
+
+
+/*
+ * do_resolve_internal - start up the resolver function (not program)
+ */
+/*
+ * On VMS, this routine will simply refuse to resolve anything.
+ *
+ * Possible implementation: keep `res_file' in memory, do async
+ * name resolution via QIO, update from within completion AST.
+ * I'm unlikely to find the time for doing this, though. -wjm
+ */
+static void
+do_resolve_internal(void)
+{
+ int i;
+
+ if (res_fp == NULL) {
+ /* belch */
+ msyslog(LOG_ERR,
+ "do_resolve_internal: Fatal: res_fp == NULL");
+ exit(1);
+ }
+
+ /* we are done with this now */
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+#if !defined(VMS) && !defined (SYS_VXWORKS)
+ req_file = res_file; /* set up pointer to res file */
+#ifndef SYS_WINNT
+ (void) signal_no_reset(SIGCHLD, catchchild);
+
+#ifndef SYS_VXWORKS
+ /* the parent process will write to the pipe
+ * in order to wake up to child process
+ * which may be waiting in a select() call
+ * on the read fd */
+ if (pipe(resolver_pipe_fd) < 0) {
+ msyslog(LOG_ERR,
+ "unable to open resolver pipe");
+ exit(1);
+ }
+
+ i = fork();
+ /* Shouldn't the code below be re-ordered?
+ * I.e. first check if the fork() returned an error, then
+ * check whether we're parent or child.
+ * Martin Burnicki
+ */
+ if (i == 0) {
+ /*
+ * this used to close everything
+ * I don't think this is necessary
+ */
+ /*
+ * To the unknown commenter above:
+ * Well, I think it's better to clean up
+ * after oneself. I have had problems with
+ * refclock-io when intres was running - things
+ * where fine again when ntpintres was gone.
+ * So some systems react erratic at least.
+ *
+ * Frank Kardel
+ *
+ * 94-11-16:
+ * Further debugging has proven that the above is
+ * absolutely harmful. The internal resolver
+ * is still in the SIGIO process group and the lingering
+ * async io information causes it to process requests from
+ * all file decriptor causing a race between the NTP daemon
+ * and the resolver. which then eats data when it wins 8-(.
+ * It is absolutly necessary to kill any IO associations
+ * shared with the NTP daemon.
+ *
+ * We also block SIGIO (currently no ports means to
+ * disable the signal handle for IO).
+ *
+ * Thanks to wgstuken@informatik.uni-erlangen.de to notice
+ * that it is the ntp-resolver child running into trouble.
+ *
+ * THUS:
+ */
+
+ /* This is the child process who will read the pipe,
+ * so we close the write fd */
+ close(resolver_pipe_fd[1]);
+ closelog();
+ kill_asyncio(0);
+
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+
+#ifdef DEBUG
+ if (0)
+ debug = 2;
+#endif
+
+# ifndef LOG_DAEMON
+ openlog("ntpd_initres", LOG_PID);
+# else /* LOG_DAEMON */
+
+# ifndef LOG_NTP
+# define LOG_NTP LOG_DAEMON
+# endif
+ openlog("ntpd_initres", LOG_PID | LOG_NDELAY, LOG_NTP);
+#ifndef SYS_CYGWIN32
+# ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+# endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+# endif /* LOG_DAEMON */
+#endif
+
+ ntp_intres();
+
+ /*
+ * If we got here, the intres code screwed up.
+ * Print something so we don't die without complaint
+ */
+ msyslog(LOG_ERR, "call to ntp_intres lost");
+ abort_resolve();
+ exit(1);
+ }
+#else
+ /* vxWorks spawns a thread... -casey */
+ i = sp (ntp_intres);
+ /*i = taskSpawn("ntp_intres",100,VX_FP_TASK,20000,ntp_intres);*/
+#endif
+ if (i == -1) {
+ msyslog(LOG_ERR, "fork() failed, can't start ntp_intres: %m");
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ abort_resolve();
+ }
+ else {
+ /* This is the parent process who will write to the pipe,
+ * so we close the read fd */
+ close(resolver_pipe_fd[0]);
+ }
+#else /* SYS_WINNT */
+ {
+ /* NT's equivalent of fork() is _spawn(), but the start point
+ * of the new process is an executable filename rather than
+ * a function name as desired here.
+ */
+ DWORD dwThreadId;
+ fflush(stdout);
+ ResolverEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (ResolverEventHandle == NULL) {
+ msyslog(LOG_ERR, "Unable to create resolver event object, can't start ntp_intres");
+ abort_resolve();
+ }
+ ResolverThreadHandle = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ntp_intres, /* thread function */
+ NULL, /* argument to thread function */
+ 0, /* use default creation flags */
+ &dwThreadId); /* returns the thread identifier */
+ if (ResolverThreadHandle == NULL) {
+ msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres");
+ CloseHandle(ResolverEventHandle);
+ ResolverEventHandle = NULL;
+ abort_resolve();
+ }
+ }
+#endif /* SYS_WINNT */
+#else /* VMS VX_WORKS */
+ msyslog(LOG_ERR,
+ "Name resolution not implemented for VMS - use numeric addresses");
+ abort_resolve();
+#endif /* VMS VX_WORKS */
+}
diff --git a/contrib/ntp/ntpd/ntp_control.c b/contrib/ntp/ntpd/ntp_control.c
new file mode 100644
index 0000000..15f5856
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_control.c
@@ -0,0 +1,3001 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+ u_short flags; /* flags word */
+ void (*handler) P((struct recvbuf *, int)); /* handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+#ifdef REFCLOCK
+static u_short ctlclkstatus P((struct refclockstat *));
+#endif
+static void ctl_flushpkt P((int));
+static void ctl_putdata P((const char *, unsigned int, int));
+static void ctl_putstr P((const char *, const char *,
+ unsigned int));
+static void ctl_putdbl P((const char *, double));
+static void ctl_putuint P((const char *, u_long));
+static void ctl_puthex P((const char *, u_long));
+static void ctl_putint P((const char *, long));
+static void ctl_putts P((const char *, l_fp *));
+static void ctl_putadr P((const char *, u_int32, struct sockaddr_storage*));
+static void ctl_putid P((const char *, char *));
+static void ctl_putarray P((const char *, double *, int));
+static void ctl_putsys P((int));
+static void ctl_putpeer P((int, struct peer *));
+#ifdef OPENSSL
+static void ctl_putfs P((const char *, tstamp_t));
+#endif
+#ifdef REFCLOCK
+static void ctl_putclock P((int, struct refclockstat *, int));
+#endif /* REFCLOCK */
+static struct ctl_var *ctl_getitem P((struct ctl_var *, char **));
+static u_long count_var P((struct ctl_var *));
+static void control_unspec P((struct recvbuf *, int));
+static void read_status P((struct recvbuf *, int));
+static void read_variables P((struct recvbuf *, int));
+static void write_variables P((struct recvbuf *, int));
+static void read_clock_status P((struct recvbuf *, int));
+static void write_clock_status P((struct recvbuf *, int));
+static void set_trap P((struct recvbuf *, int));
+static void unset_trap P((struct recvbuf *, int));
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_storage *,
+ struct interface *));
+
+static struct ctl_proc control_codes[] = {
+ { CTL_OP_UNSPEC, NOAUTH, control_unspec },
+ { CTL_OP_READSTAT, NOAUTH, read_status },
+ { CTL_OP_READVAR, NOAUTH, read_variables },
+ { CTL_OP_WRITEVAR, AUTH, write_variables },
+ { CTL_OP_READCLOCK, NOAUTH, read_clock_status },
+ { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
+ { CTL_OP_SETTRAP, NOAUTH, set_trap },
+ { CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
+ { NO_REQUEST, 0 }
+};
+
+/*
+ * System variable values. The array can be indexed by the variable
+ * index to find the textual name.
+ */
+static struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "poll" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_STATE, RO, "state" }, /* 10 */
+ { CS_OFFSET, RO, "offset" }, /* 11 */
+ { CS_DRIFT, RO, "frequency" }, /* 12 */
+ { CS_JITTER, RO, "jitter" }, /* 13 */
+ { CS_ERROR, RO, "noise" }, /* 14 */
+ { CS_CLOCK, RO, "clock" }, /* 15 */
+ { CS_PROCESSOR, RO, "processor" }, /* 16 */
+ { CS_SYSTEM, RO, "system" }, /* 17 */
+ { CS_VERSION, RO, "version" }, /* 18 */
+ { CS_STABIL, RO, "stability" }, /* 19 */
+ { CS_VARLIST, RO, "sys_var_list" }, /* 20 */
+#ifdef OPENSSL
+ { CS_FLAGS, RO, "flags" }, /* 21 */
+ { CS_HOST, RO, "hostname" }, /* 22 */
+ { CS_PUBLIC, RO, "update" }, /* 23 */
+ { CS_CERTIF, RO, "cert" }, /* 24 */
+ { CS_REVTIME, RO, "expire" }, /* 25 */
+ { CS_LEAPTAB, RO, "leapsec" }, /* 26 */
+ { CS_TAI, RO, "tai" }, /* 27 */
+ { CS_DIGEST, RO, "signature" }, /* 28 */
+ { CS_IDENT, RO, "ident" }, /* 29 */
+ { CS_REVOKE, RO, "expire" }, /* 30 */
+#endif /* OPENSSL */
+ { 0, EOV, "" } /* 21/31 */
+};
+
+static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
+
+/*
+ * System variables we print by default (in fuzzball order,
+ * more-or-less)
+ */
+static u_char def_sys_var[] = {
+ CS_VERSION,
+ CS_PROCESSOR,
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_PRECISION,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_PEERID,
+ CS_REFID,
+ CS_REFTIME,
+ CS_POLL,
+ CS_CLOCK,
+ CS_STATE,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_JITTER,
+ CS_ERROR,
+ CS_STABIL,
+#ifdef OPENSSL
+ CS_HOST,
+ CS_DIGEST,
+ CS_FLAGS,
+ CS_PUBLIC,
+ CS_IDENT,
+ CS_LEAPTAB,
+ CS_TAI,
+ CS_CERTIF,
+#endif /* OPENSSL */
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xmt" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_UNREACH, RO, "unreach" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_JITTER, RO, "jitter" }, /* 26 */
+ { CP_DISPERSION, RO, "dispersion" }, /* 27 */
+ { CP_KEYID, RO, "keyid" }, /* 28 */
+ { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */
+ { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */
+ { CP_PMODE, RO, "pmode" }, /* 31 */
+ { CP_RECEIVED, RO, "received"}, /* 32 */
+ { CP_SENT, RO, "sent" }, /* 33 */
+ { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */
+ { CP_FLASH, RO, "flash" }, /* 35 */
+ { CP_TTL, RO, "ttl" }, /* 36 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */
+#ifdef OPENSSL
+ { CP_FLAGS, RO, "flags" }, /* 38 */
+ { CP_HOST, RO, "hostname" }, /* 39 */
+ { CP_VALID, RO, "valid" }, /* 40 */
+ { CP_INITSEQ, RO, "initsequence" }, /* 41 */
+ { CP_INITKEY, RO, "initkey" }, /* 42 */
+ { CP_INITTSP, RO, "timestamp" }, /* 43 */
+ { CP_DIGEST, RO, "signature" }, /* 44 */
+ { CP_IDENT, RO, "trust" }, /* 45 */
+#endif /* OPENSSL */
+ { 0, EOV, "" } /* 38/46 */
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_LEAP,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REACH,
+ CP_UNREACH,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_FLASH,
+ CP_KEYID,
+ CP_TTL,
+ CP_OFFSET,
+ CP_DELAY,
+ CP_DISPERSION,
+ CP_JITTER,
+ CP_REFTIME,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+#ifdef OPENSSL
+ CP_HOST,
+ CP_DIGEST,
+ CP_VALID,
+ CP_FLAGS,
+ CP_IDENT,
+ CP_INITSEQ,
+#endif /* OPENSSL */
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */
+ { 0, EOV, "" } /* 14 */
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device = known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+
+/*
+ * System and processor definitions.
+ */
+#ifndef HAVE_UNAME
+# ifndef STR_SYSTEM
+# define STR_SYSTEM "UNIX"
+# endif
+# ifndef STR_PROCESSOR
+# define STR_PROCESSOR "unknown"
+# endif
+
+static char str_system[] = STR_SYSTEM;
+static char str_processor[] = STR_PROCESSOR;
+#else
+# include <sys/utsname.h>
+static struct utsname utsnamebuf;
+#endif /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send a copy of
+ * each async message to each live one. Traps time out after an hour, it
+ * is up to the trap receipient to keep resetting it to avoid being
+ * timed out.
+ */
+/* ntp_request.c */
+struct ctl_trap ctl_trap[CTL_MAXTRAPS];
+int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type. This list will only be used iff
+ * the reference clock driver doesn't set peer->sstclktype to something
+ * different than CTL_SST_TS_UNSPEC.
+ */
+static u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* deprecated REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) IRIG_AUDIO? */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* deprecated REFCLK_MSF_EES (14) */
+ CTL_SST_TS_NTP, /* not used (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */
+ CTL_SST_TS_NTP, /* not used (23) */
+ CTL_SST_TS_NTP, /* not used (24) */
+ CTL_SST_TS_NTP, /* not used (25) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */
+ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */
+ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */
+ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */
+ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */
+ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (33) */
+ CTL_SST_TS_LF, /* REFCLK_ULINK (34) */
+ CTL_SST_TS_LF, /* REFCLK_PCF (35) */
+ CTL_SST_TS_LF, /* REFCLK_WWV (36) */
+ CTL_SST_TS_LF, /* REFCLK_FG (37) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */
+ CTL_SST_TS_LF, /* REFCLK_JJY (40) */
+ CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */
+ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */
+ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */
+ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+keyid_t ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static associd_t res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_storage *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static keyid_t res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* set to 1 if this is async trap response */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control(void)
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsnamebuf);
+#endif /* HAVE_UNAME */
+
+ ctl_clr_stats();
+
+ ctl_auth_keyid = 0;
+ ctl_sys_last_event = EVNT_UNSPEC;
+ ctl_sys_num_events = 0;
+
+ num_ctl_traps = 0;
+ for (i = 0; i < CTL_MAXTRAPS; i++)
+ ctl_trap[i].tr_flags = 0;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(
+ int errcode
+ )
+{
+#ifdef DEBUG
+ if (debug >= 4)
+ printf("sending control error %d\n", errcode);
+#endif
+ /*
+ * Fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode &
+ CTL_OP_MASK));
+ rpkt.status = htons((u_short) ((errcode<<8) & 0xff00));
+ rpkt.count = 0;
+
+ /*
+ * send packet and bump counters
+ */
+ if (res_authenticate && sys_authenticate) {
+ int maclen;
+
+ *(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) =
+ htonl(res_keyid);
+ maclen = authencrypt(res_keyid, (u_int32 *)&rpkt,
+ CTL_HEADER_LEN);
+ sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN);
+ }
+ numctlerrors++;
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+void
+process_control(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ntp_control *pkt;
+ register int req_count;
+ register int req_data;
+ register struct ctl_proc *cc;
+ int properlen;
+ int maclen;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("in process_control()\n");
+#endif
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
+ || pkt->offset != 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid format in control packet\n");
+#endif
+ if (rbufp->recv_length < CTL_HEADER_LEN)
+ numctltooshort++;
+ if (pkt->r_m_e_op & CTL_RESPONSE)
+ numctlinputresp++;
+ if (pkt->r_m_e_op & CTL_MORE)
+ numctlinputfrag++;
+ if (pkt->r_m_e_op & CTL_ERROR)
+ numctlinputerr++;
+ if (pkt->offset != 0)
+ numctlbadoffset++;
+ return;
+ }
+ res_version = PKT_VERSION(pkt->li_vn_mode);
+ if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
+#ifdef DEBUG
+ if (debug)
+ printf("unknown version %d in control packet\n",
+ res_version);
+#endif
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent
+ * responses
+ */
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version,
+ MODE_CONTROL);
+ res_opcode = pkt->r_m_e_op;
+ rpkt.sequence = pkt->sequence;
+ rpkt.associd = pkt->associd;
+ rpkt.status = 0;
+ res_offset = 0;
+ res_associd = htons(pkt->associd);
+ res_async = 0;
+ res_authenticate = 0;
+ res_keyid = 0;
+ res_authokay = 0;
+ req_count = (int)htons(pkt->count);
+ datanotbinflag = 0;
+ datalinelen = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ /*
+ * We're set up now. Make sure we've got at least enough
+ * incoming data space to match the count.
+ */
+ req_data = rbufp->recv_length - CTL_HEADER_LEN;
+ if (req_data < req_count || rbufp->recv_length & 0x3) {
+ ctl_error(CERR_BADFMT);
+ numctldatatooshort++;
+ return;
+ }
+
+ properlen = req_count + CTL_HEADER_LEN;
+#ifdef DEBUG
+ if (debug > 2 && (rbufp->recv_length & 0x3) != 0)
+ printf("Packet length %d unrounded\n",
+ rbufp->recv_length);
+#endif
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+ maclen = rbufp->recv_length - properlen;
+ if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 &&
+ maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN &&
+ sys_authenticate) {
+ res_authenticate = 1;
+ res_keyid = ntohl(*(u_int32 *)((u_char *)pkt +
+ properlen));
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authistrusted(res_keyid)) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("invalid keyid %08x\n",
+ res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (u_int32 *)pkt,
+ rbufp->recv_length - maclen, maclen)) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("authentication failed\n");
+#endif
+ res_keyid = 0;
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("opcode %d, found command handler\n",
+ res_opcode);
+#endif
+ if (cc->flags == AUTH && (!res_authokay ||
+ res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict_mask);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(
+ register struct peer *peer
+ )
+{
+ register u_short status;
+
+ status = peer->status;
+ if (peer->flags & FLAG_CONFIG)
+ status |= CTL_PST_CONFIG;
+ if (peer->flags & FLAG_AUTHENABLE)
+ status |= CTL_PST_AUTHENABLE;
+ if (peer->flags & FLAG_AUTHENTIC)
+ status |= CTL_PST_AUTHENTIC;
+ if (peer->reach != 0)
+ status |= CTL_PST_REACH;
+ return (u_short)CTL_PEER_STATUS(status, peer->num_events,
+ peer->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+#ifdef REFCLOCK
+static u_short
+ctlclkstatus(
+ struct refclockstat *this_clock
+ )
+{
+ return ((u_short)(((this_clock->currentstatus) << 8) |
+ (this_clock->lastevent)));
+}
+#endif
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus(void)
+{
+ register u_char this_clock;
+
+ this_clock = CTL_SST_TS_UNSPEC;
+#ifdef REFCLOCK
+ if (sys_peer != 0) {
+ if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
+ this_clock = sys_peer->sstclktype;
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ } else {
+ if (sys_peer->refclktype < sizeof(clocktypes))
+ this_clock =
+ clocktypes[sys_peer->refclktype];
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ }
+ }
+#endif /* REFCLOCK */
+ return (u_short)CTL_SYS_STATUS(sys_leap, this_clock,
+ ctl_sys_num_events, ctl_sys_last_event);
+}
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(
+ int more
+ )
+{
+ int dlen;
+ int sendlen;
+
+ if (!more && datanotbinflag) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ }
+ dlen = datapt - (u_char *)rpkt.data;
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode &
+ CTL_OP_MASK));
+ rpkt.count = htons((u_short) dlen);
+ rpkt.offset = htons( (u_short) res_offset);
+ if (res_async) {
+ register int i;
+
+ for (i = 0; i < CTL_MAXTRAPS; i++) {
+ if (ctl_trap[i].tr_flags & TRAP_INUSE) {
+ rpkt.li_vn_mode =
+ PKT_LI_VN_MODE(sys_leap,
+ ctl_trap[i].tr_version,
+ MODE_CONTROL);
+ rpkt.sequence =
+ htons(ctl_trap[i].tr_sequence);
+ sendpkt(&ctl_trap[i].tr_addr,
+ ctl_trap[i].tr_localaddr, -4,
+ (struct pkt *)&rpkt, sendlen);
+ if (!more)
+ ctl_trap[i].tr_sequence++;
+ numasyncmsgs++;
+ }
+ }
+ } else {
+ if (res_authenticate && sys_authenticate) {
+ int maclen;
+ int totlen = sendlen;
+ keyid_t keyid = htonl(res_keyid);
+
+ /*
+ * If we are going to authenticate, then there
+ * is an additional requirement that the MAC
+ * begin on a 64 bit boundary.
+ */
+ while (totlen & 7) {
+ *datapt++ = '\0';
+ totlen++;
+ }
+ memcpy(datapt, &keyid, sizeof keyid);
+ maclen = authencrypt(res_keyid,
+ (u_int32 *)&rpkt, totlen);
+ sendpkt(rmt_addr, lcl_inter, -5,
+ (struct pkt *)&rpkt, totlen + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -6,
+ (struct pkt *)&rpkt, sendlen);
+ }
+ if (more)
+ numctlfrags++;
+ else
+ numctlresponses++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_offset += dlen;
+ datapt = (u_char *)rpkt.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and starting
+ * another if this one is full.
+ */
+static void
+ctl_putdata(
+ const char *dp,
+ unsigned int dlen,
+ int bin /* set to 1 when data is binary */
+ )
+{
+ int overhead;
+
+ overhead = 0;
+ if (!bin) {
+ datanotbinflag = 1;
+ overhead = 3;
+ if (datapt != rpkt.data) {
+ *datapt++ = ',';
+ datalinelen++;
+ if ((dlen + datalinelen + 1) >= MAXDATALINELEN)
+ {
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ datalinelen = 0;
+ } else {
+ *datapt++ = ' ';
+ datalinelen++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+ memmove((char *)datapt, dp, (unsigned)dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ */
+static void
+ctl_putstr(
+ const char *tag,
+ const char *data,
+ unsigned int len
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[400];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ if (len > 0) {
+ *cp++ = '=';
+ *cp++ = '"';
+ if (len > (int) (sizeof(buffer) - (cp - buffer) - 1))
+ len = sizeof(buffer) - (cp - buffer) - 1;
+ memmove(cp, data, (unsigned)len);
+ cp += len;
+ *cp++ = '"';
+ }
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putdbl - write a tagged, signed double into the response packet
+ */
+static void
+ctl_putdbl(
+ const char *tag,
+ double ts
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ (void)sprintf(cp, "%.3f", ts);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+static void
+ctl_putuint(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%lu", uval);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+/*
+ * ctl_putfs - write a decoded filestamp into the response
+ */
+#ifdef OPENSSL
+static void
+ctl_putfs(
+ const char *tag,
+ tstamp_t uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+ struct tm *tm = NULL;
+ time_t fstamp;
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ fstamp = uval - JAN_1970;
+ tm = gmtime(&fstamp);
+ if (tm == NULL)
+ return;
+
+ sprintf(cp, "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+#endif
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the response
+ */
+static void
+ctl_puthex(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%lx", uval);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer,(unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+static void
+ctl_putint(
+ const char *tag,
+ long ival
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%ld", ival);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+static void
+ctl_putts(
+ const char *tag,
+ l_fp *ts
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%08lx.%08lx",
+ ts->l_ui & ULONG_CONST(0xffffffff),
+ ts->l_uf & ULONG_CONST(0xffffffff));
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putadr - write an IP address into the response
+ */
+static void
+ctl_putadr(
+ const char *tag,
+ u_int32 addr32,
+ struct sockaddr_storage* addr
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ if (addr == NULL)
+ cq = numtoa(addr32);
+ else
+ cq = stoa(addr);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+/*
+ * ctl_putid - write a tagged clock ID into the response
+ */
+static void
+ctl_putid(
+ const char *tag,
+ char *id
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = id;
+ while (*cq != '\0' && (cq - id) < 4)
+ *cp++ = *cq++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element double array into the response
+ */
+static void
+ctl_putarray(
+ const char *tag,
+ double *arr,
+ int start
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+ int i;
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ i = start;
+ do {
+ if (i == 0)
+ i = NTP_SHIFT;
+ i--;
+ (void)sprintf(cp, " %.2f", arr[i] * 1e3);
+ while (*cp != '\0')
+ cp++;
+ } while(i != start);
+ ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(
+ int varid
+ )
+{
+ l_fp tmp;
+ char str[256];
+#ifdef OPENSSL
+ struct cert_info *cp;
+ char cbuf[256];
+#endif /* OPENSSL */
+
+ switch (varid) {
+
+ case CS_LEAP:
+ ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
+ break;
+
+ case CS_STRATUM:
+ ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
+ break;
+
+ case CS_PRECISION:
+ ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
+ break;
+
+ case CS_ROOTDELAY:
+ ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay *
+ 1e3);
+ break;
+
+ case CS_ROOTDISPERSION:
+ ctl_putdbl(sys_var[CS_ROOTDISPERSION].text,
+ sys_rootdispersion * 1e3);
+ break;
+
+ case CS_REFID:
+ if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL);
+ else
+ ctl_putid(sys_var[CS_REFID].text,
+ (char *)&sys_refid);
+ break;
+
+ case CS_REFTIME:
+ ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
+ break;
+
+ case CS_POLL:
+ ctl_putuint(sys_var[CS_POLL].text, sys_poll);
+ break;
+
+ case CS_PEERID:
+ if (sys_peer == NULL)
+ ctl_putuint(sys_var[CS_PEERID].text, 0);
+ else
+ ctl_putuint(sys_var[CS_PEERID].text,
+ sys_peer->associd);
+ break;
+
+ case CS_STATE:
+ ctl_putuint(sys_var[CS_STATE].text, (unsigned)state);
+ break;
+
+ case CS_OFFSET:
+ ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3);
+ break;
+
+ case CS_DRIFT:
+ ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6);
+ break;
+
+ case CS_JITTER:
+ ctl_putdbl(sys_var[CS_JITTER].text, sys_jitter * 1e3);
+ break;
+
+ case CS_ERROR:
+ ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3);
+ break;
+
+ case CS_CLOCK:
+ get_systime(&tmp);
+ ctl_putts(sys_var[CS_CLOCK].text, &tmp);
+ break;
+
+ case CS_PROCESSOR:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
+ sizeof(str_processor) - 1);
+#else
+ ctl_putstr(sys_var[CS_PROCESSOR].text,
+ utsnamebuf.machine, strlen(utsnamebuf.machine));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release);
+ ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_VERSION:
+ ctl_putstr(sys_var[CS_VERSION].text, Version,
+ strlen(Version));
+ break;
+
+ case CS_STABIL:
+ ctl_putdbl(sys_var[CS_STABIL].text, clock_stability *
+ 1e6);
+ break;
+
+ case CS_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register const char *ss;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) -
+ strlen(sys_var[CS_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name */
+
+ strcpy(s, sys_var[CS_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+ for (k = sys_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = ext_sys_var; k && !(k->flags &EOV);
+ k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+ i = ss - k->text;
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text,
+ (unsigned)i);
+ s += i;
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, (unsigned)( s - buf ),
+ 0);
+ }
+ break;
+
+#ifdef OPENSSL
+ case CS_FLAGS:
+ if (crypto_flags) {
+ ctl_puthex(sys_var[CS_FLAGS].text, crypto_flags);
+ }
+ break;
+
+ case CS_DIGEST:
+ if (crypto_flags) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(crypto_flags >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(sys_var[CS_DIGEST].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CS_HOST:
+ if (sys_hostname != NULL)
+ ctl_putstr(sys_var[CS_HOST].text, sys_hostname,
+ strlen(sys_hostname));
+ break;
+
+ case CS_CERTIF:
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ sprintf(cbuf, "%s %s 0x%x", cp->subject,
+ cp->issuer, cp->flags);
+ ctl_putstr(sys_var[CS_CERTIF].text, cbuf,
+ strlen(cbuf));
+ ctl_putfs(sys_var[CS_REVOKE].text, cp->last);
+ }
+ break;
+
+ case CS_PUBLIC:
+ if (hostval.fstamp != 0)
+ ctl_putfs(sys_var[CS_PUBLIC].text,
+ ntohl(hostval.tstamp));
+ break;
+
+ case CS_REVTIME:
+ if (hostval.tstamp != 0)
+ ctl_putfs(sys_var[CS_REVTIME].text,
+ ntohl(hostval.tstamp));
+ break;
+
+ case CS_IDENT:
+ if (iffpar_pkey != NULL)
+ ctl_putstr(sys_var[CS_IDENT].text,
+ iffpar_file, strlen(iffpar_file));
+ if (gqpar_pkey != NULL)
+ ctl_putstr(sys_var[CS_IDENT].text,
+ gqpar_file, strlen(gqpar_file));
+ if (mvpar_pkey != NULL)
+ ctl_putstr(sys_var[CS_IDENT].text,
+ mvpar_file, strlen(mvpar_file));
+ break;
+
+ case CS_LEAPTAB:
+ if (tai_leap.fstamp != 0)
+ ctl_putfs(sys_var[CS_LEAPTAB].text,
+ ntohl(tai_leap.fstamp));
+ break;
+
+ case CS_TAI:
+ ctl_putuint(sys_var[CS_TAI].text, sys_tai);
+ break;
+#endif /* OPENSSL */
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(
+ int varid,
+ struct peer *peer
+ )
+{
+ int temp;
+#ifdef OPENSSL
+ char str[256];
+ struct autokey *ap;
+#endif /* OPENSSL */
+
+ switch (varid) {
+
+ case CP_CONFIG:
+ ctl_putuint(peer_var[CP_CONFIG].text,
+ (unsigned)((peer->flags & FLAG_CONFIG) != 0));
+ break;
+
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[CP_AUTHENABLE].text,
+ (unsigned)((peer->flags & FLAG_AUTHENABLE) != 0));
+ break;
+
+ case CP_AUTHENTIC:
+ ctl_putuint(peer_var[CP_AUTHENTIC].text,
+ (unsigned)((peer->flags & FLAG_AUTHENTIC) != 0));
+ break;
+
+ case CP_SRCADR:
+ ctl_putadr(peer_var[CP_SRCADR].text, 0,
+ &peer->srcadr);
+ break;
+
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port));
+ break;
+
+ case CP_DSTADR:
+ if (peer->dstadr) {
+ ctl_putadr(peer_var[CP_DSTADR].text, 0,
+ &(peer->dstadr->sin));
+ } else {
+ ctl_putadr(peer_var[CP_DSTADR].text, 0,
+ NULL);
+ }
+ break;
+
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ (u_long)(peer->dstadr ?
+ ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0));
+ break;
+
+ case CP_LEAP:
+ ctl_putuint(peer_var[CP_LEAP].text, peer->leap);
+ break;
+
+ case CP_HMODE:
+ ctl_putuint(peer_var[CP_HMODE].text, peer->hmode);
+ break;
+
+ case CP_STRATUM:
+ ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum);
+ break;
+
+ case CP_PPOLL:
+ ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll);
+ break;
+
+ case CP_HPOLL:
+ ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll);
+ break;
+
+ case CP_PRECISION:
+ ctl_putint(peer_var[CP_PRECISION].text,
+ peer->precision);
+ break;
+
+ case CP_ROOTDELAY:
+ ctl_putdbl(peer_var[CP_ROOTDELAY].text,
+ peer->rootdelay * 1e3);
+ break;
+
+ case CP_ROOTDISPERSION:
+ ctl_putdbl(peer_var[CP_ROOTDISPERSION].text,
+ peer->rootdispersion * 1e3);
+ break;
+
+ case CP_REFID:
+ if (peer->flags & FLAG_REFCLOCK) {
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ } else {
+ if (peer->stratum > 1 && peer->stratum <
+ STRATUM_UNSPEC)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid, NULL);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ }
+ break;
+
+ case CP_REFTIME:
+ ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime);
+ break;
+
+ case CP_ORG:
+ ctl_putts(peer_var[CP_ORG].text, &peer->org);
+ break;
+
+ case CP_REC:
+ ctl_putts(peer_var[CP_REC].text, &peer->rec);
+ break;
+
+ case CP_XMT:
+ ctl_putts(peer_var[CP_XMT].text, &peer->xmt);
+ break;
+
+ case CP_REACH:
+ ctl_puthex(peer_var[CP_REACH].text, peer->reach);
+ break;
+
+ case CP_FLASH:
+ temp = peer->flash;
+ ctl_puthex(peer_var[CP_FLASH].text, temp);
+ break;
+
+ case CP_TTL:
+ ctl_putint(peer_var[CP_TTL].text, sys_ttl[peer->ttl]);
+ break;
+
+ case CP_UNREACH:
+ ctl_putuint(peer_var[CP_UNREACH].text, peer->unreach);
+ break;
+
+ case CP_TIMER:
+ ctl_putuint(peer_var[CP_TIMER].text,
+ peer->nextdate - current_time);
+ break;
+
+ case CP_DELAY:
+ ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3);
+ break;
+
+ case CP_OFFSET:
+ ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset *
+ 1e3);
+ break;
+
+ case CP_JITTER:
+ ctl_putdbl(peer_var[CP_JITTER].text, peer->jitter * 1e3);
+ break;
+
+ case CP_DISPERSION:
+ ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp *
+ 1e3);
+ break;
+
+ case CP_KEYID:
+ ctl_putuint(peer_var[CP_KEYID].text, peer->keyid);
+ break;
+
+ case CP_FILTDELAY:
+ ctl_putarray(peer_var[CP_FILTDELAY].text,
+ peer->filter_delay, (int)peer->filter_nextpt);
+ break;
+
+ case CP_FILTOFFSET:
+ ctl_putarray(peer_var[CP_FILTOFFSET].text,
+ peer->filter_offset, (int)peer->filter_nextpt);
+ break;
+
+ case CP_FILTERROR:
+ ctl_putarray(peer_var[CP_FILTERROR].text,
+ peer->filter_disp, (int)peer->filter_nextpt);
+ break;
+
+ case CP_PMODE:
+ ctl_putuint(peer_var[CP_PMODE].text, peer->pmode);
+ break;
+
+ case CP_RECEIVED:
+ ctl_putuint(peer_var[CP_RECEIVED].text, peer->received);
+ break;
+
+ case CP_SENT:
+ ctl_putuint(peer_var[CP_SENT].text, peer->sent);
+ break;
+
+ case CP_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) -
+ strlen(peer_var[CP_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name */
+
+ strcpy(s, peer_var[CP_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+ for (k = peer_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (unsigned)(s - buf), 0);
+ }
+ break;
+#ifdef OPENSSL
+ case CP_FLAGS:
+ if (peer->crypto)
+ ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto);
+ break;
+
+ case CP_DIGEST:
+ if (peer->crypto) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(peer->crypto >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(peer_var[CP_DIGEST].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CP_HOST:
+ if (peer->subject != NULL)
+ ctl_putstr(peer_var[CP_HOST].text,
+ peer->subject, strlen(peer->subject));
+ break;
+
+ case CP_VALID: /* not used */
+ break;
+
+ case CP_IDENT:
+ if (peer->issuer != NULL)
+ ctl_putstr(peer_var[CP_IDENT].text,
+ peer->issuer, strlen(peer->issuer));
+ break;
+
+ case CP_INITSEQ:
+ if ((ap = (struct autokey *)peer->recval.ptr) == NULL)
+ break;
+ ctl_putint(peer_var[CP_INITSEQ].text, ap->seq);
+ ctl_puthex(peer_var[CP_INITKEY].text, ap->key);
+ ctl_putfs(peer_var[CP_INITTSP].text,
+ ntohl(peer->recval.tstamp));
+ break;
+#endif /* OPENSSL */
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+static void
+ctl_putclock(
+ int varid,
+ struct refclockstat *clock_stat,
+ int mustput
+ )
+{
+ switch(varid) {
+
+ case CC_TYPE:
+ if (mustput || clock_stat->clockdesc == NULL
+ || *(clock_stat->clockdesc) == '\0') {
+ ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type);
+ }
+ break;
+ case CC_TIMECODE:
+ ctl_putstr(clock_var[CC_TIMECODE].text,
+ clock_stat->p_lastcode,
+ (unsigned)clock_stat->lencode);
+ break;
+
+ case CC_POLL:
+ ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls);
+ break;
+
+ case CC_NOREPLY:
+ ctl_putuint(clock_var[CC_NOREPLY].text,
+ clock_stat->noresponse);
+ break;
+
+ case CC_BADFORMAT:
+ ctl_putuint(clock_var[CC_BADFORMAT].text,
+ clock_stat->badformat);
+ break;
+
+ case CC_BADDATA:
+ ctl_putuint(clock_var[CC_BADDATA].text,
+ clock_stat->baddata);
+ break;
+
+ case CC_FUDGETIME1:
+ if (mustput || (clock_stat->haveflags & CLK_HAVETIME1))
+ ctl_putdbl(clock_var[CC_FUDGETIME1].text,
+ clock_stat->fudgetime1 * 1e3);
+ break;
+
+ case CC_FUDGETIME2:
+ if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[CC_FUDGETIME2].text,
+ clock_stat->fudgetime2 * 1e3);
+ break;
+
+ case CC_FUDGEVAL1:
+ if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1))
+ ctl_putint(clock_var[CC_FUDGEVAL1].text,
+ clock_stat->fudgeval1);
+ break;
+
+ case CC_FUDGEVAL2:
+ if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) {
+ if (clock_stat->fudgeval1 > 1)
+ ctl_putadr(clock_var[CC_FUDGEVAL2].text,
+ (u_int32)clock_stat->fudgeval2, NULL);
+ else
+ ctl_putid(clock_var[CC_FUDGEVAL2].text,
+ (char *)&clock_stat->fudgeval2);
+ }
+ break;
+
+ case CC_FLAGS:
+ if (mustput || (clock_stat->haveflags & (CLK_HAVEFLAG1 |
+ CLK_HAVEFLAG2 | CLK_HAVEFLAG3 | CLK_HAVEFLAG4)))
+ ctl_putuint(clock_var[CC_FLAGS].text,
+ clock_stat->flags);
+ break;
+
+ case CC_DEVICE:
+ if (clock_stat->clockdesc == NULL ||
+ *(clock_stat->clockdesc) == '\0') {
+ if (mustput)
+ ctl_putstr(clock_var[CC_DEVICE].text,
+ "", 0);
+ } else {
+ ctl_putstr(clock_var[CC_DEVICE].text,
+ clock_stat->clockdesc,
+ strlen(clock_stat->clockdesc));
+ }
+ break;
+
+ case CC_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register const char *ss;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf);
+ if (s + strlen(clock_var[CC_VARLIST].text) + 4 >
+ be)
+ break; /* really long var name */
+
+ strcpy(s, clock_var[CC_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = clock_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = clock_stat->kv_list; k && !(k->flags &
+ EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, (unsigned)i);
+ s += i;
+ *s = '\0';
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (unsigned)( s - buf ), 0);
+ }
+ break;
+ }
+}
+#endif
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+static struct ctl_var *
+ctl_getitem(
+ struct ctl_var *var_list,
+ char **data
+ )
+{
+ register struct ctl_var *v;
+ register char *cp;
+ register char *tp;
+ static struct ctl_var eol = { 0, EOV, };
+ static char buf[128];
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' ||
+ isspace((unsigned char)*reqpt)))
+ reqpt++;
+ if (reqpt >= reqend)
+ return (0);
+
+ if (var_list == (struct ctl_var *)0)
+ return (&eol);
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ v = var_list;
+ cp = reqpt;
+ while (!(v->flags & EOV)) {
+ if (!(v->flags & PADDING) && *cp == *(v->text)) {
+ tp = v->text;
+ while (*tp != '\0' && *tp != '=' && cp <
+ reqend && *cp == *tp) {
+ cp++;
+ tp++;
+ }
+ if ((*tp == '\0') || (*tp == '=')) {
+ while (cp < reqend && isspace((unsigned char)*cp))
+ cp++;
+ if (cp == reqend || *cp == ',') {
+ buf[0] = '\0';
+ *data = buf;
+ if (cp < reqend)
+ cp++;
+ reqpt = cp;
+ return v;
+ }
+ if (*cp == '=') {
+ cp++;
+ tp = buf;
+ while (cp < reqend && isspace((unsigned char)*cp))
+ cp++;
+ while (cp < reqend && *cp != ',') {
+ *tp++ = *cp++;
+ if (tp >= buf + sizeof(buf)) {
+ ctl_error(CERR_BADFMT);
+ numctlbadpkts++;
+#if 0 /* Avoid possible DOS attack */
+/* If we get a smarter msyslog we can re-enable this */
+ msyslog(LOG_WARNING,
+ "Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n",
+ stoa(rmt_addr), SRCPORT(rmt_addr)
+ );
+#endif
+ return (0);
+ }
+ }
+ if (cp < reqend)
+ cp++;
+ *tp-- = '\0';
+ while (tp >= buf) {
+ if (!isspace((unsigned int)(*tp)))
+ break;
+ *tp-- = '\0';
+ }
+ reqpt = cp;
+ *data = buf;
+ return (v);
+ }
+ }
+ cp = reqpt;
+ }
+ v++;
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd != 0) {
+ if ((peer = findpeerbyassoc(res_associd)) == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else {
+ rpkt.status = htons(ctlsysstatus());
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+static void
+read_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register int i;
+ register struct peer *peer;
+ u_short ass_stat[CTL_MAX_DATA_LEN / sizeof(u_short)];
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("read_status: ID %d\n", res_associd);
+#endif
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd == 0) {
+ register int n;
+
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (i = 0; i < NTP_HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ ass_stat[n++] = htons(peer->associd);
+ ass_stat[n++] =
+ htons(ctlpeerstatus(peer));
+ if (n ==
+ CTL_MAX_DATA_LEN/sizeof(u_short)) {
+ ctl_putdata((char *)ass_stat,
+ n * sizeof(u_short), 1);
+ n = 0;
+ }
+ }
+ }
+
+ if (n != 0)
+ ctl_putdata((char *)ass_stat, n *
+ sizeof(u_short), 1);
+ ctl_flushpkt(0);
+ } else {
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ } else {
+ register u_char *cp;
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ /*
+ * For now, output everything we know about the
+ * peer. May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ }
+ }
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+static void
+read_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ctl_var *v;
+ register int i;
+ char *valuep;
+ u_char *wants;
+ unsigned int gotvar = (CS_MAXCODE > CP_MAXCODE) ? (CS_MAXCODE +
+ 1) : (CP_MAXCODE + 1);
+ if (res_associd == 0) {
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ rpkt.status = htons(ctlsysstatus());
+ if (res_authokay)
+ ctl_sys_num_events = 0;
+ gotvar += count_var(ext_sys_var);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char *)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var,
+ &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[CS_MAXCODE + 1 +
+ v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CS_MAXCODE; i++)
+ if (wants[i])
+ ctl_putsys(i);
+ for (i = 0; ext_sys_var &&
+ !(ext_sys_var[i].flags & EOV); i++)
+ if (wants[i + CS_MAXCODE + 1])
+ ctl_putdata(ext_sys_var[i].text,
+ strlen(ext_sys_var[i].text),
+ 0);
+ } else {
+ register u_char *cs;
+ register struct ctl_var *kv;
+
+ for (cs = def_sys_var; *cs != 0; cs++)
+ ctl_putsys((int)*cs);
+ for (kv = ext_sys_var; kv && !(kv->flags & EOV);
+ kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ }
+ free((char *)wants);
+ } else {
+ register struct peer *peer;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(peer_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CP_MAXCODE; i++)
+ if (wants[i])
+ ctl_putpeer(i, peer);
+ } else {
+ register u_char *cp;
+
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ }
+ free((char *)wants);
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit
+ * writing this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ctl_var *v;
+ register int ext_var;
+ char *valuep;
+ long val = 0;
+
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Look through the variables. Dump out at the first sign of
+ * trouble.
+ */
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ ext_var = 0;
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) !=
+ 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ ext_var = 1;
+ } else {
+ break;
+ }
+ }
+ if (!(v->flags & CAN_WRITE)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ if (!ext_var && (*valuep == '\0' || !atoint(valuep,
+ &val))) {
+ ctl_error(CERR_BADFMT);
+ return;
+ }
+ if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (ext_var) {
+ char *s = (char *)emalloc(strlen(v->text) +
+ strlen(valuep) + 2);
+ const char *t;
+ char *tt = s;
+
+ t = v->text;
+ while (*t && *t != '=')
+ *tt++ = *t++;
+
+ *tt++ = '=';
+ strcat(tt, valuep);
+ set_sys_var(s, strlen(s)+1, v->flags);
+ free(s);
+ } else {
+ /*
+ * This one seems sane. Save it.
+ */
+ switch(v->code) {
+
+ case CS_LEAP:
+ default:
+ ctl_error(CERR_UNSPEC); /* really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it. xxx nothing to do ***
+ */
+ /*
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clock_status - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ ctl_error(CERR_BADASSOC);
+#else
+ register struct ctl_var *v;
+ register int i;
+ register struct peer *peer;
+ char *valuep;
+ u_char *wants;
+ unsigned int gotvar;
+ struct refclockstat clock_stat;
+
+ if (res_associd == 0) {
+
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search the hash tables
+ * for one.
+ */
+ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK))
+ {
+ peer = sys_peer;
+ } else {
+ peer = 0;
+ for (i = 0; peer == 0 && i < NTP_HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ if (peer->flags & FLAG_REFCLOCK)
+ break;
+ }
+ }
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+ } else {
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+
+ /*
+ * If we got here we have a peer which is a clock. Get his
+ * status.
+ */
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr, (struct refclockstat *)0,
+ &clock_stat);
+
+ /*
+ * Look for variables in the packet.
+ */
+ rpkt.status = htons(ctlclkstatus(&clock_stat));
+ gotvar = CC_MAXCODE + 1 + count_var(clock_stat.kv_list);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(clock_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(clock_stat.kv_list,
+ &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char*)wants);
+ free_varlist(clock_stat.kv_list);
+ return;
+ }
+ wants[CC_MAXCODE + 1 + v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+
+ if (gotvar) {
+ for (i = 1; i <= CC_MAXCODE; i++)
+ if (wants[i])
+ ctl_putclock(i, &clock_stat, 1);
+ for (i = 0; clock_stat.kv_list &&
+ !(clock_stat.kv_list[i].flags & EOV); i++)
+ if (wants[i + CC_MAXCODE + 1])
+ ctl_putdata(clock_stat.kv_list[i].text,
+ strlen(clock_stat.kv_list[i].text),
+ 0);
+ } else {
+ register u_char *cc;
+ register struct ctl_var *kv;
+
+ for (cc = def_clock_var; *cc != 0; cc++)
+ ctl_putclock((int)*cc, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV);
+ kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text),
+ 0);
+ }
+
+ free((char*)wants);
+ free_varlist(clock_stat.kv_list);
+
+ ctl_flushpkt(0);
+#endif
+}
+
+
+/*
+ * write_clock_status - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict_mask & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own trap unless the
+ * trap is configured. Note we also must be aware of the
+ * possibility that restriction flags were changed since this
+ * guy last set his trap. Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter,
+ int traptype,
+ int version
+ )
+{
+ register struct ctl_trap *tp;
+ register struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return (1);
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ tptouse = NULL;
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE) &&
+ !(tp->tr_flags & TRAP_CONFIGURED) &&
+ ((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ }
+ if (!(tp->tr_flags & TRAP_INUSE)) {
+ tptouse = tp;
+ } else if (!(tp->tr_flags & TRAP_CONFIGURED)) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ if (tptouse == NULL) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_flags & TRAP_NONPRIO &&
+ !(tp->tr_flags & TRAP_NONPRIO))
+ break;
+
+ if (!(tptouse->tr_flags & TRAP_NONPRIO)
+ && tp->tr_flags & TRAP_NONPRIO) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_origtime <
+ tp->tr_origtime)
+ tptouse = tp;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_NONPRIO) {
+ if (tptouse == NULL ||
+ (tptouse->tr_flags &
+ TRAP_INUSE &&
+ tptouse->tr_origtime <
+ tp->tr_origtime))
+ tptouse = tp;
+ }
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return (0);
+
+ /*
+ * Set up this structure for him.
+ */
+ tptouse->tr_settime = tptouse->tr_origtime = current_time;
+ tptouse->tr_count = tptouse->tr_resets = 0;
+ tptouse->tr_sequence = 1;
+ tptouse->tr_addr = *raddr;
+ tptouse->tr_localaddr = linter;
+ tptouse->tr_version = (u_char) version;
+ tptouse->tr_flags = TRAP_INUSE;
+ if (traptype == TRAP_TYPE_CONFIG)
+ tptouse->tr_flags |= TRAP_CONFIGURED;
+ else if (traptype == TRAP_TYPE_NONPRIO)
+ tptouse->tr_flags |= TRAP_NONPRIO;
+ num_ctl_traps++;
+ return (1);
+}
+
+
+/*
+ * ctlclrtrap - called to clear a trap
+ */
+int
+ctlclrtrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter,
+ int traptype
+ )
+{
+ register struct ctl_trap *tp;
+
+ if ((tp = ctlfindtrap(raddr, linter)) == NULL)
+ return (0);
+
+ if (tp->tr_flags & TRAP_CONFIGURED
+ && traptype != TRAP_TYPE_CONFIG)
+ return (0);
+
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ return (1);
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter
+ )
+{
+ register struct ctl_trap *tp;
+
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE)
+ && (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr))
+ && SOCKCMP(raddr, &tp->tr_addr)
+ && (linter == tp->tr_localaddr) )
+ return (tp);
+ }
+ return (struct ctl_trap *)NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(
+ int err,
+ struct peer *peer
+ )
+{
+ register int i;
+
+ /*
+ * Record error code in proper spots, but have mercy on the
+ * log file.
+ */
+ if (!(err & (PEER_EVENT | CRPT_EVENT))) {
+ if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
+ ctl_sys_num_events++;
+ if (ctl_sys_last_event != (u_char)err) {
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()), ctlsysstatus());
+#ifdef DEBUG
+ if (debug)
+ printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()),
+ ctlsysstatus());
+#endif
+ ctl_sys_last_event = (u_char)err;
+ }
+ } else if (peer != 0) {
+ char *src;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr))
+ src = refnumtoa(&peer->srcadr);
+ else
+#endif
+ src = stoa(&peer->srcadr);
+
+ peer->last_event = (u_char)(err & ~PEER_EVENT);
+ if (peer->num_events < CTL_PEER_MAXEVENTS)
+ peer->num_events++;
+ NLOG(NLOG_PEEREVENT)
+ msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)),
+ ctlpeerstatus(peer));
+#ifdef DEBUG
+ if (debug)
+ printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)),
+ ctlpeerstatus(peer));
+#endif
+ } else {
+ msyslog(LOG_ERR,
+ "report_event: err '%s' (0x%02x), no peer",
+ eventstr(err), err);
+#ifdef DEBUG
+ printf(
+ "report_event: peer event '%s' (0x%02x), no peer\n",
+ eventstr(err), err);
+#endif
+ return;
+ }
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ res_opcode = CTL_OP_ASYNCMSG;
+ res_offset = 0;
+ res_async = 1;
+ res_authenticate = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+ if (!(err & PEER_EVENT)) {
+ rpkt.associd = 0;
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * For now, put everything we know about system
+ * variables. Don't send crypto strings.
+ */
+ for (i = 1; i <= CS_MAXCODE; i++) {
+#ifdef OPENSSL
+ if (i > CS_VARLIST)
+ continue;
+#endif /* OPENSSL */
+ ctl_putsys(i);
+ }
+#ifdef REFCLOCK
+ /*
+ * for clock exception events: add clock variables to
+ * reflect info on exception
+ */
+ if (err == EVNT_CLOCKEXCPT) {
+ struct refclockstat clock_stat;
+ struct ctl_var *kv;
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock_stat);
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock_stat));
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv &&
+ !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ free_varlist(clock_stat.kv_list);
+ }
+#endif /* REFCLOCK */
+ } else {
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /*
+ * Dump it all. Later, maybe less.
+ */
+ for (i = 1; i <= CP_MAXCODE; i++) {
+#ifdef OPENSSL
+ if (i > CP_VARLIST)
+ continue;
+#endif /* OPENSSL */
+ ctl_putpeer(i, peer);
+ }
+#ifdef REFCLOCK
+ /*
+ * for clock exception events: add clock variables to
+ * reflect info on exception
+ */
+ if (err == EVNT_PEERCLOCK) {
+ struct refclockstat clock_stat;
+ struct ctl_var *kv;
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock_stat);
+
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock_stat));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv &&
+ !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ free_varlist(clock_stat.kv_list);
+ }
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+void
+ctl_clr_stats(void)
+{
+ ctltimereset = current_time;
+ numctlreq = 0;
+ numctlbadpkts = 0;
+ numctlresponses = 0;
+ numctlfrags = 0;
+ numctlerrors = 0;
+ numctlfrags = 0;
+ numctltooshort = 0;
+ numctlinputresp = 0;
+ numctlinputfrag = 0;
+ numctlinputerr = 0;
+ numctlbadoffset = 0;
+ numctlbadversion = 0;
+ numctldatatooshort = 0;
+ numctlbadop = 0;
+ numasyncmsgs = 0;
+}
+
+static u_long
+count_var(
+ struct ctl_var *k
+ )
+{
+ register u_long c;
+
+ if (!k)
+ return (0);
+
+ c = 0;
+ while (!(k++->flags & EOV))
+ c++;
+ return (c);
+}
+
+char *
+add_var(
+ struct ctl_var **kv,
+ u_long size,
+ u_short def
+ )
+{
+ register u_long c;
+ register struct ctl_var *k;
+
+ c = count_var(*kv);
+
+ k = *kv;
+ *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var));
+ if (k) {
+ memmove((char *)*kv, (char *)k,
+ sizeof(struct ctl_var)*c);
+ free((char *)k);
+ }
+ (*kv)[c].code = (u_short) c;
+ (*kv)[c].text = (char *)emalloc(size);
+ (*kv)[c].flags = def;
+ (*kv)[c+1].code = 0;
+ (*kv)[c+1].text = (char *)0;
+ (*kv)[c+1].flags = EOV;
+ return (char *)(*kv)[c].text;
+}
+
+void
+set_var(
+ struct ctl_var **kv,
+ const char *data,
+ u_long size,
+ u_short def
+ )
+{
+ register struct ctl_var *k;
+ register const char *s;
+ register const char *t;
+ char *td;
+
+ if (!data || !size)
+ return;
+
+ k = *kv;
+ if (k != NULL) {
+ while (!(k->flags & EOV)) {
+ s = data;
+ t = k->text;
+ if (t) {
+ while (*t != '=' && *s - *t == 0) {
+ s++;
+ t++;
+ }
+ if (*s == *t && ((*t == '=') || !*t)) {
+ free((void *)k->text);
+ td = (char *)emalloc(size);
+ memmove(td, data, size);
+ k->text =td;
+ k->flags = def;
+ return;
+ }
+ } else {
+ td = (char *)emalloc(size);
+ memmove(td, data, size);
+ k->text = td;
+ k->flags = def;
+ return;
+ }
+ k++;
+ }
+ }
+ td = add_var(kv, size, def);
+ memmove(td, data, size);
+}
+
+void
+set_sys_var(
+ const char *data,
+ u_long size,
+ u_short def
+ )
+{
+ set_var(&ext_sys_var, data, size, def);
+}
+
+void
+free_varlist(
+ struct ctl_var *kv
+ )
+{
+ struct ctl_var *k;
+ if (kv) {
+ for (k = kv; !(k->flags & EOV); k++)
+ free((void *)k->text);
+ free((void *)kv);
+ }
+}
diff --git a/contrib/ntp/ntpd/ntp_crypto.c b/contrib/ntp/ntpd/ntp_crypto.c
new file mode 100644
index 0000000..84adbdd
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_crypto.c
@@ -0,0 +1,4184 @@
+/*
+ * ntp_crypto.c - NTP version 4 public key routines
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OPENSSL
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_string.h"
+#include <ntp_random.h>
+
+#include "openssl/asn1_mac.h"
+#include "openssl/bn.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "openssl/pem.h"
+#include "openssl/rand.h"
+#include "openssl/x509v3.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Extension field message format
+ *
+ * These are always signed and saved before sending in network byte
+ * order. They must be converted to and from host byte order for
+ * processing.
+ *
+ * +-------+-------+
+ * | op | len | <- extension pointer
+ * +-------+-------+
+ * | assocID |
+ * +---------------+
+ * | timestamp | <- value pointer
+ * +---------------+
+ * | filestamp |
+ * +---------------+
+ * | value len |
+ * +---------------+
+ * | |
+ * = value =
+ * | |
+ * +---------------+
+ * | signature len |
+ * +---------------+
+ * | |
+ * = signature =
+ * | |
+ * +---------------+
+ *
+ * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses.
+ * Requests carry the association ID of the receiver; responses carry
+ * the association ID of the sender. Some messages include only the
+ * operation/length and association ID words and so have length 8
+ * octets. Ohers include the value structure and associated value and
+ * signature fields. These messages include the timestamp, filestamp,
+ * value and signature words and so have length at least 24 octets. The
+ * signature and/or value fields can be empty, in which case the
+ * respective length words are zero. An empty value with nonempty
+ * signature is syntactically valid, but semantically questionable.
+ *
+ * The filestamp represents the time when a cryptographic data file such
+ * as a public/private key pair is created. It follows every reference
+ * depending on that file and serves as a means to obsolete earlier data
+ * of the same type. The timestamp represents the time when the
+ * cryptographic data of the message were last signed. Creation of a
+ * cryptographic data file or signing a message can occur only when the
+ * creator or signor is synchronized to an authoritative source and
+ * proventicated to a trusted authority.
+ *
+ * Note there are four conditions required for server trust. First, the
+ * public key on the certificate must be verified, which involves a
+ * number of format, content and consistency checks. Next, the server
+ * identity must be confirmed by one of four schemes: private
+ * certificate, IFF scheme, GQ scheme or certificate trail hike to a
+ * self signed trusted certificate. Finally, the server signature must
+ * be verified.
+ */
+/*
+ * Cryptodefines
+ */
+#define TAI_1972 10 /* initial TAI offset (s) */
+#define MAX_LEAP 100 /* max UTC leapseconds (s) */
+#define VALUE_LEN (6 * 4) /* min response field length */
+#define YEAR (60 * 60 * 24 * 365) /* seconds in year */
+
+/*
+ * Global cryptodata in host byte order
+ */
+u_int32 crypto_flags = 0x0; /* status word */
+
+/*
+ * Global cryptodata in network byte order
+ */
+struct cert_info *cinfo = NULL; /* certificate info/value */
+struct value hostval; /* host value */
+struct value pubkey; /* public key */
+struct value tai_leap; /* leapseconds table */
+EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */
+EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */
+EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */
+char *iffpar_file = NULL; /* IFF parameters file */
+char *gqpar_file = NULL; /* GQ parameters file */
+char *mvpar_file = NULL; /* MV parameters file */
+
+/*
+ * Private cryptodata in host byte order
+ */
+static char *passwd = NULL; /* private key password */
+static EVP_PKEY *host_pkey = NULL; /* host key */
+static EVP_PKEY *sign_pkey = NULL; /* sign key */
+static const EVP_MD *sign_digest = NULL; /* sign digest */
+static u_int sign_siglen; /* sign key length */
+static char *rand_file = NULL; /* random seed file */
+static char *host_file = NULL; /* host key file */
+static char *sign_file = NULL; /* sign key file */
+static char *cert_file = NULL; /* certificate file */
+static char *leap_file = NULL; /* leapseconds file */
+static tstamp_t if_fstamp = 0; /* IFF filestamp */
+static tstamp_t gq_fstamp = 0; /* GQ file stamp */
+static tstamp_t mv_fstamp = 0; /* MV filestamp */
+static u_int ident_scheme = 0; /* server identity scheme */
+
+/*
+ * Cryptotypes
+ */
+static int crypto_verify P((struct exten *, struct value *,
+ struct peer *));
+static int crypto_encrypt P((struct exten *, struct value *,
+ keyid_t *));
+static int crypto_alice P((struct peer *, struct value *));
+static int crypto_alice2 P((struct peer *, struct value *));
+static int crypto_alice3 P((struct peer *, struct value *));
+static int crypto_bob P((struct exten *, struct value *));
+static int crypto_bob2 P((struct exten *, struct value *));
+static int crypto_bob3 P((struct exten *, struct value *));
+static int crypto_iff P((struct exten *, struct peer *));
+static int crypto_gq P((struct exten *, struct peer *));
+static int crypto_mv P((struct exten *, struct peer *));
+static u_int crypto_send P((struct exten *, struct value *));
+static tstamp_t crypto_time P((void));
+static u_long asn2ntp P((ASN1_TIME *));
+static struct cert_info *cert_parse P((u_char *, u_int, tstamp_t));
+static int cert_sign P((struct exten *, struct value *));
+static int cert_valid P((struct cert_info *, EVP_PKEY *));
+static int cert_install P((struct exten *, struct peer *));
+static void cert_free P((struct cert_info *));
+static EVP_PKEY *crypto_key P((char *, tstamp_t *));
+static int bighash P((BIGNUM *, BIGNUM *));
+static struct cert_info *crypto_cert P((char *));
+static void crypto_tai P((char *));
+
+#ifdef SYS_WINNT
+int
+readlink(char * link, char * file, int len) {
+ return (-1);
+}
+#endif
+
+/*
+ * session_key - generate session key
+ *
+ * This routine generates a session key from the source address,
+ * destination address, key ID and private value. The value of the
+ * session key is the MD5 hash of these values, while the next key ID is
+ * the first four octets of the hash.
+ *
+ * Returns the next key ID
+ */
+keyid_t
+session_key(
+ struct sockaddr_storage *srcadr, /* source address */
+ struct sockaddr_storage *dstadr, /* destination address */
+ keyid_t keyno, /* key ID */
+ keyid_t private, /* private value */
+ u_long lifetime /* key lifetime */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ keyid_t keyid; /* key identifer */
+ u_int32 header[10]; /* data in network byte order */
+ u_int hdlen, len;
+
+ if (!dstadr)
+ return 0;
+
+ /*
+ * Generate the session key and key ID. If the lifetime is
+ * greater than zero, install the key and call it trusted.
+ */
+ hdlen = 0;
+ switch(srcadr->ss_family) {
+ case AF_INET:
+ header[0] = ((struct sockaddr_in *)srcadr)->sin_addr.s_addr;
+ header[1] = ((struct sockaddr_in *)dstadr)->sin_addr.s_addr;
+ header[2] = htonl(keyno);
+ header[3] = htonl(private);
+ hdlen = 4 * sizeof(u_int32);
+ break;
+
+ case AF_INET6:
+ memcpy(&header[0], &GET_INADDR6(*srcadr),
+ sizeof(struct in6_addr));
+ memcpy(&header[4], &GET_INADDR6(*dstadr),
+ sizeof(struct in6_addr));
+ header[8] = htonl(keyno);
+ header[9] = htonl(private);
+ hdlen = 10 * sizeof(u_int32);
+ break;
+ }
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, (u_char *)header, hdlen);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ memcpy(&keyid, dgst, 4);
+ keyid = ntohl(keyid);
+ if (lifetime != 0) {
+ MD5auth_setkey(keyno, dgst, len);
+ authtrust(keyno, lifetime);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "session_key: %s > %s %08x %08x hash %08x life %lu\n",
+ stoa(srcadr), stoa(dstadr), keyno,
+ private, keyid, lifetime);
+#endif
+ return (keyid);
+}
+
+
+/*
+ * make_keylist - generate key list
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PER host certificate expired
+ *
+ * This routine constructs a pseudo-random sequence by repeatedly
+ * hashing the session key starting from a given source address,
+ * destination address, private value and the next key ID of the
+ * preceeding session key. The last entry on the list is saved along
+ * with its sequence number and public signature.
+ */
+int
+make_keylist(
+ struct peer *peer, /* peer structure pointer */
+ struct interface *dstadr /* interface */
+ )
+{
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ struct autokey *ap; /* autokey pointer */
+ struct value *vp; /* value pointer */
+ keyid_t keyid = 0; /* next key ID */
+ keyid_t cookie; /* private value */
+ u_long lifetime;
+ u_int len, mpoll;
+ int i;
+
+ if (!dstadr)
+ return XEVNT_OK;
+
+ /*
+ * Allocate the key list if necessary.
+ */
+ tstamp = crypto_time();
+ if (peer->keylist == NULL)
+ peer->keylist = emalloc(sizeof(keyid_t) *
+ NTP_MAXSESSION);
+
+ /*
+ * Generate an initial key ID which is unique and greater than
+ * NTP_MAXKEY.
+ */
+ while (1) {
+ keyid = (ntp_random() + NTP_MAXKEY + 1) & ((1 <<
+ sizeof(keyid_t)) - 1);
+ if (authhavekey(keyid))
+ continue;
+ break;
+ }
+
+ /*
+ * Generate up to NTP_MAXSESSION session keys. Stop if the
+ * next one would not be unique or not a session key ID or if
+ * it would expire before the next poll. The private value
+ * included in the hash is zero if broadcast mode, the peer
+ * cookie if client mode or the host cookie if symmetric modes.
+ */
+ mpoll = 1 << min(peer->ppoll, peer->hpoll);
+ lifetime = min(sys_automax, NTP_MAXSESSION * mpoll);
+ if (peer->hmode == MODE_BROADCAST)
+ cookie = 0;
+ else
+ cookie = peer->pcookie;
+ for (i = 0; i < NTP_MAXSESSION; i++) {
+ peer->keylist[i] = keyid;
+ peer->keynumber = i;
+ keyid = session_key(&dstadr->sin, &peer->srcadr, keyid,
+ cookie, lifetime);
+ lifetime -= mpoll;
+ if (auth_havekey(keyid) || keyid <= NTP_MAXKEY ||
+ lifetime <= mpoll)
+ break;
+ }
+
+ /*
+ * Save the last session key ID, sequence number and timestamp,
+ * then sign these values for later retrieval by the clients. Be
+ * careful not to use invalid key media. Use the public values
+ * timestamp as filestamp.
+ */
+ vp = &peer->sndval;
+ if (vp->ptr == NULL)
+ vp->ptr = emalloc(sizeof(struct autokey));
+ ap = (struct autokey *)vp->ptr;
+ ap->seq = htonl(peer->keynumber);
+ ap->key = htonl(keyid);
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ vp->vallen = htonl(sizeof(struct autokey));
+ vp->siglen = 0;
+ if (tstamp != 0) {
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ if (vp->sig == NULL)
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey));
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ else
+ msyslog(LOG_ERR, "make_keys %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ peer->flags |= FLAG_ASSOC;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n",
+ ntohl(ap->seq), ntohl(ap->key), cookie,
+ ntohl(vp->tstamp), ntohl(vp->fstamp), peer->hpoll);
+#endif
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_recv - parse extension fields
+ *
+ * This routine is called when the packet has been matched to an
+ * association and passed sanity, format and MAC checks. We believe the
+ * extension field values only if the field has proper format and
+ * length, the timestamp and filestamp are valid and the signature has
+ * valid length and is verified. There are a few cases where some values
+ * are believed even if the signature fails, but only if the proventic
+ * bit is not set.
+ */
+int
+crypto_recv(
+ struct peer *peer, /* peer structure pointer */
+ struct recvbuf *rbufp /* packet buffer pointer */
+ )
+{
+ const EVP_MD *dp; /* message digest algorithm */
+ u_int32 *pkt; /* receive packet pointer */
+ struct autokey *ap, *bp; /* autokey pointer */
+ struct exten *ep, *fp; /* extension pointers */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ associd_t associd; /* association ID */
+ tstamp_t tstamp = 0; /* timestamp */
+ tstamp_t fstamp = 0; /* filestamp */
+ u_int len; /* extension field length */
+ u_int code; /* extension field opcode */
+ u_int vallen = 0; /* value length */
+ X509 *cert; /* X509 certificate */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ keyid_t cookie; /* crumbles */
+ int hismode; /* packet mode */
+ int rval = XEVNT_OK;
+ u_char *ptr;
+ u_int32 temp32;
+
+ /*
+ * Initialize. Note that the packet has already been checked for
+ * valid format and extension field lengths. First extract the
+ * field length, command code and association ID in host byte
+ * order. These are used with all commands and modes. Then check
+ * the version number, which must be 2, and length, which must
+ * be at least 8 for requests and VALUE_LEN (24) for responses.
+ * Packets that fail either test sink without a trace. The
+ * association ID is saved only if nonzero.
+ */
+ authlen = LEN_PKT_NOMAC;
+ hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode);
+ while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
+ pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
+ ep = (struct exten *)pkt;
+ code = ntohl(ep->opcode) & 0xffff0000;
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ associd = (associd_t) ntohl(pkt[1]);
+ rval = XEVNT_OK;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n",
+ peer->crypto, authlen, len, code >> 16,
+ associd);
+#endif
+
+ /*
+ * Check version number and field length. If bad,
+ * quietly ignore the packet.
+ */
+ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) {
+ sys_unknownversion++;
+ code |= CRYPTO_ERROR;
+ }
+
+ /*
+ * Little vulnerability bandage here. If a perp tosses a
+ * fake association ID over the fence, we better toss it
+ * out. Only the first one counts.
+ */
+ if (code & CRYPTO_RESP) {
+ if (peer->assoc == 0)
+ peer->assoc = associd;
+ else if (peer->assoc != associd)
+ code |= CRYPTO_ERROR;
+ }
+ if (len >= VALUE_LEN) {
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ vallen = ntohl(ep->vallen);
+ }
+ switch (code) {
+
+ /*
+ * Install status word, host name, signature scheme and
+ * association ID. In OpenSSL the signature algorithm is
+ * bound to the digest algorithm, so the NID completely
+ * defines the signature scheme. Note the request and
+ * response are identical, but neither is validated by
+ * signature. The request is processed here only in
+ * symmetric modes. The server name field might be
+ * useful to implement access controls in future.
+ */
+ case CRYPTO_ASSOC:
+
+ /*
+ * If the machine is running when this message
+ * arrives, the other fellow has reset and so
+ * must we. Otherwise, pass the extension field
+ * to the transmit side.
+ */
+ if (peer->crypto) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ /* fall through */
+
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+
+ /*
+ * Discard the message if it has already been
+ * stored or the message has been amputated.
+ */
+ if (peer->crypto)
+ break;
+
+ if (vallen == 0 || vallen > MAXHOSTNAME ||
+ len < VALUE_LEN + vallen) {
+ rval = XEVNT_LEN;
+ break;
+ }
+
+ /*
+ * Check the identity schemes are compatible. If
+ * the client has PC, the server must have PC,
+ * in which case the server public key and
+ * identity are presumed valid, so we skip the
+ * certificate and identity exchanges and move
+ * immediately to the cookie exchange which
+ * confirms the server signature.
+ */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_recv: ident host 0x%x server 0x%x\n",
+ crypto_flags, fstamp);
+#endif
+ temp32 = (crypto_flags | ident_scheme) &
+ fstamp & CRYPTO_FLAG_MASK;
+ if (crypto_flags & CRYPTO_FLAG_PRIV) {
+ if (!(fstamp & CRYPTO_FLAG_PRIV)) {
+ rval = XEVNT_KEY;
+ break;
+
+ } else {
+ fstamp |= CRYPTO_FLAG_VALID |
+ CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_SIGN;
+ }
+ /*
+ * In symmetric modes it is an error if either
+ * peer requests identity and the other peer
+ * does not support it.
+ */
+ } else if ((hismode == MODE_ACTIVE || hismode ==
+ MODE_PASSIVE) && ((crypto_flags | fstamp) &
+ CRYPTO_FLAG_MASK) && !temp32) {
+ rval = XEVNT_KEY;
+ break;
+ /*
+ * It is an error if the client requests
+ * identity and the server does not support it.
+ */
+ } else if (hismode == MODE_CLIENT && (fstamp &
+ CRYPTO_FLAG_MASK) && !temp32) {
+ rval = XEVNT_KEY;
+ break;
+ }
+
+ /*
+ * Otherwise, the identity scheme(s) are those
+ * that both client and server support.
+ */
+ fstamp = temp32 | (fstamp & ~CRYPTO_FLAG_MASK);
+
+ /*
+ * Discard the message if the signature digest
+ * NID is not supported.
+ */
+ temp32 = (fstamp >> 16) & 0xffff;
+ dp =
+ (const EVP_MD *)EVP_get_digestbynid(temp32);
+ if (dp == NULL) {
+ rval = XEVNT_MD;
+ break;
+ }
+
+ /*
+ * Save status word, host name and message
+ * digest/signature type.
+ */
+ peer->crypto = fstamp;
+ peer->digest = dp;
+ peer->subject = emalloc(vallen + 1);
+ memcpy(peer->subject, ep->pkt, vallen);
+ peer->subject[vallen] = '\0';
+ peer->issuer = emalloc(vallen + 1);
+ strcpy(peer->issuer, peer->subject);
+ temp32 = (fstamp >> 16) & 0xffff;
+ sprintf(statstr,
+ "flags 0x%x host %s signature %s", fstamp,
+ peer->subject, OBJ_nid2ln(temp32));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Decode X509 certificate in ASN.1 format and extract
+ * the data containing, among other things, subject
+ * name and public key. In the default identification
+ * scheme, the certificate trail is followed to a self
+ * signed trusted certificate.
+ */
+ case CRYPTO_CERT | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list.
+ */
+ if ((rval = cert_install(ep, peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * If we snatch the certificate before the
+ * server certificate has been signed by its
+ * server, it will be self signed. When it is,
+ * we chase the certificate issuer, which the
+ * server has, and keep going until a self
+ * signed trusted certificate is found. Be sure
+ * to update the issuer field, since it may
+ * change.
+ */
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ peer->issuer = emalloc(strlen(cinfo->issuer) +
+ 1);
+ strcpy(peer->issuer, cinfo->issuer);
+
+ /*
+ * We plug in the public key and lifetime from
+ * the first certificate received. However, note
+ * that this certificate might not be signed by
+ * the server, so we can't check the
+ * signature/digest NID.
+ */
+ if (peer->pkey == NULL) {
+ ptr = (u_char *)cinfo->cert.ptr;
+ cert = d2i_X509(NULL, &ptr,
+ ntohl(cinfo->cert.vallen));
+ peer->pkey = X509_get_pubkey(cert);
+ X509_free(cert);
+ }
+ peer->flash &= ~TEST8;
+ temp32 = cinfo->nid;
+ sprintf(statstr, "cert %s 0x%x %s (%u) fs %u",
+ cinfo->subject, cinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Schnorr (IFF)identity scheme. This scheme is designed
+ * for use with shared secret group keys and where the
+ * certificate may be generated by a third party. The
+ * client sends a challenge to the server, which
+ * performs a calculation and returns the result. A
+ * positive result is possible only if both client and
+ * server contain the same secret group key.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or certificate
+ * trail not trusted.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VALID)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_iff(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "iff fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Guillou-Quisquater (GQ) identity scheme. This scheme
+ * is designed for use with public certificates carrying
+ * the GQ public key in an extension field. The client
+ * sends a challenge to the server, which performs a
+ * calculation and returns the result. A positive result
+ * is possible only if both client and server contain
+ * the same group key and the server has the matching GQ
+ * private key.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or certificate
+ * trail not trusted.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VALID)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_gq(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "gq fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * MV
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or certificate
+ * trail not trusted.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VALID)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_mv(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "mv fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Cookie request in symmetric modes. Roll a random
+ * cookie and install in symmetric mode. Encrypt for the
+ * response, which is transmitted later.
+ */
+ case CRYPTO_COOK:
+
+ /*
+ * Discard the message if invalid or certificate
+ * trail not trusted.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VALID)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Pass the extension field to the transmit
+ * side. If already agreed, walk away.
+ */
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ if (peer->crypto & CRYPTO_FLAG_AGREE) {
+ peer->flash &= ~TEST8;
+ break;
+ }
+
+ /*
+ * Install cookie values and light the cookie
+ * bit. The transmit side will pick up and
+ * encrypt it for the response.
+ */
+ key_expire(peer);
+ peer->cookval.tstamp = ep->tstamp;
+ peer->cookval.fstamp = ep->fstamp;
+ RAND_bytes((u_char *)&peer->pcookie, 4);
+ peer->crypto &= ~CRYPTO_FLAG_AUTO;
+ peer->crypto |= CRYPTO_FLAG_AGREE;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "cook %x ts %u fs %u",
+ peer->pcookie, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Cookie response in client and symmetric modes. If the
+ * cookie bit is set, the working cookie is the EXOR of
+ * the current and new values.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed or signature not verified with
+ * respect to the cookie values.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, &peer->cookval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Decrypt the cookie, hunting all the time for
+ * errors.
+ */
+ if (vallen == (u_int) EVP_PKEY_size(host_pkey)) {
+ RSA_private_decrypt(vallen,
+ (u_char *)ep->pkt,
+ (u_char *)&temp32,
+ host_pkey->pkey.rsa,
+ RSA_PKCS1_OAEP_PADDING);
+ cookie = ntohl(temp32);
+ } else {
+ rval = XEVNT_CKY;
+ break;
+ }
+
+ /*
+ * Install cookie values and light the cookie
+ * bit. If this is not broadcast client mode, we
+ * are done here.
+ */
+ key_expire(peer);
+ peer->cookval.tstamp = ep->tstamp;
+ peer->cookval.fstamp = ep->fstamp;
+ if (peer->crypto & CRYPTO_FLAG_AGREE)
+ peer->pcookie ^= cookie;
+ else
+ peer->pcookie = cookie;
+ if (peer->hmode == MODE_CLIENT &&
+ !(peer->cast_flags & MDF_BCLNT))
+ peer->crypto |= CRYPTO_FLAG_AUTO;
+ else
+ peer->crypto &= ~CRYPTO_FLAG_AUTO;
+ peer->crypto |= CRYPTO_FLAG_AGREE;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "cook %x ts %u fs %u",
+ peer->pcookie, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install autokey values in broadcast client and
+ * symmetric modes. We have to do this every time the
+ * sever/peer cookie changes or a new keylist is
+ * rolled. Ordinarily, this is automatic as this message
+ * is piggybacked on the first NTP packet sent upon
+ * either of these events. Note that a broadcast client
+ * or symmetric peer can receive this response without a
+ * matching request.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed or signature not verified with
+ * respect to the receive autokey values.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, &peer->recval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Install autokey values and light the
+ * autokey bit. This is not hard.
+ */
+ if (peer->recval.ptr == NULL)
+ peer->recval.ptr =
+ emalloc(sizeof(struct autokey));
+ bp = (struct autokey *)peer->recval.ptr;
+ peer->recval.tstamp = ep->tstamp;
+ peer->recval.fstamp = ep->fstamp;
+ ap = (struct autokey *)ep->pkt;
+ bp->seq = ntohl(ap->seq);
+ bp->key = ntohl(ap->key);
+ peer->pkeyid = bp->key;
+ peer->crypto |= CRYPTO_FLAG_AUTO;
+ peer->flash &= ~TEST8;
+ sprintf(statstr,
+ "auto seq %d key %x ts %u fs %u", bp->seq,
+ bp->key, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * X509 certificate sign response. Validate the
+ * certificate signed by the server and install. Later
+ * this can be provided to clients of this server in
+ * lieu of the self signed certificate in order to
+ * validate the public key.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or not
+ * proventic.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_PROV)) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list.
+ */
+ if ((rval = cert_install(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_SIGN;
+ peer->flash &= ~TEST8;
+ temp32 = cinfo->nid;
+ sprintf(statstr, "sign %s 0x%x %s (%u) fs %u",
+ cinfo->issuer, cinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install leapseconds table in symmetric modes. This
+ * table is proventicated to the NIST primary servers,
+ * either by copying the file containing the table from
+ * a NIST server to a trusted server or directly using
+ * this protocol. While the entire table is installed at
+ * the server, presently only the current TAI offset is
+ * provided via the kernel to other applications.
+ */
+ case CRYPTO_TAI:
+
+ /*
+ * Discard the message if invalid.
+ */
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Pass the extension field to the transmit
+ * side. Continue below if a leapseconds table
+ * accompanies the message.
+ */
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ if (len <= VALUE_LEN) {
+ peer->flash &= ~TEST8;
+ break;
+ }
+ /* fall through */
+
+ case CRYPTO_TAI | CRYPTO_RESP:
+
+ /*
+ * If this is a response, discard the message if
+ * signature not verified with respect to the
+ * leapsecond table values.
+ */
+ if (peer->cmmd == NULL) {
+ if ((rval = crypto_verify(ep,
+ &peer->tai_leap, peer)) != XEVNT_OK)
+ break;
+ }
+
+ /*
+ * Initialize peer variables with latest update.
+ */
+ peer->tai_leap.tstamp = ep->tstamp;
+ peer->tai_leap.fstamp = ep->fstamp;
+ peer->tai_leap.vallen = ep->vallen;
+
+ /*
+ * Install the new table if there is no stored
+ * table or the new table is more recent than
+ * the stored table. Since a filestamp may have
+ * changed, recompute the signatures.
+ */
+ if (ntohl(peer->tai_leap.fstamp) >
+ ntohl(tai_leap.fstamp)) {
+ tai_leap.fstamp = ep->fstamp;
+ tai_leap.vallen = ep->vallen;
+ if (tai_leap.ptr != NULL)
+ free(tai_leap.ptr);
+ tai_leap.ptr = emalloc(vallen);
+ memcpy(tai_leap.ptr, ep->pkt, vallen);
+ crypto_update();
+ }
+ crypto_flags |= CRYPTO_FLAG_TAI;
+ peer->crypto |= CRYPTO_FLAG_LEAP;
+ peer->flash &= ~TEST8;
+ sprintf(statstr, "leap %u ts %u fs %u", vallen,
+ ntohl(ep->tstamp), ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * We come here in symmetric modes for miscellaneous
+ * commands that have value fields but are processed on
+ * the transmit side. All we need do here is check for
+ * valid field length. Remaining checks are below and on
+ * the transmit side.
+ */
+ case CRYPTO_CERT:
+ case CRYPTO_IFF:
+ case CRYPTO_GQ:
+ case CRYPTO_MV:
+ case CRYPTO_SIGN:
+ if (len < VALUE_LEN) {
+ rval = XEVNT_LEN;
+ break;
+ }
+ /* fall through */
+
+ /*
+ * We come here for miscellaneous requests and unknown
+ * requests and responses. If an unknown response or
+ * error, forget it. If a request, save the extension
+ * field for later. Unknown requests will be caught on
+ * the transmit side.
+ */
+ default:
+ if (code & (CRYPTO_RESP | CRYPTO_ERROR)) {
+ rval = XEVNT_ERR;
+ } else if ((rval = crypto_verify(ep, NULL,
+ peer)) == XEVNT_OK) {
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ }
+ }
+
+ /*
+ * We don't log length/format/timestamp errors and
+ * duplicates, which are log clogging vulnerabilities.
+ * The first error found terminates the extension field
+ * scan and we return the laundry to the caller. A
+ * length/format/timestamp error on transmit is
+ * cheerfully ignored, as the message is not sent.
+ */
+ if (rval > XEVNT_TSP) {
+ sprintf(statstr,
+ "error %x opcode %x ts %u fs %u", rval,
+ code, tstamp, fstamp);
+ record_crypto_stats(&peer->srcadr, statstr);
+ report_event(rval, peer);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ } else if (rval > XEVNT_OK && (code & CRYPTO_RESP)) {
+ rval = XEVNT_OK;
+ }
+ authlen += len;
+ }
+ return (rval);
+}
+
+
+/*
+ * crypto_xmit - construct extension fields
+ *
+ * This routine is called both when an association is configured and
+ * when one is not. The only case where this matters is to retrieve the
+ * autokey information, in which case the caller has to provide the
+ * association ID to match the association.
+ *
+ * Returns length of extension field.
+ */
+int
+crypto_xmit(
+ struct pkt *xpkt, /* transmit packet pointer */
+ struct sockaddr_storage *srcadr_sin, /* active runway */
+ int start, /* offset to extension field */
+ struct exten *ep, /* extension pointer */
+ keyid_t cookie /* session cookie */
+ )
+{
+ u_int32 *pkt; /* packet pointer */
+ struct peer *peer; /* peer structure pointer */
+ u_int opcode; /* extension field opcode */
+ struct exten *fp; /* extension pointers */
+ struct cert_info *cp, *xp; /* certificate info/value pointer */
+ char certname[MAXHOSTNAME + 1]; /* subject name buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t tstamp;
+ u_int vallen;
+ u_int len;
+ struct value vtemp;
+ associd_t associd;
+ int rval;
+ keyid_t tcookie;
+
+ /*
+ * Generate the requested extension field request code, length
+ * and association ID. If this is a response and the host is not
+ * synchronized, light the error bit and go home.
+ */
+ pkt = (u_int32 *)xpkt + start / 4;
+ fp = (struct exten *)pkt;
+ opcode = ntohl(ep->opcode);
+ associd = (associd_t) ntohl(ep->associd);
+ fp->associd = htonl(associd);
+ len = 8;
+ rval = XEVNT_OK;
+ tstamp = crypto_time();
+ switch (opcode & 0xffff0000) {
+
+ /*
+ * Send association request and response with status word and
+ * host name. Note, this message is not signed and the filestamp
+ * contains only the status word.
+ */
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+ len += crypto_send(fp, &hostval);
+ fp->fstamp = htonl(crypto_flags);
+ break;
+
+ case CRYPTO_ASSOC:
+ len += crypto_send(fp, &hostval);
+ fp->fstamp = htonl(crypto_flags | ident_scheme);
+ break;
+
+ /*
+ * Send certificate request. Use the values from the extension
+ * field.
+ */
+ case CRYPTO_CERT:
+ memset(&vtemp, 0, sizeof(vtemp));
+ vtemp.tstamp = ep->tstamp;
+ vtemp.fstamp = ep->fstamp;
+ vtemp.vallen = ep->vallen;
+ vtemp.ptr = (u_char *)ep->pkt;
+ len += crypto_send(fp, &vtemp);
+ break;
+
+ /*
+ * Send certificate response or sign request. Use the values
+ * from the certificate cache. If the request contains no
+ * subject name, assume the name of this host. This is for
+ * backwards compatibility. Private certificates are never sent.
+ */
+ case CRYPTO_SIGN:
+ case CRYPTO_CERT | CRYPTO_RESP:
+ vallen = ntohl(ep->vallen);
+ if (vallen == 8) {
+ strcpy(certname, sys_hostname);
+ } else if (vallen == 0 || vallen > MAXHOSTNAME) {
+ rval = XEVNT_LEN;
+ break;
+
+ } else {
+ memcpy(certname, ep->pkt, vallen);
+ certname[vallen] = '\0';
+ }
+
+ /*
+ * Find all certificates with matching subject. If a
+ * self-signed, trusted certificate is found, use that.
+ * If not, use the first one with matching subject. A
+ * private certificate is never divulged or signed.
+ */
+ xp = NULL;
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ if (cp->flags & CERT_PRIV)
+ continue;
+
+ if (strcmp(certname, cp->subject) == 0) {
+ if (xp == NULL)
+ xp = cp;
+ if (strcmp(certname, cp->issuer) ==
+ 0 && cp->flags & CERT_TRUST) {
+ xp = cp;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Be careful who you trust. If not yet synchronized,
+ * give back an empty response. If certificate not found
+ * or beyond the lifetime, return an error. This is to
+ * avoid a bad dude trying to get an expired certificate
+ * re-signed. Otherwise, send it.
+ *
+ * Note the timestamp and filestamp are taken from the
+ * certificate value structure. For all certificates the
+ * timestamp is the latest signature update time. For
+ * host and imported certificates the filestamp is the
+ * creation epoch. For signed certificates the filestamp
+ * is the creation epoch of the trusted certificate at
+ * the base of the certificate trail. In principle, this
+ * allows strong checking for signature masquerade.
+ */
+ if (tstamp == 0)
+ break;
+
+ if (xp == NULL)
+ rval = XEVNT_CRT;
+ else if (tstamp < xp->first || tstamp > xp->last)
+ rval = XEVNT_SRV;
+ else
+ len += crypto_send(fp, &xp->cert);
+ break;
+
+ /*
+ * Send challenge in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+ if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send challenge in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+ if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send challenge in MV identity scheme.
+ */
+ case CRYPTO_MV:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send response in MV identity scheme.
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+ if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) {
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ }
+ break;
+
+ /*
+ * Send certificate sign response. The integrity of the request
+ * certificate has already been verified on the receive side.
+ * Sign the response using the local server key. Use the
+ * filestamp from the request and use the timestamp as the
+ * current time. Light the error bit if the certificate is
+ * invalid or contains an unverified signature.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+ if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send public key and signature. Use the values from the public
+ * key.
+ */
+ case CRYPTO_COOK:
+ len += crypto_send(fp, &pubkey);
+ break;
+
+ /*
+ * Encrypt and send cookie and signature. Light the error bit if
+ * anything goes wrong.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+ if ((opcode & 0xffff) < VALUE_LEN) {
+ rval = XEVNT_LEN;
+ break;
+ }
+ if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) {
+ tcookie = cookie;
+ } else {
+ if ((peer = findpeerbyassoc(associd)) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ tcookie = peer->pcookie;
+ }
+ if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) ==
+ XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Find peer and send autokey data and signature in broadcast
+ * server and symmetric modes. Use the values in the autokey
+ * structure. If no association is found, either the server has
+ * restarted with new associations or some perp has replayed an
+ * old message, in which case light the error bit.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+ if ((peer = findpeerbyassoc(associd)) == NULL) {
+ rval = XEVNT_ERR;
+ break;
+ }
+ peer->flags &= ~FLAG_ASSOC;
+ len += crypto_send(fp, &peer->sndval);
+ break;
+
+ /*
+ * Send leapseconds table and signature. Use the values from the
+ * tai structure. If no table has been loaded, just send an
+ * empty request.
+ */
+ case CRYPTO_TAI:
+ case CRYPTO_TAI | CRYPTO_RESP:
+ if (crypto_flags & CRYPTO_FLAG_TAI)
+ len += crypto_send(fp, &tai_leap);
+ break;
+
+ /*
+ * Default - Fall through for requests; for unknown responses,
+ * flag as error.
+ */
+ default:
+ if (opcode & CRYPTO_RESP)
+ rval = XEVNT_ERR;
+ }
+
+ /*
+ * In case of error, flame the log. If a request, toss the
+ * puppy; if a response, return so the sender can flame, too.
+ */
+ if (rval != XEVNT_OK) {
+ opcode |= CRYPTO_ERROR;
+ sprintf(statstr, "error %x opcode %x", rval, opcode);
+ record_crypto_stats(srcadr_sin, statstr);
+ report_event(rval, NULL);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_xmit: %s\n", statstr);
+#endif
+ if (!(opcode & CRYPTO_RESP))
+ return (0);
+ }
+
+ /*
+ * Round up the field length to a multiple of 8 bytes and save
+ * the request code and length.
+ */
+ len = ((len + 7) / 8) * 8;
+ fp->opcode = htonl((opcode & 0xffff0000) | len);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_xmit: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n",
+ crypto_flags, start, len, opcode >> 16, associd);
+#endif
+ return (len);
+}
+
+
+/*
+ * crypto_verify - parse and verify the extension field and value
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_LEN bad field format or length
+ * XEVNT_TSP bad timestamp
+ * XEVNT_FSP bad filestamp
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_SGL bad signature length
+ * XEVNT_SIG signature not verified
+ * XEVNT_ERR protocol error
+ */
+static int
+crypto_verify(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ EVP_PKEY *pkey; /* server public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp, tstamp1 = 0; /* timestamp */
+ tstamp_t fstamp, fstamp1 = 0; /* filestamp */
+ u_int vallen; /* value length */
+ u_int siglen; /* signature length */
+ u_int opcode, len;
+ int i;
+
+ /*
+ * We require valid opcode and field lengths, timestamp,
+ * filestamp, public key, digest, signature length and
+ * signature, where relevant. Note that preliminary length
+ * checks are done in the main loop.
+ */
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ opcode = ntohl(ep->opcode) & 0xffff0000;
+
+ /*
+ * Check for valid operation code and protocol. The opcode must
+ * not have the error bit set. If a response, it must have a
+ * value header. If a request and does not contain a value
+ * header, no need for further checking.
+ */
+ if (opcode & CRYPTO_ERROR)
+ return (XEVNT_ERR);
+
+ if (opcode & CRYPTO_RESP) {
+ if (len < VALUE_LEN)
+ return (XEVNT_LEN);
+ } else {
+ if (len < VALUE_LEN)
+ return (XEVNT_OK);
+ }
+
+ /*
+ * We have a value header. Check for valid field lengths. The
+ * field length must be long enough to contain the value header,
+ * value and signature. Note both the value and signature fields
+ * are rounded up to the next word.
+ */
+ vallen = ntohl(ep->vallen);
+ i = (vallen + 3) / 4;
+ siglen = ntohl(ep->pkt[i++]);
+ if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) /
+ 4) * 4)
+ return (XEVNT_LEN);
+
+ /*
+ * Punt if this is a response with no data. Punt if this is a
+ * request and a previous response is pending.
+ */
+ if (opcode & CRYPTO_RESP) {
+ if (vallen == 0)
+ return (XEVNT_LEN);
+ } else {
+ if (peer->cmmd != NULL)
+ return (XEVNT_LEN);
+ }
+
+ /*
+ * Check for valid timestamp and filestamp. If the timestamp is
+ * zero, the sender is not synchronized and signatures are
+ * disregarded. If not, the timestamp must not precede the
+ * filestamp. The timestamp and filestamp must not precede the
+ * corresponding values in the value structure, if present. Once
+ * the autokey values have been installed, the timestamp must
+ * always be later than the corresponding value in the value
+ * structure. Duplicate timestamps are illegal once the cookie
+ * has been validated.
+ */
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < fstamp)
+ return (XEVNT_TSP);
+
+ if (vp != NULL) {
+ tstamp1 = ntohl(vp->tstamp);
+ fstamp1 = ntohl(vp->fstamp);
+ if ((tstamp < tstamp1 || (tstamp == tstamp1 &&
+ (peer->crypto & CRYPTO_FLAG_AUTO))))
+ return (XEVNT_TSP);
+
+ if ((tstamp < fstamp1 || fstamp < fstamp1))
+ return (XEVNT_FSP);
+ }
+
+ /*
+ * Check for valid signature length, public key and digest
+ * algorithm.
+ */
+ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV)
+ pkey = sign_pkey;
+ else
+ pkey = peer->pkey;
+ if (siglen == 0 || pkey == NULL || peer->digest == NULL)
+ return (XEVNT_OK);
+
+ if (siglen != (u_int)EVP_PKEY_size(pkey))
+ return (XEVNT_SGL);
+
+ /*
+ * Darn, I thought we would never get here. Verify the
+ * signature. If the identity exchange is verified, light the
+ * proventic bit. If no client identity scheme is specified,
+ * avoid doing the sign exchange.
+ */
+ EVP_VerifyInit(&ctx, peer->digest);
+ EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12);
+ if (!EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey))
+ return (XEVNT_SIG);
+
+ if (peer->crypto & CRYPTO_FLAG_VRFY) {
+ peer->crypto |= CRYPTO_FLAG_PROV;
+ if (!(crypto_flags & CRYPTO_FLAG_MASK))
+ peer->crypto |= CRYPTO_FLAG_SIGN;
+ }
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_encrypt - construct encrypted cookie and signature from
+ * extension field and cookie
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_CKY bad or missing cookie
+ * XEVNT_PER host certificate expired
+ */
+static int
+crypto_encrypt(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ keyid_t *cookie /* server cookie */
+ )
+{
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int32 temp32;
+ u_int len;
+ u_char *ptr;
+
+ /*
+ * Extract the public key from the request.
+ */
+ len = ntohl(ep->vallen);
+ ptr = (u_char *)ep->pkt;
+ pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_encrypt %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Encrypt the cookie, encode in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ len = EVP_PKEY_size(pkey);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ temp32 = htonl(*cookie);
+ if (!RSA_public_encrypt(4, (u_char *)&temp32, vp->ptr,
+ pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING)) {
+ msyslog(LOG_ERR, "crypto_encrypt %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return (XEVNT_CKY);
+ }
+ EVP_PKEY_free(pkey);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_ident - construct extension field for identity scheme
+ *
+ * This routine determines which identity scheme is in use and
+ * constructs an extension field for that scheme.
+ */
+u_int
+crypto_ident(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ char filename[MAXFILENAME + 1];
+
+ /*
+ * If the server identity has already been verified, no further
+ * action is necessary. Otherwise, try to load the identity file
+ * of the certificate issuer. If the issuer file is not found,
+ * try the host file. If nothing found, declare a cryptobust.
+ * Note we can't get here unless the trusted certificate has
+ * been found and the CRYPTO_FLAG_VALID bit is set, so the
+ * certificate issuer is valid.
+ */
+ if (peer->ident_pkey != NULL)
+ EVP_PKEY_free(peer->ident_pkey);
+ if (peer->crypto & CRYPTO_FLAG_GQ) {
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_GQ);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_GQ);
+ }
+ if (peer->crypto & CRYPTO_FLAG_IFF) {
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_IFF);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_IFF);
+ }
+ if (peer->crypto & CRYPTO_FLAG_MV) {
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_MV);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_MV);
+ }
+
+ /*
+ * No compatible identity scheme is available. Life is hard.
+ */
+ msyslog(LOG_INFO,
+ "crypto_ident: no compatible identity scheme found");
+ return (0);
+}
+
+
+/*
+ * crypto_args - construct extension field from arguments
+ *
+ * This routine creates an extension field with current timestamps and
+ * specified opcode, association ID and optional string. Note that the
+ * extension field is created here, but freed after the crypto_xmit()
+ * call in the protocol module.
+ *
+ * Returns extension field pointer (no errors).
+ */
+struct exten *
+crypto_args(
+ struct peer *peer, /* peer structure pointer */
+ u_int opcode, /* operation code */
+ char *str /* argument string */
+ )
+{
+ tstamp_t tstamp; /* NTP timestamp */
+ struct exten *ep; /* extension field pointer */
+ u_int len; /* extension field length */
+
+ tstamp = crypto_time();
+ len = sizeof(struct exten);
+ if (str != NULL)
+ len += strlen(str);
+ ep = emalloc(len);
+ memset(ep, 0, len);
+ if (opcode == 0)
+ return (ep);
+
+ ep->opcode = htonl(opcode + len);
+
+ /*
+ * If a response, send our ID; if a request, send the
+ * responder's ID.
+ */
+ if (opcode & CRYPTO_RESP)
+ ep->associd = htonl(peer->associd);
+ else
+ ep->associd = htonl(peer->assoc);
+ ep->tstamp = htonl(tstamp);
+ ep->fstamp = hostval.tstamp;
+ ep->vallen = 0;
+ if (str != NULL) {
+ ep->vallen = htonl(strlen(str));
+ memcpy((char *)ep->pkt, str, strlen(str));
+ } else {
+ ep->pkt[0] = peer->associd;
+ }
+ return (ep);
+}
+
+
+/*
+ * crypto_send - construct extension field from value components
+ *
+ * Returns extension field length. Note: it is not polite to send a
+ * nonempty signature with zero timestamp or a nonzero timestamp with
+ * empty signature, but these rules are not enforced here.
+ */
+u_int
+crypto_send(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ u_int len, temp32;
+ int i;
+
+ /*
+ * Copy data. If the data field is empty or zero length, encode
+ * an empty value with length zero.
+ */
+ ep->tstamp = vp->tstamp;
+ ep->fstamp = vp->fstamp;
+ ep->vallen = vp->vallen;
+ len = 12;
+ temp32 = ntohl(vp->vallen);
+ if (temp32 > 0 && vp->ptr != NULL)
+ memcpy(ep->pkt, vp->ptr, temp32);
+
+ /*
+ * Copy signature. If the signature field is empty or zero
+ * length, encode an empty signature with length zero.
+ */
+ i = (temp32 + 3) / 4;
+ len += i * 4 + 4;
+ ep->pkt[i++] = vp->siglen;
+ temp32 = ntohl(vp->siglen);
+ if (temp32 > 0 && vp->sig != NULL)
+ memcpy(&ep->pkt[i], vp->sig, temp32);
+ len += temp32;
+ return (len);
+}
+
+
+/*
+ * crypto_update - compute new public value and sign extension fields
+ *
+ * This routine runs periodically, like once a day, and when something
+ * changes. It updates the timestamps on three value structures and one
+ * value structure list, then signs all the structures:
+ *
+ * hostval host name (not signed)
+ * pubkey public key
+ * cinfo certificate info/value list
+ * tai_leap leapseconds file
+ *
+ * Filestamps are proventicated data, so this routine is run only when
+ * the host has been synchronized to a proventicated source. Thus, the
+ * timestamp is proventicated, too, and can be used to deflect
+ * clogging attacks and even cook breakfast.
+ *
+ * Returns void (no errors)
+ */
+void
+crypto_update(void)
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ struct cert_info *cp, *cpn; /* certificate info/value */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int len;
+
+ if ((tstamp = crypto_time()) == 0)
+ return;
+
+ hostval.tstamp = htonl(tstamp);
+
+ /*
+ * Sign public key and timestamps. The filestamp is derived from
+ * the host key file extension from wherever the file was
+ * generated.
+ */
+ if (pubkey.vallen != 0) {
+ pubkey.tstamp = hostval.tstamp;
+ pubkey.siglen = 0;
+ if (pubkey.sig == NULL)
+ pubkey.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12);
+ EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen));
+ if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey))
+ pubkey.siglen = htonl(len);
+ }
+
+ /*
+ * Sign certificates and timestamps. The filestamp is derived
+ * from the certificate file extension from wherever the file
+ * was generated. Note we do not throw expired certificates
+ * away; they may have signed younger ones.
+ */
+ for (cp = cinfo; cp != NULL; cp = cpn) {
+ cpn = cp->link;
+ cp->cert.tstamp = hostval.tstamp;
+ cp->cert.siglen = 0;
+ if (cp->cert.sig == NULL)
+ cp->cert.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12);
+ EVP_SignUpdate(&ctx, cp->cert.ptr,
+ ntohl(cp->cert.vallen));
+ if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey))
+ cp->cert.siglen = htonl(len);
+ }
+
+ /*
+ * Sign leapseconds table and timestamps. The filestamp is
+ * derived from the leapsecond file extension from wherever the
+ * file was generated.
+ */
+ if (tai_leap.vallen != 0) {
+ tai_leap.tstamp = hostval.tstamp;
+ tai_leap.siglen = 0;
+ if (tai_leap.sig == NULL)
+ tai_leap.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12);
+ EVP_SignUpdate(&ctx, tai_leap.ptr,
+ ntohl(tai_leap.vallen));
+ if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey))
+ tai_leap.siglen = htonl(len);
+ }
+ sprintf(statstr, "update ts %u", ntohl(hostval.tstamp));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_update: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * value_free - free value structure components.
+ *
+ * Returns void (no errors)
+ */
+void
+value_free(
+ struct value *vp /* value structure */
+ )
+{
+ if (vp->ptr != NULL)
+ free(vp->ptr);
+ if (vp->sig != NULL)
+ free(vp->sig);
+ memset(vp, 0, sizeof(struct value));
+}
+
+
+/*
+ * crypto_time - returns current NTP time in seconds.
+ */
+tstamp_t
+crypto_time()
+{
+ l_fp tstamp; /* NTP time */ L_CLR(&tstamp);
+
+ L_CLR(&tstamp);
+ if (sys_leap != LEAP_NOTINSYNC)
+ get_systime(&tstamp);
+ return (tstamp.l_ui);
+}
+
+
+/*
+ * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds.
+ */
+u_long
+asn2ntp (
+ ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */
+ )
+{
+ char *v; /* pointer to ASN1_TIME string */
+ struct tm tm; /* used to convert to NTP time */
+
+ /*
+ * Extract time string YYMMDDHHMMSSZ from ASN1 time structure.
+ * Note that the YY, MM, DD fields start with one, the HH, MM,
+ * SS fiels start with zero and the Z character should be 'Z'
+ * for UTC. Also note that years less than 50 map to years
+ * greater than 100. Dontcha love ASN.1? Better than MIL-188.
+ */
+ if (asn1time->length > 13)
+ return ((u_long)(~0)); /* We can't use -1 here. It's invalid */
+
+ v = (char *)asn1time->data;
+ tm.tm_year = (v[0] - '0') * 10 + v[1] - '0';
+ if (tm.tm_year < 50)
+ tm.tm_year += 100;
+ tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1;
+ tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0';
+ tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0';
+ tm.tm_min = (v[8] - '0') * 10 + v[9] - '0';
+ tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0';
+ tm.tm_wday = 0;
+ tm.tm_yday = 0;
+ tm.tm_isdst = 0;
+ return (timegm(&tm) + JAN_1970);
+}
+
+
+/*
+ * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number.
+ */
+static int
+bighash(
+ BIGNUM *bn, /* BIGNUM * from */
+ BIGNUM *bk /* BIGNUM * to */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ u_char *ptr; /* a BIGNUM as binary string */
+ u_int len;
+
+ len = BN_num_bytes(bn);
+ ptr = emalloc(len);
+ BN_bn2bin(bn, ptr);
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, ptr, len);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ BN_bin2bn(dgst, len, bk);
+
+ /* XXX MEMLEAK? free ptr? */
+
+ return (1);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Schnorr (IFF) identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Schnorr (IFF) identity scheme is intended for use when
+ * the ntp-genkeys program does not generate the certificates used in
+ * the protocol and the group key cannot be conveyed in the certificate
+ * itself. For this purpose, new generations of IFF values must be
+ * securely transmitted to all members of the group before use. The
+ * scheme is self contained and independent of new generations of host
+ * keys, sign keys and certificates.
+ *
+ * The IFF identity scheme is based on DSA cryptography and algorithms
+ * described in Stinson p. 285. The IFF values hide in a DSA cuckoo
+ * structure, but only the primes and generator are used. The p is a
+ * 512-bit prime, q a 160-bit prime that divides p - 1 and is a qth root
+ * of 1 mod p; that is, g^q = 1 mod p. The TA rolls primvate random
+ * group key b disguised as a DSA structure member, then computes public
+ * key g^(q - b). These values are shared only among group members and
+ * never revealed in messages. Alice challenges Bob to confirm identity
+ * using the protocol described below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the public primes
+ * p, q and generator g. The TA gives private key b to Bob and public
+ * key v = g^(q - a) mod p to Alice.
+ *
+ * Alice rolls new random challenge r and sends to Bob in the IFF
+ * request message. Bob rolls new random k, then computes y = k + b r
+ * mod q and x = g^k mod p and sends (y, hash(x)) to Alice in the
+ * response message. Besides making the response shorter, the hash makes
+ * it effectivey impossible for an intruder to solve for b by observing
+ * a number of these messages.
+ *
+ * Alice receives the response and computes g^y v^r mod p. After a bit
+ * of algebra, this simplifies to g^k. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice - construct Alice's challenge in IFF scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group key
+ */
+static int
+crypto_alice(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(dsa->q);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r */
+ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ID bad or missing group key
+ * XEVNT_ERR protocol error
+ * XEVNT_PER host expired certificate
+ */
+static int
+crypto_bob(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ DSA_SIG *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *bn, *bk, *r;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the IFF parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (iffpar_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_bob: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ dsa = iffpar_pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), computes y = k + b r mod q
+ * and x = g^k mod p, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(bk, len * 8, -1, 1); /* k */
+ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */
+ BN_add(bn, bn, bk);
+ BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */
+ sdsa->r = BN_dup(bn);
+ BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */
+ bighash(bk, bk);
+ sdsa->s = BN_dup(bk);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(bn); BN_free(bk);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(if_fstamp);
+ len = i2d_DSA_SIG(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_iff - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group key
+ * XEVNT_FSP bad filestamp
+ */
+int
+crypto_iff(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BIGNUM *bn, *bk;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the IFF parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the k + b r and g^k values from the response.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_iff %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute g^(k + b r) g^(q - b)r mod p.
+ */
+ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx);
+ BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx);
+ BN_mod_mul(bn, bn, bk, dsa->p, bctx);
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(bn, bn);
+ temp = BN_cmp(bn, sdsa->s);
+ BN_free(bn); BN_free(bk); BN_CTX_free(bctx);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Guillou-Quisquater (GQ) *
+ * identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Guillou-Quisquater (GQ) identity scheme is intended for use when
+ * the ntp-genkeys program generates the certificates used in the
+ * protocol and the group key can be conveyed in a certificate extension
+ * field. The scheme is self contained and independent of new
+ * generations of host keys, sign keys and certificates.
+ *
+ * The GQ identity scheme is based on RSA cryptography and algorithms
+ * described in Stinson p. 300 (with errors). The GQ values hide in a
+ * RSA cuckoo structure, but only the modulus is used. The 512-bit
+ * public modulus is n = p q, where p and q are secret large primes. The
+ * TA rolls random group key b disguised as a RSA structure member.
+ * Except for the public key, these values are shared only among group
+ * members and never revealed in messages.
+ *
+ * When rolling new certificates, Bob recomputes the private and
+ * public keys. The private key u is a random roll, while the public key
+ * is the inverse obscured by the group key v = (u^-1)^b. These values
+ * replace the private and public keys normally generated by the RSA
+ * scheme. Alice challenges Bob to confirm identity using the protocol
+ * described below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the same modulus n
+ * and some random b as the group key. These values are computed and
+ * distributed in advance via secret means, although only the group key
+ * b is truly secret. Each has a private random private key u and public
+ * key (u^-1)^b, although not necessarily the same ones. Bob and Alice
+ * can regenerate the key pair from time to time without affecting
+ * operations. The public key is conveyed on the certificate in an
+ * extension field; the private key is never revealed.
+ *
+ * Alice rolls new random challenge r and sends to Bob in the GQ
+ * request message. Bob rolls new random k, then computes y = k u^r mod
+ * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response
+ * message. Besides making the response shorter, the hash makes it
+ * effectivey impossible for an intruder to solve for b by observing
+ * a number of these messages.
+ *
+ * Alice receives the response and computes y^b v^r mod n. After a bit
+ * of algebra, this simplifies to k^b. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice2 - construct Alice's challenge in GQ scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PER host certificate expired
+ */
+static int
+crypto_alice2(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+
+ if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice2: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < n). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(rsa->n);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */
+ BN_mod(peer->iffval, peer->iffval, rsa->n, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob2 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ID bad or missing group key
+ * XEVNT_ERR protocol error
+ * XEVNT_PER host certificate expired
+ */
+static int
+crypto_bob2(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *g, *y;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the GQ parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (gqpar_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_bob2: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ rsa = gqpar_pkey->pkey.rsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob2 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < n), computes y = k u^r mod n and
+ * x = k^b mod n, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(k, len * 8, -1, 1); /* k */
+ BN_mod(k, k, rsa->n, bctx);
+ BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */
+ BN_mod_mul(y, k, y, rsa->n, bctx); /* k u^r mod n */
+ sdsa->r = BN_dup(y);
+ BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */
+ bighash(g, g);
+ sdsa->s = BN_dup(g);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(k); BN_free(g); BN_free(y);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(gq_fstamp);
+ len = i2d_DSA_SIG(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob2 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_gq - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group keys
+ * XEVNT_ERR protocol error
+ * XEVNT_FSP bad filestamp
+ */
+int
+crypto_gq(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* RSA signature context fake */
+ BIGNUM *y, *v;
+ const u_char *ptr;
+ u_int len;
+ int temp;
+
+ /*
+ * If the GQ parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_gq: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the y = k u^r and hash(x = k^b) values from the
+ * response.
+ */
+ bctx = BN_CTX_new(); y = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_gq %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute v^r y^b mod n.
+ */
+ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx);
+ /* v^r mod n */
+ BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */
+ BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(y, y);
+ temp = BN_cmp(y, sdsa->s);
+ BN_CTX_free(bctx); BN_free(y); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Mu-Varadharajan (MV) identity *
+ * scheme *
+ * *
+ ***********************************************************************
+ */
+/*
+ * The Mu-Varadharajan (MV) cryptosystem was originally intended when
+ * servers broadcast messages to clients, but clients never send
+ * messages to servers. There is one encryption key for the server and a
+ * separate decryption key for each client. It operated something like a
+ * pay-per-view satellite broadcasting system where the session key is
+ * encrypted by the broadcaster and the decryption keys are held in a
+ * tamperproof set-top box.
+ *
+ * The MV parameters and private encryption key hide in a DSA cuckoo
+ * structure which uses the same parameters, but generated in a
+ * different way. The values are used in an encryption scheme similar to
+ * El Gamal cryptography and a polynomial formed from the expansion of
+ * product terms (x - x[j]), as described in Mu, Y., and V.
+ * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
+ * 223-231. The paper has significant errors and serious omissions.
+ *
+ * Let q be the product of n distinct primes s'[j] (j = 1...n), where
+ * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
+ * that q and each s'[j] divide p - 1 and p has M = n * m + 1
+ * significant bits. The elements x mod q of Zq with the elements 2 and
+ * the primes removed form a field Zq* valid for polynomial arithetic.
+ * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1
+ * mod p. We expect M to be in the 500-bit range and n relatively small,
+ * like 25, so the likelihood of a randomly generated element of x mod q
+ * of Zq colliding with a factor of p - 1 is very small and can be
+ * avoided. Associated with each s'[j] is an element s[j] such that s[j]
+ * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) /
+ * s'[j]. These are the parameters of the scheme and they are expensive
+ * to compute.
+ *
+ * We set up an instance of the scheme as follows. A set of random
+ * values x[j] mod q (j = 1...n), are generated as the zeros of a
+ * polynomial of order n. The product terms (x - x[j]) are expanded to
+ * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
+ * used as exponents of the generator g mod p to generate the private
+ * encryption key A. The pair (gbar, ghat) of public server keys and the
+ * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
+ * to construct the decryption keys. The devil is in the details.
+ *
+ * The distinguishing characteristic of this scheme is the capability to
+ * revoke keys. Included in the calculation of E, gbar and ghat is the
+ * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is
+ * subsequently removed from the product and E, gbar and ghat
+ * recomputed, the jth client will no longer be able to compute E^-1 and
+ * thus unable to decrypt the block.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Bob has the server values (p, A, q, gbar,
+ * ghat) and Alice the client values (p, xbar, xhat).
+ *
+ * Alice rolls new random challenge r (0 < r < p) and sends to Bob in
+ * the MV request message. Bob rolls new random k (0 < k < q), encrypts
+ * y = A^k mod p (a permutation) and sends (hash(y), gbar^k, ghat^k) to
+ * Alice.
+ *
+ * Alice receives the response and computes the decryption key (the
+ * inverse permutation) from previously obtained (xbar, xhat) and
+ * (gbar^k, ghat^k) in the message. She computes the inverse, which is
+ * unique by reasons explained in the ntp-keygen.c program sources. If
+ * the hash of this result matches hash(y), Alice knows that Bob has the
+ * group key b. The signed response binds this knowledge to Bob's
+ * private key and the public key previously received in his
+ * certificate.
+ *
+ * crypto_alice3 - construct Alice's challenge in MV scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group key
+ * XEVNT_PER host certificate expired
+ */
+static int
+crypto_alice3(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice3: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(dsa->p);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r */
+ BN_mod(peer->iffval, peer->iffval, dsa->p, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob3 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_ERR protocol error
+ * XEVNT_PER host certificate expired
+ */
+static int
+crypto_bob3(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *u;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the MV parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (mvpar_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_bob3: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ dsa = mvpar_pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob3 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), making sure it is not a
+ * factor of q. He then computes y = A^k r and sends (hash(y),
+ * gbar^k, ghat^k) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new();
+ sdsa = DSA_new();
+ sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new();
+ while (1) {
+ BN_rand(k, BN_num_bits(dsa->q), 0, 0);
+ BN_mod(k, k, dsa->q, bctx);
+ BN_gcd(u, k, dsa->q, bctx);
+ if (BN_is_one(u))
+ break;
+ }
+ BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A r */
+ BN_mod_mul(u, u, r, dsa->p, bctx);
+ bighash(u, sdsa->p);
+ BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */
+ BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */
+ BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(mv_fstamp);
+ len = i2d_DSAparams(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob3 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_free(sdsa);
+ return (XEVNT_ERR);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSAparams(sdsa, &ptr);
+ DSA_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_mv - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing group key
+ * XEVNT_ERR protocol error
+ * XEVNT_FSP bad filestamp
+ */
+int
+crypto_mv(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ BIGNUM *k, *u, *v;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the MV parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: scheme unavailable");
+ return (XEVNT_ID);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_mv: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: missing challenge");
+ return (XEVNT_ID);
+ }
+
+ /*
+ * Extract the (hash(y), gbar, ghat) values from the response.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_mv %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_ERR);
+ }
+
+ /*
+ * Compute (gbar^xhat ghat^xbar)^-1 mod p.
+ */
+ BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx);
+ BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx);
+ BN_mod_mul(u, u, v, dsa->p, bctx);
+ BN_mod_inverse(u, u, dsa->p, bctx);
+ BN_mod_mul(v, u, peer->iffval, dsa->p, bctx);
+
+ /*
+ * The result should match the hash of r mod p.
+ */
+ bighash(v, v);
+ temp = BN_cmp(v, sdsa->p);
+ BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used to manipulate certificates *
+ * *
+ ***********************************************************************
+ */
+/*
+ * cert_parse - parse x509 certificate and create info/value structures.
+ *
+ * The server certificate includes the version number, issuer name,
+ * subject name, public key and valid date interval. If the issuer name
+ * is the same as the subject name, the certificate is self signed and
+ * valid only if the server is configured as trustable. If the names are
+ * different, another issuer has signed the server certificate and
+ * vouched for it. In this case the server certificate is valid if
+ * verified by the issuer public key.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+struct cert_info * /* certificate information structure */
+cert_parse(
+ u_char *asn1cert, /* X509 certificate */
+ u_int len, /* certificate length */
+ tstamp_t fstamp /* filestamp */
+ )
+{
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* X509v3 extension */
+ struct cert_info *ret; /* certificate info/value */
+ BIO *bp;
+ X509V3_EXT_METHOD *method;
+ char pathbuf[MAXFILENAME];
+ u_char *uptr;
+ char *ptr;
+ int temp, cnt, i;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ */
+ uptr = asn1cert;
+ if ((cert = d2i_X509(NULL, &uptr, len)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+
+ /*
+ * Extract version, subject name and public key.
+ */
+ ret = emalloc(sizeof(struct cert_info));
+ memset(ret, 0, sizeof(struct cert_info));
+ if ((ret->pkey = X509_get_pubkey(cert)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->version = X509_get_version(cert);
+ X509_NAME_oneline(X509_get_subject_name(cert), pathbuf,
+ MAXFILENAME - 1);
+ ptr = strstr(pathbuf, "CN=");
+ if (ptr == NULL) {
+ msyslog(LOG_INFO, "cert_parse: invalid subject %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->subject = emalloc(strlen(ptr) + 1);
+ strcpy(ret->subject, ptr + 3);
+
+ /*
+ * Extract remaining objects. Note that the NTP serial number is
+ * the NTP seconds at the time of signing, but this might not be
+ * the case for other authority. We don't bother to check the
+ * objects at this time, since the real crunch can happen only
+ * when the time is valid but not yet certificated.
+ */
+ ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+ ret->digest = (const EVP_MD *)EVP_get_digestbynid(ret->nid);
+ ret->serial =
+ (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert));
+ X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf,
+ MAXFILENAME);
+ if ((ptr = strstr(pathbuf, "CN=")) == NULL) {
+ msyslog(LOG_INFO, "cert_parse: invalid issuer %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->issuer = emalloc(strlen(ptr) + 1);
+ strcpy(ret->issuer, ptr + 3);
+ ret->first = asn2ntp(X509_get_notBefore(cert));
+ ret->last = asn2ntp(X509_get_notAfter(cert));
+
+ /*
+ * Extract extension fields. These are ad hoc ripoffs of
+ * currently assigned functions and will certainly be changed
+ * before prime time.
+ */
+ cnt = X509_get_ext_count(cert);
+ for (i = 0; i < cnt; i++) {
+ ext = X509_get_ext(cert, i);
+ method = X509V3_EXT_get(ext);
+ temp = OBJ_obj2nid(ext->object);
+ switch (temp) {
+
+ /*
+ * If a key_usage field is present, we decode whether
+ * this is a trusted or private certificate. This is
+ * dorky; all we want is to compare NIDs, but OpenSSL
+ * insists on BIO text strings.
+ */
+ case NID_ext_key_usage:
+ bp = BIO_new(BIO_s_mem());
+ X509V3_EXT_print(bp, ext, 0, 0);
+ BIO_gets(bp, pathbuf, MAXFILENAME);
+ BIO_free(bp);
+#if DEBUG
+ if (debug)
+ printf("cert_parse: %s: %s\n",
+ OBJ_nid2ln(temp), pathbuf);
+#endif
+ if (strcmp(pathbuf, "Trust Root") == 0)
+ ret->flags |= CERT_TRUST;
+ else if (strcmp(pathbuf, "Private") == 0)
+ ret->flags |= CERT_PRIV;
+ break;
+
+ /*
+ * If a NID_subject_key_identifier field is present, it
+ * contains the GQ public key.
+ */
+ case NID_subject_key_identifier:
+ ret->grplen = ext->value->length - 2;
+ ret->grpkey = emalloc(ret->grplen);
+ memcpy(ret->grpkey, &ext->value->data[2],
+ ret->grplen);
+ break;
+ }
+ }
+
+ /*
+ * If certificate is self signed, verify signature.
+ */
+ if (strcmp(ret->subject, ret->issuer) == 0) {
+ if (!X509_verify(cert, ret->pkey)) {
+ msyslog(LOG_INFO,
+ "cert_parse: signature not verified %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Verify certificate valid times. Note that certificates cannot
+ * be retroactive.
+ */
+ if (ret->first > ret->last || ret->first < fstamp) {
+ msyslog(LOG_INFO,
+ "cert_parse: invalid certificate %s first %u last %u fstamp %u",
+ ret->subject, ret->first, ret->last, fstamp);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+
+ /*
+ * Build the value structure to sign and send later.
+ */
+ ret->cert.fstamp = htonl(fstamp);
+ ret->cert.vallen = htonl(len);
+ ret->cert.ptr = emalloc(len);
+ memcpy(ret->cert.ptr, asn1cert, len);
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+ X509_free(cert);
+ return (ret);
+}
+
+
+/*
+ * cert_sign - sign x509 certificate equest and update value structure.
+ *
+ * The certificate request includes a copy of the host certificate,
+ * which includes the version number, subject name and public key of the
+ * host. The resulting certificate includes these values plus the
+ * serial number, issuer name and valid interval of the server. The
+ * valid interval extends from the current time to the same time one
+ * year hence. This may extend the life of the signed certificate beyond
+ * that of the signer certificate.
+ *
+ * It is convenient to use the NTP seconds of the current time as the
+ * serial number. In the value structure the timestamp is the current
+ * time and the filestamp is taken from the extension field. Note this
+ * routine is called only when the client clock is synchronized to a
+ * proventic source, so timestamp comparisons are valid.
+ *
+ * The host certificate is valid from the time it was generated for a
+ * period of one year. A signed certificate is valid from the time of
+ * signature for a period of one year, but only the host certificate (or
+ * sign certificate if used) is actually used to encrypt and decrypt
+ * signatures. The signature trail is built from the client via the
+ * intermediate servers to the trusted server. Each signature on the
+ * trail must be valid at the time of signature, but it could happen
+ * that a signer certificate expire before the signed certificate, which
+ * remains valid until its expiration.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_CRT bad or missing certificate
+ * XEVNT_VFY certificate not verified
+ * XEVNT_PER host certificate expired
+ */
+static int
+cert_sign(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ X509 *req; /* X509 certificate request */
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* certificate extension */
+ ASN1_INTEGER *serial; /* serial number */
+ X509_NAME *subj; /* distinguished (common) name */
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* message digest context */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int len;
+ u_char *ptr;
+ int i, temp;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ * Make sure the system clock is synchronized to a proventic
+ * source.
+ */
+ tstamp = crypto_time();
+ if (tstamp == 0)
+ return (XEVNT_TSP);
+
+ if (tstamp < cinfo->first || tstamp > cinfo->last)
+ return (XEVNT_PER);
+
+ ptr = (u_char *)ep->pkt;
+ if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) {
+ msyslog(LOG_ERR, "cert_sign %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_CRT);
+ }
+ /*
+ * Extract public key and check for errors.
+ */
+ if ((pkey = X509_get_pubkey(req)) == NULL) {
+ msyslog(LOG_ERR, "cert_sign %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(req);
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Generate X509 certificate signed by this server. For this
+ * purpose the issuer name is the server name. Also copy any
+ * extensions that might be present.
+ */
+ cert = X509_new();
+ X509_set_version(cert, X509_get_version(req));
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, tstamp);
+ X509_set_serialNumber(cert, serial);
+ X509_gmtime_adj(X509_get_notBefore(cert), 0L);
+ X509_gmtime_adj(X509_get_notAfter(cert), YEAR);
+ subj = X509_get_issuer_name(cert);
+ X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
+ (u_char *)sys_hostname, strlen(sys_hostname), -1, 0);
+ subj = X509_get_subject_name(req);
+ X509_set_subject_name(cert, subj);
+ X509_set_pubkey(cert, pkey);
+ ext = X509_get_ext(req, 0);
+ temp = X509_get_ext_count(req);
+ for (i = 0; i < temp; i++) {
+ ext = X509_get_ext(req, i);
+ X509_add_ext(cert, ext, -1);
+ }
+ X509_free(req);
+
+ /*
+ * Sign and verify the certificate.
+ */
+ X509_sign(cert, sign_pkey, sign_digest);
+ if (!X509_verify(cert, sign_pkey)) {
+ printf("cert_sign\n%s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(cert);
+ return (XEVNT_VFY);
+ }
+ len = i2d_X509(cert, NULL);
+
+ /*
+ * Build and sign the value structure. We have to sign it here,
+ * since the response has to be returned right away. This is a
+ * clogging hazard.
+ */
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = ep->fstamp;
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ ptr = vp->ptr;
+ i2d_X509(cert, &ptr);
+ vp->siglen = 0;
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+ X509_free(cert);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert_valid - verify certificate with given public key
+ *
+ * This is pretty ugly, as the certificate has to be verified in the
+ * OpenSSL X509 structure, not in the DER format in the info/value
+ * structure.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_VFY certificate not verified
+ */
+int
+cert_valid(
+ struct cert_info *cinf, /* certificate information structure */
+ EVP_PKEY *pkey /* public key */
+ )
+{
+ X509 *cert; /* X509 certificate */
+ u_char *ptr;
+
+ if (cinf->flags & CERT_SIGN)
+ return (XEVNT_OK);
+
+ ptr = (u_char *)cinf->cert.ptr;
+ cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen));
+ if (cert == NULL || !X509_verify(cert, pkey))
+ return (XEVNT_VFY);
+
+ X509_free(cert);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert - install certificate in certificate list
+ *
+ * This routine encodes an extension field into a certificate info/value
+ * structure. It searches the certificate list for duplicates and
+ * expunges whichever is older. It then searches the list for other
+ * certificates that might be verified by this latest one. Finally, it
+ * inserts this certificate first on the list.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_FSP bad or missing filestamp
+ * XEVNT_CRT bad or missing certificate
+ */
+int
+cert_install(
+ struct exten *ep, /* cert info/value */
+ struct peer *peer /* peer structure */
+ )
+{
+ struct cert_info *cp, *xp, *yp, **zp;
+
+ /*
+ * Parse and validate the signed certificate. If valid,
+ * construct the info/value structure; otherwise, scamper home.
+ */
+ if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen),
+ ntohl(ep->fstamp))) == NULL)
+ return (XEVNT_CRT);
+
+ /*
+ * Scan certificate list looking for another certificate with
+ * the same subject and issuer. If another is found with the
+ * same or older filestamp, unlink it and return the goodies to
+ * the heap. If another is found with a later filestamp, discard
+ * the new one and leave the building.
+ *
+ * Make a note to study this issue again. An earlier certificate
+ * with a long lifetime might be overtaken by a later
+ * certificate with a short lifetime, thus invalidating the
+ * earlier signature. However, we gotta find a way to leak old
+ * stuff from the cache, so we do it anyway.
+ */
+ yp = cp;
+ zp = &cinfo;
+ for (xp = cinfo; xp != NULL; xp = xp->link) {
+ if (strcmp(cp->subject, xp->subject) == 0 &&
+ strcmp(cp->issuer, xp->issuer) == 0) {
+ if (ntohl(cp->cert.fstamp) <=
+ ntohl(xp->cert.fstamp)) {
+ *zp = xp->link;;
+ cert_free(xp);
+ } else {
+ cert_free(cp);
+ return (XEVNT_FSP);
+ }
+ break;
+ }
+ zp = &xp->link;
+ }
+ yp->link = cinfo;
+ cinfo = yp;
+
+ /*
+ * Scan the certificate list to see if Y is signed by X. This is
+ * independent of order.
+ */
+ for (yp = cinfo; yp != NULL; yp = yp->link) {
+ for (xp = cinfo; xp != NULL; xp = xp->link) {
+
+ /*
+ * If the issuer of certificate Y matches the
+ * subject of certificate X, verify the
+ * signature of Y using the public key of X. If
+ * so, X signs Y.
+ */
+ if (strcmp(yp->issuer, xp->subject) != 0 ||
+ xp->flags & CERT_ERROR)
+ continue;
+
+ if (cert_valid(yp, xp->pkey) != XEVNT_OK) {
+ yp->flags |= CERT_ERROR;
+ continue;
+ }
+
+ /*
+ * The signature Y is valid only if it begins
+ * during the lifetime of X; however, it is not
+ * necessarily an error, since some other
+ * certificate might sign Y.
+ */
+ if (yp->first < xp->first || yp->first >
+ xp->last)
+ continue;
+
+ yp->flags |= CERT_SIGN;
+
+ /*
+ * If X is trusted, then Y is trusted. Note that
+ * we might stumble over a self-signed
+ * certificate that is not trusted, at least
+ * temporarily. This can happen when a dude
+ * first comes up, but has not synchronized the
+ * clock and had its certificate signed by its
+ * server. In case of broken certificate trail,
+ * this might result in a loop that could
+ * persist until timeout.
+ */
+ if (!(xp->flags & (CERT_TRUST | CERT_VALID)))
+ continue;
+
+ yp->flags |= CERT_VALID;
+
+ /*
+ * If subject Y matches the server subject name,
+ * then Y has completed the certificate trail.
+ * Save the group key and light the valid bit.
+ */
+ if (strcmp(yp->subject, peer->subject) != 0)
+ continue;
+
+ if (yp->grpkey != NULL) {
+ if (peer->grpkey != NULL)
+ BN_free(peer->grpkey);
+ peer->grpkey = BN_bin2bn(yp->grpkey,
+ yp->grplen, NULL);
+ }
+ peer->crypto |= CRYPTO_FLAG_VALID;
+
+ /*
+ * If the server has an an identity scheme,
+ * fetch the identity credentials. If not, the
+ * identity is verified only by the trusted
+ * certificate. The next signature will set the
+ * server proventic.
+ */
+ if (peer->crypto & (CRYPTO_FLAG_GQ |
+ CRYPTO_FLAG_IFF | CRYPTO_FLAG_MV))
+ continue;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ }
+ }
+
+ /*
+ * That was awesome. Now update the timestamps and signatures.
+ */
+ crypto_update();
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert_free - free certificate information structure
+ */
+void
+cert_free(
+ struct cert_info *cinf /* certificate info/value structure */
+ )
+{
+ if (cinf->pkey != NULL)
+ EVP_PKEY_free(cinf->pkey);
+ if (cinf->subject != NULL)
+ free(cinf->subject);
+ if (cinf->issuer != NULL)
+ free(cinf->issuer);
+ if (cinf->grpkey != NULL)
+ free(cinf->grpkey);
+ value_free(&cinf->cert);
+ free(cinf);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used only at initialization time *
+ * *
+ ***********************************************************************
+ */
+/*
+ * crypto_key - load cryptographic parameters and keys from files
+ *
+ * This routine loads a PEM-encoded public/private key pair and extracts
+ * the filestamp from the file name.
+ *
+ * Returns public key pointer if valid, NULL if not. Side effect updates
+ * the filestamp if valid.
+ */
+static EVP_PKEY *
+crypto_key(
+ char *cp, /* file name */
+ tstamp_t *fstamp /* filestamp */
+ )
+{
+ FILE *str; /* file handle */
+ EVP_PKEY *pkey = NULL; /* public/private key */
+ char filename[MAXFILENAME]; /* name of key file */
+ char linkname[MAXFILENAME]; /* filestamp buffer) */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ char *ptr;
+
+ /*
+ * Open the key file. If the first character of the file name is
+ * not '/', prepend the keys directory string. If something goes
+ * wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: no data %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: no filestamp %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_key: invalid timestamp %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+
+ /*
+ * Read and decrypt PEM-encoded private key.
+ */
+ pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
+ fclose(str);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_key %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+
+ /*
+ * Leave tracks in the cryptostats.
+ */
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ sprintf(statstr, "%s mod %d", &linkname[2],
+ EVP_PKEY_size(pkey) * 8);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_key: %s\n", statstr);
+ if (debug > 1) {
+ if (EVP_MD_type(pkey) == EVP_PKEY_DSA)
+ DSA_print_fp(stdout, pkey->pkey.dsa, 0);
+ else
+ RSA_print_fp(stdout, pkey->pkey.rsa, 0);
+ }
+#endif
+ return (pkey);
+}
+
+
+/*
+ * crypto_cert - load certificate from file
+ *
+ * This routine loads a X.509 RSA or DSA certificate from a file and
+ * constructs a info/cert value structure for this machine. The
+ * structure includes a filestamp extracted from the file name. Later
+ * the certificate can be sent to another machine by request.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+static struct cert_info * /* certificate information */
+crypto_cert(
+ char *cp /* file name */
+ )
+{
+ struct cert_info *ret; /* certificate information */
+ FILE *str; /* file handle */
+ char filename[MAXFILENAME]; /* name of certificate file */
+ char linkname[MAXFILENAME]; /* filestamp buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t fstamp; /* filestamp */
+ long len;
+ char *ptr;
+ char *name, *header;
+ u_char *data;
+
+ /*
+ * Open the certificate file. If the first character of the file
+ * name is not '/', prepend the keys directory string. If
+ * something goes wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: no data %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: no filestamp %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", &fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s\n",
+ filename);
+ (void)fclose(str);
+ return (NULL);
+ }
+
+ /*
+ * Read PEM-encoded certificate and install.
+ */
+ if (!PEM_read(str, &name, &header, &data, &len)) {
+ msyslog(LOG_ERR, "crypto_cert %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ (void)fclose(str);
+ return (NULL);
+ }
+ free(header);
+ if (strcmp(name, "CERTIFICATE") !=0) {
+ msyslog(LOG_INFO, "crypto_cert: wrong PEM type %s",
+ name);
+ free(name);
+ free(data);
+ (void)fclose(str);
+ return (NULL);
+ }
+ free(name);
+
+ /*
+ * Parse certificate and generate info/value structure.
+ */
+ ret = cert_parse(data, len, fstamp);
+ free(data);
+ (void)fclose(str);
+ if (ret == NULL)
+ return (NULL);
+
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ sprintf(statstr, "%s 0x%x len %lu", &linkname[2], ret->flags,
+ len);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_cert: %s\n", statstr);
+#endif
+ return (ret);
+}
+
+
+/*
+ * crypto_tai - load leapseconds table from file
+ *
+ * This routine loads the ERTS leapsecond file in NIST text format,
+ * converts to a value structure and extracts a filestamp from the file
+ * name. The data are used to establish the TAI offset from UTC, which
+ * is provided to the kernel if supported. Later the data can be sent to
+ * another machine on request.
+ */
+static void
+crypto_tai(
+ char *cp /* file name */
+ )
+{
+ FILE *str; /* file handle */
+ char buf[NTP_MAXSTRLEN]; /* file line buffer */
+ u_int32 leapsec[MAX_LEAP]; /* NTP time at leaps */
+ int offset; /* offset at leap (s) */
+ char filename[MAXFILENAME]; /* name of leapseconds file */
+ char linkname[MAXFILENAME]; /* file link (for filestamp) */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t fstamp; /* filestamp */
+ u_int len;
+ u_int32 *ptr;
+ char *dp;
+ int rval, i, j;
+
+ /*
+ * Open the file and discard comment lines. If the first
+ * character of the file name is not '/', prepend the keys
+ * directory string. If the file is not found, not to worry; it
+ * can be retrieved over the net. But, if it is found with
+ * errors, we crash and burn.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ if ((str = fopen(filename, "r")) == NULL)
+ return;
+
+ /*
+ * Extract filestamp if present.
+ */
+ rval = readlink(filename, linkname, MAXFILENAME - 1);
+ if (rval > 0) {
+ linkname[rval] = '\0';
+ dp = strrchr(linkname, '.');
+ } else {
+ dp = strrchr(filename, '.');
+ }
+ if (dp != NULL)
+ sscanf(++dp, "%u", &fstamp);
+ else
+ fstamp = 0;
+ tai_leap.fstamp = htonl(fstamp);
+
+ /*
+ * We are rather paranoid here, since an intruder might cause a
+ * coredump by infiltrating naughty values. Empty lines and
+ * comments are ignored. Other lines must begin with two
+ * integers followed by junk or comments. The first integer is
+ * the NTP seconds of leap insertion, the second is the offset
+ * of TAI relative to UTC after that insertion. The second word
+ * must equal the initial insertion of ten seconds on 1 January
+ * 1972 plus one second for each succeeding insertion.
+ */
+ i = 0;
+ while (i < MAX_LEAP) {
+ dp = fgets(buf, NTP_MAXSTRLEN - 1, str);
+ if (dp == NULL)
+ break;
+
+ if (strlen(buf) < 1)
+ continue;
+
+ if (*buf == '#')
+ continue;
+
+ if (sscanf(buf, "%u %d", &leapsec[i], &offset) != 2)
+ continue;
+
+ if (i != offset - TAI_1972)
+ break;
+
+ i++;
+ }
+ fclose(str);
+ if (dp != NULL) {
+ msyslog(LOG_INFO,
+ "crypto_tai: leapseconds file %s error %d", cp,
+ rval);
+ exit (-1);
+ }
+
+ /*
+ * The extension field table entries consists of the NTP seconds
+ * of leap insertion in network byte order.
+ */
+ len = i * sizeof(u_int32);
+ tai_leap.vallen = htonl(len);
+ ptr = emalloc(len);
+ tai_leap.ptr = (u_char *)ptr;
+ for (j = 0; j < i; j++)
+ *ptr++ = htonl(leapsec[j]);
+ crypto_flags |= CRYPTO_FLAG_TAI;
+ sprintf(statstr, "%s fs %u leap %u len %u", cp, fstamp,
+ leapsec[--j], len);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_tai: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * crypto_setup - load keys, certificate and leapseconds table
+ *
+ * This routine loads the public/private host key and certificate. If
+ * available, it loads the public/private sign key, which defaults to
+ * the host key, and leapseconds table. The host key must be RSA, but
+ * the sign key can be either RSA or DSA. In either case, the public key
+ * on the certificate must agree with the sign key.
+ */
+void
+crypto_setup(void)
+{
+ EVP_PKEY *pkey; /* private/public key pair */
+ char filename[MAXFILENAME]; /* file name buffer */
+ l_fp seed; /* crypto PRNG seed as NTP timestamp */
+ tstamp_t fstamp; /* filestamp */
+ tstamp_t sstamp; /* sign filestamp */
+ u_int len, bytes;
+ u_char *ptr;
+
+ /*
+ * Initialize structures.
+ */
+ if (!crypto_flags)
+ return;
+
+ gethostname(filename, MAXFILENAME);
+ bytes = strlen(filename) + 1;
+ sys_hostname = emalloc(bytes);
+ memcpy(sys_hostname, filename, bytes);
+ if (passwd == NULL)
+ passwd = sys_hostname;
+ memset(&hostval, 0, sizeof(hostval));
+ memset(&pubkey, 0, sizeof(pubkey));
+ memset(&tai_leap, 0, sizeof(tai_leap));
+
+ /*
+ * Load required random seed file and seed the random number
+ * generator. Be default, it is found in the user home
+ * directory. The root home directory may be / or /root,
+ * depending on the system. Wiggle the contents a bit and write
+ * it back so the sequence does not repeat when we next restart.
+ */
+ ERR_load_crypto_strings();
+ if (rand_file == NULL) {
+ if ((RAND_file_name(filename, MAXFILENAME)) != NULL) {
+ rand_file = emalloc(strlen(filename) + 1);
+ strcpy(rand_file, filename);
+ }
+ } else if (*rand_file != '/') {
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir,
+ rand_file);
+ free(rand_file);
+ rand_file = emalloc(strlen(filename) + 1);
+ strcpy(rand_file, filename);
+ }
+ if (rand_file == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: random seed file not specified");
+ exit (-1);
+ }
+ if ((bytes = RAND_load_file(rand_file, -1)) == 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: random seed file %s not found\n",
+ rand_file);
+ exit (-1);
+ }
+ get_systime(&seed);
+ RAND_seed(&seed, sizeof(l_fp));
+ RAND_write_file(rand_file);
+ OpenSSL_add_all_algorithms();
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_setup: OpenSSL version %lx random seed file %s bytes read %d\n",
+ SSLeay(), rand_file, bytes);
+#endif
+
+ /*
+ * Load required host key from file "ntpkey_host_<hostname>". It
+ * also becomes the default sign key.
+ */
+ if (host_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_host_%s",
+ sys_hostname);
+ host_file = emalloc(strlen(filename) + 1);
+ strcpy(host_file, filename);
+ }
+ pkey = crypto_key(host_file, &fstamp);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key file %s not found or corrupt",
+ host_file);
+ exit (-1);
+ }
+ host_pkey = pkey;
+ sign_pkey = pkey;
+ sstamp = fstamp;
+ hostval.fstamp = htonl(fstamp);
+ if (EVP_MD_type(host_pkey) != EVP_PKEY_RSA) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key is not RSA key type");
+ exit (-1);
+ }
+ hostval.vallen = htonl(strlen(sys_hostname));
+ hostval.ptr = (u_char *)sys_hostname;
+
+ /*
+ * Construct public key extension field for agreement scheme.
+ */
+ len = i2d_PublicKey(host_pkey, NULL);
+ ptr = emalloc(len);
+ pubkey.ptr = ptr;
+ i2d_PublicKey(host_pkey, &ptr);
+ pubkey.vallen = htonl(len);
+ pubkey.fstamp = hostval.fstamp;
+
+ /*
+ * Load optional sign key from file "ntpkey_sign_<hostname>". If
+ * loaded, it becomes the sign key.
+ */
+ if (sign_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_sign_%s",
+ sys_hostname);
+ sign_file = emalloc(strlen(filename) + 1);
+ strcpy(sign_file, filename);
+ }
+ pkey = crypto_key(sign_file, &fstamp);
+ if (pkey != NULL) {
+ sign_pkey = pkey;
+ sstamp = fstamp;
+ }
+ sign_siglen = EVP_PKEY_size(sign_pkey);
+
+ /*
+ * Load optional IFF parameters from file
+ * "ntpkey_iff_<hostname>".
+ */
+ if (iffpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ sys_hostname);
+ iffpar_file = emalloc(strlen(filename) + 1);
+ strcpy(iffpar_file, filename);
+ }
+ iffpar_pkey = crypto_key(iffpar_file, &if_fstamp);
+ if (iffpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_IFF;
+
+ /*
+ * Load optional GQ parameters from file "ntpkey_gq_<hostname>".
+ */
+ if (gqpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ sys_hostname);
+ gqpar_file = emalloc(strlen(filename) + 1);
+ strcpy(gqpar_file, filename);
+ }
+ gqpar_pkey = crypto_key(gqpar_file, &gq_fstamp);
+ if (gqpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_GQ;
+
+ /*
+ * Load optional MV parameters from file "ntpkey_mv_<hostname>".
+ */
+ if (mvpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ sys_hostname);
+ mvpar_file = emalloc(strlen(filename) + 1);
+ strcpy(mvpar_file, filename);
+ }
+ mvpar_pkey = crypto_key(mvpar_file, &mv_fstamp);
+ if (mvpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_MV;
+
+ /*
+ * Load required certificate from file "ntpkey_cert_<hostname>".
+ */
+ if (cert_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_cert_%s",
+ sys_hostname);
+ cert_file = emalloc(strlen(filename) + 1);
+ strcpy(cert_file, filename);
+ }
+ if ((cinfo = crypto_cert(cert_file)) == NULL) {
+ msyslog(LOG_ERR,
+ "certificate file %s not found or corrupt",
+ cert_file);
+ exit (-1);
+ }
+
+ /*
+ * The subject name must be the same as the host name, unless
+ * the certificate is private, in which case it may have come
+ * from another host.
+ */
+ if (!(cinfo->flags & CERT_PRIV) && strcmp(cinfo->subject,
+ sys_hostname) != 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate %s not for this host",
+ cert_file);
+ cert_free(cinfo);
+ exit (-1);
+ }
+
+ /*
+ * It the certificate is trusted, the subject must be the same
+ * as the issuer, in other words it must be self signed.
+ */
+ if (cinfo->flags & CERT_TRUST && strcmp(cinfo->subject,
+ cinfo->issuer) != 0) {
+ if (cert_valid(cinfo, sign_pkey) != XEVNT_OK) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate %s is trusted, but not self signed.",
+ cert_file);
+ cert_free(cinfo);
+ exit (-1);
+ }
+ }
+ sign_digest = cinfo->digest;
+ if (cinfo->flags & CERT_PRIV)
+ crypto_flags |= CRYPTO_FLAG_PRIV;
+ crypto_flags |= cinfo->nid << 16;
+
+ /*
+ * Load optional leapseconds table from file "ntpkey_leap". If
+ * the file is missing or defective, the values can later be
+ * retrieved from a server.
+ */
+ if (leap_file == NULL)
+ leap_file = "ntpkey_leap";
+ crypto_tai(leap_file);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_setup: flags 0x%x host %s signature %s\n",
+ crypto_flags, sys_hostname, OBJ_nid2ln(cinfo->nid));
+#endif
+}
+
+
+/*
+ * crypto_config - configure data from crypto configuration command.
+ */
+void
+crypto_config(
+ int item, /* configuration item */
+ char *cp /* file name */
+ )
+{
+ switch (item) {
+
+ /*
+ * Set random seed file name.
+ */
+ case CRYPTO_CONF_RAND:
+ rand_file = emalloc(strlen(cp) + 1);
+ strcpy(rand_file, cp);
+ break;
+
+ /*
+ * Set private key password.
+ */
+ case CRYPTO_CONF_PW:
+ passwd = emalloc(strlen(cp) + 1);
+ strcpy(passwd, cp);
+ break;
+
+ /*
+ * Set host file name.
+ */
+ case CRYPTO_CONF_PRIV:
+ host_file = emalloc(strlen(cp) + 1);
+ strcpy(host_file, cp);
+ break;
+
+ /*
+ * Set sign key file name.
+ */
+ case CRYPTO_CONF_SIGN:
+ sign_file = emalloc(strlen(cp) + 1);
+ strcpy(sign_file, cp);
+ break;
+
+ /*
+ * Set iff parameters file name.
+ */
+ case CRYPTO_CONF_IFFPAR:
+ iffpar_file = emalloc(strlen(cp) + 1);
+ strcpy(iffpar_file, cp);
+ break;
+
+ /*
+ * Set gq parameters file name.
+ */
+ case CRYPTO_CONF_GQPAR:
+ gqpar_file = emalloc(strlen(cp) + 1);
+ strcpy(gqpar_file, cp);
+ break;
+
+ /*
+ * Set mv parameters file name.
+ */
+ case CRYPTO_CONF_MVPAR:
+ mvpar_file = emalloc(strlen(cp) + 1);
+ strcpy(mvpar_file, cp);
+ break;
+
+ /*
+ * Set identity scheme.
+ */
+ case CRYPTO_CONF_IDENT:
+ if (!strcasecmp(cp, "iff"))
+ ident_scheme |= CRYPTO_FLAG_IFF;
+ else if (!strcasecmp(cp, "gq"))
+ ident_scheme |= CRYPTO_FLAG_GQ;
+ else if (!strcasecmp(cp, "mv"))
+ ident_scheme |= CRYPTO_FLAG_MV;
+ break;
+
+ /*
+ * Set certificate file name.
+ */
+ case CRYPTO_CONF_CERT:
+ cert_file = emalloc(strlen(cp) + 1);
+ strcpy(cert_file, cp);
+ break;
+
+ /*
+ * Set leapseconds file name.
+ */
+ case CRYPTO_CONF_LEAP:
+ leap_file = emalloc(strlen(cp) + 1);
+ strcpy(leap_file, cp);
+ break;
+ }
+ crypto_flags |= CRYPTO_FLAG_ENAB;
+}
+# else
+int ntp_crypto_bs_pubkey;
+# endif /* OPENSSL */
diff --git a/contrib/ntp/ntpd/ntp_filegen.c b/contrib/ntp/ntpd/ntp_filegen.c
new file mode 100644
index 0000000..932d1b6
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_filegen.c
@@ -0,0 +1,576 @@
+/*
+ * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
+ *
+ * implements file generations support for NTP
+ * logfiles and statistic files
+ *
+ *
+ * Copyright (C) 1992, 1996 by Rainer Pruy
+ * Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
+ *
+ * This code may be modified and used freely
+ * provided credits remain intact.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_string.h"
+#include "ntp_calendar.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * NTP is intended to run long periods of time without restart.
+ * Thus log and statistic files generated by NTP will grow large.
+ *
+ * this set of routines provides a central interface
+ * to generating files using file generations
+ *
+ * the generation of a file is changed according to file generation type
+ */
+
+
+/*
+ * redefine this if your system dislikes filename suffixes like
+ * X.19910101 or X.1992W50 or ....
+ */
+#define SUFFIX_SEP '.'
+
+/*
+ * other constants
+ */
+#define FGEN_AGE_SECS (24*60*60) /* life time of FILEGEN_AGE in seconds */
+
+static void filegen_open P((FILEGEN *, u_long));
+static int valid_fileref P((char *, char *));
+#ifdef UNUSED
+static FILEGEN *filegen_unregister P((char *));
+#endif /* UNUSED */
+
+static void filegen_init P((char *, const char *, FILEGEN *));
+
+/*
+ * filegen_init
+ */
+
+static void
+filegen_init(char *prefix, const char *basename, FILEGEN *fp)
+{
+ fp->fp = NULL;
+ fp->prefix = prefix; /* Yes, this is TOTALLY lame! */
+ fp->basename = (char*)emalloc(strlen(basename) + 1);
+ strcpy(fp->basename, basename);
+ fp->id = 0;
+ fp->type = FILEGEN_DAY;
+ fp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+}
+
+
+/*
+ * open a file generation according to the current settings of gen
+ * will also provide a link to basename if requested to do so
+ */
+
+static void
+filegen_open(
+ FILEGEN *gen,
+ u_long newid
+ )
+{
+ char *filename;
+ char *basename;
+ u_int len;
+ FILE *fp;
+ struct calendar cal;
+
+ len = strlen(gen->prefix) + strlen(gen->basename) + 1;
+ basename = (char*)emalloc(len);
+ sprintf(basename, "%s%s", gen->prefix, gen->basename);
+
+ switch(gen->type) {
+ default:
+ msyslog(LOG_ERR, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
+ gen->type, basename);
+ gen->type = FILEGEN_NONE;
+
+ /*FALLTHROUGH*/
+ case FILEGEN_NONE:
+ filename = (char*)emalloc(len);
+ sprintf(filename,"%s", basename);
+ break;
+
+ case FILEGEN_PID:
+ filename = (char*)emalloc(len + 1 + 1 + 10);
+ sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid);
+ break;
+
+ case FILEGEN_DAY:
+ /* You can argue here in favor of using MJD, but
+ * I would assume it to be easier for humans to interpret dates
+ * in a format they are used to in everyday life.
+ */
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 2 + 2);
+ sprintf(filename, "%s%c%04d%02d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month, cal.monthday);
+ break;
+
+ case FILEGEN_WEEK:
+ /*
+ * This is still a hack
+ * - the term week is not correlated to week as it is used
+ * normally - it just refers to a period of 7 days
+ * starting at Jan 1 - 'weeks' are counted starting from zero
+ */
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 1 + 2);
+ sprintf(filename, "%s%c%04dw%02d",
+ basename, SUFFIX_SEP, cal.year, cal.yearday / 7);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 2);
+ sprintf(filename, "%s%c%04d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month);
+ break;
+
+ case FILEGEN_YEAR:
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4);
+ sprintf(filename, "%s%c%04d", basename, SUFFIX_SEP, cal.year);
+ break;
+
+ case FILEGEN_AGE:
+ filename = (char*)emalloc(len + 1 + 2 + 10);
+ sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid);
+ break;
+ }
+
+ if (gen->type != FILEGEN_NONE) {
+ /*
+ * check for existence of a file with name 'basename'
+ * as we disallow such a file
+ * if FGEN_FLAG_LINK is set create a link
+ */
+ struct stat stats;
+ /*
+ * try to resolve name collisions
+ */
+ static u_long conflicts = 0;
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
+#endif
+ if (stat(basename, &stats) == 0) {
+ /* Hm, file exists... */
+ if (S_ISREG(stats.st_mode)) {
+ if (stats.st_nlink <= 1) {
+ /*
+ * Oh, it is not linked - try to save it
+ */
+ char *savename = (char*)emalloc(len + 1 + 1 + 10 + 10);
+ sprintf(savename, "%s%c%dC%lu",
+ basename,
+ SUFFIX_SEP,
+ (int) getpid(),
+ (u_long)conflicts++);
+ if (rename(basename, savename) != 0)
+ msyslog(LOG_ERR," couldn't save %s: %m", basename);
+ free(savename);
+ } else {
+ /*
+ * there is at least a second link to
+ * this file.
+ * just remove the conflicting one
+ */
+ if (
+#if !defined(VMS)
+ unlink(basename) != 0
+#else
+ delete(basename) != 0
+#endif
+ )
+ msyslog(LOG_ERR, "couldn't unlink %s: %m", basename);
+ }
+ } else {
+ /*
+ * Ehh? Not a regular file ?? strange !!!!
+ */
+ msyslog(LOG_ERR, "expected regular file for %s (found mode 0%lo)",
+ basename, (unsigned long)stats.st_mode);
+ }
+ } else {
+ /*
+ * stat(..) failed, but it is absolutely correct for
+ * 'basename' not to exist
+ */
+ if (errno != ENOENT)
+ msyslog(LOG_ERR,"stat(%s) failed: %m", basename);
+ }
+ }
+
+ /*
+ * now, try to open new file generation...
+ */
+ fp = fopen(filename, "a");
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
+ gen->type, (u_long)newid, filename);
+#endif
+
+ if (fp == NULL) {
+ /* open failed -- keep previous state
+ *
+ * If the file was open before keep the previous generation.
+ * This will cause output to end up in the 'wrong' file,
+ * but I think this is still better than losing output
+ *
+ * ignore errors due to missing directories
+ */
+
+ if (errno != ENOENT)
+ msyslog(LOG_ERR, "can't open %s: %m", filename);
+ } else {
+ if (gen->fp != NULL) {
+ fclose(gen->fp);
+ }
+ gen->fp = fp;
+ gen->id = newid;
+
+ if (gen->flag & FGEN_FLAG_LINK) {
+ /*
+ * need to link file to basename
+ * have to use hardlink for now as I want to allow
+ * gen->basename spanning directory levels
+ * this would make it more complex to get the correct
+ * filename for symlink
+ *
+ * Ok, it would just mean taking the part following
+ * the last '/' in the name.... Should add it later....
+ */
+
+ /* Windows NT does not support file links -Greg Schueman 1/18/97 */
+
+#if defined SYS_WINNT || defined SYS_VXWORKS
+ SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
+#elif defined(VMS)
+ errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
+#else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
+ if (link(filename, basename) != 0)
+ if (errno != EEXIST)
+ msyslog(LOG_ERR, "can't link(%s, %s): %m", filename, basename);
+#endif /* SYS_WINNT || VXWORKS */
+ } /* flags & FGEN_FLAG_LINK */
+ } /* else fp == NULL */
+
+ free(basename);
+ free(filename);
+ return;
+}
+
+/*
+ * this function sets up gen->fp to point to the correct
+ * generation of the file for the time specified by 'now'
+ *
+ * 'now' usually is interpreted as second part of a l_fp as is in the cal...
+ * library routines
+ */
+
+void
+filegen_setup(
+ FILEGEN *gen,
+ u_long now
+ )
+{
+ u_long new_gen = ~ (u_long) 0;
+ struct calendar cal;
+
+ if (!(gen->flag & FGEN_FLAG_ENABLED)) {
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+ return;
+ }
+
+ switch (gen->type) {
+ case FILEGEN_NONE:
+ if (gen->fp != NULL) return; /* file already open */
+ break;
+
+ case FILEGEN_PID:
+ new_gen = getpid();
+ break;
+
+ case FILEGEN_DAY:
+ caljulian(now, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_WEEK:
+ /* Would be nice to have a calweekstart() routine */
+ /* so just use a hack ... */
+ /* just round time to integral 7 day period for actual year */
+ new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
+ + 60;
+ /*
+ * just to be sure -
+ * the computation above would fail in the presence of leap seconds
+ * so at least carry the date to the next day (+60 (seconds))
+ * and go back to the start of the day via calendar computations
+ */
+ caljulian(new_gen, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(now, &cal);
+ cal.yearday = (u_short) (cal.yearday - cal.monthday + 1);
+ cal.monthday = 1;
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_YEAR:
+ new_gen = calyearstart(now);
+ break;
+
+ case FILEGEN_AGE:
+ new_gen = current_time - (current_time % FGEN_AGE_SECS);
+ break;
+ }
+ /*
+ * try to open file if not yet open
+ * reopen new file generation file on change of generation id
+ */
+ if (gen->fp == NULL || gen->id != new_gen) {
+#if DEBUG
+ if (debug)
+ printf("filegen %0x %lu %lu %lu\n", gen->type, now,
+ gen->id, new_gen);
+#endif
+ filegen_open(gen, new_gen);
+ }
+}
+
+
+/*
+ * change settings for filegen files
+ */
+void
+filegen_config(
+ FILEGEN *gen,
+ char *basename,
+ u_int type,
+ u_int flag
+ )
+{
+ /*
+ * if nothing would be changed...
+ */
+ if ((basename == gen->basename || strcmp(basename,gen->basename) == 0) &&
+ type == gen->type &&
+ flag == gen->flag)
+ return;
+
+ /*
+ * validate parameters
+ */
+ if (!valid_fileref(gen->prefix,basename))
+ return;
+
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
+ gen->prefix, gen->basename, basename, gen->type, type, gen->flag, flag);
+#endif
+ if (gen->basename != basename || strcmp(gen->basename, basename) != 0) {
+ free(gen->basename);
+ gen->basename = (char*)emalloc(strlen(basename) + 1);
+ strcpy(gen->basename, basename);
+ }
+ gen->type = (u_char) type;
+ gen->flag = (u_char) flag;
+
+ /*
+ * make filegen use the new settings
+ * special action is only required when a generation file
+ * is currently open
+ * otherwise the new settings will be used anyway at the next open
+ */
+ if (gen->fp != NULL) {
+ l_fp now;
+
+ get_systime(&now);
+ filegen_setup(gen, now.l_ui);
+ }
+}
+
+
+/*
+ * check whether concatenating prefix and basename
+ * yields a legal filename
+ */
+static int
+valid_fileref(
+ char *prefix,
+ char *basename
+ )
+{
+ /*
+ * prefix cannot be changed dynamically
+ * (within the context of filegen)
+ * so just reject basenames containing '..'
+ *
+ * ASSUMPTION:
+ * file system parts 'below' prefix may be
+ * specified without infringement of security
+ *
+ * restricing prefix to legal values
+ * has to be ensured by other means
+ * (however, it would be possible to perform some checks here...)
+ */
+ register char *p = basename;
+
+ /*
+ * Just to catch, dumb errors opening up the world...
+ */
+ if (prefix == NULL || *prefix == '\0')
+ return 0;
+
+ if (basename == NULL)
+ return 0;
+
+ for (p = basename; p; p = strchr(p, '/')) {
+ if (*p == '.' && *(p+1) == '.' && (*(p+2) == '\0' || *(p+2) == '/'))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * filegen registry
+ */
+
+static struct filegen_entry {
+ char *name;
+ FILEGEN *filegen;
+ struct filegen_entry *next;
+} *filegen_registry = NULL;
+
+
+FILEGEN *
+filegen_get(
+ char *name
+ )
+{
+ struct filegen_entry *f = filegen_registry;
+
+ while(f) {
+ if (f->name == name || strcmp(name, f->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = %x\n", name,
+ (u_int)f->filegen);
+#endif
+ return f->filegen;
+ }
+ f = f->next;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = NULL\n", name);
+#endif
+ return NULL;
+}
+
+void
+filegen_register(
+ char *prefix,
+ const char *name,
+ FILEGEN *filegen
+ )
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_register(\"%s\",%x)\n", name, (u_int)filegen);
+#endif
+
+ filegen_init(prefix, name, filegen);
+
+ while (*f) {
+ if ((*f)->name == name || strcmp(name, (*f)->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 4) {
+ printf("replacing filegen %x\n", (u_int)(*f)->filegen);
+ }
+#endif
+ (*f)->filegen = filegen;
+ return;
+ }
+ f = &((*f)->next);
+ }
+
+ *f = (struct filegen_entry *) emalloc(sizeof(struct filegen_entry));
+ if (*f) {
+ (*f)->next = NULL;
+ (*f)->name = (char*)emalloc(strlen(name) + 1);
+ strcpy((*f)->name, name);
+ (*f)->filegen = filegen;
+#ifdef DEBUG
+ if (debug > 5) {
+ printf("adding new filegen\n");
+ }
+#endif
+ }
+
+ return;
+}
+
+#ifdef UNUSED
+static FILEGEN *
+filegen_unregister(
+ char *name
+ )
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_unregister(\"%s\")\n", name);
+#endif
+
+ while (*f) {
+ if (strcmp((*f)->name,name) == 0) {
+ struct filegen_entry *ff = *f;
+ FILEGEN *fg;
+
+ *f = (*f)->next;
+ fg = ff->filegen;
+ free(ff->name);
+ free(ff);
+ return fg;
+ }
+ f = &((*f)->next);
+ }
+ return NULL;
+}
+#endif /* UNUSED */
diff --git a/contrib/ntp/ntpd/ntp_intres.c b/contrib/ntp/ntpd/ntp_intres.c
new file mode 100644
index 0000000..2a4b51f
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_intres.c
@@ -0,0 +1,1139 @@
+/*
+ * ripped off from ../ntpres/ntpres.c by Greg Troxel 4/2/92
+ * routine callable from ntpd, rather than separate program
+ * also, key info passed in via a global, so no key file needed.
+ */
+
+/*
+ * ntpres - process configuration entries which require use of the resolver
+ *
+ * This is meant to be run by ntpd on the fly. It is not guaranteed
+ * to work properly if run by hand. This is actually a quick hack to
+ * stave off violence from people who hate using numbers in the
+ * configuration file (at least I hope the rest of the daemon is
+ * better than this). Also might provide some ideas about how one
+ * might go about autoconfiguring an NTP distribution network.
+ *
+ */
+ /*
+ * For special situations define the FORCE_DNSRETRY Macro
+ * to force retries even if it fails the lookup.
+ * Use with extreme caution since it will then retry forever.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+
+/**/
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/**/
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h> /* MAXHOSTNAMELEN (often) */
+#endif
+
+#include <isc/net.h>
+#include <isc/result.h>
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Each item we are to resolve and configure gets one of these
+ * structures defined for it.
+ */
+struct conf_entry {
+ struct conf_entry *ce_next;
+ char *ce_name; /* name we are trying to resolve */
+ struct conf_peer ce_config; /* configuration info for peer */
+ struct sockaddr_storage peer_store; /* address info for both fams */
+};
+#define ce_peeraddr ce_config.peeraddr
+#define ce_peeraddr6 ce_config.peeraddr6
+#define ce_hmode ce_config.hmode
+#define ce_version ce_config.version
+#define ce_minpoll ce_config.minpoll
+#define ce_maxpoll ce_config.maxpoll
+#define ce_flags ce_config.flags
+#define ce_ttl ce_config.ttl
+#define ce_keyid ce_config.keyid
+#define ce_keystr ce_config.keystr
+
+/*
+ * confentries is a pointer to the list of configuration entries
+ * we have left to do.
+ */
+static struct conf_entry *confentries = NULL;
+
+/*
+ * We take an interrupt every thirty seconds, at which time we decrement
+ * config_timer and resolve_timer. The former is set to 2, so we retry
+ * unsucessful reconfigurations every minute. The latter is set to
+ * an exponentially increasing value which starts at 2 and increases to
+ * 32. When this expires we retry failed name resolutions.
+ *
+ * We sleep SLEEPTIME seconds before doing anything, to give the server
+ * time to arrange itself.
+ */
+#define MINRESOLVE 2
+#define MAXRESOLVE 32
+#define CONFIG_TIME 2
+#define ALARM_TIME 30
+#define SLEEPTIME 2
+
+static volatile int config_timer = 0;
+static volatile int resolve_timer = 0;
+
+static int resolve_value; /* next value of resolve timer */
+
+/*
+ * Big hack attack
+ */
+#define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
+#define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
+
+/*
+ * Select time out. Set to 2 seconds. The server is on the local machine,
+ * after all.
+ */
+#define TIMEOUT_SEC 2
+#define TIMEOUT_USEC 0
+
+
+/*
+ * Input processing. The data on each line in the configuration file
+ * is supposed to consist of entries in the following order
+ */
+#define TOK_HOSTNAME 0
+#define TOK_HMODE 1
+#define TOK_VERSION 2
+#define TOK_MINPOLL 3
+#define TOK_MAXPOLL 4
+#define TOK_FLAGS 5
+#define TOK_TTL 6
+#define TOK_KEYID 7
+#define TOK_KEYSTR 8
+#define NUMTOK 9
+
+#define MAXLINESIZE 512
+
+
+/*
+ * File descriptor for ntp request code.
+ */
+static SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */
+
+/* stuff to be filled in by caller */
+
+keyid_t req_keyid; /* request keyid */
+char *req_file; /* name of the file with configuration info */
+
+/* end stuff to be filled in */
+
+
+static void checkparent P((void));
+static void removeentry P((struct conf_entry *));
+static void addentry P((char *, int, int, int, int, u_int,
+ int, keyid_t, char *));
+static int findhostaddr P((struct conf_entry *));
+static void openntp P((void));
+static int request P((struct conf_peer *));
+static char * nexttoken P((char **));
+static void readconf P((FILE *, char *));
+static void doconfigure P((int));
+
+struct ntp_res_t_pkt { /* Tagged packet: */
+ void *tag; /* For the caller */
+ u_int32 paddr; /* IP to look up, or 0 */
+ char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
+};
+
+struct ntp_res_c_pkt { /* Control packet: */
+ char name[MAXHOSTNAMELEN];
+ u_int32 paddr;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ u_int flags;
+ int ttl;
+ keyid_t keyid;
+ u_char keystr[MAXFILENAME];
+};
+
+
+static void resolver_exit P((int));
+
+/*
+ * Call here instead of just exiting
+ */
+
+static void resolver_exit (int code)
+{
+#ifdef SYS_WINNT
+ CloseHandle(ResolverEventHandle);
+ ResolverEventHandle = NULL;
+ ExitThread(code); /* Just to kill the thread not the process */
+#else
+ exit(code); /* kill the forked process */
+#endif
+}
+
+/*
+ * ntp_res_recv: Process an answer from the resolver
+ */
+
+void
+ntp_res_recv(void)
+{
+ /*
+ We have data ready on our descriptor.
+ It may be an EOF, meaning the resolver process went away.
+ Otherwise, it will be an "answer".
+ */
+}
+
+
+/*
+ * ntp_intres needs;
+ *
+ * req_key(???), req_keyid, req_file valid
+ * syslog still open
+ */
+
+void
+ntp_intres(void)
+{
+ FILE *in;
+ struct timeval tv;
+ fd_set fdset;
+#ifdef SYS_WINNT
+ DWORD rc;
+#else
+ int rc;
+#endif
+
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO, "NTP_INTRES running");
+ }
+#endif
+
+ /* check out auth stuff */
+ if (sys_authenticate) {
+ if (!authistrusted(req_keyid)) {
+ msyslog(LOG_ERR, "invalid request keyid %08x",
+ req_keyid );
+ resolver_exit(1);
+ }
+ }
+
+ /*
+ * Read the configuration info
+ * {this is bogus, since we are forked, but it is easier
+ * to keep this code - gdt}
+ */
+ if ((in = fopen(req_file, "r")) == NULL) {
+ msyslog(LOG_ERR, "can't open configuration file %s: %m",
+ req_file);
+ resolver_exit(1);
+ }
+ readconf(in, req_file);
+ (void) fclose(in);
+
+#ifdef DEBUG
+ if (!debug )
+#endif
+ (void) unlink(req_file);
+
+ /*
+ * Set up the timers to do first shot immediately.
+ */
+ resolve_timer = 0;
+ resolve_value = MINRESOLVE;
+ config_timer = CONFIG_TIME;
+
+ for (;;) {
+ checkparent();
+
+ if (resolve_timer == 0) {
+ /*
+ * Sleep a little to make sure the network is completely up
+ */
+ sleep(SLEEPTIME);
+ doconfigure(1);
+
+ /* prepare retry, in case there's more work to do */
+ resolve_timer = resolve_value;
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
+#endif
+ if (resolve_value < MAXRESOLVE)
+ resolve_value <<= 1;
+
+ config_timer = CONFIG_TIME;
+ } else if (config_timer == 0) { /* MB: in which case would this be required ? */
+ doconfigure(0);
+ /* MB: should we check now if we could exit, similar to the code above? */
+ config_timer = CONFIG_TIME;
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
+#endif
+ }
+
+ if (confentries == NULL)
+ resolver_exit(0); /* done */
+
+#ifdef SYS_WINNT
+ rc = WaitForSingleObject(ResolverEventHandle, 1000 * ALARM_TIME); /* in milliseconds */
+
+ if ( rc == WAIT_OBJECT_0 ) { /* signaled by the main thread */
+ resolve_timer = 0; /* retry resolving immediately */
+ continue;
+ }
+
+ if ( rc != WAIT_TIMEOUT ) /* not timeout: error */
+ resolver_exit(1);
+
+#else /* not SYS_WINNT */
+ tv.tv_sec = ALARM_TIME;
+ tv.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(resolver_pipe_fd[0], &fdset);
+ rc = select(resolver_pipe_fd[0] + 1, &fdset, (fd_set *)0, (fd_set *)0, &tv);
+
+ if (rc > 0) { /* parent process has written to the pipe */
+ read(resolver_pipe_fd[0], (char *)&rc, sizeof(rc)); /* make pipe empty */
+ resolve_timer = 0; /* retry resolving immediately */
+ continue;
+ }
+
+ if ( rc < 0 ) /* select() returned error */
+ resolver_exit(1);
+#endif
+
+ /* normal timeout, keep on waiting */
+ if (config_timer > 0)
+ config_timer--;
+ if (resolve_timer > 0)
+ resolve_timer--;
+ }
+}
+
+
+
+/*
+ * checkparent - see if our parent process is still running
+ *
+ * No need to worry in the Windows NT environment whether the
+ * main thread is still running, because if it goes
+ * down it takes the whole process down with it (in
+ * which case we won't be running this thread either)
+ * Turn function into NOP;
+ */
+
+static void
+checkparent(void)
+{
+#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
+
+ /*
+ * If our parent (the server) has died we will have been
+ * inherited by init. If so, exit.
+ */
+ if (getppid() == 1) {
+ msyslog(LOG_INFO, "parent died before we finished, exiting");
+ resolver_exit(0);
+ }
+#endif /* SYS_WINNT && SYS_VXWORKS*/
+}
+
+
+
+/*
+ * removeentry - we are done with an entry, remove it from the list
+ */
+static void
+removeentry(
+ struct conf_entry *entry
+ )
+{
+ register struct conf_entry *ce;
+
+ ce = confentries;
+ if (ce == entry) {
+ confentries = ce->ce_next;
+ return;
+ }
+
+ while (ce != NULL) {
+ if (ce->ce_next == entry) {
+ ce->ce_next = entry->ce_next;
+ return;
+ }
+ ce = ce->ce_next;
+ }
+}
+
+
+/*
+ * addentry - add an entry to the configuration list
+ */
+static void
+addentry(
+ char *name,
+ int mode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t keyid,
+ char *keystr
+ )
+{
+ register char *cp;
+ register struct conf_entry *ce;
+ unsigned int len;
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
+ mode, version, minpoll, maxpoll, flags, ttl, keyid,
+ keystr);
+#endif
+ len = strlen(name) + 1;
+ cp = (char *)emalloc(len);
+ memmove(cp, name, len);
+
+ ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
+ ce->ce_name = cp;
+ ce->ce_peeraddr = 0;
+#ifdef ISC_PLATFORM_HAVEIPV6
+ ce->ce_peeraddr6 = in6addr_any;
+#endif
+ ANYSOCK(&ce->peer_store);
+ ce->ce_hmode = (u_char)mode;
+ ce->ce_version = (u_char)version;
+ ce->ce_minpoll = (u_char)minpoll;
+ ce->ce_maxpoll = (u_char)maxpoll;
+ ce->ce_flags = (u_char)flags;
+ ce->ce_ttl = (u_char)ttl;
+ ce->ce_keyid = keyid;
+ strncpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
+ ce->ce_next = NULL;
+
+ if (confentries == NULL) {
+ confentries = ce;
+ } else {
+ register struct conf_entry *cep;
+
+ for (cep = confentries; cep->ce_next != NULL;
+ cep = cep->ce_next)
+ /* nothing */;
+ cep->ce_next = ce;
+ }
+}
+
+
+/*
+ * findhostaddr - resolve a host name into an address (Or vice-versa)
+ *
+ * Given one of {ce_peeraddr,ce_name}, find the other one.
+ * It returns 1 for "success" and 0 for an uncorrectable failure.
+ * Note that "success" includes try again errors. You can tell that you
+ * got a "try again" since {ce_peeraddr,ce_name} will still be zero.
+ */
+static int
+findhostaddr(
+ struct conf_entry *entry
+ )
+{
+ struct addrinfo *addr;
+ struct addrinfo hints;
+ int error;
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (entry->ce_name != NULL && !SOCKNUL(&entry->peer_store)) {
+ /* HMS: Squawk? */
+ msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
+ return 1;
+ }
+
+ if (entry->ce_name == NULL && SOCKNUL(&entry->peer_store)) {
+ msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
+ return 0;
+ }
+
+ if (entry->ce_name) {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
+ entry->ce_name);
+#endif /* DEBUG */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ /*
+ * If the IPv6 stack is not available look only for IPv4 addresses
+ */
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ hints.ai_family = AF_INET;
+
+ error = getaddrinfo(entry->ce_name, NULL, &hints, &addr);
+ if (error == 0) {
+ entry->peer_store = *((struct sockaddr_storage*)(addr->ai_addr));
+ if (entry->peer_store.ss_family == AF_INET) {
+ entry->ce_peeraddr =
+ GET_INADDR(entry->peer_store);
+ entry->ce_config.v6_flag = 0;
+ } else {
+ entry->ce_peeraddr6 =
+ GET_INADDR6(entry->peer_store);
+ entry->ce_config.v6_flag = 1;
+ }
+ }
+ else if (error == EAI_NONAME)
+ {
+ msyslog(LOG_ERR, "host name not found: %s", entry->ce_name);
+ }
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: Resolving %s>",
+ stoa(&entry->peer_store));
+#endif
+ entry->ce_name = emalloc(MAXHOSTNAMELEN);
+ error = getnameinfo((const struct sockaddr *)&entry->peer_store,
+ SOCKLEN(&entry->peer_store),
+ (char *)&entry->ce_name, MAXHOSTNAMELEN,
+ NULL, 0, 0);
+ }
+#ifdef DEBUG
+ if (debug > 2)
+ printf("intres: got error status of: %d\n", error);
+#endif
+
+ /*
+ * If the resolver failed, see if the failure is
+ * temporary. If so, return success.
+ */
+ if (error != 0) {
+ switch (error)
+ {
+ case EAI_AGAIN:
+ return (1);
+ case EAI_NONAME:
+#ifndef FORCE_DNSRETRY
+ return (0);
+#else
+ return (1);
+#endif
+#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
+ case EAI_NODATA:
+#endif
+ case EAI_FAIL:
+#ifdef EAI_SYSTEM
+ case EAI_SYSTEM:
+ return (1);
+#endif
+ default:
+ return (0);
+ }
+ }
+
+ if (entry->ce_name) {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: name resolved.");
+#endif
+
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: address resolved.");
+#endif
+ }
+
+ return (1);
+}
+
+
+/*
+ * openntp - open a socket to the ntp server
+ */
+static void
+openntp(void)
+{
+ struct addrinfo hints;
+ struct addrinfo *addrResult;
+ const char *localhost = "127.0.0.1"; /* Use IPv6 loopback */
+
+ if (sockfd != INVALID_SOCKET)
+ return;
+
+ memset(&hints, 0, sizeof(hints));
+
+ /*
+ * For now only bother with IPv4
+ */
+ hints.ai_family = AF_INET;
+
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo(localhost, "ntp", &hints, &addrResult)!=0) {
+ msyslog(LOG_ERR, "getaddrinfo failed: %m");
+ resolver_exit(1);
+ }
+ sockfd = socket(addrResult->ai_family, addrResult->ai_socktype, 0);
+
+ if (sockfd == -1) {
+ msyslog(LOG_ERR, "socket() failed: %m");
+ resolver_exit(1);
+ }
+
+ /*
+ * Make the socket non-blocking. We'll wait with select()
+ */
+#ifndef SYS_WINNT
+#if defined(O_NONBLOCK)
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
+ msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
+ resolver_exit(1);
+ }
+#else
+#if defined(FNDELAY)
+ if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
+ msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
+ resolver_exit(1);
+ }
+#else
+# include "Bletch: NEED NON BLOCKING IO"
+#endif /* FNDDELAY */
+#endif /* O_NONBLOCK */
+#else /* SYS_WINNT */
+ {
+ int on = 1;
+ if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
+ msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
+ resolver_exit(1); /* Windows NT - set socket in non-blocking mode */
+ }
+ }
+#endif /* SYS_WINNT */
+ if (connect(sockfd, addrResult->ai_addr, addrResult->ai_addrlen) == -1) {
+ msyslog(LOG_ERR, "openntp: connect() failed: %m");
+ resolver_exit(1);
+ }
+ freeaddrinfo(addrResult);
+}
+
+
+/*
+ * request - send a configuration request to the server, wait for a response
+ */
+static int
+request(
+ struct conf_peer *conf
+ )
+{
+ fd_set fdset;
+ struct timeval tvout;
+ struct req_pkt reqpkt;
+ l_fp ts;
+ int n;
+#ifdef SYS_WINNT
+ HANDLE hReadWriteEvent = NULL;
+ BOOL ret;
+ DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
+ OVERLAPPED overlap;
+#endif /* SYS_WINNT */
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (sockfd == INVALID_SOCKET)
+ openntp();
+
+#ifdef SYS_WINNT
+ hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif /* SYS_WINNT */
+
+ /*
+ * Try to clear out any previously received traffic so it
+ * doesn't fool us. Note the socket is nonblocking.
+ */
+ tvout.tv_sec = 0;
+ tvout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
+ 0) {
+ recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ }
+
+ /*
+ * Make up a request packet with the configuration info
+ */
+ memset((char *)&reqpkt, 0, sizeof(reqpkt));
+
+ reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
+ reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
+ reqpkt.implementation = IMPL_XNTPD; /* local implementation */
+ reqpkt.request = REQ_CONFIG; /* configure a new peer */
+ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
+ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
+ /* Make sure mbz_itemsize <= sizeof reqpkt.data */
+ if (sizeof(struct conf_peer) > sizeof (reqpkt.data)) {
+ msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!");
+ resolver_exit(1);
+ }
+ memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
+ reqpkt.keyid = htonl(req_keyid);
+
+ get_systime(&ts);
+ L_ADDUF(&ts, SKEWTIME);
+ HTONL_FP(&ts, &reqpkt.tstamp);
+ n = 0;
+ if (sys_authenticate)
+ n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
+
+ /*
+ * Done. Send it.
+ */
+#ifndef SYS_WINNT
+ n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
+ if (n < 0) {
+ msyslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0; /* maybe should exit */
+ }
+#else
+ /* In the NT world, documentation seems to indicate that there
+ * exist _write and _read routines that can be used to do blocking
+ * I/O on sockets. Problem is these routines require a socket
+ * handle obtained through the _open_osf_handle C run-time API
+ * of which there is no explanation in the documentation. We need
+ * nonblocking write's and read's anyway for our purpose here.
+ * We're therefore forced to deviate a little bit from the Unix
+ * model here and use the ReadFile and WriteFile Win32 I/O API's
+ * on the socket
+ */
+ overlap.Offset = overlap.OffsetHigh = (DWORD)0;
+ overlap.hEvent = hReadWriteEvent;
+ ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
+ NULL, (LPOVERLAPPED)&overlap);
+ if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
+ msyslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0;
+ }
+ dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
+ if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
+ if (dwWait == WAIT_FAILED)
+ msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
+ return 0;
+ }
+ if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
+ (LPDWORD)&NumberOfBytesWritten, FALSE)) {
+ msyslog(LOG_ERR, "GetOverlappedResult for WriteFile fails: %m");
+ return 0;
+ }
+#endif /* SYS_WINNT */
+
+
+ /*
+ * Wait for a response. A weakness of the mode 7 protocol used
+ * is that there is no way to associate a response with a
+ * particular request, i.e. the response to this configuration
+ * request is indistinguishable from that to any other. I should
+ * fix this some day. In any event, the time out is fairly
+ * pessimistic to make sure that if an answer is coming back
+ * at all, we get it.
+ */
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ tvout.tv_sec = TIMEOUT_SEC;
+ tvout.tv_usec = TIMEOUT_USEC;
+
+ n = select(sockfd + 1, &fdset, (fd_set *)0,
+ (fd_set *)0, &tvout);
+
+ if (n < 0)
+ {
+ if (errno != EINTR)
+ msyslog(LOG_ERR, "select() fails: %m");
+ return 0;
+ }
+ else if (n == 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ msyslog(LOG_INFO, "select() returned 0.");
+#endif
+ return 0;
+ }
+
+#ifndef SYS_WINNT
+ n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
+ if (n <= 0) {
+ if (n < 0) {
+ msyslog(LOG_ERR, "recv() fails: %m");
+ return 0;
+ }
+ continue;
+ }
+#else /* Overlapped I/O used on non-blocking sockets on Windows NT */
+ ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
+ NULL, (LPOVERLAPPED)&overlap);
+ if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
+ msyslog(LOG_ERR, "ReadFile() fails: %m");
+ return 0;
+ }
+ dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
+ if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
+ if (dwWait == WAIT_FAILED) {
+ msyslog(LOG_ERR, "WaitForSingleObject for ReadFile fails: %m");
+ return 0;
+ }
+ continue;
+ }
+ if (!GetOverlappedResult((HANDLE)sockfd, (LPOVERLAPPED)&overlap,
+ (LPDWORD)&NumberOfBytesRead, FALSE)) {
+ msyslog(LOG_ERR, "GetOverlappedResult fails: %m");
+ return 0;
+ }
+ n = NumberOfBytesRead;
+#endif /* SYS_WINNT */
+
+ /*
+ * Got one. Check through to make sure it is what
+ * we expect.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ msyslog(LOG_ERR, "received runt response (%d octets)",
+ n);
+ continue;
+ }
+
+ if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "received non-response packet");
+#endif
+ continue;
+ }
+
+ if (ISMORE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "received fragmented packet");
+#endif
+ continue;
+ }
+
+ if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
+ || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
+ || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "version (%d/%d) or mode (%d/%d) incorrect",
+ INFO_VERSION(reqpkt.rm_vn_mode),
+ NTP_VERSION,
+ INFO_MODE(reqpkt.rm_vn_mode),
+ MODE_PRIVATE);
+#endif
+ continue;
+ }
+
+ if (INFO_SEQ(reqpkt.auth_seq) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "nonzero sequence number (%d)",
+ INFO_SEQ(reqpkt.auth_seq));
+#endif
+ continue;
+ }
+
+ if (reqpkt.implementation != IMPL_XNTPD ||
+ reqpkt.request != REQ_CONFIG) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "implementation (%d) or request (%d) incorrect",
+ reqpkt.implementation, reqpkt.request);
+#endif
+ continue;
+ }
+
+ if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
+ INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
+ INFO_NITEMS(reqpkt.err_nitems),
+ INFO_MBZ(reqpkt.mbz_itemsize),
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize));
+#endif
+ continue;
+ }
+
+ n = INFO_ERR(reqpkt.err_nitems);
+ switch (n) {
+ case INFO_OKAY:
+ /* success */
+ return 1;
+
+ case INFO_ERR_IMPL:
+ msyslog(LOG_ERR,
+ "ntpd reports implementation mismatch!");
+ return 0;
+
+ case INFO_ERR_REQ:
+ msyslog(LOG_ERR,
+ "ntpd says configuration request is unknown!");
+ return 0;
+
+ case INFO_ERR_FMT:
+ msyslog(LOG_ERR,
+ "ntpd indicates a format error occurred!");
+ return 0;
+
+ case INFO_ERR_NODATA:
+ msyslog(LOG_ERR,
+ "ntpd indicates no data available!");
+ return 0;
+
+ case INFO_ERR_AUTH:
+ msyslog(LOG_ERR,
+ "ntpd returns a permission denied error!");
+ return 0;
+
+ default:
+ msyslog(LOG_ERR,
+ "ntpd returns unknown error code %d!", n);
+ return 0;
+ }
+ }
+}
+
+
+/*
+ * nexttoken - return the next token from a line
+ */
+static char *
+nexttoken(
+ char **lptr
+ )
+{
+ register char *cp;
+ register char *tstart;
+
+ cp = *lptr;
+
+ /*
+ * Skip leading white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * If this is the end of the line, return nothing.
+ */
+ if (*cp == '\n' || *cp == '\0') {
+ *lptr = cp;
+ return NULL;
+ }
+
+ /*
+ * Must be the start of a token. Record the pointer and look
+ * for the end.
+ */
+ tstart = cp++;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
+ cp++;
+
+ /*
+ * Terminate the token with a \0. If this isn't the end of the
+ * line, space to the next character.
+ */
+ if (*cp == '\n' || *cp == '\0')
+ *cp = '\0';
+ else
+ *cp++ = '\0';
+
+ *lptr = cp;
+ return tstart;
+}
+
+
+/*
+ * readconf - read the configuration information out of the file we
+ * were passed. Note that since the file is supposed to be
+ * machine generated, we bail out at the first sign of trouble.
+ */
+static void
+readconf(
+ FILE *fp,
+ char *name
+ )
+{
+ register int i;
+ char *token[NUMTOK];
+ u_long intval[NUMTOK];
+ u_int flags;
+ char buf[MAXLINESIZE];
+ char *bp;
+
+ while (fgets(buf, MAXLINESIZE, fp) != NULL) {
+
+ bp = buf;
+ for (i = 0; i < NUMTOK; i++) {
+ if ((token[i] = nexttoken(&bp)) == NULL) {
+ msyslog(LOG_ERR,
+ "tokenizing error in file `%s', quitting",
+ name);
+ resolver_exit(1);
+ }
+ }
+
+ for (i = 1; i < NUMTOK - 1; i++) {
+ if (!atouint(token[i], &intval[i])) {
+ msyslog(LOG_ERR,
+ "format error for integer token `%s', file `%s', quitting",
+ token[i], name);
+ resolver_exit(1);
+ }
+ }
+
+ if (intval[TOK_HMODE] != MODE_ACTIVE &&
+ intval[TOK_HMODE] != MODE_CLIENT &&
+ intval[TOK_HMODE] != MODE_BROADCAST) {
+ msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
+ intval[TOK_HMODE], name);
+ resolver_exit(1);
+ }
+
+ if (intval[TOK_VERSION] > NTP_VERSION ||
+ intval[TOK_VERSION] < NTP_OLDVERSION) {
+ msyslog(LOG_ERR, "invalid version (%ld) in file %s",
+ intval[TOK_VERSION], name);
+ resolver_exit(1);
+ }
+ if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
+ intval[TOK_MINPOLL] > NTP_MAXPOLL) {
+ msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
+ intval[TOK_MINPOLL], name);
+ resolver_exit(1);
+ }
+
+ if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
+ intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
+ msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
+ intval[TOK_MAXPOLL], name);
+ resolver_exit(1);
+ }
+
+ if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
+ FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
+ != 0) {
+ msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
+ intval[TOK_FLAGS], name);
+ resolver_exit(1);
+ }
+
+ flags = 0;
+ if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
+ flags |= CONF_FLAG_AUTHENABLE;
+ if (intval[TOK_FLAGS] & FLAG_PREFER)
+ flags |= CONF_FLAG_PREFER;
+ if (intval[TOK_FLAGS] & FLAG_NOSELECT)
+ flags |= CONF_FLAG_NOSELECT;
+ if (intval[TOK_FLAGS] & FLAG_BURST)
+ flags |= CONF_FLAG_BURST;
+ if (intval[TOK_FLAGS] & FLAG_IBURST)
+ flags |= CONF_FLAG_IBURST;
+ if (intval[TOK_FLAGS] & FLAG_SKEY)
+ flags |= CONF_FLAG_SKEY;
+
+ /*
+ * This is as good as we can check it. Add it in.
+ */
+ addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
+ (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
+ (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
+ intval[TOK_KEYID], token[TOK_KEYSTR]);
+ }
+}
+
+
+/*
+ * doconfigure - attempt to resolve names and configure the server
+ */
+static void
+doconfigure(
+ int dores
+ )
+{
+ register struct conf_entry *ce;
+ register struct conf_entry *ceremove;
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "Running doconfigure %s DNS",
+ dores ? "with" : "without" );
+#endif
+
+ ce = confentries;
+ while (ce != NULL) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "doconfigure: <%s> has peeraddr %s",
+ ce->ce_name, stoa(&ce->peer_store));
+#endif
+ if (dores && SOCKNUL(&(ce->peer_store))) {
+ if (!findhostaddr(ce)) {
+ msyslog(LOG_ERR,
+ "couldn't resolve `%s', giving up on it",
+ ce->ce_name);
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+
+ if (!SOCKNUL(&ce->peer_store)) {
+ if (request(&ce->ce_config)) {
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO,
+ "doconfigure: request() FAILED, maybe next time.");
+ }
+#endif
+ }
+ ce = ce->ce_next;
+ }
+}
diff --git a/contrib/ntp/ntpd/ntp_io.c b/contrib/ntp/ntpd/ntp_io.c
new file mode 100644
index 0000000..faa4646
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_io.c
@@ -0,0 +1,3913 @@
+/*
+ * ntp_io.c - input/output routines for ntpd. The socket-opening code
+ * was shamelessly stolen from ntpd.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "iosignal.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_request.h"
+#include "ntp.h"
+#include "ntp_unixtime.h"
+
+/* Don't include ISC's version of IPv6 variables and structures */
+#define ISC_IPV6_H 1
+#include <isc/interfaceiter.h>
+#include <isc/list.h>
+#include <isc/result.h>
+
+#ifdef SIM
+#include "ntpsim.h"
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H /* UXPV: SIOC* #defines (Frank Vance <fvance@waii.com>) */
+# include <sys/sockio.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+
+/*
+ * setsockopt does not always have the same arg declaration
+ * across all platforms. If it's not defined we make it empty
+ */
+
+#ifndef SETSOCKOPT_ARG_CAST
+#define SETSOCKOPT_ARG_CAST
+#endif
+
+/*
+ * Set up some macros to look for IPv6 and IPv6 multicast
+ */
+
+#if defined(ISC_PLATFORM_HAVEIPV6) && !defined(DISABLE_IPV6)
+
+#define INCLUDE_IPV6_SUPPORT
+
+#if defined(INCLUDE_IPV6_SUPPORT) && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP)
+#define INCLUDE_IPV6_MULTICAST_SUPPORT
+
+#endif /* IPV6 Multicast Support */
+#endif /* IPv6 Support */
+
+extern int listen_to_virtual_ips;
+extern const char *specific_interface;
+
+#if defined(SO_TIMESTAMP) && defined(SCM_TIMESTAMP)
+#if defined(CMSG_FIRSTHDR)
+#define HAVE_TIMESTAMP
+#define USE_TIMESTAMP_CMSG
+#ifndef TIMESTAMP_CTLMSGBUF_SIZE
+#define TIMESTAMP_CTLMSGBUF_SIZE 1536 /* moderate default */
+#endif
+#else
+/* fill in for old/other timestamp interfaces */
+#endif
+#endif
+
+#if defined(SYS_WINNT)
+#include <transmitbuff.h>
+#include <isc/win32os.h>
+/*
+ * Define this macro to control the behavior of connection
+ * resets on UDP sockets. See Microsoft KnowledgeBase Article Q263823
+ * for details.
+ * NOTE: This requires that Windows 2000 systems install Service Pack 2
+ * or later.
+ */
+#ifndef SIO_UDP_CONNRESET
+#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+#endif
+
+#endif
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see which sockets are ready and read the
+ * packets from them into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We watch the number of recvbufs available to the signal handler
+ * and allocate more when this number drops below the low water
+ * mark. If the signal handler should run out of buffers in the
+ * interim it will drop incoming frames, the idea being that it is
+ * better to drop a packet than to be inaccurate.
+ */
+
+
+/*
+ * Other statistics of possible interest
+ */
+volatile u_long packets_dropped; /* total number of packets dropped on reception */
+volatile u_long packets_ignored; /* packets received on wild card interface */
+volatile u_long packets_received; /* total number of packets received */
+u_long packets_sent; /* total number of packets sent */
+u_long packets_notsent; /* total number of packets which couldn't be sent */
+
+volatile u_long handler_calls; /* number of calls to interrupt handler */
+volatile u_long handler_pkts; /* number of pkts received by handler */
+u_long io_timereset; /* time counters were reset */
+
+/*
+ * Interface stuff
+ */
+struct interface *any_interface; /* default ipv4 interface */
+struct interface *any6_interface; /* default ipv6 interface */
+struct interface *loopback_interface; /* loopback ipv4 interface */
+
+int ninterfaces; /* Total number of interfaces */
+
+volatile int disable_dynamic_updates; /* when set to != 0 dynamic updates won't happen */
+
+#ifdef REFCLOCK
+/*
+ * Refclock stuff. We keep a chain of structures with data concerning
+ * the guys we are doing I/O for.
+ */
+static struct refclockio *refio;
+#endif /* REFCLOCK */
+
+
+/*
+ * Define what the possible "soft" errors can be. These are non-fatal returns
+ * of various network related functions, like recv() and so on.
+ *
+ * For some reason, BSDI (and perhaps others) will sometimes return <0
+ * from recv() but will have errno==0. This is broken, but we have to
+ * work around it here.
+ */
+#define SOFT_ERROR(e) ((e) == EAGAIN || \
+ (e) == EWOULDBLOCK || \
+ (e) == EINTR || \
+ (e) == 0)
+
+/*
+ * File descriptor masks etc. for call to select
+ * Not needed for I/O Completion Ports
+ */
+fd_set activefds;
+int maxactivefd;
+/*
+ * bit alternating value to detect verified interfaces during an update cycle
+ */
+static u_char sys_interphase = 0;
+
+static struct interface *new_interface P((struct interface *));
+static void add_interface P((struct interface *));
+static int update_interfaces P((u_short, interface_receiver_t, void *));
+static void remove_interface P((struct interface *));
+static struct interface *create_interface P((u_short, struct interface *));
+
+static int move_fd P((SOCKET));
+
+/*
+ * Multicast functions
+ */
+static isc_boolean_t addr_ismulticast P((struct sockaddr_storage *));
+/*
+ * Not all platforms support multicast
+ */
+#ifdef MCAST
+static isc_boolean_t socket_multicast_enable P((struct interface *, int, struct sockaddr_storage *));
+static isc_boolean_t socket_multicast_disable P((struct interface *, struct sockaddr_storage *));
+#endif
+
+#ifdef DEBUG
+static void print_interface P((struct interface *, char *, char *));
+#define DPRINT_INTERFACE(_LVL_, _ARGS_) do { if (debug >= (_LVL_)) { print_interface _ARGS_; } } while (0)
+#else
+#define DPRINT_INTERFACE(_LVL_, _ARGS_)
+#endif
+
+typedef struct vsock vsock_t;
+enum desc_type { FD_TYPE_SOCKET, FD_TYPE_FILE };
+
+struct vsock {
+ SOCKET fd;
+ enum desc_type type;
+ ISC_LINK(vsock_t) link;
+};
+
+#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET)
+/*
+ * async notification processing (e. g. routing sockets)
+ */
+/*
+ * support for receiving data on fd that is not a refclock or a socket
+ * like e. g. routing sockets
+ */
+struct asyncio_reader {
+ SOCKET fd; /* fd to be read */
+ void *data; /* possibly local data */
+ void (*receiver)(struct asyncio_reader *); /* input handler */
+ ISC_LINK(struct asyncio_reader) link; /* the list this is being kept in */
+};
+
+ISC_LIST(struct asyncio_reader) asyncio_reader_list;
+
+static void delete_asyncio_reader P((struct asyncio_reader *));
+static struct asyncio_reader *new_asyncio_reader P((void));
+static void add_asyncio_reader P((struct asyncio_reader *, enum desc_type));
+static void remove_asyncio_reader P((struct asyncio_reader *));
+
+#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */
+
+static void init_async_notifications P((void));
+
+static int create_sockets P((u_short));
+static SOCKET open_socket P((struct sockaddr_storage *, int, int, struct interface *));
+static char * fdbits P((int, fd_set *));
+static void set_reuseaddr P((int));
+static isc_boolean_t socket_broadcast_enable P((struct interface *, SOCKET, struct sockaddr_storage *));
+static isc_boolean_t socket_broadcast_disable P((struct interface *, struct sockaddr_storage *));
+
+ISC_LIST(vsock_t) fd_list;
+
+typedef struct remaddr remaddr_t;
+
+struct remaddr {
+ struct sockaddr_storage addr;
+ struct interface *interface;
+ ISC_LINK(remaddr_t) link;
+};
+
+ISC_LIST(remaddr_t) remoteaddr_list;
+
+ISC_LIST(struct interface) inter_list;
+
+static struct interface *wildipv4 = NULL;
+static struct interface *wildipv6 = NULL;
+
+static void add_fd_to_list P((SOCKET, enum desc_type));
+static void close_and_delete_fd_from_list P((SOCKET));
+static void add_addr_to_list P((struct sockaddr_storage *, struct interface *));
+static void delete_addr_from_list P((struct sockaddr_storage *));
+static struct interface *find_addr_in_list P((struct sockaddr_storage *));
+static struct interface *find_flagged_addr_in_list P((struct sockaddr_storage *, int));
+static void create_wildcards P((u_short));
+static isc_boolean_t address_okay P((struct interface *));
+static void convert_isc_if P((isc_interface_t *, struct interface *, u_short));
+static void delete_interface_from_list P((struct interface *));
+static struct interface *getinterface P((struct sockaddr_storage *, int));
+static struct interface *findlocalinterface P((struct sockaddr_storage *, int));
+static struct interface *findlocalcastinterface P((struct sockaddr_storage *, int));
+
+/*
+ * Routines to read the ntp packets
+ */
+#if !defined(HAVE_IO_COMPLETION_PORT)
+static inline int read_network_packet P((SOCKET, struct interface *, l_fp));
+static inline int read_refclock_packet P((SOCKET, struct refclockio *, l_fp));
+#endif
+
+#ifdef SYS_WINNT
+/*
+ * Windows 2000 systems incorrectly cause UDP sockets using WASRecvFrom
+ * to not work correctly, returning a WSACONNRESET error when a WSASendTo
+ * fails with an "ICMP port unreachable" response and preventing the
+ * socket from using the WSARecvFrom in subsequent operations.
+ * The function below fixes this, but requires that Windows 2000
+ * Service Pack 2 or later be installed on the system. NT 4.0
+ * systems are not affected by this and work correctly.
+ * See Microsoft Knowledge Base Article Q263823 for details of this.
+ */
+isc_result_t
+connection_reset_fix(SOCKET fd) {
+ DWORD dwBytesReturned = 0;
+ BOOL bNewBehavior = FALSE;
+ DWORD status;
+
+ if(isc_win32os_majorversion() < 5)
+ return (ISC_R_SUCCESS); /* NT 4.0 has no problem */
+
+ /* disable bad behavior using IOCTL: SIO_UDP_CONNRESET */
+ status = WSAIoctl(fd, SIO_UDP_CONNRESET, &bNewBehavior,
+ sizeof(bNewBehavior), NULL, 0,
+ &dwBytesReturned, NULL, NULL);
+ if (status != SOCKET_ERROR)
+ return (ISC_R_SUCCESS);
+ else
+ return (ISC_R_UNEXPECTED);
+}
+#endif
+
+/*
+ * on Unix systems the stdio library typically
+ * makes use of file descriptors in the lower
+ * integer range. stdio usually will make use
+ * of the file descriptor in the range of
+ * [0..FOPEN_MAX)
+ * in order to keep this range clean for socket
+ * file descriptors we attempt to move them above
+ * FOPEM_MAX. This is not as easy as it sounds as
+ * FOPEN_MAX changes from implementation to implementation
+ * and may exceed to current file decriptor limits.
+ * We are using following strategy:
+ * - keep a current socket fd boundary initialized with
+ * max(0, min(getdtablesize() - FD_CHUNK, FOPEN_MAX))
+ * - attempt to move the descriptor to the boundary or
+ * above.
+ * - if that fails and boundary > 0 set boundary
+ * to min(0, socket_fd_boundary - FD_CHUNK)
+ * -> retry
+ * if failure and boundary == 0 return old fd
+ * - on success close old fd return new fd
+ *
+ * effects:
+ * - fds will be moved above the socket fd boundary
+ * if at all possible.
+ * - the socket boundary will be reduced until
+ * allocation is possible or 0 is reached - at this
+ * point the algrithm will be disabled
+ */
+static int move_fd(SOCKET fd)
+{
+#if !defined(SYS_WINNT) && defined(F_DUPFD)
+#ifndef FD_CHUNK
+#define FD_CHUNK 10
+#endif
+/*
+ * number of fds we would like to have for
+ * stdio FILE* available.
+ * we can pick a "low" number as our use of
+ * FILE* is limited to log files and temporarily
+ * to data and config files. Except for log files
+ * we don't keep the other FILE* open beyond the
+ * scope of the function that opened it.
+ */
+#ifndef FD_PREFERRED_SOCKBOUNDARY
+#define FD_PREFERRED_SOCKBOUNDARY 48
+#endif
+
+#ifndef HAVE_GETDTABLESIZE
+/*
+ * if we have no idea about the max fd value set up things
+ * so we will start at FOPEN_MAX
+ */
+#define getdtablesize() (FOPEN_MAX+FD_CHUNK)
+#endif
+
+#ifndef FOPEN_MAX
+#define FOPEN_MAX 20 /* assume that for the lack of anything better */
+#endif
+ static SOCKET socket_boundary = -1;
+ SOCKET newfd;
+
+ /*
+ * check whether boundary has be set up
+ * already
+ */
+ if (socket_boundary == -1) {
+ socket_boundary = max(0, min(getdtablesize() - FD_CHUNK,
+ min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY)));
+#ifdef DEBUG
+ msyslog(LOG_DEBUG, "ntp_io: estimated max descriptors: %d, initial socket boundary: %d",
+ getdtablesize(), socket_boundary);
+#endif
+ }
+
+ /*
+ * Leave a space for stdio to work in. potentially moving the
+ * socket_boundary lower until allocation succeeds.
+ */
+ do {
+ if (fd >= 0 && fd < socket_boundary) {
+ /* inside reserved range: attempt to move fd */
+ newfd = fcntl(fd, F_DUPFD, socket_boundary);
+
+ if (newfd != -1) {
+ /* success: drop the old one - return the new one */
+ (void)close(fd);
+ return (newfd);
+ }
+ } else {
+ /* outside reserved range: no work - return the original one */
+ return (fd);
+ }
+ socket_boundary = max(0, socket_boundary - FD_CHUNK);
+#ifdef DEBUG
+ msyslog(LOG_DEBUG, "ntp_io: selecting new socket boundary: %d",
+ socket_boundary);
+#endif
+ } while (socket_boundary > 0);
+#endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */
+ return (fd);
+}
+
+#ifdef DEBUG_TIMING
+/*
+ * collect timing information for various processing
+ * paths. currently we only pass then on to the file
+ * for later processing. this could also do histogram
+ * based analysis in other to reduce the load (and skew)
+ * dur to the file output
+ */
+void
+collect_timing(struct recvbuf *rb, const char *tag, int count, l_fp *dts)
+{
+ char buf[2048];
+
+ snprintf(buf, sizeof(buf), "%s %d %s %s",
+ (rb != NULL) ?
+ ((rb->dstadr) ? stoa(&rb->recv_srcadr) : "-REFCLOCK-") : "-",
+ count, lfptoa(dts, 9), tag);
+ record_timing_stats(buf);
+}
+#endif
+
+/*
+ * About dynamic interfaces, sockets, reception and more...
+ *
+ * the code solves following tasks:
+ *
+ * - keep a current list of active interfaces in order
+ * to bind to to the interface address on NTP_PORT so that
+ * all wild and specific bindings for NTP_PORT are taken by ntpd
+ * to avoid other daemons messing with the time or sockets.
+ * - all interfaces keep a list of peers that are referencing
+ * the interface in order to quickly re-assign the peers to
+ * new interface in case an interface is deleted (=> gone from system or
+ * down)
+ * - have a preconfigured socket ready with the right local address
+ * for transmission and reception
+ * - have an address list for all destination addresses used within ntpd
+ * to find the "right" preconfigured socket.
+ * - facilitate updating the internal interface list with respect to
+ * the current kernel state
+ *
+ * special issues:
+ *
+ * - mapping of multicast addresses to the interface affected is not always
+ * one to one - especially on hosts with multiple interfaces
+ * the code here currently allocates a separate interface entry for those
+ * multicast addresses
+ * iff it is able to bind to a *new* socket with the multicast address (flags |= MCASTIF)
+ * in case of failure the multicast address is bound to an existing interface.
+ * - on some systems it is perfectly legal to assign the same address to
+ * multiple interfaces. Therefore this code does not keep a list of interfaces
+ * but a list of interfaces that represent a unique address as determined by the kernel
+ * by the procedure in findlocalinterface. Thus it is perfectly legal to see only
+ * one representative of a group of real interfaces if they share the same address.
+ *
+ * Frank Kardel 20050910
+ */
+
+/*
+ * init_io - initialize I/O data structures and call socket creation routine
+ */
+void
+init_io(void)
+{
+#ifdef SYS_WINNT
+ if (!Win32InitSockets())
+ {
+ netsyslog(LOG_ERR, "No useable winsock.dll: %m");
+ exit(1);
+ }
+ init_transmitbuff();
+#endif /* SYS_WINNT */
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ init_recvbuff(RECV_INIT);
+
+ packets_dropped = packets_received = 0;
+ packets_ignored = 0;
+ packets_sent = packets_notsent = 0;
+ handler_calls = handler_pkts = 0;
+ io_timereset = 0;
+ loopback_interface = NULL;
+ any_interface = NULL;
+ any6_interface = NULL;
+
+#ifdef REFCLOCK
+ refio = NULL;
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal();
+#endif
+
+ ISC_LIST_INIT(fd_list);
+
+#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET)
+ ISC_LIST_INIT(asyncio_reader_list);
+#endif
+
+ ISC_LIST_INIT(remoteaddr_list);
+
+ ISC_LIST_INIT(inter_list);
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ (void) create_sockets(htons(NTP_PORT));
+ UNBLOCKIO();
+
+ init_async_notifications();
+
+ DPRINTF(3, ("init_io: maxactivefd %d\n", maxactivefd));
+}
+
+#ifdef DEBUG
+/*
+ * function to dump the contents of the interface structure
+ * for debugging use only.
+ */
+void
+interface_dump(struct interface *itf)
+{
+ u_char* cp;
+ int i;
+ /* Limit the size of the sockaddr_storage hex dump */
+ int maxsize = min(32, sizeof(struct sockaddr_storage));
+
+ printf("Dumping interface: %p\n", itf);
+ printf("fd = %d\n", itf->fd);
+ printf("bfd = %d\n", itf->bfd);
+ printf("sin = %s,\n", stoa(&(itf->sin)));
+ cp = (u_char*) &(itf->sin);
+ for(i = 0; i < maxsize; i++)
+ {
+ printf("%02x", *cp++);
+ if((i+1)%4 == 0)
+ printf(" ");
+ }
+ printf("\n");
+ printf("bcast = %s,\n", stoa(&(itf->bcast)));
+ cp = (u_char*) &(itf->bcast);
+ for(i = 0; i < maxsize; i++)
+ {
+ printf("%02x", *cp++);
+ if((i+1)%4 == 0)
+ printf(" ");
+ }
+ printf("\n");
+ printf("mask = %s,\n", stoa(&(itf->mask)));
+ cp = (u_char*) &(itf->mask);
+ for(i = 0; i < maxsize; i++)
+ {
+ printf("%02x", *cp++);
+ if((i+1)%4 == 0)
+ printf(" ");
+ }
+ printf("\n");
+ printf("name = %s\n", itf->name);
+ printf("flags = 0x%08x\n", itf->flags);
+ printf("last_ttl = %d\n", itf->last_ttl);
+ printf("addr_refid = %08x\n", itf->addr_refid);
+ printf("num_mcast = %d\n", itf->num_mcast);
+ printf("received = %ld\n", itf->received);
+ printf("sent = %ld\n", itf->sent);
+ printf("notsent = %ld\n", itf->notsent);
+ printf("ifindex = %u\n", itf->ifindex);
+ printf("scopeid = %u\n", itf->scopeid);
+ printf("peercnt = %u\n", itf->peercnt);
+ printf("phase = %u\n", itf->phase);
+}
+
+/*
+ * print_interface - helper to output debug information
+ */
+static void
+print_interface(struct interface *iface, char *pfx, char *sfx)
+{
+ printf("%sinterface #%d: fd=%d, bfd=%d, name=%s, flags=0x%x, scope=%d, ifindex=%d",
+ pfx,
+ iface->ifnum,
+ iface->fd,
+ iface->bfd,
+ iface->name,
+ iface->flags,
+ iface->scopeid,
+ iface->ifindex);
+ /* Leave these as three printf calls. */
+ printf(", sin=%s",
+ stoa((&iface->sin)));
+ if (iface->flags & INT_BROADCAST)
+ printf(", bcast=%s,",
+ stoa((&iface->bcast)));
+ if (iface->family == AF_INET)
+ printf(", mask=%s",
+ stoa((&iface->mask)));
+ printf(", %s:%s", iface->ignore_packets == ISC_FALSE ? "Enabled" : "Disabled", sfx);
+ if (debug > 4) /* in-depth debugging only */
+ interface_dump(iface);
+}
+
+#endif
+
+#if !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET)
+/*
+ * create an asyncio_reader structure
+ */
+static struct asyncio_reader *
+new_asyncio_reader()
+{
+ struct asyncio_reader *reader;
+
+ reader = (struct asyncio_reader *)emalloc(sizeof(struct asyncio_reader));
+
+ memset((char *)reader, 0, sizeof(*reader));
+ ISC_LINK_INIT(reader, link);
+ reader->fd = INVALID_SOCKET;
+ return reader;
+}
+
+/*
+ * delete a reader
+ */
+static void
+delete_asyncio_reader(struct asyncio_reader *reader)
+{
+ free(reader);
+}
+
+/*
+ * add asynchio_reader
+ */
+static void
+add_asyncio_reader(struct asyncio_reader *reader, enum desc_type type)
+{
+ ISC_LIST_APPEND(asyncio_reader_list, reader, link);
+ add_fd_to_list(reader->fd, type);
+}
+
+/*
+ * remove asynchio_reader
+ */
+static void
+remove_asyncio_reader(struct asyncio_reader *reader)
+{
+ ISC_LIST_UNLINK_TYPE(asyncio_reader_list, reader, link, struct asyncio_reader);
+
+ if (reader->fd != INVALID_SOCKET)
+ close_and_delete_fd_from_list(reader->fd);
+
+ reader->fd = INVALID_SOCKET;
+}
+#endif /* !defined(HAVE_IO_COMPLETION_PORT) && defined(HAS_ROUTING_SOCKET) */
+
+/*
+ * interface list enumerator - visitor pattern
+ */
+void
+interface_enumerate(interface_receiver_t receiver, void *data)
+{
+ interface_info_t ifi;
+ struct interface *interf;
+
+ ifi.action = IFS_EXISTS;
+
+ for (interf = ISC_LIST_HEAD(inter_list);
+ interf != NULL;
+ interf = ISC_LIST_NEXT(interf, link)) {
+ ifi.interface = interf;
+ receiver(data, &ifi);
+ }
+}
+
+/*
+ * do standard initialization of interface structure
+ */
+static void
+init_interface(struct interface *interface)
+{
+ memset((char *)interface, 0, sizeof(struct interface));
+ ISC_LINK_INIT(interface, link);
+ ISC_LIST_INIT(interface->peers);
+ interface->fd = INVALID_SOCKET;
+ interface->bfd = INVALID_SOCKET;
+ interface->num_mcast = 0;
+ interface->received = 0;
+ interface->sent = 0;
+ interface->notsent = 0;
+ interface->peercnt = 0;
+ interface->phase = sys_interphase;
+}
+
+/*
+ * create new interface structure initialize from
+ * template structure or via standard initialization
+ * function
+ */
+static struct interface *
+new_interface(struct interface *interface)
+{
+ static u_int sys_ifnum = 0;
+
+ struct interface *iface = (struct interface *)emalloc(sizeof(struct interface));
+
+ if (interface != NULL)
+ {
+ memcpy((char*)iface, (char*)interface, sizeof(*interface));
+ }
+ else
+ {
+ init_interface(iface);
+ }
+
+ iface->ifnum = sys_ifnum++; /* count every new instance of an interface in the system */
+ iface->starttime = current_time;
+
+ return iface;
+}
+
+/*
+ * return interface storage into free memory pool
+ */
+static void
+delete_interface(struct interface *interface)
+{
+ free(interface);
+}
+
+/*
+ * link interface into list of known interfaces
+ */
+static void
+add_interface(struct interface *interface)
+{
+ /*
+ * Calculate the address hash
+ */
+ interface->addr_refid = addr2refid(&interface->sin);
+
+ ISC_LIST_APPEND(inter_list, interface, link);
+ ninterfaces++;
+}
+
+/*
+ * remove interface from known interface list and clean up
+ * associated resources
+ */
+static void
+remove_interface(struct interface *interface)
+{
+ struct sockaddr_storage resmask;
+
+ ISC_LIST_UNLINK_TYPE(inter_list, interface, link, struct interface);
+
+ delete_interface_from_list(interface);
+
+ if (interface->fd != INVALID_SOCKET)
+ {
+ msyslog(LOG_INFO, "Deleting interface #%d %s, %s#%d, interface stats: received=%ld, sent=%ld, dropped=%ld, active_time=%ld secs",
+ interface->ifnum,
+ interface->name,
+ stoa((&interface->sin)),
+ NTP_PORT, /* XXX should extract port from sin structure */
+ interface->received,
+ interface->sent,
+ interface->notsent,
+ current_time - interface->starttime);
+
+ close_and_delete_fd_from_list(interface->fd);
+ }
+
+ if (interface->bfd != INVALID_SOCKET)
+ {
+ msyslog(LOG_INFO, "Deleting interface #%d %s, broadcast address %s#%d",
+ interface->ifnum,
+ interface->name,
+ stoa((&interface->bcast)),
+ (u_short) NTP_PORT); /* XXX extract port from sin structure */
+ close_and_delete_fd_from_list(interface->bfd);
+ }
+
+ ninterfaces--;
+ ntp_monclearinterface(interface);
+
+ /* remove restrict interface entry */
+
+ /*
+ * Blacklist bound interface address
+ */
+ SET_HOSTMASK(&resmask, interface->sin.ss_family);
+ hack_restrict(RESTRICT_REMOVEIF, &interface->sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+}
+
+static void
+list_if_listening(struct interface *interface, u_short port)
+{
+ msyslog(LOG_INFO, "Listening on interface #%d %s, %s#%d %s",
+ interface->ifnum,
+ interface->name,
+ stoa((&interface->sin)),
+ ntohs( (u_short) port),
+ (interface->ignore_packets == ISC_FALSE) ?
+ "Enabled": "Disabled");
+}
+
+static void
+create_wildcards(u_short port) {
+ isc_boolean_t okipv4 = ISC_TRUE;
+ /*
+ * create pseudo-interface with wildcard IPv4 address
+ */
+#ifdef IPV6_V6ONLY
+ if(isc_net_probeipv4() != ISC_R_SUCCESS)
+ okipv4 = ISC_FALSE;
+#endif
+
+ if(okipv4 == ISC_TRUE) {
+ struct interface *interface = new_interface(NULL);
+
+ interface->family = AF_INET;
+ interface->sin.ss_family = AF_INET;
+ ((struct sockaddr_in*)&interface->sin)->sin_addr.s_addr = htonl(INADDR_ANY);
+ ((struct sockaddr_in*)&interface->sin)->sin_port = port;
+ (void) strncpy(interface->name, "wildcard", sizeof(interface->name));
+ interface->mask.ss_family = AF_INET;
+ ((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr = htonl(~(u_int32)0);
+ interface->flags = INT_BROADCAST | INT_UP | INT_WILDCARD;
+ interface->ignore_packets = ISC_TRUE;
+#if defined(MCAST)
+ /*
+ * enable possible multicast reception on the broadcast socket
+ */
+ interface->bcast.ss_family = AF_INET;
+ ((struct sockaddr_in*)&interface->bcast)->sin_port = port;
+ ((struct sockaddr_in*)&interface->bcast)->sin_addr.s_addr = htonl(INADDR_ANY);
+#endif /* MCAST */
+ interface->fd = open_socket(&interface->sin,
+ interface->flags, 1, interface);
+
+ if (interface->fd != INVALID_SOCKET) {
+ wildipv4 = interface;
+ any_interface = interface;
+
+ add_addr_to_list(&interface->sin, interface);
+ add_interface(interface);
+ list_if_listening(interface, port);
+ } else {
+ msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING",
+ stoa((&interface->sin)));
+ exit(1);
+ }
+ }
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ /*
+ * create pseudo-interface with wildcard IPv6 address
+ */
+ if (isc_net_probeipv6() == ISC_R_SUCCESS) {
+ struct interface *interface = new_interface(NULL);
+
+ interface->family = AF_INET6;
+ interface->sin.ss_family = AF_INET6;
+ ((struct sockaddr_in6*)&interface->sin)->sin6_addr = in6addr_any;
+ ((struct sockaddr_in6*)&interface->sin)->sin6_port = port;
+# ifdef ISC_PLATFORM_HAVESCOPEID
+ ((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = 0;
+# endif
+ (void) strncpy(interface->name, "wildcard", sizeof(interface->name));
+ interface->mask.ss_family = AF_INET6;
+ memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr));
+ interface->flags = INT_UP | INT_WILDCARD;
+ interface->ignore_packets = ISC_TRUE;
+
+ interface->fd = open_socket(&interface->sin,
+ interface->flags, 1, interface);
+
+ if (interface->fd != INVALID_SOCKET) {
+ wildipv6 = interface;
+ any6_interface = interface;
+ add_addr_to_list(&interface->sin, interface);
+ add_interface(interface);
+ list_if_listening(interface, port);
+ } else {
+ msyslog(LOG_ERR, "unable to bind to wildcard socket address %s - another process may be running - EXITING",
+ stoa((&interface->sin)));
+ exit(1);
+ }
+ }
+#endif
+}
+
+
+static isc_boolean_t
+address_okay(struct interface *iface) {
+
+ DPRINTF(4, ("address_okay: listen Virtual: %d, IF name: %s\n",
+ listen_to_virtual_ips, iface->name));
+
+ /*
+ * Always allow the loopback
+ */
+ if((iface->flags & INT_LOOPBACK) != 0) {
+ DPRINTF(4, ("address_okay: loopback - OK\n"));
+ return (ISC_TRUE);
+ }
+
+ /*
+ * Check if the interface is specified
+ */
+ if (specific_interface != NULL) {
+ if (strcasecmp(iface->name, specific_interface) == 0) {
+ DPRINTF(4, ("address_okay: specific interface name matched - OK\n"));
+ return (ISC_TRUE);
+ } else {
+ DPRINTF(4, ("address_okay: specific interface name NOT matched - FAIL\n"));
+ return (ISC_FALSE);
+ }
+ }
+ else {
+ if (listen_to_virtual_ips == 0 &&
+ (strchr(iface->name, (int)':') != NULL)) {
+ DPRINTF(4, ("address_okay: virtual ip/alias - FAIL\n"));
+ return (ISC_FALSE);
+ }
+ }
+
+ DPRINTF(4, ("address_okay: OK\n"));
+ return (ISC_TRUE);
+}
+
+static void
+convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port)
+{
+ itf->scopeid = 0;
+ itf->family = (short) isc_if->af;
+ strcpy(itf->name, isc_if->name);
+
+ if(isc_if->af == AF_INET) {
+ itf->sin.ss_family = (u_short) isc_if->af;
+ memcpy(&(((struct sockaddr_in*)&itf->sin)->sin_addr),
+ &(isc_if->address.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->sin)->sin_port = port;
+
+ if((isc_if->flags & INTERFACE_F_BROADCAST) != 0) {
+ itf->flags |= INT_BROADCAST;
+ itf->bcast.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in*)&itf->bcast)->sin_addr),
+ &(isc_if->broadcast.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->bcast)->sin_port = port;
+ }
+
+ itf->mask.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in*)&itf->mask)->sin_addr),
+ &(isc_if->netmask.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->mask)->sin_port = port;
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if (isc_if->af == AF_INET6) {
+ itf->sin.ss_family = (u_short) isc_if->af;
+ memcpy(&(((struct sockaddr_in6 *)&itf->sin)->sin6_addr),
+ &(isc_if->address.type.in6),
+ sizeof(((struct sockaddr_in6 *)&itf->sin)->sin6_addr));
+ ((struct sockaddr_in6 *)&itf->sin)->sin6_port = port;
+
+#ifdef ISC_PLATFORM_HAVESCOPEID
+ ((struct sockaddr_in6 *)&itf->sin)->sin6_scope_id = isc_netaddr_getzone(&isc_if->address);
+ itf->scopeid = isc_netaddr_getzone(&isc_if->address);
+#endif
+ itf->mask.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in6 *)&itf->mask)->sin6_addr),
+ &(isc_if->netmask.type.in6),
+ sizeof(struct in6_addr));
+ ((struct sockaddr_in6 *)&itf->mask)->sin6_port = port;
+ /* Copy the interface index */
+ itf->ifindex = isc_if->ifindex;
+ }
+#endif /* INCLUDE_IPV6_SUPPORT */
+
+
+ /* Process the rest of the flags */
+
+ if((isc_if->flags & INTERFACE_F_UP) != 0)
+ itf->flags |= INT_UP;
+ if((isc_if->flags & INTERFACE_F_LOOPBACK) != 0)
+ itf->flags |= INT_LOOPBACK;
+ if((isc_if->flags & INTERFACE_F_POINTTOPOINT) != 0)
+ itf->flags |= INT_PPP;
+ if((isc_if->flags & INTERFACE_F_MULTICAST) != 0)
+ itf->flags |= INT_MULTICAST;
+
+}
+
+/*
+ * refresh_interface
+ *
+ * some OSes have been observed to keep
+ * cached routes even when more specific routes
+ * become available.
+ * this can be mitigated by re-binding
+ * the socket.
+ */
+static int
+refresh_interface(struct interface * interface)
+{
+#ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES
+ if (interface->fd != INVALID_SOCKET)
+ {
+ close_and_delete_fd_from_list(interface->fd);
+ interface->fd = open_socket(&interface->sin,
+ interface->flags, 0, interface);
+ /*
+ * reset TTL indication so TTL is is set again
+ * next time around
+ */
+ interface->last_ttl = 0;
+ return interface->fd != INVALID_SOCKET;
+ }
+ else
+ {
+ return 0; /* invalid sockets are not refreshable */
+ }
+#else /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */
+ return interface->fd != INVALID_SOCKET;
+#endif /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */
+}
+
+/*
+ * interface_update - externally callable update function
+ */
+void
+interface_update(interface_receiver_t receiver, void *data)
+{
+ if (!disable_dynamic_updates) {
+ int new_interface_found;
+
+ BLOCKIO();
+ new_interface_found = update_interfaces(htons(NTP_PORT), receiver, data);
+ UNBLOCKIO();
+
+ if (new_interface_found) {
+#ifdef DEBUG
+ msyslog(LOG_DEBUG, "new interface(s) found: waking up resolver");
+#endif
+#ifdef SYS_WINNT
+ /* wake up the resolver thread */
+ if (ResolverEventHandle != NULL)
+ SetEvent(ResolverEventHandle);
+#else
+ /* write any single byte to the pipe to wake up the resolver process */
+ write( resolver_pipe_fd[1], &new_interface_found, 1 );
+#endif
+ }
+ }
+}
+
+/*
+ * find out if a given interface structure contains
+ * a wildcard address
+ */
+static int
+is_wildcard_addr(struct sockaddr_storage *sas)
+{
+ if (sas->ss_family == AF_INET &&
+ ((struct sockaddr_in*)sas)->sin_addr.s_addr == htonl(INADDR_ANY))
+ return 1;
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ if (sas->ss_family == AF_INET6 &&
+ memcmp(&((struct sockaddr_in6*)sas)->sin6_addr, &in6addr_any,
+ sizeof(in6addr_any) == 0))
+ return 1;
+#endif
+
+ return 0;
+}
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+/*
+ * enable/disable re-use of wildcard address socket
+ */
+static void
+set_wildcard_reuse(int family, int on)
+{
+ int onvalue = 1;
+ int offvalue = 0;
+ int *onoff;
+ SOCKET fd = INVALID_SOCKET;
+
+ onoff = on ? &onvalue : &offvalue;
+
+ switch (family) {
+ case AF_INET:
+ if (any_interface) {
+ fd = any_interface->fd;
+ }
+ break;
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ case AF_INET6:
+ if (any6_interface) {
+ fd = any6_interface->fd;
+ }
+ break;
+#endif /* !INCLUDE_IPV6_SUPPORT */
+ }
+
+ if (fd != INVALID_SOCKET) {
+ if (setsockopt(fd, SOL_SOCKET,
+ SO_REUSEADDR, (char *)onoff,
+ sizeof(*onoff))) {
+ netsyslog(LOG_ERR, "set_wildcard_reuse: setsockopt(SO_REUSEADDR, %s) failed: %m", *onoff ? "on" : "off");
+ }
+ DPRINTF(4, ("set SO_REUSEADDR to %s on %s\n", *onoff ? "ON" : "OFF",
+ stoa((family == AF_INET) ?
+ &any_interface->sin : &any6_interface->sin)));
+ }
+}
+#endif /* OS_NEEDS_REUSEADDR_FOR_IFADDRBIND */
+
+/*
+ * update_interface strategy
+ *
+ * toggle configuration phase
+ *
+ * Phase 1:
+ * forall currently existing interfaces
+ * if address is known:
+ * drop socket - rebind again
+ *
+ * if address is NOT known:
+ * attempt to create a new interface entry
+ *
+ * Phase 2:
+ * forall currently known non MCAST and WILDCARD interfaces
+ * if interface does not match configuration phase (not seen in phase 1):
+ * remove interface from known interface list
+ * forall peers associated with this interface
+ * disconnect peer from this interface
+ *
+ * Phase 3:
+ * attempt to re-assign interfaces to peers
+ *
+ */
+
+static int
+update_interfaces(
+ u_short port,
+ interface_receiver_t receiver,
+ void *data
+ )
+{
+ interface_info_t ifi;
+ isc_mem_t *mctx = NULL;
+ isc_interfaceiter_t *iter = NULL;
+ isc_boolean_t scan_ipv4 = ISC_FALSE;
+ isc_boolean_t scan_ipv6 = ISC_FALSE;
+ isc_result_t result;
+ int new_interface_found = 0;
+
+ DPRINTF(3, ("update_interfaces(%d)\n", ntohs( (u_short) port)));
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = ISC_TRUE;
+#if defined(DEBUG)
+ else
+ if(debug)
+ netsyslog(LOG_ERR, "no IPv6 interfaces found");
+#endif
+#endif
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = ISC_TRUE;
+#if defined(ISC_PLATFORM_HAVEIPV6) && defined(DEBUG)
+ else
+ if(debug)
+ netsyslog(LOG_ERR, "no IPv6 interfaces found");
+#endif
+
+ if (isc_net_probeipv4() == ISC_R_SUCCESS)
+ scan_ipv4 = ISC_TRUE;
+#ifdef DEBUG
+ else
+ if(debug)
+ netsyslog(LOG_ERR, "no IPv4 interfaces found");
+#endif
+ /*
+ * phase one - scan interfaces
+ * - create those that are not found
+ * - update those that are found
+ */
+
+ result = isc_interfaceiter_create(mctx, &iter);
+
+ if (result != ISC_R_SUCCESS)
+ return 0;
+
+ sys_interphase ^= 0x1; /* toggle system phase for finding untouched (to be deleted) interfaces */
+
+ for (result = isc_interfaceiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_interfaceiter_next(iter))
+ {
+ isc_interface_t isc_if;
+ unsigned int family;
+ struct interface interface;
+ struct interface *iface;
+
+ result = isc_interfaceiter_current(iter, &isc_if);
+
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ /* See if we have a valid family to use */
+ family = isc_if.address.family;
+ if (family != AF_INET && family != AF_INET6)
+ continue;
+ if (scan_ipv4 == ISC_FALSE && family == AF_INET)
+ continue;
+ if (scan_ipv6 == ISC_FALSE && family == AF_INET6)
+ continue;
+
+ /*
+ * create prototype
+ */
+ init_interface(&interface);
+
+ convert_isc_if(&isc_if, &interface, port);
+
+ /*
+ * Check to see if we are going to use the interface
+ * If we don't use it we mark it to drop any packet
+ * received but we still must create the socket and
+ * bind to it. This prevents other apps binding to it
+ * and potentially causing problems with more than one
+ * process fiddling with the clock
+ */
+ if (address_okay(&interface) == ISC_TRUE) {
+ interface.ignore_packets = ISC_FALSE;
+ }
+ else {
+ interface.ignore_packets = ISC_TRUE;
+ }
+
+ DPRINT_INTERFACE(4, (&interface, "examining ", "\n"));
+
+ if (!(interface.flags & INT_UP)) { /* interfaces must be UP to be usable */
+ DPRINTF(4, ("skipping interface %s (%s) - DOWN\n", interface.name, stoa(&interface.sin)));
+ continue;
+ }
+
+ /*
+ * skip any interfaces UP and bound to a wildcard
+ * address - some dhcp clients produce that in the
+ * wild
+ */
+ if (is_wildcard_addr(&interface.sin))
+ continue;
+
+ /*
+ * map to local *address* in order
+ * to map all duplicate interfaces to an interface structure
+ * with the appropriate socket (our name space is
+ * (ip-address) - NOT (interface name, ip-address))
+ */
+ iface = getinterface(&interface.sin, INT_WILDCARD);
+
+ if (iface && refresh_interface(iface))
+ {
+ /*
+ * found existing and up to date interface - mark present
+ */
+
+ iface->phase = sys_interphase;
+ DPRINT_INTERFACE(4, (iface, "updating ", " present\n"));
+ ifi.action = IFS_EXISTS;
+ ifi.interface = iface;
+ if (receiver)
+ receiver(data, &ifi);
+ }
+ else
+ {
+ /*
+ * this is new or refreshing failed - add to our interface list
+ * if refreshing failed we will delete the interface structure in
+ * phase 2 as the interface was not marked current. We can bind to
+ * the address as the refresh code already closed the offending socket
+ */
+
+ iface = create_interface(port, &interface);
+
+ if (iface)
+ {
+ ifi.action = IFS_CREATED;
+ ifi.interface = iface;
+ if (receiver)
+ receiver(data, &ifi);
+
+ new_interface_found = 1;
+
+ DPRINT_INTERFACE(3, (iface, "updating ", " new - created\n"));
+ }
+ else
+ {
+ DPRINT_INTERFACE(3, (&interface, "updating ", " new - creation FAILED"));
+
+ msyslog(LOG_INFO, "failed to initialize interface for address %s", stoa(&interface.sin));
+ continue;
+ }
+ }
+ }
+
+ isc_interfaceiter_destroy(&iter);
+
+ /*
+ * phase 2 - delete gone interfaces - reassigning peers to other interfaces
+ */
+ {
+ struct interface *interf = ISC_LIST_HEAD(inter_list);
+
+ while (interf != NULL)
+ {
+ struct interface *next = ISC_LIST_NEXT(interf, link);
+
+ if (!(interf->flags & (INT_WILDCARD|INT_MCASTIF))) {
+ /*
+ * if phase does not match sys_phase this interface was not
+ * enumerated during interface scan - so it is gone and
+ * will be deleted here unless it is solely an MCAST/WILDCARD interface
+ */
+ if (interf->phase != sys_interphase) {
+ struct peer *peer;
+ DPRINT_INTERFACE(3, (interf, "updating ", "GONE - deleting\n"));
+ remove_interface(interf);
+
+ ifi.action = IFS_DELETED;
+ ifi.interface = interf;
+ if (receiver)
+ receiver(data, &ifi);
+
+ peer = ISC_LIST_HEAD(interf->peers);
+ /*
+ * disconnect peer from deleted interface
+ */
+ while (peer != NULL) {
+ struct peer *npeer = ISC_LIST_NEXT(peer, ilink);
+
+ /*
+ * this one just lost it's interface
+ */
+ set_peerdstadr(peer, NULL);
+
+ peer = npeer;
+ }
+
+ /*
+ * update globals in case we lose
+ * a loopback interface
+ */
+ if (interf == loopback_interface)
+ loopback_interface = NULL;
+
+ delete_interface(interf);
+ }
+ }
+ interf = next;
+ }
+ }
+
+ /*
+ * phase 3 - re-configure as the world has changed if necessary
+ */
+ refresh_all_peerinterfaces();
+ return new_interface_found;
+}
+
+
+/*
+ * create_sockets - create a socket for each interface plus a default
+ * socket for when we don't know where to send
+ */
+static int
+create_sockets(
+ u_short port
+ )
+{
+#ifndef HAVE_IO_COMPLETION_PORT
+ /*
+ * I/O Completion Ports don't care about the select and FD_SET
+ */
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+#endif
+
+ DPRINTF(2, ("create_sockets(%d)\n", ntohs( (u_short) port)));
+
+ create_wildcards(port);
+
+ update_interfaces(port, NULL, NULL);
+
+ /*
+ * Now that we have opened all the sockets, turn off the reuse
+ * flag for security.
+ */
+ set_reuseaddr(0);
+
+ DPRINTF(2, ("create_sockets: Total interfaces = %d\n", ninterfaces));
+
+ return ninterfaces;
+}
+
+/*
+ * create_interface - create a new interface for a given prototype
+ * binding the socket.
+ */
+static struct interface *
+create_interface(
+ u_short port,
+ struct interface *iface
+ )
+{
+ struct sockaddr_storage resmask;
+ struct interface *interface;
+
+ DPRINTF(2, ("create_interface(%s#%d)\n", stoa(&iface->sin), ntohs( (u_short) port)));
+
+ /* build an interface */
+ interface = new_interface(iface);
+
+ /*
+ * create socket
+ */
+ interface->fd = open_socket(&interface->sin,
+ interface->flags, 0, interface);
+
+ if (interface->fd != INVALID_SOCKET)
+ list_if_listening(interface, port);
+
+ if ((interface->flags & INT_BROADCAST) &&
+ interface->bfd != INVALID_SOCKET)
+ msyslog(LOG_INFO, "Listening on broadcast address %s#%d",
+ stoa((&interface->bcast)),
+ ntohs( (u_short) port));
+
+ if (interface->fd == INVALID_SOCKET &&
+ interface->bfd == INVALID_SOCKET) {
+ msyslog(LOG_ERR, "unable to create socket on %s (%d) for %s#%d",
+ interface->name,
+ interface->ifnum,
+ stoa((&interface->sin)),
+ ntohs( (u_short) port));
+ delete_interface(interface);
+ return NULL;
+ }
+
+ /*
+ * Blacklist bound interface address
+ */
+
+ SET_HOSTMASK(&resmask, interface->sin.ss_family);
+ hack_restrict(RESTRICT_FLAGS, &interface->sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+
+ /*
+ * set globals with the first found
+ * loopback interface of the appropriate class
+ */
+ if ((loopback_interface == NULL) &&
+ (interface->family == AF_INET) &&
+ ((interface->flags & INT_LOOPBACK) != 0))
+ {
+ loopback_interface = interface;
+ }
+
+ /*
+ * put into our interface list
+ */
+ add_addr_to_list(&interface->sin, interface);
+ add_interface(interface);
+
+ DPRINT_INTERFACE(2, (interface, "created ", "\n"));
+ return interface;
+}
+
+/*
+ * set_reuseaddr() - set/clear REUSEADDR on all sockets
+ * NB possible hole - should we be doing this on broadcast
+ * fd's also?
+ */
+static void
+set_reuseaddr(int flag) {
+ struct interface *interf;
+
+ for (interf = ISC_LIST_HEAD(inter_list);
+ interf != NULL;
+ interf = ISC_LIST_NEXT(interf, link)) {
+ if (interf->flags & INT_WILDCARD)
+ continue;
+
+ /*
+ * if interf->fd is INVALID_SOCKET, we might have a adapter
+ * configured but not present
+ */
+ DPRINTF(4, ("setting SO_REUSEADDR on %.16s@%s to %s\n", interf->name, stoa(&interf->sin), flag ? "on" : "off"));
+
+ if (interf->fd != INVALID_SOCKET) {
+ if (setsockopt(interf->fd, SOL_SOCKET,
+ SO_REUSEADDR, (char *)&flag,
+ sizeof(flag))) {
+ netsyslog(LOG_ERR, "set_reuseaddr: setsockopt(SO_REUSEADDR, %s) failed: %m", flag ? "on" : "off");
+ }
+ }
+ }
+}
+
+/*
+ * This is just a wrapper around an internal function so we can
+ * make other changes as necessary later on
+ */
+void
+enable_broadcast(struct interface *iface, struct sockaddr_storage *baddr)
+{
+#ifdef SO_BROADCAST
+ socket_broadcast_enable(iface, iface->fd, baddr);
+#endif
+}
+
+#ifdef OPEN_BCAST_SOCKET
+/*
+ * Enable a broadcast address to a given socket
+ * The socket is in the inter_list all we need to do is enable
+ * broadcasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_broadcast_enable(struct interface *iface, SOCKET fd, struct sockaddr_storage *maddr)
+{
+#ifdef SO_BROADCAST
+ int on = 1;
+
+ if (maddr->ss_family == AF_INET)
+ {
+ /* if this interface can support broadcast, set SO_BROADCAST */
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) enable failure on address %s: %m",
+ stoa(maddr));
+ }
+#ifdef DEBUG
+ else if (debug > 1) {
+ printf("Broadcast enabled on socket %d for address %s\n",
+ fd, stoa(maddr));
+ }
+#endif
+ }
+ iface->flags |= INT_BCASTOPEN;
+ return ISC_TRUE;
+#else
+ return ISC_FALSE;
+#endif /* SO_BROADCAST */
+}
+
+/*
+ * Remove a broadcast address from a given socket
+ * The socket is in the inter_list all we need to do is disable
+ * broadcasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_broadcast_disable(struct interface *iface, struct sockaddr_storage *maddr)
+{
+#ifdef SO_BROADCAST
+ int off = 0; /* This seems to be OK as an int */
+
+ if (maddr->ss_family == AF_INET)
+ {
+ if (setsockopt(iface->fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&off, sizeof(off)))
+ {
+ netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) disable failure on address %s: %m",
+ stoa(maddr));
+ }
+ }
+ iface->flags &= ~INT_BCASTOPEN;
+ return ISC_TRUE;
+#else
+ return ISC_FALSE;
+#endif /* SO_BROADCAST */
+}
+
+#endif /* OPEN_BCAST_SOCKET */
+/*
+ * Check to see if the address is a multicast address
+ */
+static isc_boolean_t
+addr_ismulticast(struct sockaddr_storage *maddr)
+{
+ switch (maddr->ss_family)
+ {
+ case AF_INET :
+ if (!IN_CLASSD(ntohl(((struct sockaddr_in*)maddr)->sin_addr.s_addr))) {
+ DPRINTF(4, ("multicast address %s not class D\n", stoa(maddr)));
+ return (ISC_FALSE);
+ }
+ else
+ {
+ return (ISC_TRUE);
+ }
+
+ case AF_INET6 :
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)maddr)->sin6_addr)) {
+ DPRINTF(4, ("address %s not IPv6 multicast address\n", stoa(maddr)));
+ return (ISC_FALSE);
+ }
+ else
+ {
+ return (ISC_TRUE);
+ }
+
+/*
+ * If we don't have IPV6 support any IPV6 address is not multicast
+ */
+#else
+ return (ISC_FALSE);
+#endif
+ /*
+ * Never valid
+ */
+ default:
+ return (ISC_FALSE);
+ }
+}
+
+/*
+ * Multicast servers need to set the appropriate Multicast interface
+ * socket option in order for it to know which interface to use for
+ * send the multicast packet.
+ */
+void
+enable_multicast_if(struct interface *iface, struct sockaddr_storage *maddr)
+{
+#ifdef MCAST
+ /*u_char*/ TYPEOF_IP_MULTICAST_LOOP off = 0;
+
+ switch (maddr->ss_family)
+ {
+ case AF_INET:
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&(((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr),
+ sizeof(struct in_addr)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF failure: %m on socket %d, addr %s for multicast address %s",
+ iface->fd, stoa(&iface->sin), stoa(maddr));
+ return;
+ }
+#ifdef IP_MULTICAST_LOOP
+ /*
+ * Don't send back to itself, but allow it to fail to set it
+ */
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ SETSOCKOPT_ARG_CAST &off, sizeof(off)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s",
+ iface->fd, stoa(&iface->sin), stoa(maddr));
+ }
+#endif
+ DPRINTF(4, ("Added IPv4 multicast interface on socket %d, addr %s for multicast address %s\n",
+ iface->fd, stoa(&iface->sin),
+ stoa(maddr)));
+ break;
+
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &iface->scopeid, sizeof(iface->scopeid)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_MULTICAST_IF failure: %m on socket %d, addr %s, scope %d for multicast address %s",
+ iface->fd, stoa(&iface->sin), iface->scopeid,
+ stoa(maddr));
+ return;
+ }
+#ifdef IPV6_MULTICAST_LOOP
+ /*
+ * Don't send back to itself, but allow it to fail to set it
+ */
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &off, sizeof(off)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_LOOP failure: %m on socket %d, addr %s for multicast address %s",
+ iface->fd, stoa(&iface->sin), stoa(maddr));
+ }
+#endif
+ DPRINTF(4, ("Added IPv6 multicast interface on socket %d, addr %s, scope %d for multicast address %s\n",
+ iface->fd, stoa(&iface->sin), iface->scopeid,
+ stoa(maddr)));
+ break;
+#else
+ return;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+ }
+ return;
+#endif
+}
+
+/*
+ * Add a multicast address to a given socket
+ * The socket is in the inter_list all we need to do is enable
+ * multicasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_multicast_enable(struct interface *iface, int lscope, struct sockaddr_storage *maddr)
+{
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ struct ipv6_mreq mreq6;
+ struct in6_addr iaddr6;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+
+ struct ip_mreq mreq;
+
+ if (find_addr_in_list(maddr)) {
+ DPRINTF(4, ("socket_multicast_enable(%s): already enabled\n", stoa(maddr)));
+ return ISC_TRUE;
+ }
+
+ switch (maddr->ss_family)
+ {
+ case AF_INET:
+ memset((char *)&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = (((struct sockaddr_in*)maddr)->sin_addr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr, stoa(maddr));
+ return ISC_FALSE;
+ }
+ DPRINTF(4, ("Added IPv4 multicast membership on socket %d, addr %s for %x / %x (%s)\n",
+ iface->fd, stoa(&iface->sin),
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr, stoa(maddr)));
+ break;
+
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ /*
+ * Enable reception of multicast packets
+ * If the address is link-local we can get the interface index
+ * from the scope id. Don't do this for other types of multicast
+ * addresses. For now let the kernel figure it out.
+ */
+ memset((char *)&mreq6, 0, sizeof(mreq6));
+ iaddr6 = ((struct sockaddr_in6*)maddr)->sin6_addr;
+ mreq6.ipv6mr_multiaddr = iaddr6;
+ mreq6.ipv6mr_interface = lscope;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&mreq6, sizeof(mreq6)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_JOIN_GROUP failure: %m on socket %d, addr %s for interface %d(%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq6.ipv6mr_interface, stoa(maddr));
+ return ISC_FALSE;
+ }
+ DPRINTF(4, ("Added IPv6 multicast group on socket %d, addr %s for interface %d(%s)\n",
+ iface->fd, stoa(&iface->sin),
+ mreq6.ipv6mr_interface, stoa(maddr)));
+ break;
+#else
+ return ISC_FALSE;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+ }
+ iface->flags |= INT_MCASTOPEN;
+ iface->num_mcast++;
+ add_addr_to_list(maddr, iface);
+ return ISC_TRUE;
+}
+
+/*
+ * Remove a multicast address from a given socket
+ * The socket is in the inter_list all we need to do is disable
+ * multicasting. It is not this function's job to select the socket
+ */
+static isc_boolean_t
+socket_multicast_disable(struct interface *iface, struct sockaddr_storage *maddr)
+{
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ struct ipv6_mreq mreq6;
+ struct in6_addr iaddr6;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+
+ struct ip_mreq mreq;
+ memset((char *)&mreq, 0, sizeof(mreq));
+
+ if (find_addr_in_list(maddr) == NULL) {
+ DPRINTF(4, ("socket_multicast_disable(%s): not enabled\n", stoa(maddr)));
+ return ISC_TRUE;
+ }
+
+ switch (maddr->ss_family)
+ {
+ case AF_INET:
+ mreq.imr_multiaddr = (((struct sockaddr_in*)&maddr)->sin_addr);
+ mreq.imr_interface.s_addr = ((struct sockaddr_in*)&iface->sin)->sin_addr.s_addr;
+ if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IP_DROP_MEMBERSHIP failure: %m on socket %d, addr %s for %x / %x (%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr, stoa(maddr));
+ return ISC_FALSE;
+ }
+ break;
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ /*
+ * Disable reception of multicast packets
+ * If the address is link-local we can get the interface index
+ * from the scope id. Don't do this for other types of multicast
+ * addresses. For now let the kernel figure it out.
+ */
+ iaddr6 = ((struct sockaddr_in6*)&maddr)->sin6_addr;
+ mreq6.ipv6mr_multiaddr = iaddr6;
+ mreq6.ipv6mr_interface = iface->scopeid;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char *)&mreq6, sizeof(mreq6)) == -1) {
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_LEAVE_GROUP failure: %m on socket %d, addr %s for %d(%s)",
+ iface->fd, stoa(&iface->sin),
+ mreq6.ipv6mr_interface, stoa(maddr));
+ return ISC_FALSE;
+ }
+ break;
+#else
+ return ISC_FALSE;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+
+ }
+ iface->num_mcast--;
+ if (iface->num_mcast <= 0) {
+ iface->num_mcast = 0;
+ iface->flags &= ~INT_MCASTOPEN;
+ }
+ return ISC_TRUE;
+}
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient(void)
+{
+#ifdef OPEN_BCAST_SOCKET
+ struct interface *interf;
+ int nif = 0;
+ isc_boolean_t jstatus;
+ SOCKET fd;
+
+ set_reuseaddr(1);
+
+ for (interf = ISC_LIST_HEAD(inter_list);
+ interf != NULL;
+ interf = ISC_LIST_NEXT(interf, link)) {
+ if (interf->flags & INT_WILDCARD)
+ continue;
+
+ /* use only allowed addresses */
+ if (interf->ignore_packets == ISC_TRUE)
+ continue;
+ /* Only IPv4 addresses are valid for broadcast */
+ if (interf->sin.ss_family != AF_INET)
+ continue;
+
+ /* Is this a broadcast address? */
+ if (!(interf->flags & INT_BROADCAST))
+ continue;
+
+ /* Skip the loopback addresses */
+ if (interf->flags & INT_LOOPBACK)
+ continue;
+
+ /* Do we already have the broadcast address open? */
+ if (interf->flags & INT_BCASTOPEN) {
+ /* account for already open interfaces to aviod misleading warning below */
+ nif++;
+ continue;
+ }
+
+ /*
+ * Try to open the broadcast address
+ */
+ interf->family = AF_INET;
+ interf->bfd = open_socket(&interf->bcast,
+ INT_BROADCAST, 0, interf);
+
+ /*
+ * If we succeeded then we use it otherwise
+ * enable the underlying address
+ */
+ if (interf->bfd == INVALID_SOCKET) {
+ fd = interf->fd;
+ }
+ else {
+ fd = interf->bfd;
+ }
+
+ /* Enable Broadcast on socket */
+ jstatus = socket_broadcast_enable(interf, fd, &interf->sin);
+ if (jstatus == ISC_TRUE)
+ {
+ nif++;
+ netsyslog(LOG_INFO,"io_setbclient: Opened broadcast client on interface #%d %s, socket: %d",
+ interf->ifnum, interf->name, fd);
+ interf->addr_refid = addr2refid(&interf->sin);
+ }
+ }
+ set_reuseaddr(0);
+#ifdef DEBUG
+ if (debug)
+ if (nif > 0)
+ printf("io_setbclient: Opened broadcast clients\n");
+#endif
+ if (nif == 0)
+ netsyslog(LOG_ERR, "Unable to listen for broadcasts, no broadcast interfaces available");
+#else
+ netsyslog(LOG_ERR, "io_setbclient: Broadcast Client disabled by build");
+#endif
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient(void)
+{
+ struct interface *interf;
+ isc_boolean_t lstatus;
+
+ for (interf = ISC_LIST_HEAD(inter_list);
+ interf != NULL;
+ interf = ISC_LIST_NEXT(interf, link))
+ {
+ if (interf->flags & INT_WILDCARD)
+ continue;
+
+ if (!(interf->flags & INT_BCASTOPEN))
+ continue;
+ lstatus = socket_broadcast_disable(interf, &interf->sin);
+ }
+}
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(
+ struct sockaddr_storage addr
+ )
+{
+#ifdef MCAST
+ struct interface *interface, *iface;
+ int lscope = 0;
+
+ /*
+ * Check to see if this is a multicast address
+ */
+ if (addr_ismulticast(&addr) == ISC_FALSE)
+ return;
+
+ /* If we already have it we can just return */
+ if (find_flagged_addr_in_list(&addr, INT_MCASTOPEN|INT_MCASTIF) != NULL)
+ {
+ netsyslog(LOG_INFO, "Duplicate request found for multicast address %s",
+ stoa(&addr));
+ return;
+ }
+
+#ifndef MULTICAST_NONEWSOCKET
+ interface = new_interface(NULL);
+
+ /*
+ * Open a new socket for the multicast address
+ */
+ interface->sin.ss_family = addr.ss_family;
+ interface->family = addr.ss_family;
+
+ switch(addr.ss_family) {
+ case AF_INET:
+ memcpy(&(((struct sockaddr_in *)&interface->sin)->sin_addr),
+ &(((struct sockaddr_in*)&addr)->sin_addr),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&interface->sin)->sin_port = htons(NTP_PORT);
+ memset(&((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr, 0xff, sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ memcpy(&(((struct sockaddr_in6 *)&interface->sin)->sin6_addr),
+ &((struct sockaddr_in6*)&addr)->sin6_addr,
+ sizeof(struct in6_addr));
+ ((struct sockaddr_in6*)&interface->sin)->sin6_port = htons(NTP_PORT);
+#ifdef ISC_PLATFORM_HAVESCOPEID
+ ((struct sockaddr_in6*)&interface->sin)->sin6_scope_id = ((struct sockaddr_in6*)&addr)->sin6_scope_id;
+#endif
+ memset(&((struct sockaddr_in6*)&interface->mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr));
+#endif
+ iface = findlocalcastinterface(&addr, INT_MULTICAST);
+ if (iface) {
+# ifdef ISC_PLATFORM_HAVESCOPEID
+ lscope = ((struct sockaddr_in6*)&iface->sin)->sin6_scope_id;
+# endif
+ DPRINTF(4, ("Found interface #%d %s, scope: %d for address %s\n", iface->ifnum, iface->name, lscope, stoa(&addr)));
+ }
+ break;
+ }
+
+ set_reuseaddr(1);
+ interface->bfd = INVALID_SOCKET;
+ interface->fd = open_socket(&interface->sin,
+ INT_MULTICAST, 0, interface);
+
+ if (interface->fd != INVALID_SOCKET)
+ {
+ interface->bfd = INVALID_SOCKET;
+ interface->ignore_packets = ISC_FALSE;
+ interface->flags |= INT_MCASTIF;
+
+ (void) strncpy(interface->name, "multicast",
+ sizeof(interface->name));
+ ((struct sockaddr_in*)&interface->mask)->sin_addr.s_addr =
+ htonl(~(u_int32)0);
+ DPRINT_INTERFACE(2, (interface, "multicast add ", "\n"));
+ /* socket_multicast_enable() will add this address to the addresslist */
+ add_interface(interface);
+ list_if_listening(interface, htons(NTP_PORT));
+ }
+ else
+ {
+ delete_interface(interface); /* re-use existing interface */
+ interface = NULL;
+ if (addr.ss_family == AF_INET)
+ interface = wildipv4;
+ else if (addr.ss_family == AF_INET6)
+ interface = wildipv6;
+
+ if (interface != NULL) {
+ /* HACK ! -- stuff in an address */
+ interface->bcast = addr;
+ netsyslog(LOG_ERR,
+ "...multicast address %s using wildcard interface #%d %s",
+ stoa(&addr), interface->ifnum, interface->name);
+ } else {
+ netsyslog(LOG_ERR,
+ "No multicast socket available to use for address %s",
+ stoa(&addr));
+ return;
+ }
+ }
+#else
+ /*
+ * For the case where we can't use a separate socket
+ */
+ interface = findlocalcastinterface(&addr, INT_MULTICAST);
+ /*
+ * If we don't have a valid socket, just return
+ */
+ if (!interface)
+ {
+ netsyslog(LOG_ERR,
+ "Cannot add multicast address %s: Cannot find slot",
+ stoa(&addr));
+ return;
+ }
+
+#endif
+ {
+ isc_boolean_t jstatus;
+ jstatus = socket_multicast_enable(interface, lscope, &addr);
+
+ if (jstatus == ISC_TRUE)
+ netsyslog(LOG_INFO, "Added Multicast Listener %s on interface #%d %s\n", stoa(&addr), interface->ifnum, interface->name);
+ else
+ netsyslog(LOG_ERR, "Failed to add Multicast Listener %s\n", stoa(&addr));
+ }
+#else /* MCAST */
+ netsyslog(LOG_ERR,
+ "Cannot add multicast address %s: no Multicast support",
+ stoa(&addr));
+#endif /* MCAST */
+ return;
+}
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(
+ struct sockaddr_storage addr
+ )
+{
+#ifdef MCAST
+ struct interface *interface;
+ isc_boolean_t lstatus;
+
+ /*
+ * Check to see if this is a multicast address
+ */
+ if (addr_ismulticast(&addr) == ISC_FALSE)
+ {
+ netsyslog(LOG_ERR,
+ "invalid multicast address %s", stoa(&addr));
+ return;
+ }
+
+ switch (addr.ss_family)
+ {
+ case AF_INET :
+ /*
+ * Disable reception of multicast packets
+ */
+ interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN);
+ while ( interface != NULL) {
+ lstatus = socket_multicast_disable(interface, &addr);
+ interface = find_flagged_addr_in_list(&addr, INT_MCASTOPEN);
+ }
+ break;
+
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ case AF_INET6 :
+ /*
+ * Disable reception of multicast packets
+ */
+ for (interface = ISC_LIST_HEAD(inter_list);
+ interface != NULL;
+ interface = ISC_LIST_NEXT(interface, link))
+ {
+ if (interface->flags & INT_WILDCARD)
+ continue;
+
+ /* Be sure it's the correct family */
+ if (interface->sin.ss_family != AF_INET6)
+ continue;
+ if (!(interface->flags & INT_MCASTOPEN))
+ continue;
+ if (!(interface->fd < 0))
+ continue;
+ if (!SOCKCMP(&addr, &interface->sin))
+ continue;
+ lstatus = socket_multicast_disable(interface, &addr);
+ }
+ break;
+#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
+
+ }/* switch */
+
+ delete_addr_from_list(&addr);
+
+#else /* not MCAST */
+ netsyslog(LOG_ERR, "this function requires multicast kernel");
+#endif /* not MCAST */
+}
+
+/*
+ * init_nonblocking_io() - set up descriptor to be non blocking
+ */
+static void init_nonblocking_io(SOCKET fd)
+{
+ /*
+ * set non-blocking,
+ */
+
+#ifdef USE_FIONBIO
+ /* in vxWorks we use FIONBIO, but the others are defined for old systems, so
+ * all hell breaks loose if we leave them defined
+ */
+#undef O_NONBLOCK
+#undef FNDELAY
+#undef O_NDELAY
+#endif
+
+#if defined(O_NONBLOCK) /* POSIX */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails on fd #%d: %m",
+ fd);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m",
+ fd);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(O_NDELAY) /* generally the same as FNDELAY */
+ if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m",
+ fd);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FIONBIO)
+ {
+ int on = 1;
+# if defined(SYS_WINNT)
+
+ if (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR)
+# else
+ if (ioctl(fd,FIONBIO,&on) < 0)
+# endif
+ {
+ netsyslog(LOG_ERR, "ioctl(FIONBIO) fails on fd #%d: %m",
+ fd);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ }
+#elif defined(FIOSNBIO)
+ if (ioctl(fd,FIOSNBIO,&on) < 0)
+ {
+ netsyslog(LOG_ERR, "ioctl(FIOSNBIO) fails on fd #%d: %m",
+ fd);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else
+# include "Bletch: Need non-blocking I/O!"
+#endif
+}
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+
+static SOCKET
+open_socket(
+ struct sockaddr_storage *addr,
+ int flags,
+ int turn_off_reuse,
+ struct interface *interf
+ )
+{
+ int errval;
+ SOCKET fd;
+ int on = 1, off = 0; /* int is OK for REUSEADR per */
+ /* http://www.kohala.com/start/mcast.api.txt */
+#if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)
+ int tos;
+#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */
+
+ if ((addr->ss_family == AF_INET6) && (isc_net_probeipv6() != ISC_R_SUCCESS))
+ return (INVALID_SOCKET);
+
+ /* create a datagram (UDP) socket */
+#ifndef SYS_WINNT
+ if ( (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) < 0) {
+ errval = errno;
+#else
+ if ( (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
+ errval = WSAGetLastError();
+#endif
+ if(addr->ss_family == AF_INET)
+ netsyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+ else if(addr->ss_family == AF_INET6)
+ netsyslog(LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+#ifndef SYS_WINNT
+ if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT ||
+ errval == EPFNOSUPPORT)
+#else
+ if (errval == WSAEPROTONOSUPPORT || errval == WSAEAFNOSUPPORT ||
+ errval == WSAEPFNOSUPPORT)
+#endif
+ return (INVALID_SOCKET);
+ msyslog(LOG_ERR, "unexpected error code %d (not PROTONOSUPPORT|AFNOSUPPORT|FPNOSUPPORT) - exiting", errval);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#ifdef SYS_WINNT
+ if (connection_reset_fix(fd) != ISC_R_SUCCESS) {
+ netsyslog(LOG_ERR, "connection_reset_fix(fd) failed on address %s: %m",
+ stoa(addr));
+ }
+#endif /* SYS_WINNT */
+
+ /*
+ * Fixup the file descriptor for some systems
+ * See bug #530 for details of the issue.
+ */
+ fd = move_fd(fd);
+
+ /*
+ * set SO_REUSEADDR since we will be binding the same port
+ * number on each interface according to flag
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ turn_off_reuse ? (char *)&off : (char *)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR %s on fails on address %s: %m",
+ turn_off_reuse ? "off" : "on", stoa(addr));
+
+ closesocket(fd);
+
+ return INVALID_SOCKET;
+ }
+
+ /*
+ * IPv4 specific options go here
+ */
+ if (addr->ss_family == AF_INET) {
+#if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)
+ /* set IP_TOS to minimize packet delay */
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos)) < 0)
+ {
+ netsyslog(LOG_ERR, "setsockopt IPTOS_LOWDELAY on fails on address %s: %m",
+ stoa(addr));
+ }
+#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */
+ }
+
+ /*
+ * IPv6 specific options go here
+ */
+ if (addr->ss_family == AF_INET6) {
+#if defined(IPV6_V6ONLY)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char*)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt IPV6_V6ONLY on fails on address %s: %m",
+ stoa(addr));
+ }
+#endif /* IPV6_V6ONLY */
+#if defined(IPV6_BINDV6ONLY)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char*)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_BINDV6ONLY on fails on address %s: %m",
+ stoa(addr));
+ }
+#endif /* IPV6_BINDV6ONLY */
+ }
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+ /*
+ * some OSes don't allow binding to more specific
+ * addresses if a wildcard address already bound
+ * to the port and SO_REUSEADDR is not set
+ */
+ if (!is_wildcard_addr(addr)) {
+ set_wildcard_reuse(addr->ss_family, 1);
+ }
+#endif
+
+ /*
+ * bind the local address.
+ */
+ errval = bind(fd, (struct sockaddr *)addr, SOCKLEN(addr));
+
+#ifdef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND
+ /*
+ * some OSes don't allow binding to more specific
+ * addresses if a wildcard address already bound
+ * to the port and REUSE_ADDR is not set
+ */
+ if (!is_wildcard_addr(addr)) {
+ set_wildcard_reuse(addr->ss_family, 0);
+ }
+#endif
+
+ if (errval < 0) {
+ /*
+ * Don't log this under all conditions
+ */
+ if (turn_off_reuse == 0
+#ifdef DEBUG
+ || debug > 1
+#endif
+ ) {
+ if (addr->ss_family == AF_INET)
+ netsyslog(LOG_ERR,
+ "bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=0x%x fails: %m",
+ fd, addr->ss_family, (int)ntohs(((struct sockaddr_in*)addr)->sin_port),
+ stoa(addr),
+ IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)), flags);
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if (addr->ss_family == AF_INET6)
+ netsyslog(LOG_ERR,
+ "bind() fd %d, family %d, port %d, scope %d, addr %s, in6_is_addr_multicast=%d flags=0x%x fails: %m",
+ fd, addr->ss_family, (int)ntohs(((struct sockaddr_in6*)addr)->sin6_port),
+# ifdef ISC_PLATFORM_HAVESCOPEID
+ ((struct sockaddr_in6*)addr)->sin6_scope_id
+# else
+ -1
+# endif
+ , stoa(addr),
+ IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr), flags);
+#endif
+ }
+
+ closesocket(fd);
+
+ return INVALID_SOCKET;
+ }
+
+#ifdef HAVE_TIMESTAMP
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP,
+ (char*)&on, sizeof(on)))
+ {
+ netsyslog(LOG_DEBUG,
+ "setsockopt SO_TIMESTAMP on fails on address %s: %m",
+ stoa(addr));
+ }
+#ifdef DEBUG
+ else
+ {
+ DPRINTF(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n", fd, stoa(addr)));
+ }
+#endif
+ }
+#endif
+ DPRINTF(4, ("bind() fd %d, family %d, port %d, addr %s, flags=0x%x\n",
+ fd,
+ addr->ss_family,
+ (int)ntohs(((struct sockaddr_in*)addr)->sin_port),
+ stoa(addr),
+ flags));
+
+ init_nonblocking_io(fd);
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#endif /* not HAVE_SIGNALED_IO */
+
+ add_fd_to_list(fd, FD_TYPE_SOCKET);
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+ DPRINTF(4, ("flags for fd %d: 0x%x\n", fd,
+ fcntl(fd, F_GETFL, 0)));
+#endif /* SYS_WINNT || VMS */
+
+#if defined (HAVE_IO_COMPLETION_PORT)
+/*
+ * Add the socket to the completion port
+ */
+ if (io_completion_port_add_socket(fd, interf))
+ {
+ msyslog(LOG_ERR, "unable to set up io completion port - EXITING");
+ exit(1);
+ }
+#endif
+ return fd;
+}
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination. Maintain a
+ * send error cache so that only the first consecutive error for a
+ * destination is logged.
+ */
+void
+sendpkt(
+ struct sockaddr_storage *dest,
+ struct interface *inter,
+ int ttl,
+ struct pkt *pkt,
+ int len
+ )
+{
+ int cc, slot;
+#ifdef SYS_WINNT
+ DWORD err;
+#endif /* SYS_WINNT */
+
+ /*
+ * Send error caches. Empty slots have port == 0
+ * Set ERRORCACHESIZE to 0 to disable
+ */
+ struct cache {
+ u_short port;
+ struct in_addr addr;
+ };
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ struct cache6 {
+ u_short port;
+ struct in6_addr addr;
+ };
+#endif /* INCLUDE_IPV6_SUPPORT */
+
+
+#ifndef ERRORCACHESIZE
+#define ERRORCACHESIZE 8
+#endif
+#if ERRORCACHESIZE > 0
+ static struct cache badaddrs[ERRORCACHESIZE];
+#ifdef INCLUDE_IPV6_SUPPORT
+ static struct cache6 badaddrs6[ERRORCACHESIZE];
+#endif /* INCLUDE_IPV6_SUPPORT */
+#else
+#define badaddrs ((struct cache *)0) /* Only used in empty loops! */
+#ifdef INCLUDE_IPV6_SUPPORT
+#define badaddrs6 ((struct cache6 *)0) /* Only used in empty loops! */
+#endif /* INCLUDE_IPV6_SUPPORT */
+#endif
+#ifdef DEBUG
+ if (debug > 1)
+ {
+ if (inter != NULL)
+ {
+ printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n",
+ (ttl > 0) ? "\tMCAST\t***** " : "",
+ inter->fd, stoa(dest),
+ stoa(&inter->sin), ttl, len);
+ }
+ else
+ {
+ printf("%ssendpkt(dst=%s, ttl=%d, len=%d): no interface - IGNORED\n",
+ (ttl > 0) ? "\tMCAST\t***** " : "",
+ stoa(dest),
+ ttl, len);
+ }
+ }
+#endif
+
+ if (inter == NULL) /* unbound peer - drop request and wait for better network conditions */
+ return;
+
+#ifdef MCAST
+
+ /*
+ * for the moment we use the bcast option to set multicast ttl
+ */
+ if (ttl > 0 && ttl != inter->last_ttl) {
+
+ /*
+ * set the multicast ttl for outgoing packets
+ */
+ int rtc;
+
+ switch (inter->sin.ss_family) {
+
+ case AF_INET :
+ {
+ u_char mttl = (u_char) ttl;
+
+ rtc = setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (const void *) &mttl, sizeof(mttl));
+ break;
+ }
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ case AF_INET6 :
+ {
+ u_int ittl = (u_char) ttl;
+
+ rtc = setsockopt(inter->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ (const void *) &ittl, sizeof(ittl));
+ break;
+ }
+
+#endif /* INCLUDE_IPV6_SUPPORT */
+ default: /* just NOP if not supported */
+ rtc = 0;
+ break;
+ }
+
+ if (rtc != 0) {
+ netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS fails on address %s: %m",
+ stoa(&inter->sin));
+ }
+ else
+ inter->last_ttl = ttl;
+ }
+
+#endif /* MCAST */
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if(dest->ss_family == AF_INET) {
+ if (badaddrs[slot].port == ((struct sockaddr_in*)dest)->sin_port &&
+ badaddrs[slot].addr.s_addr == ((struct sockaddr_in*)dest)->sin_addr.s_addr)
+ break;
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if (dest->ss_family == AF_INET6) {
+ if (badaddrs6[slot].port == ((struct sockaddr_in6*)dest)->sin6_port &&
+ badaddrs6[slot].addr.s6_addr == ((struct sockaddr_in6*)dest)->sin6_addr.s6_addr)
+ break;
+ }
+#endif /* INCLUDE_IPV6_SUPPORT */
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+ err = io_completion_port_sendto(inter, pkt, len, dest);
+ if (err != ERROR_SUCCESS)
+#else
+#ifdef SIM
+ cc = srvr_rply(&ntp_node, dest, inter, pkt);
+#else /* SIM */
+ cc = sendto(inter->fd, (char *)pkt, (unsigned int)len, 0, (struct sockaddr *)dest,
+ SOCKLEN(dest));
+#endif /* SIM */
+ if (cc == -1)
+#endif
+ {
+ inter->notsent++;
+ packets_notsent++;
+#if defined(HAVE_IO_COMPLETION_PORT)
+ err = WSAGetLastError();
+ if (err != WSAEWOULDBLOCK && err != WSAENOBUFS && slot < 0)
+#else
+ if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0)
+#endif
+ {
+ /*
+ * Remember this, if there's an empty slot
+ */
+ switch (dest->ss_family) {
+
+ case AF_INET :
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == 0)
+ {
+ badaddrs[slot].port = SRCPORT(dest);
+ badaddrs[slot].addr = ((struct sockaddr_in*)dest)->sin_addr;
+ break;
+ }
+ break;
+
+#ifdef INCLUDE_IPV6_SUPPORT
+ case AF_INET6 :
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs6[slot].port == 0)
+ {
+ badaddrs6[slot].port = SRCPORT(dest);
+ badaddrs6[slot].addr = ((struct sockaddr_in6*)dest)->sin6_addr;
+ break;
+ }
+ break;
+#endif /* INCLUDE_IPV6_SUPPORT */
+ default: /* don't care if not supported */
+ break;
+ }
+
+ netsyslog(LOG_ERR, "sendto(%s) (fd=%d): %m",
+ stoa(dest), inter->fd);
+ }
+ }
+ else
+ {
+ inter->sent++;
+ packets_sent++;
+ /*
+ * He's not bad any more
+ */
+ if (slot >= 0)
+ {
+ netsyslog(LOG_INFO, "Connection re-established to %s", stoa(dest));
+ switch (dest->ss_family) {
+ case AF_INET :
+ badaddrs[slot].port = 0;
+ break;
+#ifdef INCLUDE_IPV6_SUPPORT
+ case AF_INET6 :
+ badaddrs6[slot].port = 0;
+ break;
+#endif /* INCLUDE_IPV6_SUPPORT */
+ default: /* don't care if not supported */
+ break;
+ }
+ }
+ }
+}
+
+#if !defined(HAVE_IO_COMPLETION_PORT)
+/*
+ * fdbits - generate ascii representation of fd_set (FAU debug support)
+ * HFDF format - highest fd first.
+ */
+static char *
+fdbits(
+ int count,
+ fd_set *set
+ )
+{
+ static char buffer[256];
+ char * buf = buffer;
+
+ count = (count < 256) ? count : 255;
+
+ while (count >= 0)
+ {
+ *buf++ = FD_ISSET(count, set) ? '#' : '-';
+ count--;
+ }
+ *buf = '\0';
+
+ return buffer;
+}
+
+/*
+ * Routine to read the refclock packets for a specific interface
+ * Return the number of bytes read. That way we know if we should
+ * read it again or go on to the next one if no bytes returned
+ */
+static inline int
+read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
+{
+ int i;
+ int buflen;
+ register struct recvbuf *rb;
+
+ rb = get_free_recv_buffer();
+
+ if (rb == NULL)
+ {
+ /*
+ * No buffer space available - just drop the packet
+ */
+ char buf[RX_BUFF_SIZE];
+
+ buflen = read(fd, buf, sizeof buf);
+ packets_dropped++;
+ return (buflen);
+ }
+
+ i = (rp->datalen == 0
+ || rp->datalen > sizeof(rb->recv_space))
+ ? sizeof(rb->recv_space) : rp->datalen;
+ buflen = read(fd, (char *)&rb->recv_space, (unsigned)i);
+
+ if (buflen < 0)
+ {
+ if (errno != EINTR && errno != EAGAIN) {
+ netsyslog(LOG_ERR, "clock read fd %d: %m", fd);
+ }
+ freerecvbuf(rb);
+ return (buflen);
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->recv_length = buflen;
+ rb->recv_srcclock = rp->srcclock;
+ rb->dstadr = 0;
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = rp->clock_recv;
+
+ if (rp->io_input)
+ {
+ /*
+ * have direct input routine for refclocks
+ */
+ if (rp->io_input(rb) == 0)
+ {
+ /*
+ * data was consumed - nothing to pass up
+ * into block input machine
+ */
+ freerecvbuf(rb);
+ return (buflen);
+ }
+ }
+
+ add_full_recv_buffer(rb);
+
+ rp->recvcount++;
+ packets_received++;
+ return (buflen);
+}
+
+#ifdef HAVE_TIMESTAMP
+/*
+ * extract timestamps from control message buffer
+ */
+static l_fp
+ fetch_timestamp(struct recvbuf *rb, struct msghdr *msghdr, l_fp ts)
+{
+#ifdef USE_TIMESTAMP_CMSG
+ struct cmsghdr *cmsghdr;
+
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ while (cmsghdr != NULL) {
+ switch (cmsghdr->cmsg_type)
+ {
+ case SCM_TIMESTAMP:
+ {
+ struct timeval *tvp = (struct timeval *)CMSG_DATA(cmsghdr);
+ double dtemp;
+ l_fp nts;
+ DPRINTF(4, ("fetch_timestamp: system network time stamp: %ld.%06ld\n", tvp->tv_sec, tvp->tv_usec));
+ nts.l_i = tvp->tv_sec + JAN_1970;
+ dtemp = tvp->tv_usec / 1e6;
+
+ /* fuzz lower bits not covered by precision */
+ if (sys_precision != 0)
+ dtemp += (ntp_random() / FRAC - .5) / (1 <<
+ -sys_precision);
+
+ nts.l_uf = (u_int32)(dtemp*FRAC);
+#ifdef DEBUG_TIMING
+ {
+ l_fp dts = ts;
+ L_SUB(&dts, &nts);
+ collect_timing(rb, "input processing delay", 1, &dts);
+ DPRINTF(4, ("fetch_timestamp: timestamp delta: %s (incl. prec fuzz)\n", lfptoa(&dts, 9)));
+ }
+#endif
+ ts = nts; /* network time stamp */
+ break;
+ }
+ default:
+ DPRINTF(4, ("fetch_timestamp: skipping control message 0x%x\n", cmsghdr->cmsg_type));
+ break;
+ }
+ cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr);
+ }
+#endif
+ return ts;
+}
+#endif
+
+/*
+ * Routine to read the network NTP packets for a specific interface
+ * Return the number of bytes read. That way we know if we should
+ * read it again or go on to the next one if no bytes returned
+ */
+static inline int
+read_network_packet(SOCKET fd, struct interface *itf, l_fp ts)
+{
+ GETSOCKNAME_SOCKLEN_TYPE fromlen;
+ int buflen;
+ register struct recvbuf *rb;
+#ifdef HAVE_TIMESTAMP
+ struct msghdr msghdr;
+ struct iovec iovec;
+ char control[TIMESTAMP_CTLMSGBUF_SIZE]; /* pick up control messages */
+#endif
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on a disallowed socket, just dump the
+ * packet.
+ */
+
+ rb = get_free_recv_buffer();
+
+ if (rb == NULL || itf->ignore_packets == ISC_TRUE)
+ {
+ char buf[RX_BUFF_SIZE];
+ struct sockaddr_storage from;
+ if (rb != NULL)
+ freerecvbuf(rb);
+
+ fromlen = sizeof(from);
+ buflen = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr*)&from, &fromlen);
+ DPRINTF(4, ("%s on (%lu) fd=%d from %s\n",
+ (itf->ignore_packets == ISC_TRUE) ? "ignore" : "drop",
+ free_recvbuffs(), fd,
+ stoa(&from)));
+ if (itf->ignore_packets == ISC_TRUE)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ return (buflen);
+ }
+
+ fromlen = sizeof(struct sockaddr_storage);
+
+#ifndef HAVE_TIMESTAMP
+ rb->recv_length = recvfrom(fd,
+ (char *)&rb->recv_space,
+ sizeof(rb->recv_space), 0,
+ (struct sockaddr *)&rb->recv_srcadr,
+ &fromlen);
+#else
+ iovec.iov_base = (void *)&rb->recv_space;
+ iovec.iov_len = sizeof(rb->recv_space);
+ msghdr.msg_name = (void *)&rb->recv_srcadr;
+ msghdr.msg_namelen = sizeof(rb->recv_srcadr);
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = (void *)&control;
+ msghdr.msg_controllen = sizeof(control);
+ msghdr.msg_flags = 0;
+ rb->recv_length = recvmsg(fd, &msghdr, 0);
+#endif
+
+ buflen = rb->recv_length;
+
+ if (buflen == 0 || (buflen == -1 &&
+ (errno==EWOULDBLOCK
+#ifdef EAGAIN
+ || errno==EAGAIN
+#endif
+ ))) {
+ freerecvbuf(rb);
+ return (buflen);
+ }
+ else if (buflen < 0)
+ {
+ netsyslog(LOG_ERR, "recvfrom(%s) fd=%d: %m",
+ stoa(&rb->recv_srcadr), fd);
+ DPRINTF(5, ("read_network_packet: fd=%d dropped (bad recvfrom)\n", fd));
+ freerecvbuf(rb);
+ return (buflen);
+ }
+
+#ifdef DEBUG
+ if (debug > 2) {
+ if(rb->recv_srcadr.ss_family == AF_INET)
+ printf("read_network_packet: fd=%d length %d from %08lx %s\n",
+ fd, buflen,
+ (u_long)ntohl(((struct sockaddr_in*)&rb->recv_srcadr)->sin_addr.s_addr) &
+ 0x00000000ffffffff,
+ stoa(&rb->recv_srcadr));
+ else
+ printf("read_network_packet: fd=%d length %d from %s\n",
+ fd, buflen,
+ stoa(&rb->recv_srcadr));
+ }
+#endif
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->dstadr = itf;
+ rb->fd = fd;
+#ifdef HAVE_TIMESTAMP
+ ts = fetch_timestamp(rb, &msghdr, ts); /* pick up a network time stamp if possible */
+#endif
+ rb->recv_time = ts;
+ rb->receiver = receive;
+
+ add_full_recv_buffer(rb);
+
+ itf->received++;
+ packets_received++;
+ return (buflen);
+}
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+void
+input_handler(
+ l_fp *cts
+ )
+{
+
+ int buflen;
+ int n;
+ int doing;
+ SOCKET fd;
+ struct timeval tvzero;
+ l_fp ts; /* Timestamp at BOselect() gob */
+#ifdef DEBUG_TIMING
+ l_fp ts_e; /* Timestamp at EOselect() gob */
+#endif
+ fd_set fds;
+ int select_count = 0;
+ struct interface *interface;
+#if defined(HAS_ROUTING_SOCKET)
+ struct asyncio_reader *asyncio_reader;
+#endif
+
+ handler_calls++;
+
+ /*
+ * If we have something to do, freeze a timestamp.
+ * See below for the other cases (nothing (left) to do or error)
+ */
+ ts = *cts;
+
+ /*
+ * Do a poll to see who has data
+ */
+
+ fds = activefds;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+
+ n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ /*
+ * If there are no packets waiting just return
+ */
+ if (n < 0)
+ {
+ int err = errno;
+ /*
+ * extended FAU debugging output
+ */
+ if (err != EINTR)
+ netsyslog(LOG_ERR,
+ "select(%d, %s, 0L, 0L, &0.0) error: %m",
+ maxactivefd+1,
+ fdbits(maxactivefd, &activefds));
+ if (err == EBADF) {
+ int j, b;
+ fds = activefds;
+ for (j = 0; j <= maxactivefd; j++)
+ if ((FD_ISSET(j, &fds) && (read(j, &b, 0) == -1)))
+ netsyslog(LOG_ERR, "Bad file descriptor %d", j);
+ }
+ return;
+ }
+ else if (n == 0)
+ return;
+
+ ++handler_pkts;
+
+#ifdef REFCLOCK
+ /*
+ * Check out the reference clocks first, if any
+ */
+
+ if (refio != NULL)
+ {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != NULL; rp = rp->next)
+ {
+ fd = rp->fd;
+
+ if (FD_ISSET(fd, &fds))
+ {
+ do {
+ ++select_count;
+ buflen = read_refclock_packet(fd, rp, ts);
+ } while (buflen > 0);
+
+ } /* End if (FD_ISSET(fd, &fds)) */
+ } /* End for (rp = refio; rp != 0 && n > 0; rp = rp->next) */
+ } /* End if (refio != 0) */
+
+#endif /* REFCLOCK */
+
+ /*
+ * Loop through the interfaces looking for data to read.
+ */
+ for (interface = ISC_LIST_TAIL(inter_list);
+ interface != NULL;
+ interface = ISC_LIST_PREV(interface, link))
+ {
+ for (doing = 0; (doing < 2); doing++)
+ {
+ if (doing == 0)
+ {
+ fd = interface->fd;
+ }
+ else
+ {
+ if (!(interface->flags & INT_BCASTOPEN))
+ break;
+ fd = interface->bfd;
+ }
+ if (fd < 0) continue;
+ if (FD_ISSET(fd, &fds))
+ {
+ do {
+ ++select_count;
+ buflen = read_network_packet(fd, interface, ts);
+ } while (buflen > 0);
+ }
+ /* Check more interfaces */
+ }
+ }
+
+#ifdef HAS_ROUTING_SOCKET
+ /*
+ * scan list of asyncio readers - currently only used for routing sockets
+ */
+ asyncio_reader = ISC_LIST_TAIL(asyncio_reader_list);
+
+ while (asyncio_reader != NULL)
+ {
+ struct asyncio_reader *next = ISC_LIST_PREV(asyncio_reader, link);
+ if (FD_ISSET(asyncio_reader->fd, &fds)) {
+ ++select_count;
+ asyncio_reader->receiver(asyncio_reader);
+ }
+ asyncio_reader = next;
+ }
+#endif /* HAS_ROUTING_SOCKET */
+
+ /*
+ * Done everything from that select.
+ */
+
+ /*
+ * If nothing to do, just return.
+ * If an error occurred, complain and return.
+ */
+ if (select_count == 0) /* We really had nothing to do */
+ {
+#ifdef DEBUG
+ if (debug)
+ netsyslog(LOG_DEBUG, "input_handler: select() returned 0");
+#endif
+ return;
+ }
+ /* We've done our work */
+#ifdef DEBUG_TIMING
+ get_systime(&ts_e);
+ /*
+ * (ts_e - ts) is the amount of time we spent
+ * processing this gob of file descriptors. Log
+ * it.
+ */
+ L_SUB(&ts_e, &ts);
+ collect_timing(NULL, "input handler", 1, &ts_e);
+ if (debug > 3)
+ netsyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6));
+#endif
+ /* just bail. */
+ return;
+}
+
+#endif
+
+/*
+ * findinterface - find local interface corresponding to address
+ */
+struct interface *
+findinterface(
+ struct sockaddr_storage *addr
+ )
+{
+ struct interface *interface;
+
+ interface = findlocalinterface(addr, INT_WILDCARD);
+
+ if (interface == NULL)
+ {
+ DPRINTF(4, ("Found no interface for address %s - returning wildcard\n",
+ stoa(addr)));
+
+ return (ANY_INTERFACE_CHOOSE(addr));
+ }
+ else
+ {
+ DPRINTF(4, ("Found interface #%d %s for address %s\n",
+ interface->ifnum, interface->name, stoa(addr)));
+
+ return (interface);
+ }
+}
+
+/*
+ * findlocalinterface - find local interface index corresponding to address
+ *
+ * This code attempts to find the local sending address for an outgoing
+ * address by connecting a new socket to destinationaddress:NTP_PORT
+ * and reading the sockname of the resulting connect.
+ * the complicated sequence simulates the routing table lookup
+ * for to first hop without duplicating any of the routing logic into
+ * ntpd. preferably we would have used an API call - but its not there -
+ * so this is the best we can do here short of duplicating to entire routing
+ * logic in ntpd which would be a silly and really unportable thing to do.
+ *
+ */
+static struct interface *
+findlocalinterface(
+ struct sockaddr_storage *addr,
+ int flags
+ )
+{
+ SOCKET s;
+ int rtn;
+ struct sockaddr_storage saddr;
+ GETSOCKNAME_SOCKLEN_TYPE saddrlen = SOCKLEN(addr);
+ struct interface *iface;
+
+ DPRINTF(4, ("Finding interface for addr %s in list of addresses\n",
+ stoa(addr));)
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.ss_family = addr->ss_family;
+ if(addr->ss_family == AF_INET) {
+ memcpy(&((struct sockaddr_in*)&saddr)->sin_addr, &((struct sockaddr_in*)addr)->sin_addr, sizeof(struct in_addr));
+ ((struct sockaddr_in*)&saddr)->sin_port = htons(NTP_PORT);
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if(addr->ss_family == AF_INET6) {
+ memcpy(&((struct sockaddr_in6*)&saddr)->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr));
+ ((struct sockaddr_in6*)&saddr)->sin6_port = htons(NTP_PORT);
+# ifdef ISC_PLATFORM_HAVESCOPEID
+ ((struct sockaddr_in6*)&saddr)->sin6_scope_id = ((struct sockaddr_in6*)addr)->sin6_scope_id;
+# endif
+ }
+#endif
+
+ s = socket(addr->ss_family, SOCK_DGRAM, 0);
+ if (s == INVALID_SOCKET)
+ return NULL;
+
+ rtn = connect(s, (struct sockaddr *)&saddr, SOCKLEN(&saddr));
+#ifndef SYS_WINNT
+ if (rtn < 0)
+#else
+ if (rtn == SOCKET_ERROR)
+#endif
+ {
+ closesocket(s);
+ return NULL;
+ }
+
+ rtn = getsockname(s, (struct sockaddr *)&saddr, &saddrlen);
+ closesocket(s);
+#ifndef SYS_WINNT
+ if (rtn < 0)
+#else
+ if (rtn == SOCKET_ERROR)
+#endif
+ return NULL;
+
+ DPRINTF(4, ("findlocalinterface: kernel maps %s to %s\n", stoa(addr), stoa(&saddr)));
+
+ iface = getinterface(&saddr, flags);
+
+ /* Don't both with ignore interfaces */
+ if (iface != NULL && iface->ignore_packets == ISC_TRUE)
+ {
+ return NULL;
+ }
+ else
+ {
+ return iface;
+ }
+}
+
+/*
+ * fetch an interface structure the matches the
+ * address is has the given flags not set
+ */
+static struct interface *
+getinterface(struct sockaddr_storage *addr, int flags)
+{
+ struct interface *interface = find_addr_in_list(addr);
+
+ if (interface != NULL && interface->flags & flags)
+ {
+ return NULL;
+ }
+ else
+ {
+ return interface;
+ }
+}
+
+/*
+ * findlocalcastinterface - find local *cast interface index corresponding to address
+ * depending on the flags passed
+ */
+static struct interface *
+findlocalcastinterface(
+ struct sockaddr_storage *addr, int flags
+ )
+{
+ struct interface *interface;
+ struct interface *nif = NULL;
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ isc_boolean_t want_linklocal;
+#endif
+
+ /*
+ * see how kernel maps the mcast address
+ */
+ nif = findlocalinterface(addr, 0);
+
+ if (nif) {
+ DPRINTF(2, ("findlocalcastinterface: kernel recommends interface #%d %s\n", nif->ifnum, nif->name));
+ return nif;
+ }
+
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ want_linklocal = ISC_FALSE;
+ if (addr_ismulticast(addr) && flags == INT_MULTICAST)
+ {
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr))
+ {
+ want_linklocal = ISC_TRUE;
+ }
+ else if (IN6_IS_ADDR_MC_SITELOCAL(&((struct sockaddr_in6*)addr)->sin6_addr))
+ {
+ want_linklocal = ISC_TRUE;
+ }
+ }
+#endif
+
+ for (interface = ISC_LIST_HEAD(inter_list);
+ interface != NULL;
+ interface = ISC_LIST_NEXT(interface, link))
+ {
+ /* use only allowed addresses */
+ if (interface->ignore_packets == ISC_TRUE)
+ continue;
+
+ /* Skip the loopback and wildcard addresses */
+ if (interface->flags & (INT_LOOPBACK|INT_WILDCARD))
+ continue;
+
+ /* Skip if different family */
+ if(interface->sin.ss_family != addr->ss_family)
+ continue;
+
+ /* Is this it one of these based on flags? */
+ if (!(interface->flags & flags))
+ continue;
+
+ /* for IPv6 multicast check the address for linklocal */
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ if (flags == INT_MULTICAST && interface->sin.ss_family == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr))
+ && want_linklocal == ISC_TRUE)
+ {
+ nif = interface;
+ break;
+ }
+ /* If we want a linklocal address and this isn't it, skip */\
+ if (want_linklocal == ISC_TRUE)
+ continue;
+#endif
+ /* Otherwise just look for the flag */
+ if((interface->flags & flags))
+ {
+ nif = interface;
+ break;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ if (nif)
+ printf("findlocalcastinterface: found interface #%d %s\n", nif->ifnum, nif->name);
+ else
+ printf("findlocalcastinterface: no interface found for %s flags 0x%x\n", stoa(addr), flags);
+ }
+#endif
+ return (nif);
+}
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+struct interface *
+findbcastinter(
+ struct sockaddr_storage *addr
+ )
+{
+#if !defined(MPE) && (defined(SIOCGIFCONF) || defined(SYS_WINNT))
+ struct interface *interface;
+
+
+ DPRINTF(4, ("Finding broadcast/multicast interface for addr %s in list of addresses\n",
+ stoa(addr)));
+
+ interface = findlocalinterface(addr, INT_LOOPBACK|INT_WILDCARD);
+
+ if (interface != NULL)
+ {
+ DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name));
+ return interface;
+ }
+
+ /* plan B - try to find something reasonable in our lists in case kernel lookup doesn't help */
+
+ for (interface = ISC_LIST_HEAD(inter_list);
+ interface != NULL;
+ interface = ISC_LIST_NEXT(interface, link))
+ {
+ if (interface->flags & INT_WILDCARD)
+ continue;
+
+ /* Don't bother with ignored interfaces */
+ if (interface->ignore_packets == ISC_TRUE)
+ continue;
+
+ /*
+ * First look if this is the correct family
+ */
+ if(interface->sin.ss_family != addr->ss_family)
+ continue;
+
+ /* Skip the loopback addresses */
+ if (interface->flags & INT_LOOPBACK)
+ continue;
+
+ /*
+ * If we are looking to match a multicast address grab it.
+ */
+ if (addr_ismulticast(addr) == ISC_TRUE && interface->flags & INT_MULTICAST)
+ {
+#ifdef INCLUDE_IPV6_SUPPORT
+ if(addr->ss_family == AF_INET6) {
+ /* Only use link-local address for link-scope mcast */
+ if(IN6_IS_ADDR_MC_LINKLOCAL(&((struct sockaddr_in6*)addr)->sin6_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)&interface->sin)->sin6_addr)) {
+ continue;
+ }
+ }
+#endif
+ break;
+ }
+
+ /*
+ * We match only those interfaces marked as
+ * broadcastable and either the explicit broadcast
+ * address or the network portion of the IP address.
+ * Sloppy.
+ */
+ if(addr->ss_family == AF_INET) {
+ if (SOCKCMP(&interface->bcast, addr)) {
+ break;
+ }
+ if ((NSRCADR(&interface->sin) &
+ NSRCADR(&interface->mask)) == (NSRCADR(addr) &
+ NSRCADR(&interface->mask)))
+ break;
+ }
+#ifdef INCLUDE_IPV6_SUPPORT
+ else if(addr->ss_family == AF_INET6) {
+ if (SOCKCMP(&interface->bcast, addr)) {
+ break;
+ }
+ if (SOCKCMP(netof(&interface->sin), netof(addr))) {
+ break;
+ }
+ }
+#endif
+ }
+#endif /* SIOCGIFCONF */
+ if (interface == NULL) {
+ DPRINTF(4, ("No bcast interface found for %s\n", stoa(addr)));
+ return ANY_INTERFACE_CHOOSE(addr);
+ } else {
+ DPRINTF(4, ("Found bcast-/mcast- interface index #%d %s\n", interface->ifnum, interface->name));
+ return interface;
+ }
+}
+
+
+/*
+ * io_clr_stats - clear I/O module statistics
+ */
+void
+io_clr_stats(void)
+{
+ packets_dropped = 0;
+ packets_ignored = 0;
+ packets_received = 0;
+ packets_sent = 0;
+ packets_notsent = 0;
+
+ handler_calls = 0;
+ handler_pkts = 0;
+ io_timereset = current_time;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * io_addclock - add a reference clock to the list and arrange that we
+ * get SIGIO interrupts from it.
+ */
+int
+io_addclock(
+ struct refclockio *rio
+ )
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+
+# ifdef HAVE_SIGNALED_IO
+ if (init_clock_sig(rio))
+ {
+ UNBLOCKIO();
+ return 0;
+ }
+# elif defined(HAVE_IO_COMPLETION_PORT)
+ if (io_completion_port_add_clock_io(rio))
+ {
+ UNBLOCKIO();
+ return 0;
+ }
+# endif
+
+ /*
+ * enqueue
+ */
+ refio = rio;
+
+ /*
+ * register fd
+ */
+ add_fd_to_list(rio->fd, FD_TYPE_FILE);
+
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_closeclock - close the clock in the I/O structure given
+ */
+void
+io_closeclock(
+ struct refclockio *rio
+ )
+{
+ BLOCKIO();
+ /*
+ * Remove structure from the list
+ */
+ if (refio == rio)
+ {
+ refio = rio->next;
+ }
+ else
+ {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != NULL; rp = rp->next)
+ if (rp->next == rio)
+ {
+ rp->next = rio->next;
+ break;
+ }
+
+ if (rp == NULL) {
+ UNBLOCKIO();
+ return;
+ }
+ }
+
+ /*
+ * Close the descriptor.
+ */
+ close_and_delete_fd_from_list(rio->fd);
+ UNBLOCKIO();
+}
+#endif /* REFCLOCK */
+
+/*
+ * On NT a SOCKET is an unsigned int so we cannot possibly keep it in
+ * an array. So we use one of the ISC_LIST functions to hold the
+ * socket value and use that when we want to enumerate it.
+ */
+void
+kill_asyncio(int startfd)
+{
+ vsock_t *lsock;
+ vsock_t *next;
+
+ BLOCKIO();
+
+ lsock = ISC_LIST_HEAD(fd_list);
+ while (lsock != NULL) {
+ /*
+ * careful here - list is being dismantled while
+ * we scan it - setting next here insures that
+ * we are able to correctly scan the list
+ */
+ next = ISC_LIST_NEXT(lsock, link);
+ /*
+ * will remove socket from list
+ */
+ close_and_delete_fd_from_list(lsock->fd);
+ lsock = next;
+ }
+
+ UNBLOCKIO();
+}
+
+/*
+ * Add and delete functions for the list of open sockets
+ */
+static void
+add_fd_to_list(SOCKET fd, enum desc_type type) {
+ vsock_t *lsock = (vsock_t *)emalloc(sizeof(vsock_t));
+ lsock->fd = fd;
+ lsock->type = type;
+
+ ISC_LIST_APPEND(fd_list, lsock, link);
+ /*
+ * I/O Completion Ports don't care about the select and FD_SET
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ /*
+ * keep activefds in sync
+ */
+ if (fd > maxactivefd)
+ maxactivefd = fd;
+ FD_SET( (u_int)fd, &activefds);
+#endif
+}
+
+static void
+close_and_delete_fd_from_list(SOCKET fd) {
+
+ vsock_t *next;
+ vsock_t *lsock = ISC_LIST_HEAD(fd_list);
+
+ while(lsock != NULL) {
+ next = ISC_LIST_NEXT(lsock, link);
+ if(lsock->fd == fd) {
+ ISC_LIST_DEQUEUE_TYPE(fd_list, lsock, link, vsock_t);
+
+ switch (lsock->type) {
+ case FD_TYPE_SOCKET:
+#ifdef SYS_WINNT
+ closesocket(lsock->fd);
+ break;
+#endif
+ case FD_TYPE_FILE:
+ (void) close(lsock->fd);
+ break;
+ default:
+ msyslog(LOG_ERR, "internal error - illegal descriptor type %d - EXITING", (int)lsock->type);
+ exit(1);
+ }
+
+ free(lsock);
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ /*
+ * remove from activefds
+ */
+ FD_CLR( (u_int) fd, &activefds);
+
+ if (fd == maxactivefd) {
+ int i, newmax = 0;
+ for (i = 0; i < maxactivefd; i++)
+ if (FD_ISSET(i, &activefds))
+ newmax = i;
+ maxactivefd = newmax;
+ }
+#endif
+ break;
+ }
+ lsock = next;
+ }
+}
+
+static void
+add_addr_to_list(struct sockaddr_storage *addr, struct interface *interface){
+#ifdef DEBUG
+ if (find_addr_in_list(addr) == NULL) {
+#endif
+ /* not there yet - add to list */
+ remaddr_t *laddr = (remaddr_t *)emalloc(sizeof(remaddr_t));
+ memcpy(&laddr->addr, addr, sizeof(struct sockaddr_storage));
+ laddr->interface = interface;
+
+ ISC_LIST_APPEND(remoteaddr_list, laddr, link);
+
+ DPRINTF(4, ("Added addr %s to list of addresses\n",
+ stoa(addr)));
+#ifdef DEBUG
+ } else {
+ DPRINTF(4, ("WARNING: Attempt to add duplicate addr %s to address list\n",
+ stoa(addr)));
+ }
+#endif
+}
+
+static void
+delete_addr_from_list(struct sockaddr_storage *addr) {
+
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if(SOCKCMP(&laddr->addr, addr)) {
+ ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t);
+ DPRINTF(4, ("Deleted addr %s from list of addresses\n",
+ stoa(addr)));
+ free(laddr);
+ break;
+ }
+ laddr = next;
+ }
+}
+
+static void
+delete_interface_from_list(struct interface *iface) {
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if (laddr->interface == iface) {
+ ISC_LIST_DEQUEUE_TYPE(remoteaddr_list, laddr, link, remaddr_t);
+ DPRINTF(4, ("Deleted addr %s for interface #%d %s from list of addresses\n",
+ stoa(&laddr->addr), iface->ifnum, iface->name));
+ free(laddr);
+ }
+ laddr = next;
+ }
+}
+
+static struct interface *
+find_addr_in_list(struct sockaddr_storage *addr) {
+
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+ DPRINTF(4, ("Searching for addr %s in list of addresses - ",
+ stoa(addr)));
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if(SOCKCMP(&laddr->addr, addr)) {
+ DPRINTF(4, ("FOUND\n"));
+ return laddr->interface;
+ }
+ else
+ laddr = next;
+ }
+ DPRINTF(4, ("NOT FOUND\n"));
+ return NULL; /* Not found */
+}
+
+/*
+ * Find the given address with the associated flag in the list
+ */
+static struct interface *
+find_flagged_addr_in_list(struct sockaddr_storage *addr, int flag) {
+
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+ DPRINTF(4, ("Finding addr %s in list of addresses\n",
+ stoa(addr)));
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if(SOCKCMP(&laddr->addr, addr) && (laddr->interface->flags & flag)) {
+ return laddr->interface;
+ break;
+ }
+ else
+ laddr = next;
+ }
+ return NULL; /* Not found */
+}
+
+#ifdef HAS_ROUTING_SOCKET
+#include <net/route.h>
+
+#ifndef UPDATE_GRACE
+#define UPDATE_GRACE 2 /* wait UPDATE_GRACE seconds before scanning */
+#endif
+
+static void
+process_routing_msgs(struct asyncio_reader *reader)
+{
+ char buffer[5120];
+ char *p = buffer;
+
+ int cnt;
+
+ if (disable_dynamic_updates) {
+ /*
+ * discard ourselves if we are not need any more
+ * usually happens when running unprivileged
+ */
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+
+ cnt = read(reader->fd, buffer, sizeof(buffer));
+
+ if (cnt < 0) {
+ msyslog(LOG_ERR, "i/o error on routing socket %m - disabling");
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+
+ /*
+ * process routing message
+ */
+ while ((p + sizeof(struct rt_msghdr)) <= (buffer + cnt))
+ {
+ struct rt_msghdr *rtm;
+
+ rtm = (struct rt_msghdr *)p;
+ if (rtm->rtm_version != RTM_VERSION) {
+ msyslog(LOG_ERR, "version mismatch on routing socket %m - disabling");
+ remove_asyncio_reader(reader);
+ delete_asyncio_reader(reader);
+ return;
+ }
+
+ switch (rtm->rtm_type) {
+#ifdef RTM_NEWADDR
+ case RTM_NEWADDR:
+#endif
+#ifdef RTM_DELADDR
+ case RTM_DELADDR:
+#endif
+#ifdef RTM_ADD
+ case RTM_ADD:
+#endif
+#ifdef RTM_DELETE
+ case RTM_DELETE:
+#endif
+#ifdef RTM_REDIRECT
+ case RTM_REDIRECT:
+#endif
+#ifdef RTM_CHANGE
+ case RTM_CHANGE:
+#endif
+#ifdef RTM_LOSING
+ case RTM_LOSING:
+#endif
+#ifdef RTM_IFINFO
+ case RTM_IFINFO:
+#endif
+#ifdef RTM_IFANNOUNCE
+ case RTM_IFANNOUNCE:
+#endif
+ /*
+ * we are keen on new and deleted addresses and if an interface goes up and down or routing changes
+ */
+ DPRINTF(3, ("routing message op = %d: scheduling interface update\n", rtm->rtm_type));
+ timer_interfacetimeout(current_time + UPDATE_GRACE);
+ break;
+ default:
+ /*
+ * the rest doesn't bother us.
+ */
+ DPRINTF(4, ("routing message op = %d: ignored\n", rtm->rtm_type));
+ break;
+ }
+ p += rtm->rtm_msglen;
+ }
+}
+
+/*
+ * set up routing notifications
+ */
+static void
+init_async_notifications()
+{
+ struct asyncio_reader *reader;
+ int fd = socket(PF_ROUTE, SOCK_RAW, 0);
+
+ if (fd >= 0) {
+ fd = move_fd(fd);
+ init_nonblocking_io(fd);
+#if defined(HAVE_SIGNALED_IO)
+ init_socket_sig(fd);
+#endif /* HAVE_SIGNALED_IO */
+
+ reader = new_asyncio_reader();
+
+ reader->fd = fd;
+ reader->receiver = process_routing_msgs;
+
+ add_asyncio_reader(reader, FD_TYPE_SOCKET);
+ msyslog(LOG_INFO, "Listening on routing socket on fd #%d for interface updates", fd);
+ } else {
+ msyslog(LOG_ERR, "unable to open routing socket (%m) - using polled interface update");
+ }
+}
+#else
+static void
+init_async_notifications()
+{
+}
+#endif
diff --git a/contrib/ntp/ntpd/ntp_loopfilter.c b/contrib/ntp/ntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..d0fa466c
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_loopfilter.c
@@ -0,0 +1,1058 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <signal.h>
+#include <setjmp.h>
+
+#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/
+#include "ntp_refclock.h"
+#endif /* VMS */
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * This is an implementation of the clock discipline algorithm described
+ * in UDel TR 97-4-3, as amended. It operates as an adaptive parameter,
+ * hybrid phase/frequency-lock loop. A number of sanity checks are
+ * included to protect against timewarps, timespikes and general mayhem.
+ * All units are in s and s/s, unless noted otherwise.
+ */
+#define CLOCK_MAX .128 /* default step threshold (s) */
+#define CLOCK_MINSTEP 900. /* default stepout threshold (s) */
+#define CLOCK_PANIC 1000. /* default panic threshold (s) */
+#define CLOCK_PHI 15e-6 /* max frequency error (s/s) */
+#define CLOCK_PLL 16. /* PLL loop gain (log2) */
+#define CLOCK_AVG 8. /* parameter averaging constant */
+#define CLOCK_FLL (NTP_MAXPOLL + CLOCK_AVG) /* FLL loop gain */
+#define CLOCK_ALLAN 1500. /* compromise Allan intercept (s) */
+#define CLOCK_DAY 86400. /* one day in seconds (s) */
+#define CLOCK_JUNE (CLOCK_DAY * 30) /* June in seconds (s) */
+#define CLOCK_LIMIT 30 /* poll-adjust threshold */
+#define CLOCK_PGATE 4. /* poll-adjust gate */
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+
+/*
+ * Clock discipline state machine. This is used to control the
+ * synchronization behavior during initialization and following a
+ * timewarp.
+ *
+ * State < step > step Comments
+ * ====================================================
+ * NSET FREQ step, FREQ no ntp.drift
+ *
+ * FSET SYNC step, SYNC ntp.drift
+ *
+ * FREQ if (mu < 900) if (mu < 900) set freq
+ * ignore ignore
+ * else else
+ * freq, SYNC freq, step, SYNC
+ *
+ * SYNC SYNC if (mu < 900) adjust phase/freq
+ * ignore
+ * else
+ * SPIK
+ *
+ * SPIK SYNC step, SYNC set phase
+ */
+#define S_NSET 0 /* clock never set */
+#define S_FSET 1 /* frequency set from the drift file */
+#define S_SPIK 2 /* spike detected */
+#define S_FREQ 3 /* frequency mode */
+#define S_SYNC 4 /* clock synchronized */
+
+/*
+ * Kernel PLL/PPS state machine. This is used with the kernel PLL
+ * modifications described in the README.kernel file.
+ *
+ * If kernel support for the ntp_adjtime() system call is available, the
+ * ntp_control flag is set. The ntp_enable and kern_enable flags can be
+ * set at configuration time or run time using ntpdc. If ntp_enable is
+ * false, the discipline loop is unlocked and no corrections of any kind
+ * are made. If both ntp_control and kern_enable are set, the kernel
+ * support is used as described above; if false, the kernel is bypassed
+ * entirely and the daemon discipline used instead.
+ *
+ * There have been three versions of the kernel discipline code. The
+ * first (microkernel) now in Solaris discipilnes the microseconds. The
+ * second and third (nanokernel) disciplines the clock in nanoseconds.
+ * These versions are identifed if the symbol STA_PLL is present in the
+ * header file /usr/include/sys/timex.h. The third and current version
+ * includes TAI offset and is identified by the symbol NTP_API with
+ * value 4.
+ *
+ * Each update to a prefer peer sets pps_stratum if it survives the
+ * intersection algorithm and its time is within range. The PPS time
+ * discipline is enabled (STA_PPSTIME bit set in the status word) when
+ * pps_stratum is true and the PPS frequency discipline is enabled. If
+ * the PPS time discipline is enabled and the kernel reports a PPS
+ * signal is present, the pps_control variable is set to the current
+ * time. If the current time is later than pps_control by PPS_MAXAGE
+ * (120 s), this variable is set to zero.
+ *
+ * If an external clock is present, the clock driver sets STA_CLK in the
+ * status word. When the local clock driver sees this bit, it updates
+ * via this routine, which then calls ntp_adjtime() with the STA_PLL bit
+ * set to zero, in which case the system clock is not adjusted. This is
+ * also a signal for the external clock driver to discipline the system
+ * clock.
+ */
+/*
+ * Program variables that can be tinkered.
+ */
+double clock_max = CLOCK_MAX; /* step threshold (s) */
+double clock_minstep = CLOCK_MINSTEP; /* stepout threshold (s) */
+double clock_panic = CLOCK_PANIC; /* panic threshold (s) */
+double clock_phi = CLOCK_PHI; /* dispersion rate (s/s) */
+double allan_xpt = CLOCK_ALLAN; /* Allan intercept (s) */
+
+/*
+ * Program variables
+ */
+static double clock_offset; /* offset (s) */
+double clock_jitter; /* offset jitter (s) */
+double drift_comp; /* frequency (s/s) */
+double clock_stability; /* frequency stability (wander) (s/s) */
+u_long sys_clocktime; /* last system clock update */
+u_long pps_control; /* last pps update */
+u_long sys_tai; /* UTC offset from TAI (s) */
+static void rstclock P((int, u_long, double)); /* transition function */
+
+#ifdef KERNEL_PLL
+struct timex ntv; /* kernel API parameters */
+int pll_status; /* status bits for kernel pll */
+#endif /* KERNEL_PLL */
+
+/*
+ * Clock state machine control flags
+ */
+int ntp_enable; /* clock discipline enabled */
+int pll_control; /* kernel support available */
+int kern_enable; /* kernel support enabled */
+int pps_enable; /* kernel PPS discipline enabled */
+int ext_enable; /* external clock enabled */
+int pps_stratum; /* pps stratum */
+int allow_panic = FALSE; /* allow panic correction */
+int mode_ntpdate = FALSE; /* exit on first clock set */
+
+/*
+ * Clock state machine variables
+ */
+int state; /* clock discipline state */
+u_char sys_poll = NTP_MINDPOLL; /* time constant/poll (log2 s) */
+int tc_counter; /* jiggle counter */
+double last_offset; /* last offset (s) */
+
+/*
+ * Huff-n'-puff filter variables
+ */
+static double *sys_huffpuff; /* huff-n'-puff filter */
+static int sys_hufflen; /* huff-n'-puff filter stages */
+static int sys_huffptr; /* huff-n'-puff filter pointer */
+static double sys_mindly; /* huff-n'-puff filter min delay */
+
+#if defined(KERNEL_PLL)
+/* Emacs cc-mode goes nuts if we split the next line... */
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
+ MOD_STATUS | MOD_TIMECONST)
+#ifdef SIGSYS
+static void pll_trap P((int)); /* configuration trap */
+static struct sigaction sigsys; /* current sigaction status */
+static struct sigaction newsigsys; /* new sigaction status */
+static sigjmp_buf env; /* environment var. for pll_trap() */
+#endif /* SIGSYS */
+#endif /* KERNEL_PLL */
+
+/*
+ * init_loopfilter - initialize loop filter data
+ */
+void
+init_loopfilter(void)
+{
+ /*
+ * Initialize state variables. Initially, we expect no drift
+ * file, so set the state to S_NSET. If a drift file is present,
+ * it will be detected later and the state set to S_FSET.
+ */
+ rstclock(S_NSET, 0, 0);
+ clock_jitter = LOGTOD(sys_precision);
+}
+
+/*
+ * local_clock - the NTP logical clock loop filter.
+ *
+ * Return codes:
+ * -1 update ignored: exceeds panic threshold
+ * 0 update ignored: popcorn or exceeds step threshold
+ * 1 clock was slewed
+ * 2 clock was stepped
+ *
+ * LOCKCLOCK: The only thing this routine does is set the
+ * sys_rootdispersion variable equal to the peer dispersion.
+ */
+int
+local_clock(
+ struct peer *peer, /* synch source peer structure */
+ double fp_offset /* clock offset (s) */
+ )
+{
+ int rval; /* return code */
+ u_long mu; /* interval since last update (s) */
+ double flladj; /* FLL frequency adjustment (ppm) */
+ double plladj; /* PLL frequency adjustment (ppm) */
+ double clock_frequency; /* clock frequency adjustment (ppm) */
+ double dtemp, etemp; /* double temps */
+#ifdef OPENSSL
+ u_int32 *tpt;
+ int i;
+ u_int len;
+ long togo;
+#endif /* OPENSSL */
+
+ /*
+ * If the loop is opened or the NIST LOCKCLOCK is in use,
+ * monitor and record the offsets anyway in order to determine
+ * the open-loop response and then go home.
+ */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: assocID %d offset %.9f freq %.3f state %d\n",
+ peer->associd, fp_offset, drift_comp * 1e6, state);
+#endif
+#ifdef LOCKCLOCK
+ return (0);
+
+#else /* LOCKCLOCK */
+ if (!ntp_enable) {
+ record_loop_stats(fp_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+ return (0);
+ }
+
+ /*
+ * If the clock is way off, panic is declared. The clock_panic
+ * defaults to 1000 s; if set to zero, the panic will never
+ * occur. The allow_panic defaults to FALSE, so the first panic
+ * will exit. It can be set TRUE by a command line option, in
+ * which case the clock will be set anyway and time marches on.
+ * But, allow_panic will be set FALSE when the update is less
+ * than the step threshold; so, subsequent panics will exit.
+ */
+ if (fabs(fp_offset) > clock_panic && clock_panic > 0 &&
+ !allow_panic) {
+ msyslog(LOG_ERR,
+ "time correction of %.0f seconds exceeds sanity limit (%.0f); set clock manually to the correct UTC time.",
+ fp_offset, clock_panic);
+ return (-1);
+ }
+
+ /*
+ * If simulating ntpdate, set the clock directly, rather than
+ * using the discipline. The clock_max defines the step
+ * threshold, above which the clock will be stepped instead of
+ * slewed. The value defaults to 128 ms, but can be set to even
+ * unreasonable values. If set to zero, the clock will never be
+ * stepped. Note that a slew will persist beyond the life of
+ * this program.
+ *
+ * Note that if ntpdate is active, the terminal does not detach,
+ * so the termination comments print directly to the console.
+ */
+ if (mode_ntpdate) {
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time reset %+.6f s",
+ fp_offset);
+ printf("ntpd: time set %+.6fs\n", fp_offset);
+ } else {
+ adj_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time slew %+.6f s",
+ fp_offset);
+ printf("ntpd: time slew %+.6fs\n", fp_offset);
+ }
+ record_loop_stats(fp_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+ exit (0);
+ }
+
+ /*
+ * The huff-n'-puff filter finds the lowest delay in the recent
+ * interval. This is used to correct the offset by one-half the
+ * difference between the sample delay and minimum delay. This
+ * is most effective if the delays are highly assymetric and
+ * clockhopping is avoided and the clock frequency wander is
+ * relatively small.
+ *
+ * Note either there is no prefer peer or this update is from
+ * the prefer peer.
+ */
+ if (sys_huffpuff != NULL && (sys_prefer == NULL || sys_prefer ==
+ peer)) {
+ if (peer->delay < sys_huffpuff[sys_huffptr])
+ sys_huffpuff[sys_huffptr] = peer->delay;
+ if (peer->delay < sys_mindly)
+ sys_mindly = peer->delay;
+ if (fp_offset > 0)
+ dtemp = -(peer->delay - sys_mindly) / 2;
+ else
+ dtemp = (peer->delay - sys_mindly) / 2;
+ fp_offset += dtemp;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: size %d mindly %.6f huffpuff %.6f\n",
+ sys_hufflen, sys_mindly, dtemp);
+#endif
+ }
+
+ /*
+ * Clock state machine transition function. This is where the
+ * action is and defines how the system reacts to large phase
+ * and frequency errors. There are two main regimes: when the
+ * offset exceeds the step threshold and when it does not.
+ * However, if the step threshold is set to zero, a step will
+ * never occur. See the instruction manual for the details how
+ * these actions interact with the command line options.
+ *
+ * Note the system poll is set to minpoll only if the clock is
+ * stepped. Note also the kernel is disabled if step is
+ * disabled or greater than 0.5 s.
+ */
+ clock_frequency = flladj = plladj = 0;
+ mu = peer->epoch - sys_clocktime;
+ if (clock_max == 0 || clock_max > 0.5)
+ kern_enable = 0;
+ rval = 1;
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ switch (state) {
+
+ /*
+ * In S_SYNC state we ignore the first outlyer amd
+ * switch to S_SPIK state.
+ */
+ case S_SYNC:
+ state = S_SPIK;
+ return (0);
+
+ /*
+ * In S_FREQ state we ignore outlyers and inlyers. At
+ * the first outlyer after the stepout threshold,
+ * compute the apparent frequency correction and step
+ * the phase.
+ */
+ case S_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+
+ clock_frequency = (fp_offset - clock_offset) /
+ mu;
+
+ /* fall through to S_SPIK */
+
+ /*
+ * In S_SPIK state we ignore succeeding outlyers until
+ * either an inlyer is found or the stepout threshold is
+ * exceeded.
+ */
+ case S_SPIK:
+ if (mu < clock_minstep)
+ return (0);
+
+ /* fall through to default */
+
+ /*
+ * We get here by default in S_NSET and S_FSET states
+ * and from above in S_FREQ or S_SPIK states.
+ *
+ * In S_NSET state an initial frequency correction is
+ * not available, usually because the frequency file has
+ * not yet been written. Since the time is outside the
+ * step threshold, the clock is stepped. The frequency
+ * will be set directly following the stepout interval.
+ *
+ * In S_FSET state the initial frequency has been set
+ * from the frequency file. Since the time is outside
+ * the step threshold, the clock is stepped immediately,
+ * rather than after the stepout interval. Guys get
+ * nervous if it takes 17 minutes to set the clock for
+ * the first time.
+ *
+ * In S_FREQ and S_SPIK states the stepout threshold has
+ * expired and the phase is still above the step
+ * threshold. Note that a single spike greater than the
+ * step threshold is always suppressed, even at the
+ * longer poll intervals.
+ */
+ default:
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time reset %+.6f s",
+ fp_offset);
+ reinit_timer();
+ tc_counter = 0;
+ sys_poll = NTP_MINPOLL;
+ sys_tai = 0;
+ clock_jitter = LOGTOD(sys_precision);
+ rval = 2;
+ if (state == S_NSET) {
+ rstclock(S_FREQ, peer->epoch, 0);
+ return (rval);
+ }
+ break;
+ }
+ rstclock(S_SYNC, peer->epoch, 0);
+ } else {
+
+ /*
+ * The offset is less than the step threshold. Calculate
+ * the jitter as the exponentially weighted offset
+ * differences.
+ */
+ etemp = SQUARE(clock_jitter);
+ dtemp = SQUARE(max(fabs(fp_offset - last_offset),
+ LOGTOD(sys_precision)));
+ clock_jitter = SQRT(etemp + (dtemp - etemp) /
+ CLOCK_AVG);
+ switch (state) {
+
+ /*
+ * In S_NSET state this is the first update received and
+ * the frequency has not been initialized. Adjust the
+ * phase, but do not adjust the frequency until after
+ * the stepout threshold.
+ */
+ case S_NSET:
+ rstclock(S_FREQ, peer->epoch, fp_offset);
+ break;
+
+ /*
+ * In S_FSET state this is the first update received and
+ * the frequency has been initialized. Adjust the phase,
+ * but do not adjust the frequency until the next
+ * update.
+ */
+ case S_FSET:
+ rstclock(S_SYNC, peer->epoch, fp_offset);
+ break;
+
+ /*
+ * In S_FREQ state ignore updates until the stepout
+ * threshold. After that, correct the phase and
+ * frequency and switch to S_SYNC state.
+ */
+ case S_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+
+ clock_frequency = (fp_offset - clock_offset) /
+ mu;
+ rstclock(S_SYNC, peer->epoch, fp_offset);
+ break;
+
+ /*
+ * We get here by default in S_SYNC and S_SPIK states.
+ * Here we compute the frequency update due to PLL and
+ * FLL contributions.
+ */
+ default:
+ allow_panic = FALSE;
+
+ /*
+ * The FLL and PLL frequency gain constants
+ * depend on the poll interval and Allan
+ * intercept. The PLL is always used, but
+ * becomes ineffective above the Allan
+ * intercept. The FLL is not used below one-half
+ * the Allan intercept. Above that the loop gain
+ * increases in steps to 1 / CLOCK_AVG.
+ */
+ if (ULOGTOD(sys_poll) > allan_xpt / 2) {
+ dtemp = CLOCK_FLL - sys_poll;
+ flladj = (fp_offset - clock_offset) /
+ (max(mu, allan_xpt) * dtemp);
+ }
+
+ /*
+ * For the PLL the integration interval
+ * (numerator) is the minimum of the update
+ * interval and poll interval. This allows
+ * oversampling, but not undersampling.
+ */
+ etemp = min(mu, (u_long)ULOGTOD(sys_poll));
+ dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
+ plladj = fp_offset * etemp / (dtemp * dtemp);
+ rstclock(S_SYNC, peer->epoch, fp_offset);
+ break;
+ }
+ }
+
+#ifdef OPENSSL
+ /*
+ * Scan the loopsecond table to determine the TAI offset. If
+ * there is a scheduled leap in future, set the leap warning,
+ * but only if less than 30 days before the leap.
+ */
+ tpt = (u_int32 *)tai_leap.ptr;
+ len = ntohl(tai_leap.vallen) / sizeof(u_int32);
+ if (tpt != NULL) {
+ for (i = 0; i < len; i++) {
+ togo = (long)ntohl(tpt[i]) -
+ (long)peer->rec.l_ui;
+ if (togo > 0) {
+ if (togo < CLOCK_JUNE)
+ leap_next |= LEAP_ADDSECOND;
+ break;
+ }
+ }
+#if defined(STA_NANO) && NTP_API == 4
+ if (pll_control && kern_enable && sys_tai == 0) {
+ memset(&ntv, 0, sizeof(ntv));
+ ntv.modes = MOD_TAI;
+ ntv.constant = i + TAI_1972 - 1;
+ ntp_adjtime(&ntv);
+ }
+#endif /* STA_NANO */
+ sys_tai = i + TAI_1972 - 1;
+ }
+#endif /* OPENSSL */
+#ifdef KERNEL_PLL
+ /*
+ * This code segment works when clock adjustments are made using
+ * precision time kernel support and the ntp_adjtime() system
+ * call. This support is available in Solaris 2.6 and later,
+ * Digital Unix 4.0 and later, FreeBSD, Linux and specially
+ * modified kernels for HP-UX 9 and Ultrix 4. In the case of the
+ * DECstation 5000/240 and Alpha AXP, additional kernel
+ * modifications provide a true microsecond clock and nanosecond
+ * clock, respectively.
+ *
+ * Important note: The kernel discipline is used only if the
+ * step threshold is less than 0.5 s, as anything higher can
+ * lead to overflow problems. This might occur if some misguided
+ * lad set the step threshold to something ridiculous.
+ */
+ if (pll_control && kern_enable) {
+
+ /*
+ * We initialize the structure for the ntp_adjtime()
+ * system call. We have to convert everything to
+ * microseconds or nanoseconds first. Do not update the
+ * system variables if the ext_enable flag is set. In
+ * this case, the external clock driver will update the
+ * variables, which will be read later by the local
+ * clock driver. Afterwards, remember the time and
+ * frequency offsets for jitter and stability values and
+ * to update the frequency file.
+ */
+ memset(&ntv, 0, sizeof(ntv));
+ if (ext_enable) {
+ ntv.modes = MOD_STATUS;
+ } else {
+ struct tm *tm = NULL;
+ time_t tstamp;
+
+#ifdef STA_NANO
+ ntv.modes = MOD_BITS | MOD_NANO;
+#else /* STA_NANO */
+ ntv.modes = MOD_BITS;
+#endif /* STA_NANO */
+ if (clock_offset < 0)
+ dtemp = -.5;
+ else
+ dtemp = .5;
+#ifdef STA_NANO
+ ntv.offset = (int32)(clock_offset * 1e9 +
+ dtemp);
+ ntv.constant = sys_poll;
+#else /* STA_NANO */
+ ntv.offset = (int32)(clock_offset * 1e6 +
+ dtemp);
+ ntv.constant = sys_poll - 4;
+#endif /* STA_NANO */
+
+ /*
+ * The frequency is set directly only if
+ * clock_frequency is nonzero coming out of FREQ
+ * state.
+ */
+ if (clock_frequency != 0) {
+ ntv.modes |= MOD_FREQUENCY;
+ ntv.freq = (int32)((clock_frequency +
+ drift_comp) * 65536e6);
+ }
+ ntv.esterror = (u_int32)(clock_jitter * 1e6);
+ ntv.maxerror = (u_int32)((sys_rootdelay / 2 +
+ sys_rootdispersion) * 1e6);
+ ntv.status = STA_PLL;
+
+ /*
+ * Set the leap bits in the status word, but
+ * only on the last day of June or December.
+ */
+ tstamp = peer->rec.l_ui - JAN_1970;
+ tm = gmtime(&tstamp);
+ if (tm != NULL) {
+ if ((tm->tm_mon + 1 == 6 &&
+ tm->tm_mday == 30) || (tm->tm_mon +
+ 1 == 12 && tm->tm_mday == 31)) {
+ if (leap_next & LEAP_ADDSECOND)
+ ntv.status |= STA_INS;
+ else if (leap_next &
+ LEAP_DELSECOND)
+ ntv.status |= STA_DEL;
+ }
+ }
+
+ /*
+ * If the PPS signal is up and enabled, light
+ * the frequency bit. If the PPS driver is
+ * working, light the phase bit as well. If not,
+ * douse the lights, since somebody else may
+ * have left the switch on.
+ */
+ if (pps_enable && pll_status & STA_PPSSIGNAL) {
+ ntv.status |= STA_PPSFREQ;
+ if (pps_stratum < STRATUM_UNSPEC)
+ ntv.status |= STA_PPSTIME;
+ } else {
+ ntv.status &= ~(STA_PPSFREQ |
+ STA_PPSTIME);
+ }
+ }
+
+ /*
+ * Pass the stuff to the kernel. If it squeals, turn off
+ * the pig. In any case, fetch the kernel offset and
+ * frequency and pretend we did it here.
+ */
+ if (ntp_adjtime(&ntv) == TIME_ERROR) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "kernel time sync error %04x", ntv.status);
+ ntv.status &= ~(STA_PPSFREQ | STA_PPSTIME);
+ } else {
+ if ((ntv.status ^ pll_status) & ~STA_FLL)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "kernel time sync status change %04x",
+ ntv.status);
+ }
+ pll_status = ntv.status;
+#ifdef STA_NANO
+ clock_offset = ntv.offset / 1e9;
+#else /* STA_NANO */
+ clock_offset = ntv.offset / 1e6;
+#endif /* STA_NANO */
+ clock_frequency = ntv.freq / 65536e6;
+ flladj = plladj = 0;
+
+ /*
+ * If the kernel PPS is lit, monitor its performance.
+ */
+ if (ntv.status & STA_PPSTIME) {
+ pps_control = current_time;
+#ifdef STA_NANO
+ clock_jitter = ntv.jitter / 1e9;
+#else /* STA_NANO */
+ clock_jitter = ntv.jitter / 1e6;
+#endif /* STA_NANO */
+ }
+ } else {
+#endif /* KERNEL_PLL */
+
+ /*
+ * We get here if the kernel discipline is not enabled.
+ * Adjust the clock frequency as the sum of the directly
+ * computed frequency (if measured) and the PLL and FLL
+ * increments.
+ */
+ clock_frequency = drift_comp + clock_frequency +
+ flladj + plladj;
+#ifdef KERNEL_PLL
+ }
+#endif /* KERNEL_PLL */
+
+ /*
+ * Clamp the frequency within the tolerance range and calculate
+ * the frequency change since the last update.
+ */
+ if (fabs(clock_frequency) > NTP_MAXFREQ)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "frequency error %.0f PPM exceeds tolerance %.0f PPM",
+ clock_frequency * 1e6, NTP_MAXFREQ * 1e6);
+ dtemp = SQUARE(clock_frequency - drift_comp);
+ if (clock_frequency > NTP_MAXFREQ)
+ drift_comp = NTP_MAXFREQ;
+ else if (clock_frequency < -NTP_MAXFREQ)
+ drift_comp = -NTP_MAXFREQ;
+ else
+ drift_comp = clock_frequency;
+
+ /*
+ * Calculate the wander as the exponentially weighted frequency
+ * differences.
+ */
+ etemp = SQUARE(clock_stability);
+ clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG);
+
+ /*
+ * Here we adjust the poll interval by comparing the current
+ * offset with the clock jitter. If the offset is less than the
+ * clock jitter times a constant, then the averaging interval is
+ * increased, otherwise it is decreased. A bit of hysteresis
+ * helps calm the dance. Works best using burst mode.
+ */
+ if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) {
+ tc_counter += sys_poll;
+ if (tc_counter > CLOCK_LIMIT) {
+ tc_counter = CLOCK_LIMIT;
+ if (sys_poll < peer->maxpoll) {
+ tc_counter = 0;
+ sys_poll++;
+ }
+ }
+ } else {
+ tc_counter -= sys_poll << 1;
+ if (tc_counter < -CLOCK_LIMIT) {
+ tc_counter = -CLOCK_LIMIT;
+ if (sys_poll > peer->minpoll) {
+ tc_counter = 0;
+ sys_poll--;
+ }
+ }
+ }
+
+ /*
+ * Yibbidy, yibbbidy, yibbidy; that'h all folks.
+ */
+ record_loop_stats(clock_offset, drift_comp, clock_jitter,
+ clock_stability, sys_poll);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: mu %lu jitr %.6f freq %.3f stab %.6f poll %d count %d\n",
+ mu, clock_jitter, drift_comp * 1e6,
+ clock_stability * 1e6, sys_poll, tc_counter);
+#endif /* DEBUG */
+ return (rval);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * adj_host_clock - Called once every second to update the local clock.
+ *
+ * LOCKCLOCK: The only thing this routine does is increment the
+ * sys_rootdispersion variable.
+ */
+void
+adj_host_clock(
+ void
+ )
+{
+ double adjustment;
+
+ /*
+ * Update the dispersion since the last update. In contrast to
+ * NTPv3, NTPv4 does not declare unsynchronized after one day,
+ * since the dispersion check serves this function. Also,
+ * since the poll interval can exceed one day, the old test
+ * would be counterproductive. Note we do this even with
+ * external clocks, since the clock driver will recompute the
+ * maximum error and the local clock driver will pick it up and
+ * pass to the common refclock routines. Very elegant.
+ */
+ sys_rootdispersion += clock_phi;
+
+#ifndef LOCKCLOCK
+ /*
+ * If clock discipline is disabled or if the kernel is enabled,
+ * get out of Dodge quick.
+ */
+ if (!ntp_enable || mode_ntpdate || (pll_control &&
+ kern_enable))
+ return;
+
+ /*
+ * Declare PPS kernel unsync if the pps signal has not been
+ * heard for a few minutes.
+ */
+ if (pps_control && current_time - pps_control > PPS_MAXAGE) {
+ if (pps_control)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE, "pps sync disabled");
+ pps_control = 0;
+ }
+
+ /*
+ * Implement the phase and frequency adjustments. The gain
+ * factor (denominator) is not allowed to increase beyond the
+ * Allan intercept. It doesn't make sense to average phase noise
+ * beyond this point and it helps to damp residual offset at the
+ * longer poll intervals.
+ */
+ adjustment = clock_offset / (CLOCK_PLL * min(ULOGTOD(sys_poll),
+ allan_xpt));
+ clock_offset -= adjustment;
+ adj_systime(adjustment + drift_comp);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * Clock state machine. Enter new state and set state variables. Note we
+ * use the time of the last clock filter sample, which may be earlier
+ * than the current time.
+ */
+static void
+rstclock(
+ int trans, /* new state */
+ u_long update, /* new update time */
+ double offset /* new offset */
+ )
+{
+#ifdef DEBUG
+ if (debug)
+ printf("local_clock: time %lu offset %.6f freq %.3f state %d\n",
+ update, offset, drift_comp * 1e6, trans);
+#endif
+ state = trans;
+ sys_clocktime = update;
+ last_offset = clock_offset = offset;
+}
+
+
+/*
+ * huff-n'-puff filter
+ */
+void
+huffpuff()
+{
+ int i;
+
+ if (sys_huffpuff == NULL)
+ return;
+
+ sys_huffptr = (sys_huffptr + 1) % sys_hufflen;
+ sys_huffpuff[sys_huffptr] = 1e9;
+ sys_mindly = 1e9;
+ for (i = 0; i < sys_hufflen; i++) {
+ if (sys_huffpuff[i] < sys_mindly)
+ sys_mindly = sys_huffpuff[i];
+ }
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ *
+ * LOCKCLOCK: The LOOP_DRIFTINIT and LOOP_DRIFTCOMP cases are no-ops.
+ */
+void
+loop_config(
+ int item,
+ double freq
+ )
+{
+ int i;
+
+ switch (item) {
+
+ case LOOP_DRIFTINIT:
+
+#ifndef LOCKCLOCK
+#ifdef KERNEL_PLL
+ /*
+ * Assume the kernel supports the ntp_adjtime() syscall.
+ * If that syscall works, initialize the kernel time
+ * variables. Otherwise, continue leaving no harm
+ * behind. While at it, ask to set nanosecond mode. If
+ * the kernel agrees, rejoice; othewise, it does only
+ * microseconds.
+ */
+ if (mode_ntpdate)
+ break;
+
+ pll_control = 1;
+ memset(&ntv, 0, sizeof(ntv));
+#ifdef STA_NANO
+ ntv.modes = MOD_BITS | MOD_NANO;
+#else /* STA_NANO */
+ ntv.modes = MOD_BITS;
+#endif /* STA_NANO */
+ ntv.maxerror = MAXDISPERSE;
+ ntv.esterror = MAXDISPERSE;
+ ntv.status = STA_UNSYNC;
+#ifdef SIGSYS
+ /*
+ * Use sigsetjmp() to save state and then call
+ * ntp_adjtime(); if it fails, then siglongjmp() is used
+ * to return control
+ */
+ newsigsys.sa_handler = pll_trap;
+ newsigsys.sa_flags = 0;
+ if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
+ msyslog(LOG_ERR,
+ "sigaction() fails to save SIGSYS trap: %m");
+ pll_control = 0;
+ }
+ if (sigsetjmp(env, 1) == 0)
+ ntp_adjtime(&ntv);
+ if ((sigaction(SIGSYS, &sigsys,
+ (struct sigaction *)NULL))) {
+ msyslog(LOG_ERR,
+ "sigaction() fails to restore SIGSYS trap: %m");
+ pll_control = 0;
+ }
+#else /* SIGSYS */
+ ntp_adjtime(&ntv);
+#endif /* SIGSYS */
+
+ /*
+ * Save the result status and light up an external clock
+ * if available.
+ */
+ pll_status = ntv.status;
+ if (pll_control) {
+#ifdef STA_NANO
+ if (pll_status & STA_CLK)
+ ext_enable = 1;
+#endif /* STA_NANO */
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_INFO,
+ "kernel time sync status %04x",
+ pll_status);
+ }
+#endif /* KERNEL_PLL */
+#endif /* LOCKCLOCK */
+ break;
+
+ case LOOP_DRIFTCOMP:
+
+#ifndef LOCKCLOCK
+ /*
+ * If the frequency value is reasonable, set the initial
+ * frequency to the given value and the state to S_FSET.
+ * Otherwise, the drift file may be missing or broken,
+ * so set the frequency to zero. This erases past
+ * history should somebody break something.
+ */
+ if (freq <= NTP_MAXFREQ && freq >= -NTP_MAXFREQ) {
+ drift_comp = freq;
+ rstclock(S_FSET, 0, 0);
+ } else {
+ drift_comp = 0;
+ }
+
+#ifdef KERNEL_PLL
+ /*
+ * Sanity check. If the kernel is available, load the
+ * frequency and light up the loop. Make sure the offset
+ * is zero to cancel any previous nonsense. If you don't
+ * want this initialization, remove the ntp.drift file.
+ */
+ if (pll_control && kern_enable) {
+ memset((char *)&ntv, 0, sizeof(ntv));
+ ntv.modes = MOD_OFFSET | MOD_FREQUENCY;
+ ntv.freq = (int32)(drift_comp * 65536e6);
+ ntp_adjtime(&ntv);
+ }
+#endif /* KERNEL_PLL */
+#endif /* LOCKCLOCK */
+ break;
+
+ case LOOP_KERN_CLEAR:
+#ifndef LOCKCLOCK
+#ifdef KERNEL_PLL
+ /* Completely turn off the kernel time adjustments. */
+ if (pll_control) {
+ memset((char *)&ntv, 0, sizeof(ntv));
+ ntv.modes = MOD_BITS | MOD_OFFSET | MOD_FREQUENCY;
+ ntv.status = STA_UNSYNC;
+ ntp_adjtime(&ntv);
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_INFO,
+ "kernel time sync disabled %04x",
+ ntv.status);
+ }
+#endif /* KERNEL_PLL */
+#endif /* LOCKCLOCK */
+ break;
+
+ /*
+ * Special tinker variables for Ulrich Windl. Very dangerous.
+ */
+ case LOOP_MAX: /* step threshold */
+ clock_max = freq;
+ break;
+
+ case LOOP_PANIC: /* panic threshold */
+ clock_panic = freq;
+ break;
+
+ case LOOP_PHI: /* dispersion rate */
+ clock_phi = freq;
+ break;
+
+ case LOOP_MINSTEP: /* watchdog bark */
+ clock_minstep = freq;
+ break;
+
+ case LOOP_ALLAN: /* Allan intercept */
+ allan_xpt = freq;
+ break;
+
+ case LOOP_HUFFPUFF: /* huff-n'-puff filter length */
+ if (freq < HUFFPUFF)
+ freq = HUFFPUFF;
+ sys_hufflen = (int)(freq / HUFFPUFF);
+ sys_huffpuff = (double *)emalloc(sizeof(double) *
+ sys_hufflen);
+ for (i = 0; i < sys_hufflen; i++)
+ sys_huffpuff[i] = 1e9;
+ sys_mindly = 1e9;
+ break;
+
+ case LOOP_FREQ: /* initial frequency */
+ drift_comp = freq / 1e6;
+ rstclock(S_FSET, 0, 0);
+ break;
+ }
+}
+
+
+#if defined(KERNEL_PLL) && defined(SIGSYS)
+/*
+ * _trap - trap processor for undefined syscalls
+ *
+ * This nugget is called by the kernel when the SYS_ntp_adjtime()
+ * syscall bombs because the silly thing has not been implemented in
+ * the kernel. In this case the phase-lock loop is emulated by
+ * the stock adjtime() syscall and a lot of indelicate abuse.
+ */
+static RETSIGTYPE
+pll_trap(
+ int arg
+ )
+{
+ pll_control = 0;
+ siglongjmp(env, 1);
+}
+#endif /* KERNEL_PLL && SIGSYS */
diff --git a/contrib/ntp/ntpd/ntp_monitor.c b/contrib/ntp/ntpd/ntp_monitor.c
new file mode 100644
index 0000000..753fa76
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_monitor.c
@@ -0,0 +1,358 @@
+/*
+ * ntp_monitor - monitor ntpd statistics
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * I'm still not sure I like what I've done here. It certainly consumes
+ * memory like it is going out of style, and also may not be as low
+ * overhead as I'd imagined.
+ *
+ * Anyway, we record statistics based on source address, mode and
+ * version (for now, anyway. Check the code). The receive procedure
+ * calls us with the incoming rbufp before it does anything else.
+ *
+ * Each entry is doubly linked into two lists, a hash table and a
+ * most-recently-used list. When a packet arrives it is looked up in
+ * the hash table. If found, the statistics are updated and the entry
+ * relinked at the head of the MRU list. If not found, a new entry is
+ * allocated, initialized and linked into both the hash table and at the
+ * head of the MRU list.
+ *
+ * Memory is usually allocated by grabbing a big chunk of new memory and
+ * cutting it up into littler pieces. The exception to this when we hit
+ * the memory limit. Then we free memory by grabbing entries off the
+ * tail for the MRU list, unlinking from the hash table, and
+ * reinitializing.
+ *
+ * trimmed back memory consumption ... jdg 8/94
+ */
+/*
+ * Limits on the number of structures allocated. This limit is picked
+ * with the illicit knowlege that we can only return somewhat less
+ * than 8K bytes in a mode 7 response packet, and that each structure
+ * will require about 20 bytes of space in the response.
+ *
+ * ... I don't believe the above is true anymore ... jdg
+ */
+#ifndef MAXMONMEM
+#define MAXMONMEM 600 /* we allocate up to 600 structures */
+#endif
+#ifndef MONMEMINC
+#define MONMEMINC 40 /* allocate them 40 at a time */
+#endif
+
+/*
+ * Hashing stuff
+ */
+#define MON_HASH_SIZE 128
+#define MON_HASH_MASK (MON_HASH_SIZE-1)
+#define MON_HASH(addr) sock_hash(addr)
+
+/*
+ * Pointers to the hash table, the MRU list and the count table. Memory
+ * for the hash and count tables is only allocated if monitoring is
+ * turned on.
+ */
+static struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */
+struct mon_data mon_mru_list;
+
+/*
+ * List of free structures structures, and counters of free and total
+ * structures. The free structures are linked with the hash_next field.
+ */
+static struct mon_data *mon_free; /* free list or null if none */
+static int mon_total_mem; /* total structures allocated */
+static int mon_mem_increments; /* times called malloc() */
+
+/*
+ * Initialization state. We may be monitoring, we may not. If
+ * we aren't, we may not even have allocated any memory yet.
+ */
+int mon_enabled; /* enable switch */
+u_long mon_age = 3000; /* preemption limit */
+static int mon_have_memory;
+static void mon_getmoremem P((void));
+static void remove_from_hash P((struct mon_data *));
+
+/*
+ * init_mon - initialize monitoring global data
+ */
+void
+init_mon(void)
+{
+ /*
+ * Don't do much of anything here. We don't allocate memory
+ * until someone explicitly starts us.
+ */
+ mon_enabled = MON_OFF;
+ mon_have_memory = 0;
+
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ memset(&mon_hash[0], 0, sizeof mon_hash);
+ memset(&mon_mru_list, 0, sizeof mon_mru_list);
+}
+
+
+/*
+ * mon_start - start up the monitoring software
+ */
+void
+mon_start(
+ int mode
+ )
+{
+
+ if (mon_enabled != MON_OFF) {
+ mon_enabled |= mode;
+ return;
+ }
+ if (mode == MON_OFF)
+ return;
+
+ if (!mon_have_memory) {
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ mon_getmoremem();
+ mon_have_memory = 1;
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+ mon_enabled = mode;
+}
+
+
+/*
+ * mon_stop - stop the monitoring software
+ */
+void
+mon_stop(
+ int mode
+ )
+{
+ register struct mon_data *md, *md_next;
+ register int i;
+
+ if (mon_enabled == MON_OFF)
+ return;
+ if ((mon_enabled & mode) == 0 || mode == MON_OFF)
+ return;
+
+ mon_enabled &= ~mode;
+ if (mon_enabled != MON_OFF)
+ return;
+
+ /*
+ * Put everything back on the free list
+ */
+ for (i = 0; i < MON_HASH_SIZE; i++) {
+ md = mon_hash[i]; /* get next list */
+ mon_hash[i] = NULL; /* zero the list head */
+ while (md != NULL) {
+ md_next = md->hash_next;
+ md->hash_next = mon_free;
+ mon_free = md;
+ md = md_next;
+ }
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+}
+
+void
+ntp_monclearinterface(struct interface *interface)
+{
+ struct mon_data *md;
+
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list;
+ md = md->mru_next) {
+ if (md->interface == interface)
+ {
+ /* dequeue from mru list and put to free list */
+ md->mru_prev->mru_next = md->mru_next;
+ md->mru_next->mru_prev = md->mru_prev;
+ remove_from_hash(md);
+ md->hash_next = mon_free;
+ mon_free = md;
+ }
+ }
+}
+
+/*
+ * ntp_monitor - record stats about this packet
+ *
+ * Returns 1 if the packet is at the head of the list, 0 otherwise.
+ */
+int
+ntp_monitor(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pkt *pkt;
+ register struct mon_data *md;
+ struct sockaddr_storage addr;
+ register int hash;
+ register int mode;
+
+ if (mon_enabled == MON_OFF)
+ return 0;
+
+ pkt = &rbufp->recv_pkt;
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr));
+ hash = MON_HASH(&addr);
+ mode = PKT_MODE(pkt->li_vn_mode);
+ md = mon_hash[hash];
+ while (md != NULL) {
+
+ /*
+ * Match address only to conserve MRU size.
+ */
+ if (SOCKCMP(&md->rmtadr, &addr)) {
+ md->drop_count = current_time - md->lasttime;
+ md->lasttime = current_time;
+ md->count++;
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+
+ /*
+ * Shuffle to the head of the MRU list.
+ */
+ md->mru_next->mru_prev = md->mru_prev;
+ md->mru_prev->mru_next = md->mru_next;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+ return 1;
+ }
+ md = md->hash_next;
+ }
+
+ /*
+ * If we got here, this is the first we've heard of this
+ * guy. Get him some memory, either from the free list
+ * or from the tail of the MRU list.
+ */
+ if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
+
+ /*
+ * Preempt from the MRU list if old enough.
+ */
+ md = mon_mru_list.mru_prev;
+ /* We get 31 bits from ntp_random() */
+ if (((u_long)ntp_random()) / FRAC >
+ (double)(current_time - md->lasttime) / mon_age)
+ return 0;
+
+ md->mru_prev->mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = md->mru_prev;
+ remove_from_hash(md);
+ } else {
+ if (mon_free == NULL)
+ mon_getmoremem();
+ md = mon_free;
+ mon_free = md->hash_next;
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ md->avg_interval = 0;
+ md->lasttime = current_time;
+ md->count = 1;
+ md->drop_count = 0;
+ memset(&md->rmtadr, 0, sizeof(md->rmtadr));
+ memcpy(&md->rmtadr, &addr, sizeof(addr));
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->interface = rbufp->dstadr;
+ md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MCASTOPEN) &&
+ rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
+ md->interface->bfd ? MDF_BCAST : MDF_UCAST);
+
+ /*
+ * Drop him into front of the hash table. Also put him on top of
+ * the MRU list.
+ */
+ md->hash_next = mon_hash[hash];
+ mon_hash[hash] = md;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+ return 1;
+}
+
+
+/*
+ * mon_getmoremem - get more memory and put it on the free list
+ */
+static void
+mon_getmoremem(void)
+{
+ register struct mon_data *md;
+ register int i;
+ struct mon_data *freedata; /* 'old' free list (null) */
+
+ md = (struct mon_data *)emalloc(MONMEMINC *
+ sizeof(struct mon_data));
+ freedata = mon_free;
+ mon_free = md;
+ for (i = 0; i < (MONMEMINC-1); i++) {
+ md->hash_next = (md + 1);
+ md++;
+ }
+
+ /*
+ * md now points at the last. Link in the rest of the chain.
+ */
+ md->hash_next = freedata;
+ mon_total_mem += MONMEMINC;
+ mon_mem_increments++;
+}
+
+static void
+remove_from_hash(
+ struct mon_data *md
+ )
+{
+ register int hash;
+ register struct mon_data *md_prev;
+
+ hash = MON_HASH(&md->rmtadr);
+ if (mon_hash[hash] == md) {
+ mon_hash[hash] = md->hash_next;
+ } else {
+ md_prev = mon_hash[hash];
+ while (md_prev->hash_next != md) {
+ md_prev = md_prev->hash_next;
+ if (md_prev == NULL) {
+ /* logic error */
+ return;
+ }
+ }
+ md_prev->hash_next = md->hash_next;
+ }
+}
diff --git a/contrib/ntp/ntpd/ntp_peer.c b/contrib/ntp/ntpd/ntp_peer.c
new file mode 100644
index 0000000..deeec5b
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_peer.c
@@ -0,0 +1,1051 @@
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+#ifdef OPENSSL
+#include "openssl/rand.h"
+#endif /* OPENSSL */
+
+#ifdef SYS_WINNT
+extern int accept_wildcard_if_for_winnt;
+#endif
+
+/*
+ * Table of valid association combinations
+ * ---------------------------------------
+ *
+ * packet->mode
+ * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST
+ * ---------- | ---------------------------------------------
+ * NO_PEER | e 1 0 1 1 1
+ * ACTIVE | e 1 1 0 0 0
+ * PASSIVE | e 1 e 0 0 0
+ * CLIENT | e 0 0 0 1 1
+ * SERVER | e 0 0 0 0 0
+ * BCAST | e 0 0 0 0 0
+ * BCLIENT | e 0 0 0 e 1
+ *
+ * One point to note here: a packet in BCAST mode can potentially match
+ * a peer in CLIENT mode, but we that is a special case and we check for
+ * that early in the decision process. This avoids having to keep track
+ * of what kind of associations are possible etc... We actually
+ * circumvent that problem by requiring that the first b(m)roadcast
+ * received after the change back to BCLIENT mode sets the clock.
+ */
+#define AM_MODES 7 /* number of rows and columns */
+#define NO_PEER 0 /* action when no peer is found */
+
+int AM[AM_MODES][AM_MODES] = {
+/* { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */
+
+/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
+
+/*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_POSSBCL},
+
+/*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT},
+};
+
+#define MATCH_ASSOC(x,y) AM[(x)][(y)]
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of the peer hash table. The two main entry
+ * points are findpeer(), which looks for matching peer sturctures in
+ * the peer list, newpeer(), which allocates a new peer structure and
+ * adds it to the list, and unpeer(), which demobilizes the association
+ * and deallocates the structure.
+ */
+/*
+ * Peer hash tables
+ */
+struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */
+int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */
+struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */
+int assoc_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */
+static struct peer *peer_free; /* peer structures free list */
+int peer_free_count; /* count of free structures */
+
+/*
+ * Association ID. We initialize this value randomly, then assign a new
+ * value every time the peer structure is incremented.
+ */
+static associd_t current_association_ID; /* association ID */
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 15 /* initialize for 15 peers */
+#define INC_PEER_ALLOC 5 /* when run out, add 5 more */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters zeroed */
+u_long findpeer_calls; /* calls to findpeer */
+u_long assocpeer_calls; /* calls to findpeerbyassoc */
+u_long peer_allocations; /* allocations from free list */
+u_long peer_demobilizations; /* structs freed to free list */
+int total_peer_structs; /* peer structs */
+int peer_associations; /* mobilized associations */
+int peer_preempt; /* preemptable associations */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
+
+static void getmorepeermem P((void));
+static struct interface *select_peerinterface P((struct peer *, struct sockaddr_storage *, struct interface *, u_char));
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer(void)
+{
+ register int i;
+
+ /*
+ * Clear hash table and counters.
+ */
+ for (i = 0; i < NTP_HASH_SIZE; i++) {
+ peer_hash[i] = 0;
+ peer_hash_count[i] = 0;
+ assoc_hash[i] = 0;
+ assoc_hash_count[i] = 0;
+ }
+
+ /*
+ * Clear stat counters
+ */
+ findpeer_calls = peer_allocations = 0;
+ assocpeer_calls = peer_demobilizations = 0;
+
+ /*
+ * Initialize peer memory.
+ */
+ peer_free = 0;
+ for (i = 0; i < INIT_PEER_ALLOC; i++) {
+ init_peer_alloc[i].next = peer_free;
+ peer_free = &init_peer_alloc[i];
+ }
+ total_peer_structs = INIT_PEER_ALLOC;
+ peer_free_count = INIT_PEER_ALLOC;
+
+ /*
+ * Initialize our first association ID
+ */
+ while ((current_association_ID = ntp_random() & 0xffff) == 0);
+}
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem(void)
+{
+ register int i;
+ register struct peer *peer;
+
+ peer = (struct peer *)emalloc(INC_PEER_ALLOC *
+ sizeof(struct peer));
+ for (i = 0; i < INC_PEER_ALLOC; i++) {
+ peer->next = peer_free;
+ peer_free = peer;
+ peer++;
+ }
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+/*
+ * findexistingpeer - return a pointer to a peer in the hash table
+ */
+struct peer *
+findexistingpeer(
+ struct sockaddr_storage *addr,
+ struct peer *start_peer,
+ int mode
+ )
+{
+ register struct peer *peer;
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ */
+ if (start_peer == 0)
+ peer = peer_hash[NTP_HASH_ADDR(addr)];
+ else
+ peer = start_peer->next;
+
+ while (peer != 0) {
+ if (SOCKCMP(addr, &peer->srcadr)
+ && NSRCPORT(addr) == NSRCPORT(&peer->srcadr)) {
+ if (mode == -1)
+ return (peer);
+ else if (peer->hmode == mode)
+ break;
+ }
+ peer = peer->next;
+ }
+ return (peer);
+}
+
+
+/*
+ * findpeer - find and return a peer in the hash table.
+ */
+struct peer *
+findpeer(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int pkt_mode,
+ int *action
+ )
+{
+ register struct peer *peer;
+ int hash;
+
+ findpeer_calls++;
+ hash = NTP_HASH_ADDR(srcadr);
+ for (peer = peer_hash[hash]; peer != NULL; peer = peer->next) {
+ if (SOCKCMP(srcadr, &peer->srcadr) &&
+ NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
+
+ /*
+ * if the association matching rules determine
+ * that this is not a valid combination, then
+ * look for the next valid peer association.
+ */
+ *action = MATCH_ASSOC(peer->hmode, pkt_mode);
+
+ /*
+ * if an error was returned, exit back right
+ * here.
+ */
+ if (*action == AM_ERR)
+ return ((struct peer *)0);
+
+ /*
+ * if a match is found, we stop our search.
+ */
+ if (*action != AM_NOMATCH)
+ break;
+ }
+ }
+
+ /*
+ * If no matching association is found
+ */
+ if (peer == 0) {
+ *action = MATCH_ASSOC(NO_PEER, pkt_mode);
+ return ((struct peer *)0);
+ }
+
+ set_peerdstadr(peer, dstadr);
+
+ return (peer);
+}
+
+/*
+ * findpeerbyassocid - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(
+ u_int assoc
+ )
+{
+ register struct peer *peer;
+ int hash;
+
+ assocpeer_calls++;
+
+ hash = assoc & NTP_HASH_MASK;
+ for (peer = assoc_hash[hash]; peer != 0; peer =
+ peer->ass_next) {
+ if (assoc == peer->associd)
+ return (peer);
+ }
+ return (NULL);
+}
+
+
+/*
+ * clear_all - flush all time values for all associations
+ */
+void
+clear_all(void)
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ /*
+ * This routine is called when the clock is stepped, and so all
+ * previously saved time values are untrusted.
+ */
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (!(peer->cast_flags & (MDF_ACAST | MDF_MCAST |
+ MDF_BCAST))) {
+ peer->hpoll = peer->minpoll;
+ peer_clear(peer, "STEP");
+ }
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("clear_all: at %lu\n", current_time);
+#endif
+}
+
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(
+ struct peer *peer_to_remove
+ )
+{
+ int hash;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+
+ if (peer_to_remove->flags & FLAG_SKEY) {
+ sprintf(statstr, "unpeer %d flash %x reach %03o flags %04x",
+ peer_to_remove->associd, peer_to_remove->flash,
+ peer_to_remove->reach, peer_to_remove->flags);
+ record_crypto_stats(&peer_to_remove->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("peer: %s\n", statstr);
+#endif
+ }
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug)
+ printf("demobilize %u %d %d\n", peer_to_remove->associd,
+ peer_associations, peer_preempt);
+#endif
+ set_peerdstadr(peer_to_remove, NULL);
+
+ /* XXXMEMLEAK? peer_clear->crypto allocation */
+
+ hash = NTP_HASH_ADDR(&peer_to_remove->srcadr);
+ peer_hash_count[hash]--;
+ peer_demobilizations++;
+ peer_associations--;
+ if (peer_to_remove->flags & FLAG_PREEMPT)
+ peer_preempt--;
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (peer_to_remove->flags & FLAG_REFCLOCK)
+ refclock_unpeer(peer_to_remove);
+#endif
+ peer_to_remove->action = 0; /* disable timeout actions */
+ if (peer_hash[hash] == peer_to_remove)
+ peer_hash[hash] = peer_to_remove->next;
+ else {
+ register struct peer *peer;
+
+ peer = peer_hash[hash];
+ while (peer != 0 && peer->next != peer_to_remove)
+ peer = peer->next;
+
+ if (peer == 0) {
+ peer_hash_count[hash]++;
+ msyslog(LOG_ERR, "peer struct for %s not in table!",
+ stoa(&peer->srcadr));
+ } else {
+ peer->next = peer_to_remove->next;
+ }
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = peer_to_remove->associd & NTP_HASH_MASK;
+ assoc_hash_count[hash]--;
+ if (assoc_hash[hash] == peer_to_remove)
+ assoc_hash[hash] = peer_to_remove->ass_next;
+ else {
+ register struct peer *peer;
+
+ peer = assoc_hash[hash];
+ while (peer != 0 && peer->ass_next != peer_to_remove)
+ peer = peer->ass_next;
+
+ if (peer == 0) {
+ assoc_hash_count[hash]++;
+ msyslog(LOG_ERR,
+ "peer struct for %s not in association table!",
+ stoa(&peer->srcadr));
+ } else {
+ peer->ass_next = peer_to_remove->ass_next;
+ }
+ }
+ peer_to_remove->next = peer_free;
+ peer_free = peer_to_remove;
+ peer_free_count++;
+}
+
+
+/*
+ * peer_config - configure a new association
+ */
+struct peer *
+peer_config(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t key,
+ u_char *keystr
+ )
+{
+ register struct peer *peer;
+ u_char cast_flags;
+
+ /*
+ * First search from the beginning for an association with given
+ * remote address and mode. If an interface is given, search
+ * from there to find the association which matches that
+ * destination. If the given interface is "any", track down
+ * the actual interface, because that's what gets put into the
+ * peer structure.
+ */
+ peer = findexistingpeer(srcadr, (struct peer *)0, hmode);
+ if (dstadr != 0) {
+ while (peer != 0) {
+ if (peer->dstadr == dstadr)
+ break;
+ if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
+ peer->dstadr == findinterface(srcadr))
+ break;
+ peer = findexistingpeer(srcadr, peer, hmode);
+ }
+ }
+
+ /*
+ * We do a dirty little jig to figure the cast flags. This is
+ * probably not the best place to do this, at least until the
+ * configure code is rebuilt. Note only one flag can be set.
+ */
+ switch (hmode) {
+
+ case MODE_BROADCAST:
+ if(srcadr->ss_family == AF_INET) {
+ if (IN_CLASSD(ntohl(((struct sockaddr_in*)srcadr)->sin_addr.s_addr)))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+ }
+ else {
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)srcadr)->sin6_addr))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+ }
+
+ case MODE_CLIENT:
+ if(srcadr->ss_family == AF_INET) {
+ if (IN_CLASSD(ntohl(((struct sockaddr_in*)srcadr)->sin_addr.s_addr)))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+ }
+ else {
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)srcadr)->sin6_addr))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+ }
+
+ default:
+ cast_flags = MDF_UCAST;
+ }
+
+ /*
+ * If the peer is already configured, some dope has a duplicate
+ * configureation entry or another dope is wiggling from afar.
+ */
+ if (peer != 0) {
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char) version;
+ peer->minpoll = (u_char) minpoll;
+ peer->maxpoll = (u_char) maxpoll;
+ peer->flags = flags | FLAG_CONFIG |
+ (peer->flags & FLAG_REFCLOCK);
+ peer->cast_flags = cast_flags;
+ peer->ttl = (u_char) ttl;
+ peer->keyid = key;
+ peer->precision = sys_precision;
+ peer_clear(peer, "RMOT");
+ return (peer);
+ }
+
+ /*
+ * Here no match has been found, so presumably this is a new
+ * persistent association. Mobilize the thing and initialize its
+ * variables. If emulating ntpdate, force iburst.
+ */
+ if (mode_ntpdate)
+ flags |= FLAG_IBURST;
+ peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
+ flags | FLAG_CONFIG, cast_flags, ttl, key);
+ return (peer);
+}
+
+/*
+ * setup peer dstadr field keeping it in sync with the interface structures
+ */
+void
+set_peerdstadr(struct peer *peer, struct interface *interface)
+{
+ if (peer->dstadr != interface) {
+ if (interface != NULL &&
+ (peer->cast_flags & MDF_BCLNT) &&
+ (interface->flags & INT_MCASTIF) &&
+ peer->burst) {
+ /*
+ * don't accept updates to a true multicast reception
+ * interface while a BCLNT peer is running it's
+ * unicast protocol
+ */
+ return;
+ }
+
+ if (peer->dstadr != NULL)
+ {
+ peer->dstadr->peercnt--;
+ ISC_LIST_UNLINK_TYPE(peer->dstadr->peers, peer, ilink, struct peer);
+ }
+
+ DPRINTF(4, ("set_peerdstadr(%s): change interface from %s to %s\n",
+ stoa(&peer->srcadr),
+ (peer->dstadr != NULL) ? stoa(&peer->dstadr->sin) : "<null>",
+ (interface != NULL) ? stoa(&interface->sin) : "<null>"));
+
+ peer->dstadr = interface;
+
+ if (peer->dstadr != NULL)
+ {
+ ISC_LIST_APPEND(peer->dstadr->peers, peer, ilink);
+ peer->dstadr->peercnt++;
+ }
+ }
+}
+
+/*
+ * attempt to re-rebind interface if necessary
+ */
+static void
+peer_refresh_interface(struct peer *peer)
+{
+ struct interface *niface, *piface;
+
+ niface = select_peerinterface(peer, &peer->srcadr, NULL, peer->cast_flags);
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf(
+ "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x: new interface: ",
+ peer->dstadr == NULL ? "<null>" : stoa(&peer->dstadr->sin),
+ stoa(&peer->srcadr),
+ peer->hmode, peer->version, peer->minpoll,
+ peer->maxpoll, peer->flags, peer->cast_flags,
+ peer->ttl, peer->keyid);
+ if (niface != NULL)
+ {
+ printf("fd=%d, bfd=%d, name=%.16s, flags=0x%x, scope=%d, ",
+ niface->fd,
+ niface->bfd,
+ niface->name,
+ niface->flags,
+ niface->scopeid);
+ /* Leave these as three printf calls. */
+ printf(", sin=%s",
+ stoa((&niface->sin)));
+ if (niface->flags & INT_BROADCAST)
+ printf(", bcast=%s,",
+ stoa((&niface->bcast)));
+ printf(", mask=%s\n",
+ stoa((&niface->mask)));
+ }
+ else
+ {
+ printf("<NONE>\n");
+ }
+ }
+#endif
+
+ piface = peer->dstadr;
+
+ set_peerdstadr(peer, niface);
+
+ if (peer->dstadr) {
+ /*
+ * clear crypto if we change the local address
+ */
+ if (peer->dstadr != piface && !(peer->cast_flags & MDF_BCLNT)) {
+ peer_crypto_clear(peer);
+ }
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if (peer->cast_flags & MDF_BCAST) {
+ enable_broadcast(peer->dstadr, &peer->srcadr);
+ }
+
+ /*
+ * Multicast needs the socket interface enabled for multicast
+ */
+ if (peer->cast_flags & MDF_MCAST) {
+ enable_multicast_if(peer->dstadr, &peer->srcadr);
+ }
+ }
+}
+
+/*
+ * refresh_all_peerinterfaces - see that all interface bindings are up to date
+ */
+void
+refresh_all_peerinterfaces(void)
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ /*
+ * this is called when the interface list has changed
+ * give all peers a chance to find a better interface
+ */
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ peer_refresh_interface(peer);
+ }
+ }
+}
+
+
+/*
+ * find an interface suitable for the src address
+ */
+static struct interface *
+select_peerinterface(struct peer *peer, struct sockaddr_storage *srcadr, struct interface *dstadr, u_char cast_flags)
+{
+ struct interface *interface;
+
+ /*
+ * Initialize the peer structure and dance the interface jig.
+ * Reference clocks step the loopback waltz, the others
+ * squaredance around the interface list looking for a buddy. If
+ * the dance peters out, there is always the wildcard interface.
+ * This might happen in some systems and would preclude proper
+ * operation with public key cryptography.
+ */
+ if (ISREFCLOCKADR(srcadr))
+ interface = loopback_interface;
+ else
+ if (cast_flags & (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) {
+ interface = findbcastinter(srcadr);
+#ifdef DEBUG
+ if (debug > 3) {
+ if (interface != NULL)
+ printf("Found *-cast interface address %s, for address %s\n",
+ stoa(&(interface)->sin), stoa(srcadr));
+ else
+ printf("No *-cast local address found for address %s\n",
+ stoa(srcadr));
+ }
+#endif
+ /*
+ * If it was a multicast packet, findbcastinter() may not
+ * find it, so try a little harder.
+ */
+ if (interface == ANY_INTERFACE_CHOOSE(srcadr))
+ interface = findinterface(srcadr);
+ }
+ else if (dstadr != NULL && dstadr != ANY_INTERFACE_CHOOSE(srcadr))
+ interface = dstadr;
+ else
+ interface = findinterface(srcadr);
+
+ /*
+ * we do not bind to the wildcard interfaces for output
+ * as our (network) source address would be undefined and
+ * crypto will not work without knowing the own transmit address
+ */
+ if (interface != NULL && interface->flags & INT_WILDCARD)
+#ifdef SYS_WINNT
+ if ( !accept_wildcard_if_for_winnt )
+#endif
+ interface = NULL;
+
+
+ return interface;
+}
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ u_char cast_flags,
+ int ttl,
+ keyid_t key
+ )
+{
+ register struct peer *peer;
+ register int i;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+#endif /* OPENSSL */
+
+ /*
+ * Allocate a new peer structure. Some dirt here, since some of
+ * the initialization requires knowlege of our system state.
+ */
+ if (peer_free_count == 0)
+ getmorepeermem();
+ peer = peer_free;
+ peer_free = peer->next;
+ peer_free_count--;
+ peer_associations++;
+ if (flags & FLAG_PREEMPT)
+ peer_preempt++;
+ memset((char *)peer, 0, sizeof(struct peer));
+
+ /*
+ * Assign an association ID and increment the system variable.
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ DPRINTF(3, ("newpeer: cast flags: 0x%x for address: %s\n",
+ cast_flags, stoa(srcadr)));
+
+ ISC_LINK_INIT(peer, ilink); /* set up interface link chain */
+ peer->srcadr = *srcadr;
+ set_peerdstadr(peer, select_peerinterface(peer, srcadr, dstadr,
+ cast_flags));
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)max(NTP_MINPOLL, minpoll);
+ peer->maxpoll = (u_char)min(NTP_MAXPOLL, maxpoll);
+ peer->flags = flags;
+#ifdef DEBUG
+ if (debug > 2) {
+ if (peer->dstadr)
+ printf("newpeer: using fd %d and our addr %s\n",
+ peer->dstadr->fd,
+ stoa(&peer->dstadr->sin));
+ else
+ printf("newpeer: local interface currently not bound\n");
+ }
+#endif
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if (cast_flags & MDF_BCAST && peer->dstadr) {
+ enable_broadcast(peer->dstadr, srcadr);
+ }
+ /*
+ * Multicast needs the socket interface enabled for multicast
+ */
+ if (cast_flags & MDF_MCAST && peer->dstadr) {
+ enable_multicast_if(peer->dstadr, srcadr);
+ }
+ if (key != 0)
+ peer->flags |= FLAG_AUTHENABLE;
+ if (key > NTP_MAXKEY)
+ peer->flags |= FLAG_SKEY;
+ peer->cast_flags = cast_flags;
+ peer->ttl = (u_char)ttl;
+ peer->keyid = key;
+ peer->precision = sys_precision;
+ peer->hpoll = peer->minpoll;
+ if (cast_flags & MDF_ACAST)
+ peer_clear(peer, "ACST");
+ else if (cast_flags & MDF_MCAST)
+ peer_clear(peer, "MCST");
+ else if (cast_flags & MDF_BCAST)
+ peer_clear(peer, "BCST");
+ else
+ peer_clear(peer, "INIT");
+ if (mode_ntpdate)
+ peer_ntpdate++;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ set_peerdstadr(peer, NULL);
+
+ peer->next = peer_free;
+ peer_free = peer;
+ peer_free_count++;
+ return (NULL);
+ }
+ }
+#endif
+
+ /*
+ * Put the new peer in the hash tables.
+ */
+ i = NTP_HASH_ADDR(&peer->srcadr);
+ peer->next = peer_hash[i];
+ peer_hash[i] = peer;
+ peer_hash_count[i]++;
+ i = peer->associd & NTP_HASH_MASK;
+ peer->ass_next = assoc_hash[i];
+ assoc_hash[i] = peer;
+ assoc_hash_count[i]++;
+
+#ifdef OPENSSL
+ if (peer->flags & FLAG_SKEY) {
+ sprintf(statstr, "newpeer %d", peer->associd);
+ record_crypto_stats(&peer->srcadr, statstr);
+ DPRINTF(1, ("peer: %s\n", statstr));
+ }
+#endif /* OPENSSL */
+
+ DPRINTF(1, ("newpeer: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x\n",
+ peer->dstadr == NULL ? "<null>" : stoa(&peer->dstadr->sin),
+ stoa(&peer->srcadr),
+ peer->hmode, peer->version, peer->minpoll,
+ peer->maxpoll, peer->flags, peer->cast_flags,
+ peer->ttl, peer->keyid));
+
+ return (peer);
+}
+
+
+/*
+ * peer_unconfig - remove the configuration bit from a peer
+ */
+int
+peer_unconfig(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int mode
+ )
+{
+ register struct peer *peer;
+ int num_found;
+
+ num_found = 0;
+ peer = findexistingpeer(srcadr, (struct peer *)0, mode);
+ while (peer != 0) {
+ if (peer->flags & FLAG_CONFIG
+ && (dstadr == 0 || peer->dstadr == dstadr)) {
+ num_found++;
+
+ /*
+ * Tricky stuff here. If the peer is polling us
+ * in active mode, turn off the configuration
+ * bit and make the mode passive. This allows us
+ * to avoid dumping a lot of history for peers
+ * we might choose to keep track of in passive
+ * mode. The protocol will eventually terminate
+ * undesirables on its own.
+ */
+ if (peer->hmode == MODE_ACTIVE
+ && peer->pmode == MODE_ACTIVE) {
+ peer->hmode = MODE_PASSIVE;
+ peer->flags &= ~FLAG_CONFIG;
+ } else {
+ unpeer(peer);
+ peer = 0;
+ }
+ }
+ peer = findexistingpeer(srcadr, peer, mode);
+ }
+ return (num_found);
+}
+
+/*
+ * peer_clr_stats - clear peer module stat counters
+ */
+void
+peer_clr_stats(void)
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+/*
+ * peer_reset - reset stat counters in a peer structure
+ */
+void
+peer_reset(
+ struct peer *peer
+ )
+{
+ if (peer == 0)
+ return;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->timereset = current_time;
+}
+
+
+/*
+ * peer_all_reset - reset all peer stat counters
+ */
+void
+peer_all_reset(void)
+{
+ struct peer *peer;
+ int hash;
+
+ for (hash = 0; hash < NTP_HASH_SIZE; hash++)
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next)
+ peer_reset(peer);
+}
+
+
+#ifdef OPENSSL
+/*
+ * expire_all - flush all crypto data and update timestamps.
+ */
+void
+expire_all(void)
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ /*
+ * This routine is called about once per day from the timer
+ * routine and when the client is first synchronized. Search the
+ * peer list for all associations and flush only the key list
+ * and cookie. If a manycast client association, flush
+ * everything. Then, recompute and sign the agreement public
+ * value, if present.
+ */
+ if (!crypto_flags)
+ return;
+
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (!(peer->flags & FLAG_SKEY)) {
+ continue;
+
+ } else if (peer->hmode == MODE_ACTIVE ||
+ peer->hmode == MODE_PASSIVE) {
+ key_expire(peer);
+ peer->crypto &= ~(CRYPTO_FLAG_AUTO |
+ CRYPTO_FLAG_AGREE);
+ }
+
+ }
+ }
+ RAND_bytes((u_char *)&sys_private, 4);
+ crypto_update();
+}
+#endif /* OPENSSL */
+
+
+/*
+ * findmanycastpeer - find and return a manycast peer
+ */
+struct peer *
+findmanycastpeer(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer;
+ struct pkt *pkt;
+ l_fp p_org;
+ int i;
+
+ /*
+ * This routine is called upon arrival of a server-mode message
+ * from a manycast client. Search the peer list for a manycast
+ * client association where the last transmit timestamp matches
+ * the originate timestamp. This assumes the transmit timestamps
+ * for possibly more than one manycast association are unique.
+ */
+ pkt = &rbufp->recv_pkt;
+ for (i = 0; i < NTP_HASH_SIZE; i++) {
+ if (peer_hash_count[i] == 0)
+ continue;
+
+ for (peer = peer_hash[i]; peer != 0; peer =
+ peer->next) {
+ if (peer->cast_flags & MDF_ACAST) {
+ NTOHL_FP(&pkt->org, &p_org);
+ if (L_ISEQU(&peer->xmt, &p_org))
+ return (peer);
+ }
+ }
+ }
+ return (NULL);
+}
diff --git a/contrib/ntp/ntpd/ntp_proto.c b/contrib/ntp/ntpd/ntp_proto.c
new file mode 100644
index 0000000..0ab2498
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_proto.c
@@ -0,0 +1,3450 @@
+/*
+ * ntp_proto.c - NTP version 4 protocol machinery
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+
+#include <stdio.h>
+
+#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/
+#include "ntp_refclock.h"
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/sysctl.h>
+#endif
+
+/*
+ * This macro defines the authentication state. If x is 1 authentication
+ * is required; othewise it is optional.
+ */
+#define AUTH(x, y) ((x) ? (y) == AUTH_OK : (y) == AUTH_OK || \
+ (y) == AUTH_NONE)
+
+/*
+ * System variables are declared here. See Section 3.2 of the
+ * specification.
+ */
+u_char sys_leap; /* system leap indicator */
+u_char sys_stratum; /* stratum of system */
+s_char sys_precision; /* local clock precision (log2 s) */
+double sys_rootdelay; /* roundtrip delay to primary source */
+double sys_rootdispersion; /* dispersion to primary source */
+u_int32 sys_refid; /* source/loop in network byte order */
+static double sys_offset; /* current local clock offset */
+l_fp sys_reftime; /* time we were last updated */
+struct peer *sys_peer; /* our current peer */
+struct peer *sys_pps; /* our PPS peer */
+struct peer *sys_prefer; /* our cherished peer */
+int sys_kod; /* kod credit */
+int sys_kod_rate = 2; /* max kod packets per second */
+#ifdef OPENSSL
+u_long sys_automax; /* maximum session key lifetime */
+#endif /* OPENSSL */
+
+/*
+ * Nonspecified system state variables.
+ */
+int sys_bclient; /* broadcast client enable */
+double sys_bdelay; /* broadcast client default delay */
+int sys_calldelay; /* modem callup delay (s) */
+int sys_authenticate; /* requre authentication for config */
+l_fp sys_authdelay; /* authentication delay */
+static u_long sys_authdly[2]; /* authentication delay shift reg */
+static double sys_mindisp = MINDISPERSE; /* min disp increment (s) */
+static double sys_maxdist = MAXDISTANCE; /* selection threshold (s) */
+double sys_jitter; /* system jitter (s) */
+static int sys_hopper; /* anticlockhop counter */
+static int sys_maxhop = MAXHOP; /* anticlockhop counter threshold */
+int leap_next; /* leap consensus */
+keyid_t sys_private; /* private value for session seed */
+int sys_manycastserver; /* respond to manycast client pkts */
+int peer_ntpdate; /* active peers in ntpdate mode */
+int sys_survivors; /* truest of the truechimers */
+#ifdef OPENSSL
+char *sys_hostname; /* gethostname() name */
+#endif /* OPENSSL */
+
+/*
+ * TOS and multicast mapping stuff
+ */
+int sys_floor = 0; /* cluster stratum floor */
+int sys_ceiling = STRATUM_UNSPEC; /* cluster stratum ceiling */
+int sys_minsane = 1; /* minimum candidates */
+int sys_minclock = NTP_MINCLOCK; /* minimum survivors */
+int sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */
+int sys_cohort = 0; /* cohort switch */
+int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */
+double sys_orphandelay = 0; /* orphan root delay */
+int sys_beacon = BEACON; /* manycast beacon interval */
+int sys_ttlmax; /* max ttl mapping vector index */
+u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */
+
+/*
+ * Statistics counters
+ */
+u_long sys_stattime; /* time since reset */
+u_long sys_received; /* packets received */
+u_long sys_processed; /* packets processed */
+u_long sys_newversionpkt; /* current version */
+u_long sys_oldversionpkt; /* recent version */
+u_long sys_unknownversion; /* invalid version */
+u_long sys_restricted; /* access denied */
+u_long sys_badlength; /* bad length or format */
+u_long sys_badauth; /* bad authentication */
+u_long sys_limitrejected; /* rate exceeded */
+
+static double root_distance P((struct peer *));
+static void clock_combine P((struct peer **, int));
+static void peer_xmit P((struct peer *));
+static void fast_xmit P((struct recvbuf *, int, keyid_t,
+ int));
+static void clock_update P((void));
+static int default_get_precision P((void));
+static int peer_unfit P((struct peer *));
+
+
+/*
+ * transmit - Transmit Procedure. See Section 3.4.2 of the
+ * specification.
+ */
+void
+transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int hpoll;
+
+ /*
+ * The polling state machine. There are two kinds of machines,
+ * those that never expect a reply (broadcast and manycast
+ * server modes) and those that do (all other modes). The dance
+ * is intricate...
+ */
+ /*
+ * Orphan mode is active when enabled and when no servers less
+ * than the orphan statum are available. In this mode packets
+ * are sent at the orphan stratum. An orphan with no other
+ * synchronization source is an orphan parent. It assumes root
+ * delay zero and reference ID the loopback address. All others
+ * are orphan children with root delay randomized over a 1-s
+ * range. The root delay is used by the election algorithm to
+ * select the order of synchronization.
+ */
+ hpoll = peer->hpoll;
+ if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL) {
+ sys_leap = LEAP_NOWARNING;
+ sys_stratum = sys_orphan;
+ sys_refid = htonl(LOOPBACKADR);
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ }
+
+ /*
+ * In broadcast mode the poll interval is never changed from
+ * minpoll.
+ */
+ if (peer->cast_flags & (MDF_BCAST | MDF_MCAST)) {
+ peer->outdate = current_time;
+ peer_xmit(peer);
+ poll_update(peer, hpoll);
+ return;
+ }
+
+ /*
+ * In manycast mode we start with unity ttl. The ttl is
+ * increased by one for each poll until either sys_maxclock
+ * servers have been found or the maximum ttl is reached. When
+ * sys_maxclock servers are found we stop polling until one or
+ * more servers have timed out or until less than minpoll
+ * associations turn up. In this case additional better servers
+ * are dragged in and preempt the existing ones.
+ */
+ if (peer->cast_flags & MDF_ACAST) {
+ peer->outdate = current_time;
+ if (peer->unreach > sys_beacon) {
+ peer->unreach = 0;
+ peer->ttl = 0;
+ peer_xmit(peer);
+ } else if (sys_survivors < sys_minclock ||
+ peer_preempt < sys_maxclock) {
+ if (peer->ttl < sys_ttlmax)
+ peer->ttl++;
+ peer_xmit(peer);
+ }
+ peer->unreach++;
+ poll_update(peer, hpoll);
+ return;
+ }
+
+ /*
+ * In unicast modes the dance is much more intricate. It is
+ * desigmed to back off whenever possible to minimize network
+ * traffic.
+ */
+ if (peer->burst == 0) {
+ u_char oreach;
+
+ /*
+ * Update the reachability status. If not heard for
+ * three consecutive polls, stuff infinity in the clock
+ * filter.
+ */
+ oreach = peer->reach;
+ peer->outdate = current_time;
+ if (peer == sys_peer)
+ sys_hopper++;
+ peer->reach <<= 1;
+ if (!(peer->reach & 0x07))
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ if (!peer->reach) {
+
+ /*
+ * Here the peer is unreachable. If it was
+ * previously reachable, raise a trap.
+ */
+ if (oreach) {
+ report_event(EVNT_UNREACH, peer);
+ peer->timereachable = current_time;
+ }
+
+ /*
+ * Send a burst if enabled, but only once after
+ * a peer becomes unreachable. If the prempt
+ * flag is dim, bump the unreach counter by one;
+ * otherwise, bump it by three.
+ */
+ if (peer->flags & FLAG_IBURST &&
+ peer->unreach == 0) {
+ peer->burst = NTP_BURST;
+ }
+ if (!(peer->flags & FLAG_PREEMPT))
+ peer->unreach++;
+ else
+ peer->unreach += 3;
+ } else {
+
+ /*
+ * Here the peer is reachable. Set the poll
+ * interval to the system poll interval. Send a
+ * burst only if enabled and the peer is fit.
+ *
+ * Respond to the peer evaluation produced by
+ * the selection algorithm. If less than the
+ * outlyer level, up the unreach by three. If
+ * there are excess associations, up the unreach
+ * by two if not a candidate and by one if so.
+ */
+ if (!(peer->flags & FLAG_PREEMPT)) {
+ peer->unreach = 0;
+ } else if (peer->status < CTL_PST_SEL_SELCAND) {
+ peer->unreach += 3;
+ } else if (peer_preempt > sys_maxclock) {
+ if (peer->status < CTL_PST_SEL_SYNCCAND)
+ peer->unreach += 2;
+ else
+ peer->unreach++;
+ } else {
+ peer->unreach = 0;
+ }
+ hpoll = sys_poll;
+ if (peer->flags & FLAG_BURST &&
+ !peer_unfit(peer))
+ peer->burst = NTP_BURST;
+ }
+
+ /*
+ * Watch for timeout. If ephemeral or preemptable, toss
+ * the rascal; otherwise, bump the poll interval.
+ */
+ if (peer->unreach >= NTP_UNREACH) {
+ if (peer->flags & FLAG_PREEMPT ||
+ !(peer->flags & FLAG_CONFIG)) {
+ peer_clear(peer, "TIME");
+ unpeer(peer);
+ return;
+ } else {
+ hpoll++;
+ }
+ }
+ } else {
+ peer->burst--;
+
+ /*
+ * If a broadcast client at this point, the burst has
+ * concluded, so we switch to client mode and purge the
+ * keylist, since no further transmissions will be made.
+ */
+ if (peer->burst == 0) {
+ if (peer->cast_flags & MDF_BCLNT) {
+ peer->hmode = MODE_BCLIENT;
+#ifdef OPENSSL
+ key_expire(peer);
+#endif /* OPENSSL */
+ }
+
+ /*
+ * If ntpdate mode and the clock has not been
+ * set and all peers have completed the burst,
+ * we declare a successful failure.
+ */
+ if (mode_ntpdate) {
+ peer_ntpdate--;
+ if (peer_ntpdate == 0) {
+ msyslog(LOG_NOTICE,
+ "no reply; clock not set");
+ exit (0);
+ }
+ }
+ }
+ }
+
+ /*
+ * Do not transmit if in broadcast client mode.
+ */
+ if (peer->hmode != MODE_BCLIENT)
+ peer_xmit(peer);
+ poll_update(peer, hpoll);
+}
+
+
+/*
+ * receive - Receive Procedure. See section 3.4.3 in the specification.
+ */
+void
+receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer; /* peer structure pointer */
+ register struct pkt *pkt; /* receive packet pointer */
+ int hisversion; /* packet version */
+ int hisleap; /* packet leap indicator */
+ int hismode; /* packet mode */
+ int hisstratum; /* packet stratum */
+ int restrict_mask; /* restrict bits */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ int is_authentic = 0; /* cryptosum ok */
+ keyid_t skeyid = 0; /* key ID */
+ struct sockaddr_storage *dstadr_sin; /* active runway */
+ struct peer *peer2; /* aux peer structure pointer */
+ l_fp p_org; /* origin timestamp */
+ l_fp p_rec; /* receive timestamp */
+ l_fp p_xmt; /* transmit timestamp */
+#ifdef OPENSSL
+ keyid_t tkeyid = 0; /* temporary key ID */
+ keyid_t pkeyid = 0; /* previous key ID */
+ struct autokey *ap; /* autokey structure pointer */
+ int rval; /* cookie snatcher */
+#endif /* OPENSSL */
+ int retcode = AM_NOMATCH;
+ int at_listhead;
+
+ /*
+ * Monitor the packet and get restrictions. Note that the packet
+ * length for control and private mode packets must be checked
+ * by the service routines. Note that no statistics counters are
+ * recorded for restrict violations, since these counters are in
+ * the restriction routine. Note the careful distinctions here
+ * between a packet with a format error and a packet that is
+ * simply discarded without prejudice. Some restrictions have to
+ * be handled later in order to generate a kiss-of-death packet.
+ */
+ /*
+ * Bogus port check is before anything, since it probably
+ * reveals a clogging attack.
+ */
+ sys_received++;
+ if (SRCPORT(&rbufp->recv_srcadr) == 0) {
+ sys_badlength++;
+ return; /* bogus port */
+ }
+ at_listhead = ntp_monitor(rbufp);
+ restrict_mask = restrictions(&rbufp->recv_srcadr, at_listhead);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("receive: at %ld %s<-%s flags %x restrict %03x\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr),
+ rbufp->dstadr->flags, restrict_mask);
+#endif
+ if (restrict_mask & RES_IGNORE) {
+ sys_restricted++;
+ return; /* ignore everything */
+ }
+ pkt = &rbufp->recv_pkt;
+ hisversion = PKT_VERSION(pkt->li_vn_mode);
+ hisleap = PKT_LEAP(pkt->li_vn_mode);
+ hismode = (int)PKT_MODE(pkt->li_vn_mode);
+ hisstratum = PKT_TO_STRATUM(pkt->stratum);
+ if (hismode == MODE_PRIVATE) {
+ if (restrict_mask & RES_NOQUERY) {
+ sys_restricted++;
+ return; /* no query private */
+ }
+ process_private(rbufp, ((restrict_mask &
+ RES_NOMODIFY) == 0));
+ return;
+ }
+ if (hismode == MODE_CONTROL) {
+ if (restrict_mask & RES_NOQUERY) {
+ sys_restricted++;
+ return; /* no query control */
+ }
+ process_control(rbufp, restrict_mask);
+ return;
+ }
+ if (restrict_mask & RES_DONTSERVE) {
+ sys_restricted++;
+ return; /* no time */
+ }
+ if (rbufp->recv_length < LEN_PKT_NOMAC) {
+ sys_badlength++;
+ return; /* runt packet */
+ }
+
+ /*
+ * Version check must be after the query packets, since they
+ * intentionally use early version.
+ */
+ if (hisversion == NTP_VERSION) {
+ sys_newversionpkt++; /* new version */
+ } else if (!(restrict_mask & RES_VERSION) && hisversion >=
+ NTP_OLDVERSION) {
+ sys_oldversionpkt++; /* previous version */
+ } else {
+ sys_unknownversion++;
+ return; /* old version */
+ }
+
+ /*
+ * Figure out his mode and validate the packet. This has some
+ * legacy raunch that probably should be removed. In very early
+ * NTP versions mode 0 was equivalent to what later versions
+ * would interpret as client mode.
+ */
+ if (hismode == MODE_UNSPEC) {
+ if (hisversion == NTP_OLDVERSION) {
+ hismode = MODE_CLIENT;
+ } else {
+ sys_badlength++;
+ return; /* invalid mode */
+ }
+ }
+
+ /*
+ * Parse the extension field if present. We figure out whether
+ * an extension field is present by measuring the MAC size. If
+ * the number of words following the packet header is 0, no MAC
+ * is present and the packet is not authenticated. If 1, the
+ * packet is a crypto-NAK; if 3, the packet is authenticated
+ * with DES; if 5, the packet is authenticated with MD5. If 2 or
+ * 4, the packet is a runt and discarded forthwith. If greater
+ * than 5, an extension field is present, so we subtract the
+ * length of the field and go around again.
+ */
+ authlen = LEN_PKT_NOMAC;
+ has_mac = rbufp->recv_length - authlen;
+ while (has_mac > 0) {
+ int temp;
+
+ if (has_mac % 4 != 0 || has_mac < 0) {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ if (has_mac == 1 * 4 || has_mac == 3 * 4 || has_mac ==
+ MAX_MAC_LEN) {
+ skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]);
+ break;
+
+ } else if (has_mac > MAX_MAC_LEN) {
+ temp = ntohl(((u_int32 *)pkt)[authlen / 4]) &
+ 0xffff;
+ if (temp < 4 || temp > NTP_MAXEXTEN || temp % 4
+ != 0) {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ authlen += temp;
+ has_mac -= temp;
+ } else {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ }
+#ifdef OPENSSL
+ pkeyid = tkeyid = 0;
+#endif /* OPENSSL */
+
+ /*
+ * We have tossed out as many buggy packets as possible early in
+ * the game to reduce the exposure to a clogging attack. Now we
+ * have to burn some cycles to find the association and
+ * authenticate the packet if required. Note that we burn only
+ * MD5 cycles, again to reduce exposure. There may be no
+ * matching association and that's okay.
+ *
+ * More on the autokey mambo. Normally the local interface is
+ * found when the association was mobilized with respect to a
+ * designated remote address. We assume packets arriving from
+ * the remote address arrive via this interface and the local
+ * address used to construct the autokey is the unicast address
+ * of the interface. However, if the sender is a broadcaster,
+ * the interface broadcast address is used instead.
+ & Notwithstanding this technobabble, if the sender is a
+ * multicaster, the broadcast address is null, so we use the
+ * unicast address anyway. Don't ask.
+ */
+ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, hismode,
+ &retcode);
+ dstadr_sin = &rbufp->dstadr->sin;
+ NTOHL_FP(&pkt->org, &p_org);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+
+ /*
+ * Authentication is conditioned by three switches:
+ *
+ * NOPEER (RES_NOPEER) do not mobilize an association unless
+ * authenticated
+ * NOTRUST (RES_DONTTRUST) do not allow access unless
+ * authenticated (implies NOPEER)
+ * enable (sys_authenticate) master NOPEER switch, by default
+ * on
+ *
+ * The NOPEER and NOTRUST can be specified on a per-client basis
+ * using the restrict command. The enable switch if on implies
+ * NOPEER for all clients. There are four outcomes:
+ *
+ * NONE The packet has no MAC.
+ * OK the packet has a MAC and authentication succeeds
+ * ERROR the packet has a MAC and authentication fails
+ * CRYPTO crypto-NAK. The MAC has four octets only.
+ *
+ * Note: The AUTH(x, y) macro is used to filter outcomes. If x
+ * is zero, acceptable outcomes of y are NONE and OK. If x is
+ * one, the only acceptable outcome of y is OK.
+ */
+ if (has_mac == 0) {
+ is_authentic = AUTH_NONE; /* not required */
+#ifdef DEBUG
+ if (debug)
+ printf("receive: at %ld %s<-%s mode %d code %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, retcode,
+ is_authentic);
+#endif
+ } else if (has_mac == 4) {
+ is_authentic = AUTH_CRYPTO; /* crypto-NAK */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, retcode,
+ skeyid, authlen, has_mac, is_authentic);
+#endif
+ } else {
+#ifdef OPENSSL
+ /*
+ * For autokey modes, generate the session key
+ * and install in the key cache. Use the socket
+ * broadcast or unicast address as appropriate.
+ */
+ if (skeyid > NTP_MAXKEY) {
+
+ /*
+ * More on the autokey dance (AKD). A cookie is
+ * constructed from public and private values.
+ * For broadcast packets, the cookie is public
+ * (zero). For packets that match no
+ * association, the cookie is hashed from the
+ * addresses and private value. For server
+ * packets, the cookie was previously obtained
+ * from the server. For symmetric modes, the
+ * cookie was previously constructed using an
+ * agreement protocol; however, should PKI be
+ * unavailable, we construct a fake agreement as
+ * the EXOR of the peer and host cookies.
+ *
+ * hismode ephemeral persistent
+ * =======================================
+ * active 0 cookie#
+ * passive 0% cookie#
+ * client sys cookie 0%
+ * server 0% sys cookie
+ * broadcast 0 0
+ *
+ * # if unsync, 0
+ * % can't happen
+ */
+ if (hismode == MODE_BROADCAST) {
+
+ /*
+ * For broadcaster, use the interface
+ * broadcast address when available;
+ * otherwise, use the unicast address
+ * found when the association was
+ * mobilized. However, if this is from
+ * the wildcard interface, game over.
+ */
+ if (crypto_flags && rbufp->dstadr ==
+ any_interface) {
+ sys_restricted++;
+ return; /* no wildcard */
+ }
+ pkeyid = 0;
+ if (!SOCKNUL(&rbufp->dstadr->bcast))
+ dstadr_sin =
+ &rbufp->dstadr->bcast;
+ } else if (peer == NULL) {
+ pkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin, 0,
+ sys_private, 0);
+ } else {
+ pkeyid = peer->pcookie;
+ }
+
+ /*
+ * The session key includes both the public
+ * values and cookie. In case of an extension
+ * field, the cookie used for authentication
+ * purposes is zero. Note the hash is saved for
+ * use later in the autokey mambo.
+ */
+ if (authlen > LEN_PKT_NOMAC && pkeyid != 0) {
+ session_key(&rbufp->recv_srcadr,
+ dstadr_sin, skeyid, 0, 2);
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 0);
+ } else {
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 2);
+ }
+
+ }
+#endif /* OPENSSL */
+
+ /*
+ * Compute the cryptosum. Note a clogging attack may
+ * succeed in bloating the key cache. If an autokey,
+ * purge it immediately, since we won't be needing it
+ * again. If the packet is authentic, it can mobilize an
+ * association. Note that there is no key zero.
+ */
+ if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
+ has_mac)) {
+ is_authentic = AUTH_ERROR;
+ sys_badauth++;
+ } else {
+ is_authentic = AUTH_OK;
+ }
+#ifdef OPENSSL
+ if (skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, retcode,
+ skeyid, authlen, has_mac, is_authentic);
+#endif
+ }
+
+ /*
+ * The association matching rules are implemented by a set of
+ * routines and an association table. A packet matching an
+ * association is processed by the peer process for that
+ * association. If there are no errors, an ephemeral association
+ * is mobilized: a broadcast packet mobilizes a broadcast client
+ * aassociation; a manycast server packet mobilizes a manycast
+ * client association; a symmetric active packet mobilizes a
+ * symmetric passive association.
+ */
+ switch (retcode) {
+
+ /*
+ * This is a client mode packet not matching any association. If
+ * an ordinary client, simply toss a server mode packet back
+ * over the fence. If a manycast client, we have to work a
+ * little harder.
+ */
+ case AM_FXMIT:
+
+ /*
+ * The vanilla case is when this is not a multicast
+ * interface. If authentication succeeds, return a
+ * server mode packet; if not and the key ID is nonzero,
+ * return a crypto-NAK.
+ */
+ if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) {
+ if (AUTH(restrict_mask & RES_DONTTRUST,
+ is_authentic))
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+ else if (is_authentic == AUTH_ERROR)
+ fast_xmit(rbufp, MODE_SERVER, 0,
+ restrict_mask);
+ return; /* hooray */
+ }
+
+ /*
+ * This must be manycast. Do not respond if not
+ * configured as a manycast server.
+ */
+ if (!sys_manycastserver) {
+ sys_restricted++;
+ return; /* not enabled */
+ }
+
+ /*
+ * Do not respond if unsynchronized or stratum is below
+ * the floor or at or above the ceiling.
+ */
+ if (sys_leap == LEAP_NOTINSYNC || sys_stratum <
+ sys_floor || sys_stratum >= sys_ceiling)
+ return; /* bad stratum */
+
+ /*
+ * Do not respond if our stratum is greater than the
+ * manycaster or it has already synchronized to us.
+ */
+ if (sys_peer == NULL || hisstratum < sys_stratum ||
+ (sys_cohort && hisstratum == sys_stratum) ||
+ rbufp->dstadr->addr_refid == pkt->refid)
+ return; /* no help */
+
+ /*
+ * Respond only if authentication succeeds. Don't do a
+ * crypto-NAK, as that would not be useful.
+ */
+ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+
+ return; /* hooray */
+
+ /*
+ * This is a server mode packet returned in response to a client
+ * mode packet sent to a multicast group address. The origin
+ * timestamp is a good nonce to reliably associate the reply
+ * with what was sent. If there is no match, that's curious and
+ * could be an intruder attempting to clog, so we just ignore
+ * it.
+ *
+ * If the packet is authentic and the manycast association is
+ * found, we mobilize a client association and copy pertinent
+ * variables from the manycast association to the new client
+ * association. If not, just ignore the packet.
+ *
+ * There is an implosion hazard at the manycast client, since
+ * the manycast servers send the server packet immediately. If
+ * the guy is already here, don't fire up a duplicate.
+ */
+ case AM_MANYCAST:
+ if (!AUTH(sys_authenticate | (restrict_mask &
+ (RES_NOPEER | RES_DONTTRUST)), is_authentic))
+ return; /* bad auth */
+
+ if ((peer2 = findmanycastpeer(rbufp)) == NULL) {
+ sys_restricted++;
+ return; /* not enabled */
+ }
+ if ((peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_CLIENT,
+ hisversion, NTP_MINDPOLL, NTP_MAXDPOLL,
+ FLAG_IBURST | FLAG_PREEMPT, MDF_UCAST | MDF_ACLNT,
+ 0, skeyid)) == NULL)
+ return; /* system error */
+
+ /*
+ * We don't need these, but it warms the billboards.
+ */
+ peer->ttl = peer2->ttl;
+ break;
+
+ /*
+ * This is the first packet received from a broadcast server. If
+ * the packet is authentic and we are enabled as broadcast
+ * client, mobilize a broadcast client association. We don't
+ * kiss any frogs here.
+ */
+ case AM_NEWBCL:
+ if (!AUTH(sys_authenticate | (restrict_mask &
+ (RES_NOPEER | RES_DONTTRUST)), is_authentic))
+ return; /* bad auth */
+
+ /*
+ * Do not respond if unsynchronized or stratum is below
+ * the floor or at or above the ceiling.
+ */
+ if (hisleap == LEAP_NOTINSYNC || hisstratum <
+ sys_floor || hisstratum >= sys_ceiling)
+ return; /* bad stratum */
+
+ switch (sys_bclient) {
+
+ /*
+ * If not enabled, just skedaddle.
+ */
+ case 0:
+ sys_restricted++;
+ return; /* not enabled */
+
+ /*
+ * Execute the initial volley in order to calibrate the
+ * propagation delay and run the Autokey protocol, if
+ * enabled.
+ */
+ case 1:
+ if ((peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_CLIENT, hisversion,
+ NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_MCAST |
+ FLAG_IBURST, MDF_BCLNT, 0, skeyid)) ==
+ NULL)
+ return; /* system error */
+#ifdef OPENSSL
+ if (skeyid > NTP_MAXKEY)
+ crypto_recv(peer, rbufp);
+#endif /* OPENSSL */
+ return; /* hooray */
+
+
+ /*
+ * Do not execute the initial volley.
+ */
+ case 2:
+#ifdef OPENSSL
+ /*
+ * If a two-way exchange is not possible,
+ * neither is Autokey.
+ */
+ if (skeyid > NTP_MAXKEY) {
+ msyslog(LOG_INFO,
+ "receive: autokey requires two-way communication");
+ return; /* no autokey */
+ }
+#endif /* OPENSSL */
+ if ((peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_BCLIENT, hisversion,
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_BCLNT, 0,
+ skeyid)) == NULL)
+ return; /* system error */
+ }
+ break;
+
+ /*
+ * This is the first packet received from a symmetric active
+ * peer. If the packet is authentic and the first he sent,
+ * mobilize a passive association. If not, kiss the frog.
+ */
+ case AM_NEWPASS:
+
+ /*
+ * If the inbound packet is correctly authenticated and
+ * enabled, a symmetric passive association is
+ * mobilized. If not but correctly authenticated, a
+ * symmetric active response is sent. If authentication
+ * fails, send a crypto-NAK packet.
+ */
+ if (!AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
+ {
+ if (is_authentic == AUTH_ERROR)
+ fast_xmit(rbufp, MODE_ACTIVE, 0,
+ restrict_mask);
+ return; /* bad auth */
+ }
+ if (!AUTH(sys_authenticate | (restrict_mask &
+ RES_NOPEER), is_authentic)) {
+ fast_xmit(rbufp, MODE_ACTIVE, skeyid,
+ restrict_mask);
+ return; /* hooray */
+ }
+
+ /*
+ * Do not respond if stratum is below the floor.
+ */
+ if (hisstratum < sys_floor)
+ return; /* bad stratum */
+
+ if ((peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_PASSIVE, hisversion,
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_UCAST, 0,
+ skeyid)) == NULL)
+ return; /* system error */
+ break;
+
+ /*
+ * Process regular packet. Nothing special.
+ */
+ case AM_PROCPKT:
+ break;
+
+ /*
+ * A passive packet matches a passive association. This is
+ * usually the result of reconfiguring a client on the fly. As
+ * this association might be legitamate and this packet an
+ * attempt to deny service, just ignore it.
+ */
+ case AM_ERR:
+ return;
+
+ /*
+ * For everything else there is the bit bucket.
+ */
+ default:
+ return;
+ }
+ peer->flash &= ~PKT_TEST_MASK;
+
+ /*
+ * Next comes a rigorous schedule of timestamp checking. If the
+ * transmit timestamp is zero, the server is horribly broken.
+ */
+ if (L_ISZERO(&p_xmt)) {
+ return; /* read rfc1305 */
+
+ /*
+ * If the transmit timestamp duplicates a previous one, the
+ * packet is a replay. This prevents the bad guys from replaying
+ * the most recent packet, authenticated or not.
+ */
+ } else if (L_ISEQU(&peer->org, &p_xmt)) {
+ peer->flash |= TEST1;
+ peer->oldpkt++;
+ return; /* duplicate packet */
+
+
+ /*
+ * If this is a broadcast mode packet, skip further checking.
+ */
+ } else if (hismode != MODE_BROADCAST) {
+ if (L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* protocol unsynch */
+ else if (!L_ISEQU(&p_org, &peer->xmt))
+ peer->flash |= TEST2; /* bogus packet */
+ }
+
+ /*
+ * Update the origin and destination timestamps. If
+ * unsynchronized or bogus abandon ship. If the crypto machine
+ * breaks, light the crypto bit and plaint the log.
+ */
+ peer->org = p_xmt;
+ peer->rec = rbufp->recv_time;
+ if (peer->flash & PKT_TEST_MASK) {
+#ifdef OPENSSL
+ if (crypto_flags && (peer->flags & FLAG_SKEY)) {
+ rval = crypto_recv(peer, rbufp);
+ if (rval != XEVNT_OK) {
+ peer_clear(peer, "CRYP");
+ peer->flash |= TEST9; /* crypto error */
+ }
+ }
+#endif /* OPENSSL */
+ return; /* unsynch */
+ }
+
+ /*
+ * The timestamps are valid and the receive packet matches the
+ * last one sent. If the packet is a crypto-NAK, the server
+ * might have just changed keys. We reset the association
+ * and restart the protocol.
+ */
+ if (is_authentic == AUTH_CRYPTO) {
+ peer_clear(peer, "AUTH");
+ return; /* crypto-NAK */
+
+ /*
+ * If the association is authenticated, the key ID is nonzero
+ * and received packets must be authenticated. This is designed
+ * to avoid a bait-and-switch attack, which was possible in past
+ * versions. If symmetric modes, return a crypto-NAK. The peer
+ * should restart the protocol.
+ */
+ } else if (!AUTH(peer->keyid || (restrict_mask & RES_DONTTRUST),
+ is_authentic)) {
+ peer->flash |= TEST5;
+ if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE)
+ fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask);
+ return; /* bad auth */
+ }
+
+ /*
+ * That was hard and I am sweaty, but the packet is squeaky
+ * clean. Get on with real work.
+ */
+ peer->received++;
+ peer->timereceived = current_time;
+ if (is_authentic == AUTH_OK)
+ peer->flags |= FLAG_AUTHENTIC;
+ else
+ peer->flags &= ~FLAG_AUTHENTIC;
+#ifdef OPENSSL
+ /*
+ * More autokey dance. The rules of the cha-cha are as follows:
+ *
+ * 1. If there is no key or the key is not auto, do nothing.
+ *
+ * 2. If this packet is in response to the one just previously
+ * sent or from a broadcast server, do the extension fields.
+ * Otherwise, assume bogosity and bail out.
+ *
+ * 3. If an extension field contains a verified signature, it is
+ * self-authenticated and we sit the dance.
+ *
+ * 4. If this is a server reply, check only to see that the
+ * transmitted key ID matches the received key ID.
+ *
+ * 5. Check to see that one or more hashes of the current key ID
+ * matches the previous key ID or ultimate original key ID
+ * obtained from the broadcaster or symmetric peer. If no
+ * match, sit the dance and wait for timeout.
+ *
+ * In case of crypto error, fire the orchestra and stop dancing.
+ * This is considered a permanant error, so light the crypto bit
+ * to suppress further requests. If preemptable or ephemeral,
+ * scuttle the ship.
+ */
+ if (crypto_flags && (peer->flags & FLAG_SKEY)) {
+ peer->flash |= TEST8;
+ rval = crypto_recv(peer, rbufp);
+ if (rval != XEVNT_OK) {
+ peer_clear(peer, "CRYP");
+ peer->flash |= TEST9; /* crypto error */
+ if (peer->flags & FLAG_PREEMPT ||
+ !(peer->flags & FLAG_CONFIG))
+ unpeer(peer);
+ return;
+
+ } else if (hismode == MODE_SERVER) {
+ if (skeyid == peer->keyid)
+ peer->flash &= ~TEST8;
+ } else if (!(peer->flash & TEST8)) {
+ peer->pkeyid = skeyid;
+ } else if ((ap = (struct autokey *)peer->recval.ptr) !=
+ NULL) {
+ int i;
+
+ for (i = 0; ; i++) {
+ if (tkeyid == peer->pkeyid ||
+ tkeyid == ap->key) {
+ peer->flash &= ~TEST8;
+ peer->pkeyid = skeyid;
+ break;
+ }
+ if (i > ap->seq)
+ break;
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ tkeyid, pkeyid, 0);
+ }
+ }
+ if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 9 */
+ peer->flash |= TEST8; /* not proventic */
+
+ /*
+ * If the transmit queue is nonempty, clamp the host
+ * poll interval to the packet poll interval.
+ */
+ if (peer->cmmd != 0) {
+ peer->ppoll = pkt->ppoll;
+ poll_update(peer, peer->hpoll);
+ }
+ }
+#endif /* OPENSSL */
+
+ /*
+ * The dance is complete and the flash bits have been lit. Toss
+ * the packet over the fence for processing, which may light up
+ * more flashers.
+ */
+ process_packet(peer, pkt);
+
+ /*
+ * Well, that was nice. If TEST4 is lit, either the crypto
+ * machine jammed or a kiss-o'-death packet flew in, either of
+ * which is fatal.
+ */
+ if (peer->flash & TEST4) {
+ msyslog(LOG_INFO, "receive: fatal error %04x for %s",
+ peer->flash, stoa(&peer->srcadr));
+ return;
+ }
+}
+
+
+/*
+ * process_packet - Packet Procedure, a la Section 3.4.4 of the
+ * specification. Or almost, at least. If we're in here we have a
+ * reasonable expectation that we will be having a long term
+ * relationship with this host.
+ */
+void
+process_packet(
+ register struct peer *peer,
+ register struct pkt *pkt
+ )
+{
+ double t34, t21;
+ double p_offset, p_del, p_disp;
+ l_fp p_rec, p_xmt, p_org, p_reftime;
+ l_fp ci;
+ u_char pmode, pleap, pstratum;
+
+ sys_processed++;
+ peer->processed++;
+ p_del = FPTOD(NTOHS_FP(pkt->rootdelay));
+ p_disp = FPTOD(NTOHS_FP(pkt->rootdispersion));
+ NTOHL_FP(&pkt->reftime, &p_reftime);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+ pmode = PKT_MODE(pkt->li_vn_mode);
+ pleap = PKT_LEAP(pkt->li_vn_mode);
+ if (pmode != MODE_BROADCAST)
+ NTOHL_FP(&pkt->org, &p_org);
+ else
+ p_org = peer->rec;
+ pstratum = PKT_TO_STRATUM(pkt->stratum);
+
+ /*
+ * Test for kiss-o'death packet)
+ */
+ if (pleap == LEAP_NOTINSYNC && pstratum == STRATUM_UNSPEC) {
+ if (memcmp(&pkt->refid, "DENY", 4) == 0) {
+ peer_clear(peer, "DENY");
+ peer->flash |= TEST4; /* access denied */
+ }
+ }
+
+ /*
+ * Capture the header values.
+ */
+ record_raw_stats(&peer->srcadr, peer->dstadr ? &peer->dstadr->sin : NULL, &p_org,
+ &p_rec, &p_xmt, &peer->rec);
+ peer->leap = pleap;
+ peer->stratum = min(pstratum, STRATUM_UNSPEC);
+ peer->pmode = pmode;
+ peer->ppoll = pkt->ppoll;
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_del;
+ peer->rootdispersion = p_disp;
+ peer->refid = pkt->refid; /* network byte order */
+ peer->reftime = p_reftime;
+
+ /*
+ * Verify the server is synchronized; that is, the leap bits and
+ * stratum are valid, the root delay and root dispersion are
+ * valid and the reference timestamp is not later than the
+ * transmit timestamp.
+ */
+ if (pleap == LEAP_NOTINSYNC || /* test 6 */
+ pstratum < sys_floor || pstratum >= sys_ceiling)
+ peer->flash |= TEST6; /* peer not synch */
+ if (p_del < 0 || p_disp < 0 || p_del / /* test 7 */
+ 2 + p_disp >= MAXDISPERSE || !L_ISHIS(&p_xmt, &p_reftime))
+ peer->flash |= TEST7; /* bad header */
+
+ /*
+ * If any tests fail at this point, the packet is discarded.
+ * Note that some flashers may have already been set in the
+ * receive() routine.
+ */
+ if (peer->flash & PKT_TEST_MASK) {
+#ifdef DEBUG
+ if (debug)
+ printf("packet: flash header %04x\n",
+ peer->flash);
+#endif
+ return;
+ }
+ if (!(peer->reach)) {
+ report_event(EVNT_REACH, peer);
+ peer->timereachable = current_time;
+ }
+ poll_update(peer, peer->hpoll);
+ peer->reach |= 1;
+
+ /*
+ * For a client/server association, calculate the clock offset,
+ * roundtrip delay and dispersion. The equations are reordered
+ * from the spec for more efficient use of temporaries. For a
+ * broadcast association, offset the last measurement by the
+ * computed delay during the client/server volley. Note that
+ * org has been set to the time of last reception. Note the
+ * computation of dispersion includes the system precision plus
+ * that due to the frequency error since the origin time.
+ *
+ * It is very important to respect the hazards of overflow. The
+ * only permitted operation on raw timestamps is subtraction,
+ * where the result is a signed quantity spanning from 68 years
+ * in the past to 68 years in the future. To avoid loss of
+ * precision, these calculations are done using 64-bit integer
+ * arithmetic. However, the offset and delay calculations are
+ * sums and differences of these first-order differences, which
+ * if done using 64-bit integer arithmetic, would be valid over
+ * only half that span. Since the typical first-order
+ * differences are usually very small, they are converted to 64-
+ * bit doubles and all remaining calculations done in floating-
+ * point arithmetic. This preserves the accuracy while retaining
+ * the 68-year span.
+ *
+ * Let t1 = p_org, t2 = p_rec, t3 = p_xmt, t4 = peer->rec:
+ */
+ ci = p_xmt; /* t3 - t4 */
+ L_SUB(&ci, &peer->rec);
+ LFPTOD(&ci, t34);
+ ci = p_rec; /* t2 - t1 */
+ L_SUB(&ci, &p_org);
+ LFPTOD(&ci, t21);
+ ci = peer->rec; /* t4 - t1 */
+ L_SUB(&ci, &p_org);
+
+ /*
+ * If running in a broadcast association, the clock offset is
+ * (t1 - t0) corrected by the one-way delay, but we can't
+ * measure that directly. Therefore, we start up in MODE_CLIENT
+ * mode, set FLAG_MCAST and exchange eight messages to determine
+ * the clock offset. When the last message is sent, we switch to
+ * MODE_BCLIENT mode. The next broadcast message after that
+ * computes the broadcast offset and clears FLAG_MCAST.
+ */
+ if (pmode == MODE_BROADCAST) {
+ p_offset = t34;
+ if (peer->flags & FLAG_MCAST) {
+ peer->estbdelay = peer->offset - p_offset;
+ if (peer->hmode == MODE_CLIENT)
+ return;
+
+ peer->flags &= ~(FLAG_MCAST | FLAG_BURST);
+ }
+ p_offset += peer->estbdelay;
+ p_del = peer->delay;
+ p_disp = 0;
+ } else {
+ p_offset = (t21 + t34) / 2.;
+ p_del = t21 - t34;
+ LFPTOD(&ci, p_disp);
+ p_disp = LOGTOD(sys_precision) +
+ LOGTOD(peer->precision) + clock_phi * p_disp;
+ }
+ p_del = max(p_del, LOGTOD(sys_precision));
+ clock_filter(peer, p_offset, p_del, p_disp);
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, peer->disp, peer->jitter);
+}
+
+
+/*
+ * clock_update - Called at system process update intervals.
+ */
+static void
+clock_update(void)
+{
+ u_char oleap;
+ u_char ostratum;
+ double dtemp;
+
+ /*
+ * There must be a system peer at this point. If we just changed
+ * the system peer, but have a newer sample from the old one,
+ * wait until newer data are available.
+ */
+ if (sys_poll < sys_peer->minpoll)
+ sys_poll = sys_peer->minpoll;
+ if (sys_poll > sys_peer->maxpoll)
+ sys_poll = sys_peer->maxpoll;
+ poll_update(sys_peer, sys_poll);
+ if (sys_peer->epoch <= sys_clocktime)
+ return;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clock_update: at %ld assoc %d \n", current_time,
+ peer_associations);
+#endif
+ oleap = sys_leap;
+ ostratum = sys_stratum;
+ switch (local_clock(sys_peer, sys_offset)) {
+
+ /*
+ * Clock exceeds panic threshold. Life as we know it ends.
+ */
+ case -1:
+ report_event(EVNT_SYSFAULT, NULL);
+ exit (-1);
+ /* not reached */
+
+ /*
+ * Clock was stepped. Flush all time values of all peers.
+ */
+ case 2:
+ clear_all();
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ sys_peer = NULL;
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ memcpy(&sys_refid, "STEP", 4);
+ report_event(EVNT_CLOCKRESET, NULL);
+ break;
+
+ /*
+ * Clock was slewed. Update the system stratum, leap bits, root
+ * delay, root dispersion, reference ID and reference time. If
+ * the leap changes, we gotta reroll the keys. Except for
+ * reference clocks, the minimum dispersion increment is not
+ * less than sys_mindisp.
+ */
+ case 1:
+ sys_leap = leap_next;
+ sys_stratum = min(sys_peer->stratum + 1,
+ STRATUM_UNSPEC);
+ sys_reftime = sys_peer->rec;
+
+ /*
+ * In orphan mode the stratum defaults to the orphan
+ * stratum. The root delay is set to a random value
+ * generated at startup. The root dispersion is set from
+ * the peer dispersion; the peer root dispersion is
+ * ignored.
+ */
+ dtemp = sys_peer->disp + clock_phi * (current_time -
+ sys_peer->update) + sys_jitter +
+ fabs(sys_peer->offset);
+#ifdef REFCLOCK
+ if (!(sys_peer->flags & FLAG_REFCLOCK) && dtemp <
+ sys_mindisp)
+ dtemp = sys_mindisp;
+#else
+ if (dtemp < sys_mindisp)
+ dtemp = sys_mindisp;
+#endif /* REFCLOCK */
+ if (sys_stratum >= sys_orphan) {
+ sys_stratum = sys_orphan;
+ sys_rootdelay = sys_peer->delay;
+ sys_rootdispersion = dtemp;
+ } else {
+ sys_rootdelay = sys_peer->delay +
+ sys_peer->rootdelay;
+ sys_rootdispersion = dtemp +
+ sys_peer->rootdispersion;
+ }
+ if (oleap == LEAP_NOTINSYNC) {
+ report_event(EVNT_SYNCCHG, NULL);
+#ifdef OPENSSL
+ expire_all();
+ crypto_update();
+#endif /* OPENSSL */
+ }
+ break;
+ /*
+ * Popcorn spike or step threshold exceeded. Pretend it never
+ * happened.
+ */
+ default:
+ break;
+ }
+ if (ostratum != sys_stratum)
+ report_event(EVNT_PEERSTCHG, NULL);
+}
+
+
+/*
+ * poll_update - update peer poll interval
+ */
+void
+poll_update(
+ struct peer *peer,
+ int mpoll
+ )
+{
+ int hpoll;
+
+ /*
+ * This routine figures out when the next poll should be sent.
+ * That turns out to be wickedly complicated. The big problem is
+ * that sometimes the time for the next poll is in the past.
+ * Watch out for races here between the receive process and the
+ * poll process. The key assertion is that, if nextdate equals
+ * current_time, the call is from the poll process; otherwise,
+ * it is from the receive process.
+ *
+ * First, bracket the poll interval according to the type of
+ * association and options. If a fixed interval is configured,
+ * use minpoll. This primarily is for reference clocks, but
+ * works for any association.
+ */
+ if (peer->flags & FLAG_FIXPOLL) {
+ hpoll = peer->minpoll;
+
+ /*
+ * The ordinary case; clamp the poll interval between minpoll
+ * and maxpoll.
+ */
+ } else {
+ hpoll = max(min(peer->maxpoll, mpoll), peer->minpoll);
+ }
+#ifdef OPENSSL
+ /*
+ * Bit of crass arrogance at this point. If the poll interval
+ * has changed and we have a keylist, the lifetimes in the
+ * keylist are probably bogus. In this case purge the keylist
+ * and regenerate it later.
+ */
+ if (hpoll != peer->hpoll)
+ key_expire(peer);
+#endif /* OPENSSL */
+ peer->hpoll = hpoll;
+
+ /*
+ * Now we figure out if there is an override. If during the
+ * crypto protocol and a message is pending, make it wait not
+ * more than two seconds.
+ */
+#ifdef OPENSSL
+ if (peer->cmmd != NULL && (sys_leap != LEAP_NOTINSYNC ||
+ peer->crypto)) {
+ peer->nextdate = current_time + RESP_DELAY;
+
+ /*
+ * If we get called from the receive routine while a burst is
+ * pending, just slink away. If from the poll routine and a
+ * reference clock or a pending crypto response, delay for one
+ * second. If this is the first sent in a burst, wait for the
+ * modem to come up. For others in the burst, delay two seconds.
+ */
+ } else if (peer->burst > 0) {
+#else /* OPENSSL */
+ if (peer->burst > 0) {
+#endif /* OPENSSL */
+ if (peer->nextdate != current_time)
+ return;
+#ifdef REFCLOCK
+ else if (peer->flags & FLAG_REFCLOCK)
+ peer->nextdate += RESP_DELAY;
+#endif /* REFCLOCK */
+ else if (peer->flags & (FLAG_IBURST | FLAG_BURST) &&
+ peer->burst == NTP_BURST)
+ peer->nextdate += sys_calldelay;
+ else
+ peer->nextdate += BURST_DELAY;
+ /*
+ * The ordinary case; use the minimum of the host and peer
+ * intervals, but not less than minpoll. In other words,
+ * oversampling is okay but understampling is evil.
+ */
+ } else {
+ peer->nextdate = peer->outdate +
+ RANDPOLL(max(min(peer->ppoll, hpoll),
+ peer->minpoll));
+ }
+
+ /*
+ * If the time for the next poll has already happened, bring it
+ * up to the next second after this one. This way the only way
+ * to get nexdate == current time is from the poll routine.
+ */
+ if (peer->nextdate <= current_time)
+ peer->nextdate = current_time + 1;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("poll_update: at %lu %s flags %04x poll %d burst %d last %lu next %lu\n",
+ current_time, ntoa(&peer->srcadr), peer->flags,
+ peer->hpoll, peer->burst, peer->outdate,
+ peer->nextdate);
+#endif
+}
+
+/*
+ * peer_crypto_clear - discard crypto information
+ */
+void
+peer_crypto_clear(
+ struct peer *peer
+ )
+{
+ /*
+ * If cryptographic credentials have been acquired, toss them to
+ * Valhalla. Note that autokeys are ephemeral, in that they are
+ * tossed immediately upon use. Therefore, the keylist can be
+ * purged anytime without needing to preserve random keys. Note
+ * that, if the peer is purged, the cryptographic variables are
+ * purged, too. This makes it much harder to sneak in some
+ * unauthenticated data in the clock filter.
+ */
+ DPRINTF(1, ("peer_crypto_clear: at %ld next %ld assoc ID %d\n",
+ current_time, peer->nextdate, peer->associd));
+
+#ifdef OPENSSL
+ peer->assoc = 0;
+ peer->crypto = 0;
+
+ if (peer->pkey != NULL)
+ EVP_PKEY_free(peer->pkey);
+ peer->pkey = NULL;
+
+ peer->digest = NULL; /* XXX MEMLEAK? check whether this needs to be freed in any way - never was freed */
+
+ if (peer->subject != NULL)
+ free(peer->subject);
+ peer->subject = NULL;
+
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ peer->issuer = NULL;
+
+ peer->pkeyid = 0;
+
+ peer->pcookie = 0;
+
+ if (peer->ident_pkey != NULL)
+ EVP_PKEY_free(peer->ident_pkey);
+ peer->ident_pkey = NULL;
+
+ memset(&peer->fstamp, 0, sizeof(peer->fstamp));
+
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+
+ if (peer->grpkey != NULL)
+ BN_free(peer->grpkey);
+ peer->grpkey = NULL;
+
+ value_free(&peer->cookval);
+ value_free(&peer->recval);
+
+ if (peer->cmmd != NULL) {
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ }
+
+ key_expire(peer);
+
+ value_free(&peer->encrypt);
+#endif /* OPENSSL */
+}
+
+/*
+ * peer_clear - clear peer filter registers. See Section 3.4.8 of the spec.
+ */
+void
+peer_clear(
+ struct peer *peer, /* peer structure */
+ char *ident /* tally lights */
+ )
+{
+ int i;
+
+ peer_crypto_clear(peer);
+
+ if (peer == sys_peer)
+ sys_peer = NULL;
+
+ /*
+ * Wipe the association clean and initialize the nonzero values.
+ */
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO);
+ peer->estbdelay = sys_bdelay;
+ peer->ppoll = peer->maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->disp = MAXDISPERSE;
+ peer->jitter = LOGTOD(sys_precision);
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = i;
+ peer->filter_disp[i] = MAXDISPERSE;
+ }
+#ifdef REFCLOCK
+ if (!(peer->flags & FLAG_REFCLOCK)) {
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ memcpy(&peer->refid, ident, 4);
+ }
+#else
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ memcpy(&peer->refid, ident, 4);
+#endif /* REFCLOCK */
+
+ /*
+ * During initialization use the association count to spread out
+ * the polls at one-second intervals. Othersie, randomize over
+ * the minimum poll interval in order to avoid broadcast
+ * implosion.
+ */
+ peer->nextdate = peer->update = peer->outdate = current_time;
+ if (initializing)
+ peer->nextdate += peer_associations;
+ else if (peer->hmode == MODE_PASSIVE)
+ peer->nextdate += RESP_DELAY;
+ else
+ peer->nextdate += (ntp_random() & ((1 << NTP_MINDPOLL) -
+ 1));
+
+ DPRINTF(1, ("peer_clear: at %ld next %ld assoc ID %d refid %s\n",
+ current_time, peer->nextdate, peer->associd, ident));
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(
+ struct peer *peer, /* peer structure pointer */
+ double sample_offset, /* clock offset */
+ double sample_delay, /* roundtrip delay */
+ double sample_disp /* dispersion */
+ )
+{
+ double dst[NTP_SHIFT]; /* distance vector */
+ int ord[NTP_SHIFT]; /* index vector */
+ int i, j, k, m;
+ double dtemp, etemp;
+
+ /*
+ * Shift the new sample into the register and discard the oldest
+ * one. The new offset and delay come directly from the
+ * timestamp calculations. The dispersion grows from the last
+ * outbound packet or reference clock update to the present time
+ * and increased by the sum of the peer precision and the system
+ * precision. The delay can sometimes swing negative due to
+ * frequency skew, so it is clamped non-negative.
+ */
+ j = peer->filter_nextpt;
+ peer->filter_offset[j] = sample_offset;
+ peer->filter_delay[j] = max(0, sample_delay);
+ peer->filter_disp[j] = sample_disp;
+ peer->filter_epoch[j] = current_time;
+ j = (j + 1) % NTP_SHIFT;
+ peer->filter_nextpt = j;
+
+ /*
+ * Update dispersions since the last update and at the same
+ * time initialize the distance and index lists. The distance
+ * list uses a compound metric. If the sample is valid and
+ * younger than the minimum Allan intercept, use delay;
+ * otherwise, use biased dispersion.
+ */
+ dtemp = clock_phi * (current_time - peer->update);
+ peer->update = current_time;
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ if (i != 0)
+ peer->filter_disp[j] += dtemp;
+ if (peer->filter_disp[j] >= MAXDISPERSE)
+ peer->filter_disp[j] = MAXDISPERSE;
+ if (peer->filter_disp[j] >= MAXDISPERSE)
+ dst[i] = MAXDISPERSE;
+ else if (peer->update - peer->filter_epoch[j] >
+ allan_xpt)
+ dst[i] = sys_maxdist + peer->filter_disp[j];
+ else
+ dst[i] = peer->filter_delay[j];
+ ord[i] = j;
+ j++; j %= NTP_SHIFT;
+ }
+
+ /*
+ * If the clock discipline has stabilized, sort the samples in
+ * both lists by distance. Note, we do not displace a higher
+ * distance sample by a lower distance one unless lower by at
+ * least the precision.
+ */
+ if (state == 4) {
+ for (i = 1; i < NTP_SHIFT; i++) {
+ for (j = 0; j < i; j++) {
+ if (dst[j] > dst[i] +
+ LOGTOD(sys_precision)) {
+ k = ord[j];
+ ord[j] = ord[i];
+ ord[i] = k;
+ etemp = dst[j];
+ dst[j] = dst[i];
+ dst[i] = etemp;
+ }
+ }
+ }
+ }
+
+ /*
+ * Copy the index list to the association structure so ntpq
+ * can see it later. Prune the distance list to samples less
+ * than max distance, but keep at least two valid samples for
+ * jitter calculation.
+ */
+ m = 0;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = (u_char) ord[i];
+ if (dst[i] >= MAXDISPERSE || (m >= 2 && dst[i] >=
+ sys_maxdist))
+ continue;
+ m++;
+ }
+
+ /*
+ * Compute the dispersion and jitter. The dispersion is weighted
+ * exponentially by NTP_FWEIGHT (0.5) so it is normalized close
+ * to 1.0. The jitter is the RMS differences relative to the
+ * lowest delay sample. If no acceptable samples remain in the
+ * shift register, quietly tiptoe home leaving only the
+ * dispersion.
+ */
+ peer->disp = peer->jitter = 0;
+ k = ord[0];
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ j = ord[i];
+ peer->disp = NTP_FWEIGHT * (peer->disp +
+ peer->filter_disp[j]);
+ if (i < m)
+ peer->jitter += DIFF(peer->filter_offset[j],
+ peer->filter_offset[k]);
+ }
+
+ /*
+ * If no acceptable samples remain in the shift register,
+ * quietly tiptoe home leaving only the dispersion. Otherwise,
+ * save the offset, delay and jitter. Note the jitter must not
+ * be less than the precision.
+ */
+ if (m == 0)
+ return;
+
+ etemp = fabs(peer->offset - peer->filter_offset[k]);
+ peer->offset = peer->filter_offset[k];
+ peer->delay = peer->filter_delay[k];
+ if (m > 1)
+ peer->jitter /= m - 1;
+ peer->jitter = max(SQRT(peer->jitter), LOGTOD(sys_precision));
+
+ /*
+ * A new sample is useful only if it is younger than the last
+ * one used. Note the order is FIFO if the clock discipline has
+ * not stabilized.
+ */
+ if (peer->filter_epoch[k] <= peer->epoch) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: discard %lu\n",
+ peer->epoch - peer->filter_epoch[k]);
+#endif
+ return;
+ }
+
+ /*
+ * If the difference between the last offset and the current one
+ * exceeds the jitter by CLOCK_SGATE and the interval since the
+ * last update is less than twice the system poll interval,
+ * consider the update a popcorn spike and ignore it.
+ */
+ if (etemp > CLOCK_SGATE * peer->jitter && m > 1 &&
+ peer->filter_epoch[k] - peer->epoch < 2. *
+ ULOGTOD(sys_poll)) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: popcorn %.6f %.6f\n",
+ etemp, dtemp);
+#endif
+ return;
+ }
+
+ /*
+ * The mitigated sample statistics are saved for later
+ * processing. If not in a burst, tickle the select.
+ */
+ peer->epoch = peer->filter_epoch[k];
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f, age %lu\n",
+ m, peer->offset, peer->delay, peer->disp,
+ peer->jitter, current_time - peer->epoch);
+#endif
+ if (peer->burst == 0 || sys_leap == LEAP_NOTINSYNC)
+ clock_select();
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ *
+ * LOCKCLOCK: If the local clock is the prefer peer, it will always be
+ * enabled, even if declared falseticker, (2) only the prefer peer can
+ * be selected as the system peer, (3) if the external source is down,
+ * the system leap bits are set to 11 and the stratum set to infinity.
+ */
+void
+clock_select(void)
+{
+ struct peer *peer;
+ int i, j, k, n;
+ int nlist, nl3;
+
+ int allow, osurv;
+ double d, e, f, g;
+ double high, low;
+ double synch[NTP_MAXASSOC], error[NTP_MAXASSOC];
+ struct peer *osys_peer;
+ struct peer *typeacts = NULL;
+ struct peer *typelocal = NULL;
+ struct peer *typesystem = NULL;
+
+ static int list_alloc = 0;
+ static struct endpoint *endpoint = NULL;
+ static int *indx = NULL;
+ static struct peer **peer_list = NULL;
+ static u_int endpoint_size = 0;
+ static u_int indx_size = 0;
+ static u_int peer_list_size = 0;
+
+ /*
+ * Initialize and create endpoint, index and peer lists big
+ * enough to handle all associations.
+ */
+ osys_peer = sys_peer;
+ sys_peer = NULL;
+ sys_pps = NULL;
+ sys_prefer = NULL;
+ osurv = sys_survivors;
+ sys_survivors = 0;
+#ifdef LOCKCLOCK
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "DOWN", 4);
+#endif /* LOCKCLOCK */
+ nlist = 0;
+ for (n = 0; n < NTP_HASH_SIZE; n++)
+ nlist += peer_hash_count[n];
+ if (nlist > list_alloc) {
+ if (list_alloc > 0) {
+ free(endpoint);
+ free(indx);
+ free(peer_list);
+ }
+ while (list_alloc < nlist) {
+ list_alloc += 5;
+ endpoint_size += 5 * 3 * sizeof(*endpoint);
+ indx_size += 5 * 3 * sizeof(*indx);
+ peer_list_size += 5 * sizeof(*peer_list);
+ }
+ endpoint = (struct endpoint *)emalloc(endpoint_size);
+ indx = (int *)emalloc(indx_size);
+ peer_list = (struct peer **)emalloc(peer_list_size);
+ }
+
+ /*
+ * Initially, we populate the island with all the rifraff peers
+ * that happen to be lying around. Those with seriously
+ * defective clocks are immediately booted off the island. Then,
+ * the falsetickers are culled and put to sea. The truechimers
+ * remaining are subject to repeated rounds where the most
+ * unpopular at each round is kicked off. When the population
+ * has dwindled to sys_minclock, the survivors split a million
+ * bucks and collectively crank the chimes.
+ */
+ nlist = nl3 = 0; /* none yet */
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != NULL; peer =
+ peer->next) {
+ peer->flags &= ~FLAG_SYSPEER;
+ peer->status = CTL_PST_SEL_REJECT;
+
+ /*
+ * Leave the island immediately if the peer is
+ * unfit to synchronize.
+ */
+ if (peer_unfit(peer))
+ continue;
+
+ /*
+ * Don't allow the local clock or modem drivers
+ * in the kitchen at this point, unless the
+ * prefer peer. Do that later, but only if
+ * nobody else is around. These guys are all
+ * configured, so we never throw them away.
+ */
+#ifdef REFCLOCK
+ if (peer->refclktype == REFCLK_LOCALCLOCK
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ /* wjm: VMS_LOCALUNIT taken seriously */
+ && REFCLOCKUNIT(&peer->srcadr) !=
+ VMS_LOCALUNIT
+#endif /* VMS && VMS_LOCALUNIT */
+ ) {
+ typelocal = peer;
+#ifndef LOCKCLOCK
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no local clock */
+#endif /* LOCKCLOCK */
+ }
+ if (peer->sstclktype == CTL_SST_TS_TELEPHONE) {
+ typeacts = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no acts */
+ }
+#endif /* REFCLOCK */
+
+ /*
+ * If we get this far, the peer can stay on the
+ * island, but does not yet have the immunity
+ * idol.
+ */
+ peer->status = CTL_PST_SEL_SANE;
+ peer_list[nlist++] = peer;
+
+ /*
+ * Insert each interval endpoint on the sorted
+ * list.
+ */
+ e = peer->offset; /* Upper end */
+ f = root_distance(peer);
+ e = e + f;
+ for (i = nl3 - 1; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+
+ indx[i + 3] = indx[i];
+ }
+ indx[i + 3] = nl3;
+ endpoint[nl3].type = 1;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Center point */
+ for (; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+
+ indx[i + 2] = indx[i];
+ }
+ indx[i + 2] = nl3;
+ endpoint[nl3].type = 0;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Lower end */
+ for (; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+
+ indx[i + 1] = indx[i];
+ }
+ indx[i + 1] = nl3;
+ endpoint[nl3].type = -1;
+ endpoint[nl3++].val = e;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nl3; i++)
+ printf("select: endpoint %2d %.6f\n",
+ endpoint[indx[i]].type,
+ endpoint[indx[i]].val);
+#endif
+ /*
+ * This is the actual algorithm that cleaves the truechimers
+ * from the falsetickers. The original algorithm was described
+ * in Keith Marzullo's dissertation, but has been modified for
+ * better accuracy.
+ *
+ * Briefly put, we first assume there are no falsetickers, then
+ * scan the candidate list first from the low end upwards and
+ * then from the high end downwards. The scans stop when the
+ * number of intersections equals the number of candidates less
+ * the number of falsetickers. If this doesn't happen for a
+ * given number of falsetickers, we bump the number of
+ * falsetickers and try again. If the number of falsetickers
+ * becomes equal to or greater than half the number of
+ * candidates, the Albanians have won the Byzantine wars and
+ * correct synchronization is not possible.
+ *
+ * Here, nlist is the number of candidates and allow is the
+ * number of falsetickers. Upon exit, the truechimers are the
+ * susvivors with offsets not less than low and not greater than
+ * high. There may be none of them.
+ */
+ low = 1e9;
+ high = -1e9;
+ for (allow = 0; 2 * allow < nlist; allow++) {
+ int found;
+
+ /*
+ * Bound the interval (low, high) as the largest
+ * interval containing points from presumed truechimers.
+ */
+ found = 0;
+ n = 0;
+ for (i = 0; i < nl3; i++) {
+ low = endpoint[indx[i]].val;
+ n -= endpoint[indx[i]].type;
+ if (n >= nlist - allow)
+ break;
+ if (endpoint[indx[i]].type == 0)
+ found++;
+ }
+ n = 0;
+ for (j = nl3 - 1; j >= 0; j--) {
+ high = endpoint[indx[j]].val;
+ n += endpoint[indx[j]].type;
+ if (n >= nlist - allow)
+ break;
+ if (endpoint[indx[j]].type == 0)
+ found++;
+ }
+
+ /*
+ * If the number of candidates found outside the
+ * interval is greater than the number of falsetickers,
+ * then at least one truechimer is outside the interval,
+ * so go around again. This is what makes this algorithm
+ * different than Marzullo's.
+ */
+ if (found > allow)
+ continue;
+
+ /*
+ * If an interval containing truechimers is found, stop.
+ * If not, increase the number of falsetickers and go
+ * around again.
+ */
+ if (high > low)
+ break;
+ }
+
+ /*
+ * Clustering algorithm. Construct candidate list in order first
+ * by stratum then by root distance, but keep only the best
+ * NTP_MAXASSOC of them. Scan the list to find falsetickers, who
+ * leave the island immediately. The TRUE peer is always a
+ * truechimer. We must leave at least one peer to collect the
+ * million bucks. If in orphan mode, rascals found with lower
+ * stratum are guaranteed a seat on the bus.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ if (nlist > 1 && (peer->offset <= low || peer->offset >=
+ high) && !(peer->flags & FLAG_TRUE) &&
+ !(sys_stratum >= sys_orphan && peer->stratum <
+ sys_orphan))
+ continue;
+
+ peer->status = CTL_PST_SEL_DISTSYSPEER;
+
+ /*
+ * The order metric is formed from the stratum times
+ * max distance (1.) plus the root distance. It strongly
+ * favors the lowest stratum, but a higher stratum peer
+ * can capture the clock if the low stratum dominant
+ * hasn't been heard for awhile.
+ */
+ d = root_distance(peer) + peer->stratum * sys_maxdist;
+ if (j >= NTP_MAXASSOC) {
+ if (d >= synch[j - 1])
+ continue;
+ else
+ j--;
+ }
+ for (k = j; k > 0; k--) {
+ if (d >= synch[k - 1])
+ break;
+
+ peer_list[k] = peer_list[k - 1];
+ error[k] = error[k - 1];
+ synch[k] = synch[k - 1];
+ }
+ peer_list[k] = peer;
+ error[k] = peer->jitter;
+ synch[k] = d;
+ j++;
+ }
+ nlist = j;
+
+ /*
+ * If no survivors remain at this point, check if the local
+ * clock or modem drivers have been found. If so, nominate one
+ * of them as the only survivor. Otherwise, give up and leave
+ * the island to the rats.
+ */
+ if (nlist == 0) {
+ if (typeacts != 0) {
+ typeacts->status = CTL_PST_SEL_DISTSYSPEER;
+ peer_list[0] = typeacts;
+ nlist = 1;
+ } else if (typelocal != 0) {
+ typelocal->status = CTL_PST_SEL_DISTSYSPEER;
+ peer_list[0] = typelocal;
+ nlist = 1;
+ } else {
+ if (osys_peer != NULL) {
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO,
+ "no servers reachable");
+ report_event(EVNT_PEERSTCHG, NULL);
+ }
+ }
+ }
+
+ /*
+ * We can only trust the survivors if the number of candidates
+ * sys_minsane is at least the number required to detect and
+ * cast out one falsticker. For the Byzantine agreement
+ * algorithm used here, that number is 4; however, the default
+ * sys_minsane is 1 to speed initial synchronization. Careful
+ * operators will tinker a higher value and use at least that
+ * number of synchronization sources.
+ */
+ if (nlist < sys_minsane)
+ return;
+
+ for (i = 0; i < nlist; i++)
+ peer_list[i]->status = CTL_PST_SEL_SELCAND;
+
+ /*
+ * Now, vote outlyers off the island by select jitter weighted
+ * by root distance. Continue voting as long as there are more
+ * than sys_minclock survivors and the minimum select jitter is
+ * greater than the maximum peer jitter. Stop if we are about to
+ * discard a TRUE or PREFER peer, who of course has the
+ * immunity idol.
+ */
+ while (1) {
+ d = 1e9;
+ e = -1e9;
+ f = g = 0;
+ k = 0;
+ for (i = 0; i < nlist; i++) {
+ if (error[i] < d)
+ d = error[i];
+ f = 0;
+ if (nlist > 1) {
+ for (j = 0; j < nlist; j++)
+ f += DIFF(peer_list[j]->offset,
+ peer_list[i]->offset);
+ f = SQRT(f / (nlist - 1));
+ }
+ if (f * synch[i] > e) {
+ g = f;
+ e = f * synch[i];
+ k = i;
+ }
+ }
+ f = max(f, LOGTOD(sys_precision));
+ if (nlist <= sys_minclock || f <= d ||
+ peer_list[k]->flags & (FLAG_TRUE | FLAG_PREFER))
+ break;
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "select: drop %s select %.6f jitter %.6f\n",
+ ntoa(&peer_list[k]->srcadr), g, d);
+#endif
+ for (j = k + 1; j < nlist; j++) {
+ peer_list[j - 1] = peer_list[j];
+ error[j - 1] = error[j];
+ }
+ nlist--;
+ }
+
+ /*
+ * What remains is a list usually not greater than sys_minclock
+ * peers. We want only a peer at the lowest stratum to become
+ * the system peer, although all survivors are eligible for the
+ * combining algorithm. Consider each peer in turn and OR the
+ * leap bits on the assumption that, if some of them honk
+ * nonzero bits, they must know what they are doing. Check for
+ * prefer and pps peers at any stratum. Note that the head of
+ * the list is at the lowest stratum and that unsynchronized
+ * peers cannot survive this far.
+ */
+ leap_next = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ sys_survivors++;
+ leap_next |= peer->leap;
+ peer->status = CTL_PST_SEL_SYNCCAND;
+ if (peer->flags & FLAG_PREFER)
+ sys_prefer = peer;
+ if (peer == osys_peer)
+ typesystem = peer;
+#ifdef REFCLOCK
+ if (peer->refclktype == REFCLK_ATOM_PPS)
+ sys_pps = peer;
+#endif /* REFCLOCK */
+#if DEBUG
+ if (debug > 1)
+ printf("cluster: survivor %s metric %.6f\n",
+ ntoa(&peer_list[i]->srcadr), synch[i]);
+#endif
+ }
+
+ /*
+ * Anticlockhop provision. Keep the current system peer if it is
+ * a survivor but not first in the list. But do that only HOPPER
+ * times.
+ */
+ if (osys_peer == NULL || typesystem == NULL || typesystem ==
+ peer_list[0] || sys_hopper > sys_maxhop) {
+ typesystem = peer_list[0];
+ sys_hopper = 0;
+ } else {
+ peer->selbroken++;
+ }
+
+ /*
+ * Mitigation rules of the game. There are several types of
+ * peers that can be selected here: (1) orphan, (2) prefer peer
+ * (flag FLAG_PREFER) (3) pps peers (type REFCLK_ATOM_PPS), (4)
+ * the existing system peer, if any, and (5) the head of the
+ * survivor list.
+ */
+ if (typesystem->stratum >= sys_orphan) {
+
+ /*
+ * If in orphan mode, choose the system peer. If the
+ * lowest distance, we are the orphan parent and the
+ * offset is zero.
+ */
+ sys_peer = typesystem;
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ if (sys_orphandelay < sys_peer->rootdelay) {
+ sys_offset = 0;
+ sys_refid = htonl(LOOPBACKADR);
+ } else {
+ sys_offset = sys_peer->offset;
+ sys_refid = addr2refid(&sys_peer->srcadr);
+ }
+ sys_jitter = LOGTOD(sys_precision);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: orphan offset %.6f\n",
+ sys_offset);
+#endif
+ } else if (sys_prefer) {
+
+ /*
+ * If a pps peer is present, choose it; otherwise,
+ * choose the prefer peer.
+ */
+ if (sys_pps) {
+ sys_peer = sys_pps;
+ sys_peer->status = CTL_PST_SEL_PPS;
+ sys_offset = sys_peer->offset;
+ if (!pps_control)
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO,
+ "pps sync enabled");
+ pps_control = current_time;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: pps offset %.6f\n",
+ sys_offset);
+#endif
+ } else {
+ sys_peer = sys_prefer;
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ sys_offset = sys_peer->offset;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: prefer offset %.6f\n",
+ sys_offset);
+#endif
+ }
+ if (sys_peer->stratum == STRATUM_REFCLOCK ||
+ sys_peer->stratum == STRATUM_UNSPEC)
+ sys_refid = sys_peer->refid;
+ else
+ sys_refid = addr2refid(&sys_peer->srcadr);
+ sys_jitter = sys_peer->jitter;
+ } else {
+
+ /*
+ * Otherwise, choose the anticlockhopper.
+ */
+ sys_peer = typesystem;
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ clock_combine(peer_list, nlist);
+ if (sys_peer->stratum == STRATUM_REFCLOCK ||
+ sys_peer->stratum == STRATUM_UNSPEC)
+ sys_refid = sys_peer->refid;
+ else
+ sys_refid = addr2refid(&sys_peer->srcadr);
+ sys_jitter = SQRT(SQUARE(sys_peer->jitter) +
+ SQUARE(sys_jitter));
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: combine offset %.6f\n",
+ sys_offset);
+#endif
+ }
+
+ /*
+ * We have found the alpha male.
+ */
+ sys_peer->flags |= FLAG_SYSPEER;
+ if (osys_peer != sys_peer) {
+ char *src;
+
+ report_event(EVNT_PEERSTCHG, NULL);
+
+#ifdef REFCLOCK
+ if (sys_peer->flags & FLAG_REFCLOCK)
+ src = refnumtoa(&sys_peer->srcadr);
+ else
+#endif /* REFCLOCK */
+ src = ntoa(&sys_peer->srcadr);
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO, "synchronized to %s, stratum %d",
+ src, sys_peer->stratum);
+ }
+ clock_update();
+}
+
+
+/*
+ * clock_combine - compute system offset and jitter from selected peers
+ */
+static void
+clock_combine(
+ struct peer **peers, /* survivor list */
+ int npeers /* number of survivors */
+ )
+{
+ int i;
+ double x, y, z, w;
+
+ y = z = w = 0;
+ for (i = 0; i < npeers; i++) {
+ x = root_distance(peers[i]);
+ y += 1. / x;
+ z += peers[i]->offset / x;
+ w += SQUARE(peers[i]->offset - peers[0]->offset) / x;
+ }
+ sys_offset = z / y;
+ sys_jitter = SQRT(w / y);
+}
+
+/*
+ * root_distance - compute synchronization distance from peer to root
+ */
+static double
+root_distance(
+ struct peer *peer
+ )
+{
+ double dist;
+
+ /*
+ * Careful squeak here. The value returned must be greater than
+ * the minimum root dispersion in order to avoid clockhop with
+ * highly precise reference clocks. In orphan mode lose the peer
+ * root delay, as that is used by the election algorithm.
+ */
+ if (peer->stratum >= sys_orphan)
+ dist = 0;
+ else
+ dist = peer->rootdelay;
+ dist += max(sys_mindisp, dist + peer->delay) / 2 +
+ peer->rootdispersion + peer->disp + clock_phi *
+ (current_time - peer->update) + peer->jitter;
+ return (dist);
+}
+
+/*
+ * peer_xmit - send packet for persistent association.
+ */
+static void
+peer_xmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct pkt xpkt; /* transmit packet */
+ int sendlen, authlen;
+ keyid_t xkeyid = 0; /* transmit key ID */
+ l_fp xmt_tx;
+
+ if (!peer->dstadr) /* don't bother with peers without interface */
+ return;
+
+ /*
+ * This is deliciously complicated. There are three cases.
+ *
+ * case leap stratum refid delay dispersion
+ *
+ * normal system system system system system
+ * orphan child 00 orphan system orphan system
+ * orphan parent 00 orphan loopbk 0 0
+ */
+ /*
+ * This is a normal packet. Use the system variables.
+ */
+ if (sys_stratum < sys_orphan) {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ peer->version, peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+
+ /*
+ * This is a orphan child packet. The host is synchronized to an
+ * orphan parent. Show leap synchronized, orphan stratum, system
+ * reference ID, orphan root delay and system root dispersion.
+ */
+ } else if (sys_peer != NULL) {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING,
+ peer->version, peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_orphan);
+ xpkt.refid = htonl(LOOPBACKADR);
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+
+ /*
+ * This is an orphan parent. Show leap synchronized, orphan
+ * stratum, loopack reference ID and zero root delay and root
+ * dispersion.
+ */
+ } else {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING,
+ peer->version, peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_orphan);
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = 0;
+ xpkt.rootdispersion = 0;
+ }
+ xpkt.ppoll = peer->hpoll;
+ xpkt.precision = sys_precision;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->org, &xpkt.org);
+ HTONL_FP(&peer->rec, &xpkt.rec);
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ *
+ * It is most important when autokey is in use that the local
+ * interface IP address be known before the first packet is
+ * sent. Otherwise, it is not possible to compute a correct MAC
+ * the recipient will accept. Thus, the I/O semantics have to do
+ * a little more work. In particular, the wildcard interface
+ * might not be usable.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (!(peer->flags & FLAG_AUTHENABLE)) {
+ get_systime(&peer->xmt);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl],
+ &xpkt, sendlen);
+ peer->sent++;
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d\n",
+ current_time, peer->dstadr ? stoa(&peer->dstadr->sin) : "-",
+ stoa(&peer->srcadr), peer->hmode);
+#endif
+ return;
+ }
+
+ /*
+ * The received packet contains a MAC, so the transmitted packet
+ * must be authenticated. If autokey is enabled, fuss with the
+ * various modes; otherwise, symmetric key cryptography is used.
+ */
+#ifdef OPENSSL
+ if (crypto_flags && (peer->flags & FLAG_SKEY)) {
+ struct exten *exten; /* extension field */
+
+ /*
+ * The Public Key Dance (PKD): Cryptographic credentials
+ * are contained in extension fields, each including a
+ * 4-octet length/code word followed by a 4-octet
+ * association ID and optional additional data. Optional
+ * data includes a 4-octet data length field followed by
+ * the data itself. Request messages are sent from a
+ * configured association; response messages can be sent
+ * from a configured association or can take the fast
+ * path without ever matching an association. Response
+ * messages have the same code as the request, but have
+ * a response bit and possibly an error bit set. In this
+ * implementation, a message may contain no more than
+ * one command and no more than one response.
+ *
+ * Cryptographic session keys include both a public and
+ * a private componet. Request and response messages
+ * using extension fields are always sent with the
+ * private component set to zero. Packets without
+ * extension fields indlude the private component when
+ * the session key is generated.
+ */
+ while (1) {
+
+ /*
+ * Allocate and initialize a keylist if not
+ * already done. Then, use the list in inverse
+ * order, discarding keys once used. Keep the
+ * latest key around until the next one, so
+ * clients can use client/server packets to
+ * compute propagation delay.
+ *
+ * Note that once a key is used from the list,
+ * it is retained in the key cache until the
+ * next key is used. This is to allow a client
+ * to retrieve the encrypted session key
+ * identifier to verify authenticity.
+ *
+ * If for some reason a key is no longer in the
+ * key cache, a birthday has happened and the
+ * pseudo-random sequence is probably broken. In
+ * that case, purge the keylist and regenerate
+ * it.
+ */
+ if (peer->keynumber == 0)
+ make_keylist(peer, peer->dstadr);
+ else
+ peer->keynumber--;
+ xkeyid = peer->keylist[peer->keynumber];
+ if (authistrusted(xkeyid))
+ break;
+ else
+ key_expire(peer);
+ }
+ peer->keyid = xkeyid;
+ exten = NULL;
+ switch (peer->hmode) {
+
+ /*
+ * In broadcast server mode the autokey values are
+ * required by the broadcast clients. Push them when a
+ * new keylist is generated; otherwise, push the
+ * association message so the client can request them at
+ * other times.
+ */
+ case MODE_BROADCAST:
+ if (peer->flags & FLAG_ASSOC)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, NULL);
+ else
+ exten = crypto_args(peer, CRYPTO_ASSOC |
+ CRYPTO_RESP, NULL);
+ break;
+
+ /*
+ * In symmetric modes the digest, certificate, agreement
+ * parameters, cookie and autokey values are required.
+ * The leapsecond table is optional. But, a passive peer
+ * will not believe the active peer until the latter has
+ * synchronized, so the agreement must be postponed
+ * until then. In any case, if a new keylist is
+ * generated, the autokey values are pushed.
+ *
+ * If the crypto bit is lit, don't send requests.
+ */
+ case MODE_ACTIVE:
+ case MODE_PASSIVE:
+ if (peer->flash & TEST9)
+ break;
+ /*
+ * Parameter and certificate.
+ */
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ sys_hostname);
+ else if (!(peer->crypto & CRYPTO_FLAG_VALID))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->issuer);
+
+ /*
+ * Identity. Note we have to sign the
+ * certificate before the cookie to avoid a
+ * deadlock when the passive peer is walking the
+ * certificate trail. Awesome.
+ */
+ else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ exten = crypto_args(peer,
+ crypto_ident(peer), NULL);
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ sys_hostname);
+
+ /*
+ * Autokey. We request the cookie only when the
+ * server and client are synchronized and
+ * signatures work both ways. On the other hand,
+ * the active peer needs the autokey values
+ * before then and when the passive peer is
+ * waiting for the active peer to synchronize.
+ * Any time we regenerate the key list, we offer
+ * the autokey values without being asked.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_AGREE))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ NULL);
+ else if (peer->flags & FLAG_ASSOC)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ NULL);
+
+ /*
+ * Postamble. We trade leapseconds only when the
+ * server and client are synchronized.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->leap != LEAP_NOTINSYNC &&
+ peer->crypto & CRYPTO_FLAG_TAI &&
+ !(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_TAI,
+ NULL);
+ break;
+
+ /*
+ * In client mode the digest, certificate, agreement
+ * parameters and cookie are required. The leapsecond
+ * table is optional. If broadcast client mode, the
+ * autokey values are required as well. In broadcast
+ * client mode, these values must be acquired during the
+ * client/server exchange to avoid having to wait until
+ * the next key list regeneration. Otherwise, the poor
+ * dude may die a lingering death until becoming
+ * unreachable and attempting rebirth.
+ *
+ * If neither the server or client have the agreement
+ * parameters, the protocol transmits the cookie in the
+ * clear. If the server has the parameters, the client
+ * requests them and the protocol blinds it using the
+ * agreed key. It is a protocol error if the client has
+ * the parameters but the server does not.
+ *
+ * If the crypto bit is lit, don't send requests.
+ */
+ case MODE_CLIENT:
+ if (peer->flash & TEST9)
+ break;
+ /*
+ * Parameter and certificate.
+ */
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ sys_hostname);
+ else if (!(peer->crypto & CRYPTO_FLAG_VALID))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->issuer);
+
+ /*
+ * Identity
+ */
+ else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ exten = crypto_args(peer,
+ crypto_ident(peer), NULL);
+
+ /*
+ * Autokey
+ */
+ else if (!(peer->crypto & CRYPTO_FLAG_AGREE))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO) &&
+ (peer->cast_flags & MDF_BCLNT))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ NULL);
+
+ /*
+ * Postamble. We can sign the certificate here,
+ * since there is no chance of deadlock.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ sys_hostname);
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->crypto & CRYPTO_FLAG_TAI &&
+ !(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_TAI,
+ NULL);
+ break;
+ }
+
+ /*
+ * Build the extension fields as directed. A response to
+ * a request is always sent, even if an error. If an
+ * error occurs when sending a request, the crypto
+ * machinery broke or was misconfigured. In that case
+ * light the crypto bit to suppress further requests.
+ */
+ if (peer->cmmd != NULL) {
+ peer->cmmd->associd = htonl(peer->associd);
+ sendlen += crypto_xmit(&xpkt, &peer->srcadr,
+ sendlen, peer->cmmd, 0);
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ }
+ if (exten != NULL) {
+ int ltemp = 0;
+
+ if (exten->opcode != 0) {
+ ltemp = crypto_xmit(&xpkt,
+ &peer->srcadr, sendlen, exten, 0);
+ if (ltemp == 0) {
+ peer->flash |= TEST9; /* crypto error */
+ free(exten);
+ return;
+ }
+ }
+ sendlen += ltemp;
+ free(exten);
+ }
+
+ /*
+ * If extension fields are present, we must use a
+ * private cookie value of zero. Don't send if the
+ * crypto bit is set and no extension field is present,
+ * but in that case give back the key. Most intricate.
+ */
+ if (sendlen > LEN_PKT_NOMAC) {
+ session_key(&peer->dstadr->sin, &peer->srcadr,
+ xkeyid, 0, 2);
+ } else if (peer->flash & TEST9) {
+ authtrust(xkeyid, 0);
+ return;
+ }
+ }
+#endif /* OPENSSL */
+
+ /*
+ * Stash the transmit timestamp corrected for the encryption
+ * delay. If autokey, give back the key, as we use keys only
+ * once. Check for errors such as missing keys, buffer overflow,
+ * etc.
+ */
+ xkeyid = peer->keyid;
+ get_systime(&peer->xmt);
+ L_ADD(&peer->xmt, &sys_authdelay);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ if (authlen == 0) {
+ msyslog(LOG_INFO, "transmit: %s key %u not found",
+ stoa(&peer->srcadr), xkeyid);
+ peer->flash |= TEST9; /* no key found */
+ return;
+ }
+ sendlen += authlen;
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* OPENSSL */
+ get_systime(&xmt_tx);
+ if (sendlen > sizeof(xpkt)) {
+ msyslog(LOG_ERR, "buffer overflow %u", sendlen);
+ exit (-1);
+ }
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt,
+ sendlen);
+
+ /*
+ * Calculate the encryption delay. Keep the minimum over
+ * the latest two samples.
+ */
+ L_SUB(&xmt_tx, &peer->xmt);
+ L_ADD(&xmt_tx, &sys_authdelay);
+ sys_authdly[1] = sys_authdly[0];
+ sys_authdly[0] = xmt_tx.l_uf;
+ if (sys_authdly[0] < sys_authdly[1])
+ sys_authdelay.l_uf = sys_authdly[0];
+ else
+ sys_authdelay.l_uf = sys_authdly[1];
+ peer->sent++;
+#ifdef OPENSSL
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d index %d\n",
+ current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-",
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen -
+ authlen, authlen, peer->keynumber);
+#endif
+#else
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n",
+ current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-",
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen -
+ authlen, authlen);
+#endif
+#endif /* OPENSSL */
+}
+
+
+/*
+ * fast_xmit - Send packet for nonpersistent association. Note that
+ * neither the source or destination can be a broadcast address.
+ */
+static void
+fast_xmit(
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* transmit mode */
+ keyid_t xkeyid, /* transmit key ID */
+ int mask /* restrict mask */
+ )
+{
+ struct pkt xpkt; /* transmit packet structure */
+ struct pkt *rpkt; /* receive packet structure */
+ l_fp xmt_ts; /* timestamp */
+ l_fp xmt_tx; /* timestamp after authent */
+ int sendlen, authlen;
+#ifdef OPENSSL
+ u_int32 temp32;
+#endif
+
+ /*
+ * Initialize transmit packet header fields from the receive
+ * buffer provided. We leave some fields intact as received. If
+ * the gazinta was from a multicast address, the gazoutta must
+ * go out another way.
+ *
+ * The root delay field is special. If the system stratum is
+ * less than the orphan stratum, send the real root delay.
+ * Otherwise, if there is no system peer, send the orphan delay.
+ * Otherwise, we must be an orphan parent, so send zero.
+ */
+ rpkt = &rbufp->recv_pkt;
+ if (rbufp->dstadr->flags & INT_MCASTOPEN)
+ rbufp->dstadr = findinterface(&rbufp->recv_srcadr);
+
+ /*
+ * This is deliciously complicated. There are four cases.
+ *
+ * case leap stratum refid delay dispersion
+ *
+ * KoD 11 16 KISS system system
+ * normal system system system system system
+ * orphan child 00 orphan system orphan system
+ * orphan parent 00 orphan loopbk 0 0
+ */
+ /*
+ * This is a kiss-of-death (KoD) packet. Show leap
+ * unsynchronized, stratum zero, reference ID the four-character
+ * kiss code and system root delay. Note the rate limit on these
+ * packets. Once a second initialize a bucket counter. Every
+ * packet sent decrements the counter until reaching zero. If
+ * the counter is zero, drop the kiss.
+ */
+ if (mask & RES_LIMITED) {
+ sys_limitrejected++;
+ if (sys_kod == 0 || !(mask & RES_DEMOBILIZE))
+ return;
+
+ sys_kod--;
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_UNSPEC;
+ memcpy(&xpkt.refid, "RATE", 4);
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+
+ /*
+ * This is a normal packet. Use the system variables.
+ */
+ } else if (sys_stratum < sys_orphan) {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+
+ /*
+ * This is a orphan child packet. The host is synchronized to an
+ * orphan parent. Show leap synchronized, orphan stratum, system
+ * reference ID and orphan root delay.
+ */
+ } else if (sys_peer != NULL) {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_orphan);
+ xpkt.refid = sys_refid;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+
+ /*
+ * This is an orphan parent. Show leap synchronized, orphan
+ * stratum, loopack reference ID and zero root delay.
+ */
+ } else {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_orphan);
+ xpkt.refid = htonl(LOOPBACKADR);
+ xpkt.rootdelay = HTONS_FP(DTOFP(0));
+ xpkt.rootdispersion = HTONS_FP(DTOFP(0));
+ }
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion));
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (rbufp->recv_length == sendlen) {
+ get_systime(&xmt_ts);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt,
+ sendlen);
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr), xmode);
+#endif
+ return;
+ }
+
+ /*
+ * The received packet contains a MAC, so the transmitted packet
+ * must be authenticated. For symmetric key cryptography, use
+ * the predefined and trusted symmetric keys to generate the
+ * cryptosum. For autokey cryptography, use the server private
+ * value to generate the cookie, which is unique for every
+ * source-destination-key ID combination.
+ */
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY) {
+ keyid_t cookie;
+
+ /*
+ * The only way to get here is a reply to a legitimate
+ * client request message, so the mode must be
+ * MODE_SERVER. If an extension field is present, there
+ * can be only one and that must be a command. Do what
+ * needs, but with private value of zero so the poor
+ * jerk can decode it. If no extension field is present,
+ * use the cookie to generate the session key.
+ */
+ cookie = session_key(&rbufp->recv_srcadr,
+ &rbufp->dstadr->sin, 0, sys_private, 0);
+ if (rbufp->recv_length >= (int)(sendlen + MAX_MAC_LEN +
+ 2 * sizeof(u_int32))) {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, 0, 2);
+ temp32 = CRYPTO_RESP;
+ rpkt->exten[0] |= htonl(temp32);
+ sendlen += crypto_xmit(&xpkt,
+ &rbufp->recv_srcadr, sendlen,
+ (struct exten *)rpkt->exten, cookie);
+ } else {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, cookie, 2);
+ }
+ }
+#endif /* OPENSSL */
+ get_systime(&xmt_ts);
+ L_ADD(&xmt_ts, &sys_authdelay);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ sendlen += authlen;
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* OPENSSL */
+ get_systime(&xmt_tx);
+ if (sendlen > sizeof(xpkt)) {
+ msyslog(LOG_ERR, "buffer overflow %u", sendlen);
+ exit (-1);
+ }
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen);
+
+ /*
+ * Calculate the encryption delay. Keep the minimum over the
+ * latest two samples.
+ */
+ L_SUB(&xmt_tx, &xmt_ts);
+ L_ADD(&xmt_tx, &sys_authdelay);
+ sys_authdly[1] = sys_authdly[0];
+ sys_authdly[0] = xmt_tx.l_uf;
+ if (sys_authdly[0] < sys_authdly[1])
+ sys_authdelay.l_uf = sys_authdly[0];
+ else
+ sys_authdelay.l_uf = sys_authdly[1];
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n",
+ current_time, ntoa(&rbufp->dstadr->sin),
+ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen -
+ authlen, authlen);
+#endif
+}
+
+
+#ifdef OPENSSL
+/*
+ * key_expire - purge the key list
+ */
+void
+key_expire(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int i;
+
+ if (peer->keylist != NULL) {
+ for (i = 0; i <= peer->keynumber; i++)
+ authtrust(peer->keylist[i], 0);
+ free(peer->keylist);
+ peer->keylist = NULL;
+ }
+ value_free(&peer->sndval);
+ peer->keynumber = 0;
+#ifdef DEBUG
+ if (debug)
+ printf("key_expire: at %lu\n", current_time);
+#endif
+}
+#endif /* OPENSSL */
+
+
+/*
+ * Determine if the peer is unfit for synchronization
+ *
+ * A peer is unfit for synchronization if
+ * > TEST10 bad leap or stratum below floor or at or above ceiling
+ * > TEST11 root distance exceeded
+ * > TEST12 a direct or indirect synchronization loop would form
+ * > TEST13 unreachable or noselect
+ */
+int /* FALSE if fit, TRUE if unfit */
+peer_unfit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int rval = 0;
+
+ /*
+ * A stratum error occurs if (1) the server has never been
+ * synchronized, (2) the server stratum is below the floor or
+ * greater than or equal to the ceiling, (3) the system stratum
+ * is below the orphan stratum and the server stratum is greater
+ * than or equal to the orphan stratum.
+ */
+ if (peer->leap == LEAP_NOTINSYNC || peer->stratum < sys_floor ||
+ peer->stratum >= sys_ceiling || (sys_stratum < sys_orphan &&
+ peer->stratum >= sys_orphan))
+ rval |= TEST10; /* stratum out of bounds */
+
+ /*
+ * A distance error occurs if the root distance is greater than
+ * or equal to the distance threshold plus the increment due to
+ * one poll interval.
+ */
+ if (root_distance(peer) >= sys_maxdist + clock_phi *
+ ULOGTOD(sys_poll))
+ rval |= TEST11; /* distance exceeded */
+
+ /*
+ * A loop error occurs if the remote peer is synchronized to the
+ * local peer of if the remote peer is synchronized to the same
+ * server as the local peer, but only if the remote peer is not
+ * the orphan parent.
+ */
+ if (peer->stratum > 1 && peer->refid != htonl(LOOPBACKADR) &&
+ ((!peer->dstadr || peer->refid == peer->dstadr->addr_refid) ||
+ peer->refid == sys_refid))
+ rval |= TEST12; /* synch loop */
+
+ /*
+ * An unreachable error occurs if the server is unreachable or
+ * the noselect bit is set.
+ */
+ if (!peer->reach || peer->flags & FLAG_NOSELECT)
+ rval |= TEST13; /* unreachable */
+
+ peer->flash &= ~PEER_TEST_MASK;
+ peer->flash |= rval;
+ return (rval);
+}
+
+
+/*
+ * Find the precision of this particular machine
+ */
+#define MINSTEP 100e-9 /* minimum clock increment (s) */
+#define MAXSTEP 20e-3 /* maximum clock increment (s) */
+#define MINLOOPS 5 /* minimum number of step samples */
+
+/*
+ * This routine calculates the system precision, defined as the minimum
+ * of a sequence of differences between successive readings of the
+ * system clock. However, if the system clock can be read more than once
+ * during a tick interval, the difference can be zero or one LSB unit,
+ * where the LSB corresponds to one nanosecond or one microsecond.
+ * Conceivably, if some other process preempts this one and reads the
+ * clock, the difference can be more than one LSB unit.
+ *
+ * For hardware clock frequencies of 10 MHz or less, we assume the
+ * logical clock advances only at the hardware clock tick. For higher
+ * frequencies, we assume the logical clock can advance no more than 100
+ * nanoseconds between ticks.
+ */
+int
+default_get_precision(void)
+{
+ l_fp val; /* current seconds fraction */
+ l_fp last; /* last seconds fraction */
+ l_fp diff; /* difference */
+ double tick; /* computed tick value */
+ double dtemp; /* scratch */
+ int i; /* log2 precision */
+
+ /*
+ * Loop to find tick value in nanoseconds. Toss out outlyer
+ * values less than the minimun tick value. In wacky cases, use
+ * the default maximum value.
+ */
+ get_systime(&last);
+ tick = MAXSTEP;
+ for (i = 0; i < MINLOOPS;) {
+ get_systime(&val);
+ diff = val;
+ L_SUB(&diff, &last);
+ last = val;
+ LFPTOD(&diff, dtemp);
+ if (dtemp < MINSTEP)
+ continue;
+ i++;
+ if (dtemp < tick)
+ tick = dtemp;
+ }
+
+ /*
+ * Find the nearest power of two.
+ */
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "precision = %.3f usec", tick * 1e6);
+ for (i = 0; tick <= 1; i++)
+ tick *= 2;
+ if (tick - 1. > 1. - tick / 2)
+ i--;
+ return (-i);
+}
+
+
+/*
+ * kod_proto - called once per second to limit kiss-of-death packets
+ */
+void
+kod_proto(void)
+{
+ sys_kod = sys_kod_rate;
+}
+
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto(void)
+{
+ l_fp dummy;
+ int i;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, authenticate.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "INIT", 4);
+ sys_precision = (s_char)default_get_precision();
+ sys_jitter = LOGTOD(sys_precision);
+ sys_rootdelay = 0;
+ sys_orphandelay = (double)(ntp_random() & 0xffff) / 65536. *
+ sys_maxdist;
+ sys_rootdispersion = 0;
+ L_CLR(&sys_reftime);
+ sys_peer = NULL;
+ sys_survivors = 0;
+ get_systime(&dummy);
+ sys_manycastserver = 0;
+ sys_bclient = 0;
+ sys_bdelay = DEFBROADDELAY;
+ sys_calldelay = BURST_DELAY;
+ sys_authenticate = 1;
+ L_CLR(&sys_authdelay);
+ sys_authdly[0] = sys_authdly[1] = 0;
+ sys_stattime = 0;
+ proto_clr_stats();
+ for (i = 0; i < MAX_TTL; i++) {
+ sys_ttl[i] = (u_char)((i * 256) / MAX_TTL);
+ sys_ttlmax = i;
+ }
+#ifdef OPENSSL
+ sys_automax = 1 << NTP_AUTOMAX;
+#endif /* OPENSSL */
+
+ /*
+ * Default these to enable
+ */
+ ntp_enable = 1;
+#ifndef KERNEL_FLL_BUG
+ kern_enable = 1;
+#endif
+ pps_enable = 0;
+ stats_control = 1;
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(
+ int item,
+ u_long value,
+ double dvalue,
+ struct sockaddr_storage* svalue
+ )
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ switch (item) {
+
+ /*
+ * Turn on/off kernel discipline.
+ */
+ case PROTO_KERNEL:
+ kern_enable = (int)value;
+ break;
+
+ /*
+ * Turn on/off clock discipline.
+ */
+ case PROTO_NTP:
+ ntp_enable = (int)value;
+ break;
+
+ /*
+ * Turn on/off monitoring.
+ */
+ case PROTO_MONITOR:
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ /*
+ * Turn on/off statistics.
+ */
+ case PROTO_FILEGEN:
+ stats_control = (int)value;
+ break;
+
+ /*
+ * Turn on/off enable broadcasts.
+ */
+ case PROTO_BROADCLIENT:
+ sys_bclient = (int)value;
+ if (sys_bclient == 0)
+ io_unsetbclient();
+ else
+ io_setbclient();
+ break;
+
+ /*
+ * Turn on/off PPS discipline.
+ */
+ case PROTO_PPS:
+ pps_enable = (int)value;
+ break;
+
+ /*
+ * Add muliticast group address.
+ */
+ case PROTO_MULTICAST_ADD:
+ if (svalue)
+ io_multicast_add(*svalue);
+ sys_bclient = 1;
+ break;
+
+ /*
+ * Delete multicast group address.
+ */
+ case PROTO_MULTICAST_DEL:
+ if (svalue)
+ io_multicast_del(*svalue);
+ break;
+
+ /*
+ * Set default broadcast delay.
+ */
+ case PROTO_BROADDELAY:
+ sys_bdelay = dvalue;
+ break;
+
+ /*
+ * Set modem call delay.
+ */
+ case PROTO_CALLDELAY:
+ sys_calldelay = (int)value;
+ break;
+
+ /*
+ * Turn on/off authentication to mobilize ephemeral
+ * associations.
+ */
+ case PROTO_AUTHENTICATE:
+ sys_authenticate = (int)value;
+ break;
+
+ /*
+ * Set minimum number of survivors.
+ */
+ case PROTO_MINCLOCK:
+ sys_minclock = (int)dvalue;
+ break;
+
+ /*
+ * Set maximum number of preemptable associations.
+ */
+ case PROTO_MAXCLOCK:
+ sys_maxclock = (int)dvalue;
+ break;
+
+ /*
+ * Set minimum number of survivors.
+ */
+ case PROTO_MINSANE:
+ sys_minsane = (int)dvalue;
+ break;
+
+ /*
+ * Set stratum floor.
+ */
+ case PROTO_FLOOR:
+ sys_floor = (int)dvalue;
+ break;
+
+ /*
+ * Set stratum ceiling.
+ */
+ case PROTO_CEILING:
+ sys_ceiling = (int)dvalue;
+ break;
+
+ /*
+ * Set orphan stratum.
+ */
+ case PROTO_ORPHAN:
+ sys_orphan = (int)dvalue;
+ break;
+
+ /*
+ * Set cohort switch.
+ */
+ case PROTO_COHORT:
+ sys_cohort = (int)dvalue;
+ break;
+
+ /*
+ * Set minimum dispersion increment.
+ */
+ case PROTO_MINDISP:
+ sys_mindisp = dvalue;
+ break;
+
+ /*
+ * Set maximum distance (select threshold).
+ */
+ case PROTO_MAXDIST:
+ sys_maxdist = dvalue;
+ break;
+
+ /*
+ * Set anticlockhop threshold.
+ */
+ case PROTO_MAXHOP:
+ sys_maxhop = (int)dvalue;
+ break;
+
+ /*
+ * Set adjtime() resolution (s).
+ */
+ case PROTO_ADJ:
+ sys_tick = dvalue;
+ break;
+
+ /*
+ * Set manycast beacon interval.
+ */
+ case PROTO_BEACON:
+ sys_beacon = (int)dvalue;
+ break;
+
+#ifdef REFCLOCK
+ /*
+ * Turn on/off refclock calibrate
+ */
+ case PROTO_CAL:
+ cal_enable = (int)value;
+ break;
+#endif /* REFCLOCK */
+ default:
+
+ /*
+ * Log this error.
+ */
+ msyslog(LOG_INFO,
+ "proto_config: illegal item %d, value %ld", item,
+ value);
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats(void)
+{
+ sys_stattime = current_time;
+ sys_received = 0;
+ sys_processed = 0;
+ sys_newversionpkt = 0;
+ sys_oldversionpkt = 0;
+ sys_unknownversion = 0;
+ sys_restricted = 0;
+ sys_badlength = 0;
+ sys_badauth = 0;
+ sys_limitrejected = 0;
+}
diff --git a/contrib/ntp/ntpd/ntp_refclock.c b/contrib/ntp/ntpd/ntp_refclock.c
new file mode 100644
index 0000000..a29ef08
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_refclock.c
@@ -0,0 +1,1229 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#ifdef REFCLOCK
+
+#ifdef TTYCLK
+# ifdef HAVE_SYS_CLKDEFS_H
+# include <sys/clkdefs.h>
+# include <stropts.h>
+# endif
+# ifdef HAVE_SYS_SIO_H
+# include <sys/sio.h>
+# endif
+#endif /* TTYCLK */
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with a
+ * reference clock, however, we replace the transmit, receive and packet
+ * procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, edit input lines to extract
+ * embedded timestamps and to peform various debugging functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants of
+ * the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes the
+ * receive timestamp, exception report, statistics tallies, etc. In
+ * addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a pointer
+ * to the refclockproc structure, which in turn containes a pointer to
+ * the unit structure, if used. The peer structure is identified by an
+ * interface address in the dotted quad form 127.127.t.u (for now only
+ * IPv4 addresses are used, so we need to be sure the address is it),
+ * where t is the clock type and u the unit. Some legacy drivers derive
+ * the refclockproc structure pointer from the table
+ * typeunit[type][unit]. This interface is strongly discouraged and may
+ * be abandoned in future.
+ */
+#define MAXUNIT 4 /* max units */
+#define FUDGEFAC .1 /* fudge correction factor */
+#define LF 0x0a /* ASCII LF */
+
+#ifdef PPS
+int fdpps; /* ppsclock legacy */
+#endif /* PPS */
+int cal_enable; /* enable refclock calibrate */
+
+/*
+ * Type/unit peer index. Used to find the peer structure for control and
+ * debugging. When all clock drivers have been converted to new style,
+ * this dissapears.
+ */
+static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];
+
+/*
+ * Forward declarations
+ */
+#ifdef QSORT_USES_VOID_P
+static int refclock_cmpl_fp P((const void *, const void *));
+#else
+static int refclock_cmpl_fp P((const double *, const double *));
+#endif /* QSORT_USES_VOID_P */
+static int refclock_sample P((struct refclockproc *));
+
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(
+ struct peer *peer,
+ int code
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (pp == NULL)
+ return;
+
+ switch (code) {
+ case CEVNT_NOMINAL:
+ break;
+
+ case CEVNT_TIMEOUT:
+ pp->noreply++;
+ break;
+
+ case CEVNT_BADREPLY:
+ pp->badformat++;
+ break;
+
+ case CEVNT_FAULT:
+ break;
+
+ case CEVNT_PROP:
+ break;
+
+ case CEVNT_BADDATE:
+ case CEVNT_BADTIME:
+ pp->baddata++;
+ break;
+
+ default:
+ /* shouldn't happen */
+ break;
+ }
+
+ if (pp->currentstatus != code) {
+ pp->currentstatus = (u_char)code;
+
+ /* RFC1305: copy only iff not CEVNT_NOMINAL */
+ if (code != CEVNT_NOMINAL)
+ pp->lastevent = (u_char)code;
+
+ if (code == CEVNT_FAULT)
+ msyslog(LOG_ERR,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
+ else {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_INFO,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
+ }
+
+ /* RFC1305: post peer clock event */
+ report_event(EVNT_PEERCLOCK, peer);
+ }
+}
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock(void)
+{
+ int i, j;
+
+ for (i = 0; i < (int)num_refclock_conf; i++) {
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+ for (j = 0; j < MAXUNIT; j++)
+ typeunit[i][j] = 0;
+ }
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it
+ * down first.
+ */
+ if (peer->srcadr.ss_family != AF_INET) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid, address family not implemented for refclock",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc));
+ if (pp == NULL)
+ return (0);
+
+ memset((char *)pp, 0, sizeof(struct refclockproc));
+ typeunit[clktype][unit] = peer;
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = (u_char)unit;
+ peer->flags |= FLAG_REFCLOCK | FLAG_FIXPOLL;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->ppoll = peer->maxpoll;
+ pp->type = clktype;
+ pp->timestarted = current_time;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Do driver dependent initialization. The above defaults
+ * can be wiggled, then finish up for consistency.
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ refclock_unpeer(peer);
+ return (0);
+ }
+ peer->refid = pp->refid;
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (!peer->procptr)
+ return;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = 0;
+}
+
+
+/*
+ * refclock_timer - called once per second for housekeeping.
+ */
+void
+refclock_timer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_timer != noentry)
+ (refclock_conf[clktype]->clock_timer)(unit, peer);
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+ get_systime(&peer->xmt);
+
+ /*
+ * This is a ripoff of the peer transmit routine, but
+ * specialized for reference clocks. We do a little less
+ * protocol here and call the driver-specific transmit routine.
+ */
+ if (peer->burst == 0) {
+ u_char oreach;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_transmit: at %ld %s\n",
+ current_time, stoa(&(peer->srcadr)));
+#endif
+
+ /*
+ * Update reachability and poll variables like the
+ * network code.
+ */
+ oreach = peer->reach;
+ peer->reach <<= 1;
+ peer->outdate = current_time;
+ if (!peer->reach) {
+ if (oreach) {
+ report_event(EVNT_UNREACH, peer);
+ peer->timereachable = current_time;
+ }
+ } else {
+ if (!(oreach & 0x07)) {
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ clock_select();
+ }
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NSTAGE;
+ }
+ } else {
+ peer->burst--;
+ }
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+ poll_update(peer, peer->hpoll);
+}
+
+
+/*
+ * Compare two doubles - used with qsort()
+ */
+#ifdef QSORT_USES_VOID_P
+static int
+refclock_cmpl_fp(
+ const void *p1,
+ const void *p2
+ )
+{
+ const double *dp1 = (const double *)p1;
+ const double *dp2 = (const double *)p2;
+
+ if (*dp1 < *dp2)
+ return (-1);
+
+ if (*dp1 > *dp2)
+ return (1);
+
+ return (0);
+}
+
+#else
+static int
+refclock_cmpl_fp(
+ const double *dp1,
+ const double *dp2
+ )
+{
+ if (*dp1 < *dp2)
+ return (-1);
+
+ if (*dp1 > *dp2)
+ return (1);
+
+ return (0);
+}
+#endif /* QSORT_USES_VOID_P */
+
+
+/*
+ * refclock_process_offset - update median filter
+ *
+ * This routine uses the given offset and timestamps to construct a new
+ * entry in the median filter circular buffer. Samples that overflow the
+ * filter are quietly discarded.
+ */
+void
+refclock_process_offset(
+ struct refclockproc *pp, /* refclock structure pointer */
+ l_fp lasttim, /* last timecode timestamp */
+ l_fp lastrec, /* last receive timestamp */
+ double fudge
+ )
+{
+ l_fp lftemp;
+ double doffset;
+
+ pp->lastrec = lastrec;
+ lftemp = lasttim;
+ L_SUB(&lftemp, &lastrec);
+ LFPTOD(&lftemp, doffset);
+ SAMPLE(doffset + fudge);
+}
+
+
+/*
+ * refclock_process - process a sample from the clock
+ *
+ * This routine converts the timecode in the form days, hours, minutes,
+ * seconds and milliseconds/microseconds to internal timestamp format,
+ * then constructs a new entry in the median filter circular buffer.
+ * Return success (1) if the data are correct and consistent with the
+ * converntional calendar.
+ *
+ * Important for PPS users: Normally, the pp->lastrec is set to the
+ * system time when the on-time character is received and the pp->year,
+ * ..., pp->second decoded and the seconds fraction pp->nsec in
+ * nanoseconds). When a PPS offset is available, pp->nsec is forced to
+ * zero and the fraction for pp->lastrec is set to the PPS offset.
+ */
+int
+refclock_process(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ l_fp offset, ltemp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))
+ return (0);
+
+ offset.l_uf = 0;
+ DTOLFP(pp->nsec / 1e9, &ltemp);
+ L_ADD(&offset, &ltemp);
+ refclock_process_offset(pp, offset, pp->lastrec,
+ pp->fudgetime1);
+ return (1);
+}
+
+
+/*
+ * refclock_sample - process a pile of samples from the clock
+ *
+ * This routine implements a recursive median filter to suppress spikes
+ * in the data, as well as determine a performance statistic. It
+ * calculates the mean offset and RMS jitter. A time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns the number of samples
+ * processed, which could be zero.
+ */
+static int
+refclock_sample(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ int i, j, k, m, n;
+ double off[MAXSTAGE];
+ double offset;
+
+ /*
+ * Copy the raw offsets and sort into ascending order. Don't do
+ * anything if the buffer is empty.
+ */
+ n = 0;
+ while (pp->codeproc != pp->coderecv) {
+ pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;
+ off[n] = pp->filter[pp->codeproc];
+ n++;
+ }
+ if (n == 0)
+ return (0);
+
+ if (n > 1)
+ qsort(
+#ifdef QSORT_USES_VOID_P
+ (void *)
+#else
+ (char *)
+#endif
+ off, (size_t)n, sizeof(double), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of the samples until
+ * approximately 60 percent of the samples remain.
+ */
+ i = 0; j = n;
+ m = n - (n * 4) / 10;
+ while ((j - i) > m) {
+ offset = off[(j + i) / 2];
+ if (off[j - 1] - offset < offset - off[i])
+ i++; /* reject low end */
+ else
+ j--; /* reject high end */
+ }
+
+ /*
+ * Determine the offset and jitter.
+ */
+ pp->offset = 0;
+ pp->jitter = 0;
+ for (k = i; k < j; k++) {
+ pp->offset += off[k];
+ if (k > i)
+ pp->jitter += SQUARE(off[k] - off[k - 1]);
+ }
+ pp->offset /= m;
+ pp->jitter = max(SQRT(pp->jitter / m), LOGTOD(sys_precision));
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",
+ n, pp->offset, pp->disp, pp->jitter);
+#endif
+ return (n);
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: at %lu %s\n",
+ current_time, stoa(&peer->srcadr));
+#endif
+
+ /*
+ * Do a little sanity dance and update the peer structure. Groom
+ * the median filter samples and give the data to the clock
+ * filter.
+ */
+ pp = peer->procptr;
+ peer->leap = pp->leap;
+ if (peer->leap == LEAP_NOTINSYNC)
+ return;
+
+ peer->received++;
+ peer->timereceived = current_time;
+ if (!peer->reach) {
+ report_event(EVNT_REACH, peer);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+ peer->reftime = pp->lastref;
+ peer->org = pp->lastrec;
+ peer->rootdispersion = pp->disp;
+ get_systime(&peer->rec);
+ if (!refclock_sample(pp))
+ return;
+
+ clock_filter(peer, pp->offset, 0., pp->jitter);
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, clock_phi * (current_time -
+ peer->epoch), peer->jitter);
+ if (cal_enable && last_offset < MINDISPERSE) {
+#ifdef KERNEL_PLL
+ if (peer != sys_peer || pll_status & STA_PPSTIME)
+#else
+ if (peer != sys_peer)
+#endif /* KERNEL_PLL */
+ pp->fudgetime1 -= pp->offset * FUDGEFAC;
+ else
+ pp->fudgetime1 -= pp->fudgetime1 * FUDGEFAC;
+ }
+}
+
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * strips the parity bit and control characters. It returns the number
+ * of characters in the line followed by a NULL character ('\0'), which
+ * is not included in the count. In case of an empty line, the previous
+ * line is preserved.
+ */
+int
+refclock_gtlin(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ char s[BMAX];
+ char *dpt, *dpend, *dp;
+
+ dpt = s;
+ dpend = s + refclock_gtraw(rbufp, s, BMAX - 1, tsptr);
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++) {
+ char c;
+
+ c = *dpt & 0x7f;
+ if (c >= 0x20 && c < 0x7f)
+ *dp++ = c;
+ }
+ if (dp == lineptr)
+ return (0);
+
+ *dp = '\0';
+ return (dp - lineptr);
+}
+
+
+/*
+ * refclock_gtraw - get next line/chunk of data
+ *
+ * This routine returns the raw data received from the clock in both
+ * canonical or raw modes. The terminal interface routines map CR to LF.
+ * In canonical mode this results in two lines, one containing data
+ * followed by LF and another containing only LF. In raw mode the
+ * interface routines can deliver arbitraty chunks of data from one
+ * character to a maximum specified by the calling routine. In either
+ * mode the routine returns the number of characters in the line
+ * followed by a NULL character ('\0'), which is not included in the
+ * count.
+ *
+ * If a timestamp is present in the timecode, as produced by the tty_clk
+ * STREAMS module, it returns that as the timestamp; otherwise, it
+ * returns the buffer timestamp.
+ */
+int
+refclock_gtraw(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ char *dpt, *dpend, *dp;
+ l_fp trtmp, tstmp;
+ int i;
+
+ /*
+ * Check for the presence of a timestamp left by the tty_clock
+ * module and, if present, use that instead of the buffer
+ * timestamp captured by the I/O routines. We recognize a
+ * timestamp by noting its value is earlier than the buffer
+ * timestamp, but not more than one second earlier.
+ */
+ dpt = (char *)rbufp->recv_buffer;
+ dpend = dpt + rbufp->recv_length;
+ trtmp = rbufp->recv_time;
+ if (dpend >= dpt + 8) {
+ if (buftvtots(dpend - 8, &tstmp)) {
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug > 1) {
+ printf(
+ "refclock_gtlin: fd %d ldisc %s",
+ rbufp->fd, lfptoa(&trtmp,
+ 6));
+ get_systime(&trtmp);
+ L_SUB(&trtmp, &tstmp);
+ printf(" sigio %s\n",
+ lfptoa(&trtmp, 6));
+ }
+#endif
+ dpend -= 8;
+ trtmp = tstmp;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+ }
+
+ /*
+ * Copy the raw buffer to the user string. The string is padded
+ * with a NULL, which is not included in the character count.
+ */
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++)
+ *dp++ = *dpt;
+ *dp = '\0';
+ i = dp - lineptr;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("refclock_gtraw: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
+#endif
+ *tsptr = trtmp;
+ return (i);
+}
+
+
+/*
+ * The following code does not apply to WINNT & VMS ...
+ */
+#if !defined SYS_VXWORKS && !defined SYS_WINNT
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if success and zero if failure.
+ */
+int
+refclock_open(
+ char *dev, /* device name pointer */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int fd;
+ int omode;
+
+ /*
+ * Open serial port and set default options
+ */
+ omode = O_RDWR;
+#ifdef O_NONBLOCK
+ omode |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ omode |= O_NOCTTY;
+#endif
+
+ fd = open(dev, omode, 0777);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "refclock_open %s: %m", dev);
+ return (0);
+ }
+ if (!refclock_setup(fd, speed, lflags)) {
+ close(fd);
+ return (0);
+ }
+ if (!refclock_ioctl(fd, lflags)) {
+ close(fd);
+ return (0);
+ }
+ return (fd);
+}
+
+/*
+ * refclock_setup - initialize terminal interface structure
+ */
+int
+refclock_setup(
+ int fd, /* file descriptor */
+ u_int speed, /* serial port speed (code) */
+ u_int lflags /* line discipline flags */
+ )
+{
+ int i;
+ TTY ttyb, *ttyp;
+#ifdef PPS
+ fdpps = fd; /* ppsclock legacy */
+#endif /* PPS */
+
+ /*
+ * By default, the serial line port is initialized in canonical
+ * (line-oriented) mode at specified line speed, 8 bits and no
+ * parity. LF ends the line and CR is mapped to LF. The break,
+ * erase and kill functions are disabled. There is a different
+ * section for each terminal interface, as selected at compile
+ * time. The flag bits can be used to set raw mode and echo.
+ */
+ ttyp = &ttyb;
+#ifdef HAVE_TERMIOS
+
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d tcgetattr: %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ if (lflags & LDISC_7O1) {
+ /* HP Z3801A needs 7-bit, odd parity */
+ ttyp->c_cflag = CS7 | PARENB | PARODD | CLOCAL | CREAD;
+ }
+ cfsetispeed(&ttyb, speed);
+ cfsetospeed(&ttyb, speed);
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: 0x%x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR && lflags & LDISC_REMOTE)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (lflags & LDISC_ECHO)
+ ttyp->c_lflag |= ECHO;
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSANOW: %m", fd);
+ return (0);
+ }
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCGETA: %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ if (speed) {
+ u_int ltemp = 0;
+
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ for (i = 0; i < NCCS; ++i)
+ ttyp->c_cc[i] = '\0';
+
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+
+ /*
+ * If we have modem control, check to see if modem leads
+ * are active; if so, set remote connection. This is
+ * necessary for the kernel pps mods to work.
+ */
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCMGET: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_setup fd %d modem status: %x\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ }
+
+ /*
+ * Set raw and echo modes. These can be changed on-fly.
+ */
+ ttyp->c_lflag = ICANON;
+ if (lflags & LDISC_RAW) {
+ ttyp->c_lflag = 0;
+ ttyp->c_iflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TCSETA: %m", fd);
+ return (0);
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup fd %d TIOCGETP: %m", fd);
+ return (0);
+ }
+ if (speed)
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_setup TIOCSETP: %m");
+ return (0);
+ }
+#endif /* HAVE_BSD_TTYS */
+ return(1);
+}
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+#endif /* SYS_VXWORKS SYS_WINNT */
+
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up optional features such as tty_clk. The routine returns 1 if
+ * success and 0 if failure.
+ */
+int
+refclock_ioctl(
+ int fd, /* file descriptor */
+ u_int lflags /* line discipline flags */
+ )
+{
+ /*
+ * simply return 1 if no UNIX line discipline is supported
+ */
+#if !defined SYS_VXWORKS && !defined SYS_WINNT
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_ioctl: fd %d flags 0x%x\n", fd,
+ lflags);
+#endif
+#ifdef TTYCLK
+
+ /*
+ * The TTYCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module and System V STREAMS
+ * support. If not available, don't complain.
+ */
+ if (lflags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ int rval = 0;
+
+ if (ioctl(fd, I_PUSH, "clk") < 0) {
+ msyslog(LOG_NOTICE,
+ "refclock_ioctl fd %d I_PUSH: %m", fd);
+ return (0);
+#ifdef CLK_SETSTR
+ } else {
+ char *str;
+
+ if (lflags & LDISC_CLKPPS)
+ str = "\377";
+ else if (lflags & LDISC_ACTS)
+ str = "*";
+ else
+ str = "\n";
+ if (ioctl(fd, CLK_SETSTR, str) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl fd %d CLK_SETSTR: %m", fd);
+ return (0);
+ }
+#endif /*CLK_SETSTR */
+ }
+ }
+#endif /* TTYCLK */
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+#endif /* SYS_VXWORKS SYS_WINNT */
+ return (1);
+}
+
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(
+ struct sockaddr_storage *srcadr,
+ struct refclockstat *in,
+ struct refclockstat *out
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (srcadr->ss_family != AF_INET)
+ return;
+
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char)REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+
+ peer = typeunit[clktype][unit];
+ if (peer == NULL)
+ return;
+
+ if (peer->procptr == NULL)
+ return;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ peer->stratum = pp->stratum = (u_char)in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ peer->refid = pp->refid = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != 0) {
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG4;
+ out->fudgetime1 = pp->fudgetime1;
+ out->fudgetime2 = pp->fudgetime2;
+ out->fudgeval1 = pp->stratum;
+ out->fudgeval2 = pp->refid;
+ out->flags = (u_char) pp->sloppyclockflag;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = pp->lencode;
+ out->p_lastcode = pp->a_lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out, peer);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(
+ struct sockaddr_storage *srcadr, /* clock address */
+ struct refclockbug *bug /* output structure */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ int i;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (srcadr->ss_family != AF_INET)
+ return;
+
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+
+ clktype = (u_char) REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+
+ peer = typeunit[clktype][unit];
+ if (peer == NULL)
+ return;
+
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->svalues = 0x0000003f;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->nsec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ for (i = 2; i < (int)bug->ntimes; i++)
+ DTOLFP(pp->filter[i - 2], &bug->times[i]);
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer);
+}
+
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/ntp_request.c b/contrib/ntp/ntpd/ntp_request.c
new file mode 100644
index 0000000..b1bc99d
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_request.c
@@ -0,0 +1,2888 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "recvbuff.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+/*
+ * Because we now have v6 addresses in the messages, we need to compensate
+ * for the larger size. Therefore, we introduce the alternate size to
+ * keep us friendly with older implementations. A little ugly.
+ */
+static int client_v6_capable = 0; /* the client can handle longer messages */
+
+#define v6sizeof(type) (client_v6_capable ? sizeof(type) : v4sizeof(type))
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item (older size)*/
+ short v6_sizeofitem; /* size of request data item (new size)*/
+ void (*handler) P((struct sockaddr_storage *, struct interface *,
+ struct req_pkt *)); /* routine to handle request */
+};
+
+/*
+ * Universal request codes
+ */
+static struct req_proc univ_codes[] = {
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+static void req_ack P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static char * prepare_pkt P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_int));
+static char * more_pkt P((void));
+static void flush_pkt P((void));
+static void peer_list P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_list_sum P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void sys_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void sys_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void mem_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void io_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void timer_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void loop_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_conf P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_unconf P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_sys_flag P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void clr_sys_flag P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void setclr_flags P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_long));
+static void list_restrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_resaddflags P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_ressubflags P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_unrestrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_restrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static void mon_getlist_0 P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void mon_getlist_1 P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_peer P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_key_reread P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void trust_key P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void untrust_key P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_trustkey P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_long));
+static void get_auth_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_auth_stats P((void));
+static void req_get_traps P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void req_set_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void req_clr_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_setclr_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static void set_request_keyid P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_control_keyid P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void get_ctl_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void get_if_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_if_reload P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#ifdef KERNEL_PLL
+static void get_kernel_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_clock_fudge P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+#ifdef REFCLOCK
+static void get_clkbug_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+
+/*
+ * ntpd request codes
+ */
+static struct req_proc ntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, 0, peer_list },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, 0, peer_list_sum },
+ { REQ_PEER_INFO, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_info},
+ { REQ_PEER_STATS, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_stats},
+ { REQ_SYS_INFO, NOAUTH, 0, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, 0, timer_stats },
+ { REQ_CONFIG, AUTH, v4sizeof(struct conf_peer),
+ sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_GET_RESTRICT, NOAUTH, 0, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, 0, mon_getlist_0 },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, 0, mon_getlist_1 },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), 0, reset_stats },
+ { REQ_RESET_PEER, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, 0, do_key_reread },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, 0, get_ctl_stats },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, 0, get_kernel_info },
+#endif
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge),
+ sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clkbug_info },
+#endif
+ { REQ_IF_STATS, AUTH, 0, 0, get_if_stats },
+ { REQ_IF_RELOAD, AUTH, 0, 0, do_if_reload },
+
+ { NO_REQUEST, NOAUTH, 0, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+keyid_t info_auth_keyid;
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long numrequests; /* number of requests we've received */
+u_long numresppkts; /* number of resp packets sent with data */
+
+u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */
+/* by the error code */
+
+/*
+ * A hack. To keep the authentication module clear of ntp-ism's, we
+ * include a time reset variable for its stats here.
+ */
+static u_long auth_timereset;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct resp_pkt rpkt;
+static int reqver;
+static int seqno;
+static int nitems;
+static int itemsize;
+static int databytes;
+static char exbuf[RESP_DATA_SIZE];
+static int usingexbuf;
+static struct sockaddr_storage *toaddr;
+static struct interface *frominter;
+
+/*
+ * init_request - initialize request data
+ */
+void
+init_request (void)
+{
+ int i;
+
+ numrequests = 0;
+ numresppkts = 0;
+ auth_timereset = 0;
+ info_auth_keyid = 0; /* by default, can't do this */
+
+ for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++)
+ errorcounter[i] = 0;
+}
+
+
+/*
+ * req_ack - acknowledge request with no data
+ */
+static void
+req_ack(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int errcode
+ )
+{
+ /*
+ * fill in the fields
+ */
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, 0);
+ rpkt.implementation = inpkt->implementation;
+ rpkt.request = inpkt->request;
+ rpkt.err_nitems = ERR_NITEMS(errcode, 0);
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+
+ /*
+ * send packet and bump counters
+ */
+ sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE);
+ errorcounter[errcode]++;
+}
+
+
+/*
+ * prepare_pkt - prepare response packet for transmission, return pointer
+ * to storage for data item.
+ */
+static char *
+prepare_pkt(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *pkt,
+ u_int structsize
+ )
+{
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: preparing pkt\n");
+#endif
+
+ /*
+ * Fill in the implementation, request and itemsize fields
+ * since these won't change.
+ */
+ rpkt.implementation = pkt->implementation;
+ rpkt.request = pkt->request;
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize);
+
+ /*
+ * Compute the static data needed to carry on.
+ */
+ toaddr = srcadr;
+ frominter = inter;
+ seqno = 0;
+ nitems = 0;
+ itemsize = structsize;
+ databytes = 0;
+ usingexbuf = 0;
+
+ /*
+ * return the beginning of the packet buffer.
+ */
+ return &rpkt.data[0];
+}
+
+
+/*
+ * more_pkt - return a data pointer for a new item.
+ */
+static char *
+more_pkt(void)
+{
+ /*
+ * If we were using the extra buffer, send the packet.
+ */
+ if (usingexbuf) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: sending pkt\n");
+#endif
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+
+ /*
+ * Copy data out of exbuf into the packet.
+ */
+ memmove(&rpkt.data[0], exbuf, (unsigned)itemsize);
+ seqno++;
+ databytes = 0;
+ nitems = 0;
+ usingexbuf = 0;
+ }
+
+ databytes += itemsize;
+ nitems++;
+ if (databytes + itemsize <= RESP_DATA_SIZE) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: giving him more data\n");
+#endif
+ /*
+ * More room in packet. Give him the
+ * next address.
+ */
+ return &rpkt.data[databytes];
+ } else {
+ /*
+ * No room in packet. Give him the extra
+ * buffer unless this was the last in the sequence.
+ */
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: into extra buffer\n");
+#endif
+ if (seqno == MAXSEQ)
+ return (char *)0;
+ else {
+ usingexbuf = 1;
+ return exbuf;
+ }
+ }
+}
+
+
+/*
+ * flush_pkt - we're done, return remaining information.
+ */
+static void
+flush_pkt(void)
+{
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: flushing packet, %d items\n", nitems);
+#endif
+ /*
+ * Must send the last packet. If nothing in here and nothing
+ * has been sent, send an error saying no data to be found.
+ */
+ if (seqno == 0 && nitems == 0)
+ req_ack(toaddr, frominter, (struct req_pkt *)&rpkt,
+ INFO_ERR_NODATA);
+ else {
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+ }
+}
+
+
+
+/*
+ * process_private - process private mode (7) packets
+ */
+void
+process_private(
+ struct recvbuf *rbufp,
+ int mod_okay
+ )
+{
+ struct req_pkt *inpkt;
+ struct req_pkt_tail *tailinpkt;
+ struct sockaddr_storage *srcadr;
+ struct interface *inter;
+ struct req_proc *proc;
+ int ec;
+ short temp_size;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("process_private: impl %d req %d\n",
+ inpkt->implementation, inpkt->request);
+#endif
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ ec = 0;
+ if ( (++ec, ISRESPONSE(inpkt->rm_vn_mode))
+ || (++ec, ISMORE(inpkt->rm_vn_mode))
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION)
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION)
+ || (++ec, INFO_SEQ(inpkt->auth_seq) != 0)
+ || (++ec, INFO_ERR(inpkt->err_nitems) != 0)
+ || (++ec, INFO_MBZ(inpkt->mbz_itemsize) != 0)
+ || (++ec, rbufp->recv_length < REQ_LEN_HDR)
+ ) {
+ msyslog(LOG_ERR, "process_private: INFO_ERR_FMT: test %d failed, pkt from %s", ec, stoa(srcadr));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ reqver = INFO_VERSION(inpkt->rm_vn_mode);
+
+ /*
+ * Get the appropriate procedure list to search.
+ */
+ if (inpkt->implementation == IMPL_UNIV)
+ proc = univ_codes;
+ else if ((inpkt->implementation == IMPL_XNTPD) ||
+ (inpkt->implementation == IMPL_XNTPD_OLD))
+ proc = ntp_codes;
+ else {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
+ return;
+ }
+
+ /*
+ * Search the list for the request codes. If it isn't one
+ * we know, return an error.
+ */
+ while (proc->request_code != NO_REQUEST) {
+ if (proc->request_code == (short) inpkt->request)
+ break;
+ proc++;
+ }
+ if (proc->request_code == NO_REQUEST) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("found request in tables\n");
+#endif
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+
+ /* This part is a bit tricky, we want to be sure that the size
+ * returned is either the old or the new size. We also can find
+ * out if the client can accept both types of messages this way.
+ *
+ * Handle the exception of REQ_CONFIG. It can have two data sizes.
+ */
+ temp_size = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ if ((temp_size != proc->sizeofitem &&
+ temp_size != proc->v6_sizeofitem) &&
+ !(inpkt->implementation == IMPL_XNTPD &&
+ inpkt->request == REQ_CONFIG &&
+ temp_size == sizeof(struct old_conf_peer))) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("process_private: wrong item size, received %d, should be %d or %d\n",
+ temp_size, proc->sizeofitem, proc->v6_sizeofitem);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if ((proc->sizeofitem != 0) &&
+ ((temp_size * INFO_NITEMS(inpkt->err_nitems)) >
+ (rbufp->recv_length - REQ_LEN_HDR))) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("process_private: not enough data\n");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ switch (inpkt->implementation) {
+ case IMPL_XNTPD:
+ client_v6_capable = 1;
+ break;
+ case IMPL_XNTPD_OLD:
+ client_v6_capable = 0;
+ break;
+ default:
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * If we need to authenticate, do so. Note that an
+ * authenticatable packet must include a mac field, must
+ * have used key info_auth_keyid and must have included
+ * a time stamp in the appropriate field. The time stamp
+ * must be within INFO_TS_MAXSKEW of the receive
+ * time stamp.
+ */
+ if (proc->needs_auth && sys_authenticate) {
+ l_fp ftmp;
+ double dtemp;
+
+ if (rbufp->recv_length < (int)((REQ_LEN_HDR +
+ (INFO_ITEMSIZE(inpkt->mbz_itemsize) *
+ INFO_NITEMS(inpkt->err_nitems))
+ + sizeof(struct req_pkt_tail)))) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ }
+ tailinpkt = (struct req_pkt_tail *)((char *)&rbufp->recv_pkt +
+ rbufp->recv_length - sizeof(struct req_pkt_tail));
+
+ /*
+ * If this guy is restricted from doing this, don't let him
+ * If wrong key was used, or packet doesn't have mac, return.
+ */
+ if (!INFO_IS_AUTH(inpkt->auth_seq) || info_auth_keyid == 0
+ || ntohl(tailinpkt->keyid) != info_auth_keyid) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth %d info_auth_keyid %lu pkt keyid %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ (u_long)info_auth_keyid,
+ (u_long)ntohl(tailinpkt->keyid));
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth %d info_auth_keyid %lu pkt keyid %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ (u_long)info_auth_keyid,
+ (u_long)ntohl(tailinpkt->keyid));
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ if (rbufp->recv_length > REQ_LEN_MAC) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("bad pkt length %d\n",
+ rbufp->recv_length);
+#endif
+ msyslog(LOG_ERR, "process_private: bad pkt length %d",
+ rbufp->recv_length);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (!mod_okay || !authhavekey(info_auth_keyid)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth mod_okay %d\n", mod_okay);
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth mod_okay %d\n",
+ mod_okay);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * calculate absolute time difference between xmit time stamp
+ * and receive time stamp. If too large, too bad.
+ */
+ NTOHL_FP(&tailinpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ LFPTOD(&ftmp, dtemp);
+ if (fabs(dtemp) >= INFO_TS_MAXSKEW) {
+ /*
+ * He's a loser. Tell him.
+ */
+#ifdef DEBUG
+ if (debug > 4)
+ printf("xmit/rcv timestamp delta > INFO_TS_MAXSKEW\n");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * So far so good. See if decryption works out okay.
+ */
+ if (!authdecrypt(info_auth_keyid, (u_int32 *)inpkt,
+ rbufp->recv_length - sizeof(struct req_pkt_tail) +
+ REQ_LEN_HDR, sizeof(struct req_pkt_tail) - REQ_LEN_HDR)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("authdecrypt failed\n");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("process_private: all okay, into handler\n");
+#endif
+
+ /*
+ * Packet is okay. Call the handler to send him data.
+ */
+ (proc->handler)(srcadr, inter, inpkt);
+}
+
+
+/*
+ * peer_list - send a list of the peers
+ */
+static void
+peer_list(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ip;
+ register struct peer *pp;
+ register int i;
+ register int skip = 0;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_list));
+ for (i = 0; i < NTP_HASH_SIZE && ip != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ip != 0) {
+ if (pp->srcadr.ss_family == AF_INET6) {
+ if (client_v6_capable) {
+ ip->addr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ip->addr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ skip = 0;
+ }
+
+ if(!skip) {
+ ip->port = NSRCPORT(&pp->srcadr);
+ ip->hmode = pp->hmode;
+ ip->flags = 0;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip = (struct info_peer_list *)more_pkt();
+ }
+ pp = pp->next;
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_list_sum - return extended peer list
+ */
+static void
+peer_list_sum(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ register int i;
+ l_fp ltmp;
+ register int skip;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_summary));
+ for (i = 0; i < NTP_HASH_SIZE && ips != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ips != 0) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("sum: got one\n");
+#endif
+ /*
+ * Be careful here not to return v6 peers when we
+ * want only v4.
+ */
+ if (pp->srcadr.ss_family == AF_INET6) {
+ if (client_v6_capable) {
+ ips->srcadr6 = GET_INADDR6(pp->srcadr);
+ ips->v6_flag = 1;
+ if (pp->dstadr)
+ ips->dstadr6 = GET_INADDR6(pp->dstadr->sin);
+ else
+ memset(&ips->dstadr6, 0, sizeof(ips->dstadr6));
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ips->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ips->v6_flag = 0;
+/* XXX PDM This code is buggy. Need to replace with a straightforward assignment */
+
+ if (pp->dstadr)
+ ips->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 1 : GET_INADDR(pp->dstadr->sin);
+ else
+ memset(&ips->dstadr, 0, sizeof(ips->dstadr));
+
+ skip = 0;
+ }
+
+ if (!skip){
+ ips->srcport = NSRCPORT(&pp->srcadr);
+ ips->stratum = pp->stratum;
+ ips->hpoll = pp->hpoll;
+ ips->ppoll = pp->ppoll;
+ ips->reach = pp->reach;
+ ips->flags = 0;
+ if (pp == sys_peer)
+ ips->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ips->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ips->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ips->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ips->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ips->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ips->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ips->flags |= INFO_FLAG_SHORTLIST;
+ ips->hmode = pp->hmode;
+ ips->delay = HTONS_FP(DTOFP(pp->delay));
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ips->offset);
+ ips->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp)));
+ }
+ pp = pp->next;
+ ips = (struct info_peer_summary *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_info - send information for one or more peers
+ */
+static void
+peer_info (
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer *ip;
+ register int items;
+ register int i, j;
+ struct sockaddr_storage addr;
+ extern struct peer *sys_peer;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+
+ ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer));
+ while (items-- > 0 && ip != 0) {
+ memset((char *)&addr, 0, sizeof(addr));
+ NSRCPORT(&addr) = ipl->port;
+ if (client_v6_capable && ipl->v6_flag != 0) {
+ addr.ss_family = AF_INET6;
+ GET_INADDR6(addr) = ipl->addr6;
+ } else {
+ addr.ss_family = AF_INET;
+ GET_INADDR(addr) = ipl->addr;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+ if (pp->srcadr.ss_family == AF_INET6) {
+ if (pp->dstadr)
+ ip->dstadr6 = pp->cast_flags == MDF_BCAST ?
+ GET_INADDR6(pp->dstadr->bcast) :
+ GET_INADDR6(pp->dstadr->sin);
+ else
+ memset(&ip->dstadr6, 0, sizeof(ip->dstadr6));
+
+ ip->srcadr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ } else {
+/* XXX PDM This code is buggy. Need to replace with a straightforward assignment */
+ if (pp->dstadr)
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 2 : GET_INADDR(pp->dstadr->sin);
+ else
+ memset(&ip->dstadr, 0, sizeof(ip->dstadr));
+
+ ip->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->leap = pp->leap;
+ ip->hmode = pp->hmode;
+ ip->keyid = pp->keyid;
+ ip->stratum = pp->stratum;
+ ip->ppoll = pp->ppoll;
+ ip->hpoll = pp->hpoll;
+ ip->precision = pp->precision;
+ ip->version = pp->version;
+ ip->reach = pp->reach;
+ ip->unreach = (u_char) pp->unreach;
+ ip->flash = (u_char)pp->flash;
+ ip->flash2 = (u_short) pp->flash;
+ ip->estbdelay = HTONS_FP(DTOFP(pp->estbdelay));
+ ip->ttl = pp->ttl;
+ ip->associd = htons(pp->associd);
+ ip->rootdelay = HTONS_FP(DTOUFP(pp->rootdelay));
+ ip->rootdispersion = HTONS_FP(DTOUFP(pp->rootdispersion));
+ ip->refid = pp->refid;
+ HTONL_FP(&pp->reftime, &ip->reftime);
+ HTONL_FP(&pp->org, &ip->org);
+ HTONL_FP(&pp->rec, &ip->rec);
+ HTONL_FP(&pp->xmt, &ip->xmt);
+ j = pp->filter_nextpt - 1;
+ for (i = 0; i < NTP_SHIFT; i++, j--) {
+ if (j < 0)
+ j = NTP_SHIFT-1;
+ ip->filtdelay[i] = HTONS_FP(DTOFP(pp->filter_delay[j]));
+ DTOLFP(pp->filter_offset[j], &ltmp);
+ HTONL_FP(&ltmp, &ip->filtoffset[i]);
+ ip->order[i] = (u_char)((pp->filter_nextpt+NTP_SHIFT-1)
+ - pp->filter_order[i]);
+ if (ip->order[i] >= NTP_SHIFT)
+ ip->order[i] -= NTP_SHIFT;
+ }
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ip->offset);
+ ip->delay = HTONS_FP(DTOFP(pp->delay));
+ ip->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp)));
+ ip->selectdisp = HTONS_FP(DTOUFP(SQRT(pp->jitter)));
+ ip = (struct info_peer *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer_stats *ip;
+ register int items;
+ struct sockaddr_storage addr;
+ extern struct peer *sys_peer;
+
+#ifdef DEBUG
+ if (debug)
+ printf("peer_stats: called\n");
+#endif
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != 0) {
+ memset((char *)&addr, 0, sizeof(addr));
+ NSRCPORT(&addr) = ipl->port;
+ if (client_v6_capable && ipl->v6_flag) {
+ addr.ss_family = AF_INET6;
+ GET_INADDR6(addr) = ipl->addr6;
+ } else {
+ addr.ss_family = AF_INET;
+ GET_INADDR(addr) = ipl->addr;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+#ifdef DEBUG
+ if (debug)
+ printf("peer_stats: looking for %s, %d, %d\n", stoa(&addr),
+ ipl->port, ((struct sockaddr_in6 *)&addr)->sin6_port);
+#endif
+ ipl = (struct info_peer_list *)((char *)ipl +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+#ifdef DEBUG
+ if (debug)
+ printf("peer_stats: found %s\n", stoa(&addr));
+#endif
+ if (pp->srcadr.ss_family == AF_INET) {
+ if (pp->dstadr)
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 3 : 7;
+ else
+ memset(&ip->dstadr, 0, sizeof(ip->dstadr));
+
+ ip->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ } else {
+ if (pp->dstadr)
+ ip->dstadr6 = pp->cast_flags == MDF_BCAST ?
+ GET_INADDR6(pp->dstadr->bcast):
+ GET_INADDR6(pp->dstadr->sin);
+ else
+ memset(&ip->dstadr6, 0, sizeof(ip->dstadr6));
+
+ ip->srcadr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->flags & FLAG_IBURST)
+ ip->flags |= INFO_FLAG_IBURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->flags = htons(ip->flags);
+ ip->timereceived = htonl((u_int32)(current_time - pp->timereceived));
+ ip->timetosend = htonl(pp->nextdate - current_time);
+ ip->timereachable = htonl((u_int32)(current_time - pp->timereachable));
+ ip->sent = htonl((u_int32)(pp->sent));
+ ip->processed = htonl((u_int32)(pp->processed));
+ ip->badauth = htonl((u_int32)(pp->badauth));
+ ip->bogusorg = htonl((u_int32)(pp->bogusorg));
+ ip->oldpkt = htonl((u_int32)(pp->oldpkt));
+ ip->seldisp = htonl((u_int32)(pp->seldisptoolarge));
+ ip->selbroken = htonl((u_int32)(pp->selbroken));
+ ip->candidate = pp->status;
+ ip = (struct info_peer_stats *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * sys_info - return system info
+ */
+static void
+sys_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys *is;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_sys));
+
+ if (sys_peer != 0) {
+ if (sys_peer->srcadr.ss_family == AF_INET) {
+ is->peer = GET_INADDR(sys_peer->srcadr);
+ if (client_v6_capable)
+ is->v6_flag = 0;
+ } else if (client_v6_capable) {
+ is->peer6 = GET_INADDR6(sys_peer->srcadr);
+ is->v6_flag = 1;
+ }
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 0;
+ if (client_v6_capable) {
+ is->v6_flag = 0;
+ }
+ is->peer_mode = 0;
+ }
+
+ is->leap = sys_leap;
+ is->stratum = sys_stratum;
+ is->precision = sys_precision;
+ is->rootdelay = htonl(DTOFP(sys_rootdelay));
+ is->rootdispersion = htonl(DTOUFP(sys_rootdispersion));
+ is->frequency = htonl(DTOFP(sys_jitter));
+ is->stability = htonl(DTOUFP(clock_stability));
+ is->refid = sys_refid;
+ HTONL_FP(&sys_reftime, &is->reftime);
+
+ is->poll = sys_poll;
+
+ is->flags = 0;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (sys_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+#ifdef REFCLOCK
+ if (cal_enable)
+ is->flags |= INFO_FLAG_CAL;
+#endif /* REFCLOCK */
+ if (kern_enable)
+ is->flags |= INFO_FLAG_KERNEL;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ if (ntp_enable)
+ is->flags |= INFO_FLAG_NTP;
+ if (pps_enable)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (stats_control)
+ is->flags |= INFO_FLAG_FILEGEN;
+ is->bdelay = HTONS_FP(DTOFP(sys_bdelay));
+ HTONL_UF(sys_authdelay.l_f, &is->authdelay);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * sys_stats - return system statistics
+ */
+static void
+sys_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys_stats *ss;
+
+ /*
+ * Importations from the protocol module
+ */
+ ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys_stats));
+ ss->timeup = htonl((u_int32)current_time);
+ ss->timereset = htonl((u_int32)(current_time - sys_stattime));
+ ss->denied = htonl((u_int32)sys_restricted);
+ ss->oldversionpkt = htonl((u_int32)sys_oldversionpkt);
+ ss->newversionpkt = htonl((u_int32)sys_newversionpkt);
+ ss->unknownversion = htonl((u_int32)sys_unknownversion);
+ ss->badlength = htonl((u_int32)sys_badlength);
+ ss->processed = htonl((u_int32)sys_processed);
+ ss->badauth = htonl((u_int32)sys_badauth);
+ ss->limitrejected = htonl((u_int32)sys_limitrejected);
+ ss->received = htonl((u_int32)sys_received);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_mem_stats *ms;
+ register int i;
+
+ /*
+ * Importations from the peer module
+ */
+ extern int peer_hash_count[NTP_HASH_SIZE];
+ extern int peer_free_count;
+ extern u_long peer_timereset;
+ extern u_long findpeer_calls;
+ extern u_long peer_allocations;
+ extern u_long peer_demobilizations;
+ extern int total_peer_structs;
+
+ ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_mem_stats));
+
+ ms->timereset = htonl((u_int32)(current_time - peer_timereset));
+ ms->totalpeermem = htons((u_short)total_peer_structs);
+ ms->freepeermem = htons((u_short)peer_free_count);
+ ms->findpeer_calls = htonl((u_int32)findpeer_calls);
+ ms->allocations = htonl((u_int32)peer_allocations);
+ ms->demobilizations = htonl((u_int32)peer_demobilizations);
+
+ for (i = 0; i < NTP_HASH_SIZE; i++) {
+ if (peer_hash_count[i] > 255)
+ ms->hashcount[i] = 255;
+ else
+ ms->hashcount[i] = (u_char)peer_hash_count[i];
+ }
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * io_stats - return io statistics
+ */
+static void
+io_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_io_stats *io;
+
+ /*
+ * Importations from the io module
+ */
+ extern u_long io_timereset;
+
+ io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_io_stats));
+
+ io->timereset = htonl((u_int32)(current_time - io_timereset));
+ io->totalrecvbufs = htons((u_short) total_recvbuffs());
+ io->freerecvbufs = htons((u_short) free_recvbuffs());
+ io->fullrecvbufs = htons((u_short) full_recvbuffs());
+ io->lowwater = htons((u_short) lowater_additions());
+ io->dropped = htonl((u_int32)packets_dropped);
+ io->ignored = htonl((u_int32)packets_ignored);
+ io->received = htonl((u_int32)packets_received);
+ io->sent = htonl((u_int32)packets_sent);
+ io->notsent = htonl((u_int32)packets_notsent);
+ io->interrupts = htonl((u_int32)handler_calls);
+ io->int_received = htonl((u_int32)handler_pkts);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * timer_stats - return timer statistics
+ */
+static void
+timer_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_timer_stats *ts;
+
+ /*
+ * Importations from the timer module
+ */
+ extern u_long timer_timereset;
+ extern u_long timer_overflows;
+ extern u_long timer_xmtcalls;
+
+ ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_timer_stats));
+
+ ts->timereset = htonl((u_int32)(current_time - timer_timereset));
+ ts->alarms = htonl((u_int32)alarm_overflow);
+ ts->overflows = htonl((u_int32)timer_overflows);
+ ts->xmtcalls = htonl((u_int32)timer_xmtcalls);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * loop_info - return the current state of the loop filter
+ */
+static void
+loop_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_loop *li;
+ l_fp ltmp;
+
+ /*
+ * Importations from the loop filter module
+ */
+ extern double last_offset;
+ extern double drift_comp;
+ extern int tc_counter;
+ extern u_long sys_clocktime;
+
+ li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_loop));
+
+ DTOLFP(last_offset, &ltmp);
+ HTONL_FP(&ltmp, &li->last_offset);
+ DTOLFP(drift_comp * 1e6, &ltmp);
+ HTONL_FP(&ltmp, &li->drift_comp);
+ li->compliance = htonl((u_int32)(tc_counter));
+ li->watchdog_timer = htonl((u_int32)(current_time - sys_clocktime));
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * do_conf - add a peer to the configuration list
+ */
+static void
+do_conf(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ int items;
+ u_int fl;
+ struct conf_peer *cp;
+ struct conf_peer temp_cp;
+ struct sockaddr_storage peeraddr;
+ struct sockaddr_in tmp_clock;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+ memset(&temp_cp, 0, sizeof(struct conf_peer));
+ memcpy(&temp_cp, (char *)cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ fl = 0;
+ while (items-- > 0 && !fl) {
+ if (((temp_cp.version) > NTP_VERSION)
+ || ((temp_cp.version) < NTP_OLDVERSION))
+ fl = 1;
+ if (temp_cp.hmode != MODE_ACTIVE
+ && temp_cp.hmode != MODE_CLIENT
+ && temp_cp.hmode != MODE_BROADCAST)
+ fl = 1;
+ if (temp_cp.flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER
+ | CONF_FLAG_BURST | CONF_FLAG_IBURST | CONF_FLAG_SKEY))
+ fl = 1;
+ cp = (struct conf_peer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (fl) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+
+ while (items-- > 0) {
+ memset(&temp_cp, 0, sizeof(struct conf_peer));
+ memcpy(&temp_cp, (char *)cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_storage));
+
+ fl = 0;
+ if (temp_cp.flags & CONF_FLAG_AUTHENABLE)
+ fl |= FLAG_AUTHENABLE;
+ if (temp_cp.flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ if (temp_cp.flags & CONF_FLAG_BURST)
+ fl |= FLAG_BURST;
+ if (temp_cp.flags & CONF_FLAG_IBURST)
+ fl |= FLAG_IBURST;
+ if (temp_cp.flags & CONF_FLAG_SKEY)
+ fl |= FLAG_SKEY;
+
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ /*
+ * Make sure the address is valid
+ */
+ tmp_clock = *CAST_V4(peeraddr);
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&tmp_clock) &&
+#endif
+ ISBADADR(&tmp_clock)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, (struct interface *)0,
+ temp_cp.hmode, temp_cp.version, temp_cp.minpoll,
+ temp_cp.maxpoll, fl, temp_cp.ttl, temp_cp.keyid,
+ NULL) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ cp = (struct conf_peer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+#if 0
+/* XXX */
+/*
+ * dns_a - Snarf DNS info for an association ID
+ */
+static void
+dns_a(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_dns_assoc *dp;
+ register int items;
+ struct sockaddr_in peeraddr;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ dp = (struct info_dns_assoc *)inpkt->data;
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ dp = (struct info_dns_assoc *)inpkt->data;
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_in));
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ /*
+ * Make sure the address is valid
+ */
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+#ifdef REFCLOCK
+ msyslog(LOG_ERR, "dns_a: !ISREFCLOCK && ISBADADR");
+#else
+ msyslog(LOG_ERR, "dns_a: ISBADADR");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ associd_t associd;
+ size_t hnl;
+ struct peer *peer;
+ int bogon = 0;
+
+ associd = dp->associd;
+ peer = findpeerbyassoc(associd);
+ if (peer == 0 || peer->flags & FLAG_REFCLOCK) {
+ msyslog(LOG_ERR, "dns_a: %s",
+ (peer == 0)
+ ? "peer == 0"
+ : "peer->flags & FLAG_REFCLOCK");
+ ++bogon;
+ }
+ peeraddr.sin_addr.s_addr = dp->peeraddr;
+ for (hnl = 0; dp->hostname[hnl] && hnl < sizeof dp->hostname; ++hnl) ;
+ if (hnl >= sizeof dp->hostname) {
+ msyslog(LOG_ERR, "dns_a: hnl (%ld) >= %ld",
+ (long)hnl, (long)sizeof dp->hostname);
+ ++bogon;
+ }
+
+ msyslog(LOG_INFO, "dns_a: <%s> for %s, AssocID %d, bogon %d",
+ dp->hostname,
+ stoa((struct sockaddr_storage *)&peeraddr), associd,
+ bogon);
+
+ if (bogon) {
+ /* If it didn't work */
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ } else {
+#if 0
+#ifdef PUBKEY
+ crypto_public(peer, dp->hostname);
+#endif /* PUBKEY */
+#endif
+ }
+
+ dp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif /* 0 */
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ struct conf_unpeer temp_cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_storage peeraddr;
+ int bad, found;
+
+ /*
+ * This is a bit unstructured, but I like to be careful.
+ * We check to see that every peer exists and is actually
+ * configured. If so, we remove them. If not, we return
+ * an error.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ memset(&temp_cp, 0, sizeof(temp_cp));
+ memset(&peeraddr, 0, sizeof(peeraddr));
+ memcpy(&temp_cp, cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ found = 0;
+ peer = (struct peer *)0;
+#ifdef DEBUG
+ if (debug)
+ printf("searching for %s\n", stoa(&peeraddr));
+#endif
+ while (!found) {
+ peer = findexistingpeer(&peeraddr, peer, -1);
+ if (peer == (struct peer *)0)
+ break;
+ if (peer->flags & FLAG_CONFIG)
+ found = 1;
+ }
+ if (!found)
+ bad = 1;
+ cp = (struct conf_unpeer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ memset(&temp_cp, 0, sizeof(temp_cp));
+ memset(&peeraddr, 0, sizeof(peeraddr));
+ memcpy(&temp_cp, cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer_unconfig(&peeraddr, (struct interface *)0, -1);
+ cp = (struct conf_unpeer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * clr_sys_flag - clear system flags
+ */
+static void
+clr_sys_flag(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * setclr_flags - do the grunge work of flag setting/clearing
+ */
+static void
+setclr_flags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ u_long set
+ )
+{
+ register u_int flags;
+ int prev_kern_enable;
+
+ prev_kern_enable = kern_enable;
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "setclr_flags: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct conf_sys_flags *)inpkt->data)->flags;
+ flags = ntohl(flags);
+
+ if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN | SYS_FLAG_AUTH | SYS_FLAG_CAL)) {
+ msyslog(LOG_ERR, "setclr_flags: extra flags: %#x",
+ flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL |
+ SYS_FLAG_MONITOR | SYS_FLAG_FILEGEN |
+ SYS_FLAG_AUTH | SYS_FLAG_CAL));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set, 0., NULL);
+ if (flags & SYS_FLAG_PPS)
+ proto_config(PROTO_PPS, set, 0., NULL);
+ if (flags & SYS_FLAG_NTP)
+ proto_config(PROTO_NTP, set, 0., NULL);
+ if (flags & SYS_FLAG_KERNEL)
+ proto_config(PROTO_KERNEL, set, 0., NULL);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set, 0., NULL);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set, 0., NULL);
+ if (flags & SYS_FLAG_AUTH)
+ proto_config(PROTO_AUTHENTICATE, set, 0., NULL);
+ if (flags & SYS_FLAG_CAL)
+ proto_config(PROTO_CAL, set, 0., NULL);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+
+ /* Reset the kernel ntp parameters if the kernel flag changed. */
+ if (prev_kern_enable && !kern_enable)
+ loop_config(LOOP_KERN_CLEAR, 0.0);
+ if (!prev_kern_enable && kern_enable)
+ loop_config(LOOP_DRIFTCOMP, drift_comp);
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_restrict *ir;
+ register struct restrictlist *rl;
+ register struct restrictlist6 *rl6;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants restrict list summary\n");
+#endif
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_restrict));
+
+ for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) {
+ ir->addr = htonl(rl->addr);
+ if (client_v6_capable)
+ ir->v6_flag = 0;
+ ir->mask = htonl(rl->mask);
+ ir->count = htonl((u_int32)rl->count);
+ ir->flags = htons(rl->flags);
+ ir->mflags = htons(rl->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ if (client_v6_capable)
+ for (rl6 = restrictlist6; rl6 != 0 && ir != 0; rl6 = rl6->next) {
+ ir->addr6 = rl6->addr6;
+ ir->mask6 = rl6->mask6;
+ ir->v6_flag = 1;
+ ir->count = htonl((u_int32)rl6->count);
+ ir->flags = htons(rl6->flags);
+ ir->mflags = htons(rl6->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS);
+}
+
+
+
+/*
+ * do_ressubflags - remove flags from a restrict entry
+ */
+static void
+do_ressubflags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG);
+}
+
+
+/*
+ * do_unrestrict - remove a restrict entry from the list
+ */
+static void
+do_unrestrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE);
+}
+
+
+
+
+
+/*
+ * do_restrict - do the dirty stuff of dealing with restrictions
+ */
+static void
+do_restrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int op
+ )
+{
+ register struct conf_restrict *cr;
+ register int items;
+ struct sockaddr_storage matchaddr;
+ struct sockaddr_storage matchmask;
+ int bad;
+
+ /*
+ * Do a check of the flags to make sure that only
+ * the NTPPORT flag is set, if any. If not, complain
+ * about it. Note we are very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+
+ bad = 0;
+ cr->flags = ntohs(cr->flags);
+ cr->mflags = ntohs(cr->mflags);
+ while (items-- > 0 && !bad) {
+ if (cr->mflags & ~(RESM_NTPONLY))
+ bad |= 1;
+ if (cr->flags & ~(RES_ALLFLAGS))
+ bad |= 2;
+ if (cr->mask != htonl(INADDR_ANY)) {
+ if (client_v6_capable && cr->v6_flag != 0) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&cr->addr6))
+ bad |= 4;
+ } else
+ if (cr->addr == htonl(INADDR_ANY))
+ bad |= 8;
+ }
+ cr = (struct conf_restrict *)((char *)cr +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ msyslog(LOG_ERR, "do_restrict: bad = %#x", bad);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+ memset((char *)&matchaddr, 0, sizeof(struct sockaddr_storage));
+ memset((char *)&matchmask, 0, sizeof(struct sockaddr_storage));
+
+ while (items-- > 0) {
+ if (client_v6_capable && cr->v6_flag != 0) {
+ GET_INADDR6(matchaddr) = cr->addr6;
+ GET_INADDR6(matchmask) = cr->mask6;
+ matchaddr.ss_family = AF_INET6;
+ matchmask.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(matchaddr) = cr->addr;
+ GET_INADDR(matchmask) = cr->mask;
+ matchaddr.ss_family = AF_INET;
+ matchmask.ss_family = AF_INET;
+ }
+ hack_restrict(op, &matchaddr, &matchmask, cr->mflags,
+ cr->flags);
+ cr++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_0(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_monitor *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 0 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_monitor));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl((u_int32)md->avg_interval);
+ im->firsttime = htonl((u_int32)(current_time - md->lasttime));
+ im->lastdrop = htonl((u_int32)md->drop_count);
+ im->count = htonl((u_int32)(md->count));
+ if (md->rmtadr.ss_family == AF_INET6) {
+ if (!client_v6_capable)
+ continue;
+ im->addr6 = GET_INADDR6(md->rmtadr);
+ im->v6_flag = 1;
+ } else {
+ im->addr = GET_INADDR(md->rmtadr);
+ if (client_v6_capable)
+ im->v6_flag = 0;
+ }
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_1(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_monitor_1 *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_monitor_1));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl((u_int32)md->avg_interval);
+ im->firsttime = htonl((u_int32)(current_time - md->lasttime));
+ im->lastdrop = htonl((u_int32)md->drop_count);
+ im->count = htonl((u_int32)md->count);
+ if (md->rmtadr.ss_family == AF_INET6) {
+ if (!client_v6_capable)
+ continue;
+ im->addr6 = GET_INADDR6(md->rmtadr);
+ im->v6_flag = 1;
+ im->daddr6 = GET_INADDR6(md->interface->sin);
+ } else {
+ im->addr = GET_INADDR(md->rmtadr);
+ if (client_v6_capable)
+ im->v6_flag = 0;
+ im->daddr = (md->cast_flags == MDF_BCAST)
+ ? GET_INADDR(md->interface->bcast)
+ : (md->cast_flags
+ ? (GET_INADDR(md->interface->sin)
+ ? GET_INADDR(md->interface->sin)
+ : GET_INADDR(md->interface->bcast))
+ : 4);
+ }
+ im->flags = htonl(md->cast_flags);
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor_1 *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * Module entry points and the flags they correspond with
+ */
+struct reset_entry {
+ int flag; /* flag this corresponds to */
+ void (*handler) P((void)); /* routine to handle request */
+};
+
+struct reset_entry reset_entries[] = {
+ { RESET_FLAG_ALLPEERS, peer_all_reset },
+ { RESET_FLAG_IO, io_clr_stats },
+ { RESET_FLAG_SYS, proto_clr_stats },
+ { RESET_FLAG_MEM, peer_clr_stats },
+ { RESET_FLAG_TIMER, timer_clr_stats },
+ { RESET_FLAG_AUTH, reset_auth_stats },
+ { RESET_FLAG_CTL, ctl_clr_stats },
+ { 0, 0 }
+};
+
+/*
+ * reset_stats - reset statistic counters here and there
+ */
+static void
+reset_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "reset_stats: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct reset_flags *)inpkt->data)->flags;
+ flags = ntohl(flags);
+
+ if (flags & ~RESET_ALLFLAGS) {
+ msyslog(LOG_ERR, "reset_stats: reset leaves %#lx",
+ flags & ~RESET_ALLFLAGS);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ for (rent = reset_entries; rent->flag != 0; rent++) {
+ if (flags & rent->flag)
+ (rent->handler)();
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * reset_peer - clear a peer's statistics
+ */
+static void
+reset_peer(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_storage peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (client_v6_capable && cp->v6_flag != 0) {
+ GET_INADDR6(peeraddr) = cp->peeraddr6;
+ peeraddr.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(peeraddr) = cp->peeraddr;
+ peeraddr.ss_family = AF_INET;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ if (peer == (struct peer *)0)
+ bad++;
+ cp = (struct conf_unpeer *)((char *)cp +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (client_v6_capable && cp->v6_flag != 0) {
+ GET_INADDR6(peeraddr) = cp->peeraddr6;
+ peeraddr.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(peeraddr) = cp->peeraddr;
+ peeraddr.ss_family = AF_INET;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ while (peer != 0) {
+ peer_reset(peer);
+ peer = findexistingpeer(&peeraddr, (struct peer *)peer, -1);
+ }
+ cp = (struct conf_unpeer *)((char *)cp +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ rereadkeys();
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * trust_key - make one or more keys trusted
+ */
+static void
+trust_key(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * untrust_key - make one or more keys untrusted
+ */
+static void
+untrust_key(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * do_trustkey - make keys either trustable or untrustable
+ */
+static void
+do_trustkey(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ u_long trust
+ )
+{
+ register u_long *kp;
+ register int items;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ kp = (u_long *)inpkt->data;
+ while (items-- > 0) {
+ authtrust(*kp, trust);
+ kp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * get_auth_info - return some stats concerning the authentication module
+ */
+static void
+get_auth_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_auth *ia;
+
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authnumkeys;
+ extern int authnumfreekeys;
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+ extern u_long authkeyexpired;
+
+ ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_auth));
+
+ ia->numkeys = htonl((u_int32)authnumkeys);
+ ia->numfreekeys = htonl((u_int32)authnumfreekeys);
+ ia->keylookups = htonl((u_int32)authkeylookups);
+ ia->keynotfound = htonl((u_int32)authkeynotfound);
+ ia->encryptions = htonl((u_int32)authencryptions);
+ ia->decryptions = htonl((u_int32)authdecryptions);
+ ia->keyuncached = htonl((u_int32)authkeyuncached);
+ ia->expired = htonl((u_int32)authkeyexpired);
+ ia->timereset = htonl((u_int32)(current_time - auth_timereset));
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * reset_auth_stats - reset the authentication stat counters. Done here
+ * to keep ntp-isms out of the authentication module
+ */
+static void
+reset_auth_stats(void)
+{
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ authkeylookups = 0;
+ authkeynotfound = 0;
+ authencryptions = 0;
+ authdecryptions = 0;
+ authkeyuncached = 0;
+ auth_timereset = current_time;
+}
+
+
+/*
+ * req_get_traps - return information about current trap holders
+ */
+static void
+req_get_traps(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_trap *it;
+ register struct ctl_trap *tr;
+ register int i;
+
+ /*
+ * Imported from the control module
+ */
+ extern struct ctl_trap ctl_trap[];
+ extern int num_ctl_traps;
+
+ if (num_ctl_traps == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (tr->tr_addr.ss_family == AF_INET) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = GET_INADDR(tr->tr_localaddr->sin);
+ it->trap_address = GET_INADDR(tr->tr_addr);
+ if (client_v6_capable)
+ it->v6_flag = 0;
+ } else {
+ if (!client_v6_capable)
+ continue;
+ it->local_address6
+ = GET_INADDR6(tr->tr_localaddr->sin);
+ it->trap_address6 = GET_INADDR6(tr->tr_addr);
+ it->v6_flag = 1;
+ }
+ it->trap_port = NSRCPORT(&tr->tr_addr);
+ it->sequence = htons(tr->tr_sequence);
+ it->settime = htonl((u_int32)(current_time - tr->tr_settime));
+ it->origtime = htonl((u_int32)(current_time - tr->tr_origtime));
+ it->resets = htonl((u_int32)tr->tr_resets);
+ it->flags = htonl((u_int32)tr->tr_flags);
+ it = (struct info_trap *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * req_set_trap - configure a trap
+ */
+static void
+req_set_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 1);
+}
+
+
+
+/*
+ * req_clr_trap - unconfigure a trap
+ */
+static void
+req_clr_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 0);
+}
+
+
+
+/*
+ * do_setclr_trap - do the grunge work of (un)configuring a trap
+ */
+static void
+do_setclr_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int set
+ )
+{
+ register struct conf_trap *ct;
+ register struct interface *linter;
+ int res;
+ struct sockaddr_storage laddr;
+
+ /*
+ * Prepare sockaddr_storage structure
+ */
+ memset((char *)&laddr, 0, sizeof laddr);
+ laddr.ss_family = srcadr->ss_family;
+ NSRCPORT(&laddr) = ntohs(NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "do_setclr_trap: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ct = (struct conf_trap *)inpkt->data;
+
+ /*
+ * Look for the local interface. If none, use the default.
+ */
+ if (ct->local_address == 0) {
+ linter = any_interface;
+ } else {
+ if (laddr.ss_family == AF_INET)
+ GET_INADDR(laddr) = ct->local_address;
+ else
+ GET_INADDR6(laddr) = ct->local_address6;
+ linter = findinterface(&laddr);
+ if (linter == NULL) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ if (laddr.ss_family == AF_INET)
+ GET_INADDR(laddr) = ct->trap_address;
+ else
+ GET_INADDR6(laddr) = ct->trap_address6;
+ if (ct->trap_port != 0)
+ NSRCPORT(&laddr) = ct->trap_port;
+ else
+ NSRCPORT(&laddr) = htons(TRAPPORT);
+
+ if (set) {
+ res = ctlsettrap(&laddr, linter, 0,
+ INFO_VERSION(inpkt->rm_vn_mode));
+ } else {
+ res = ctlclrtrap(&laddr, linter, 0);
+ }
+
+ if (!res) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ } else {
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+ }
+ return;
+}
+
+
+
+/*
+ * set_request_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_request_keyid(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_request_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_int32 *)(inpkt->data)));
+ info_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * set_control_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_control_keyid(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t keyid;
+ extern keyid_t ctl_auth_keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_control_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_int32 *)(inpkt->data)));
+ ctl_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * get_ctl_stats - return some stats concerning the control message module
+ */
+static void
+get_ctl_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_control *ic;
+
+ /*
+ * Importations from the control module
+ */
+ extern u_long ctltimereset;
+ extern u_long numctlreq;
+ extern u_long numctlbadpkts;
+ extern u_long numctlresponses;
+ extern u_long numctlfrags;
+ extern u_long numctlerrors;
+ extern u_long numctltooshort;
+ extern u_long numctlinputresp;
+ extern u_long numctlinputfrag;
+ extern u_long numctlinputerr;
+ extern u_long numctlbadoffset;
+ extern u_long numctlbadversion;
+ extern u_long numctldatatooshort;
+ extern u_long numctlbadop;
+ extern u_long numasyncmsgs;
+
+ ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_control));
+
+ ic->ctltimereset = htonl((u_int32)(current_time - ctltimereset));
+ ic->numctlreq = htonl((u_int32)numctlreq);
+ ic->numctlbadpkts = htonl((u_int32)numctlbadpkts);
+ ic->numctlresponses = htonl((u_int32)numctlresponses);
+ ic->numctlfrags = htonl((u_int32)numctlfrags);
+ ic->numctlerrors = htonl((u_int32)numctlerrors);
+ ic->numctltooshort = htonl((u_int32)numctltooshort);
+ ic->numctlinputresp = htonl((u_int32)numctlinputresp);
+ ic->numctlinputfrag = htonl((u_int32)numctlinputfrag);
+ ic->numctlinputerr = htonl((u_int32)numctlinputerr);
+ ic->numctlbadoffset = htonl((u_int32)numctlbadoffset);
+ ic->numctlbadversion = htonl((u_int32)numctlbadversion);
+ ic->numctldatatooshort = htonl((u_int32)numctldatatooshort);
+ ic->numctlbadop = htonl((u_int32)numctlbadop);
+ ic->numasyncmsgs = htonl((u_int32)numasyncmsgs);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+#ifdef KERNEL_PLL
+/*
+ * get_kernel_info - get kernel pll/pps information
+ */
+static void
+get_kernel_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_kernel *ik;
+ struct timex ntx;
+
+ if (!pll_control) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&ntx, 0, sizeof(ntx));
+ if (ntp_adjtime(&ntx) < 0)
+ msyslog(LOG_ERR, "get_kernel_info: ntp_adjtime() failed: %m");
+ ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_kernel));
+
+ /*
+ * pll variables
+ */
+ ik->offset = htonl((u_int32)ntx.offset);
+ ik->freq = htonl((u_int32)ntx.freq);
+ ik->maxerror = htonl((u_int32)ntx.maxerror);
+ ik->esterror = htonl((u_int32)ntx.esterror);
+ ik->status = htons(ntx.status);
+ ik->constant = htonl((u_int32)ntx.constant);
+ ik->precision = htonl((u_int32)ntx.precision);
+ ik->tolerance = htonl((u_int32)ntx.tolerance);
+
+ /*
+ * pps variables
+ */
+ ik->ppsfreq = htonl((u_int32)ntx.ppsfreq);
+ ik->jitter = htonl((u_int32)ntx.jitter);
+ ik->shift = htons(ntx.shift);
+ ik->stabil = htonl((u_int32)ntx.stabil);
+ ik->jitcnt = htonl((u_int32)ntx.jitcnt);
+ ik->calcnt = htonl((u_int32)ntx.calcnt);
+ ik->errcnt = htonl((u_int32)ntx.errcnt);
+ ik->stbcnt = htonl((u_int32)ntx.stbcnt);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef REFCLOCK
+/*
+ * get_clock_info - get info about a clock
+ */
+static void
+get_clock_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_clock *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockstat clock_stat;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (u_int32 *) inpkt->data;
+
+ ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clock));
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = *clkaddr++;
+ CAST_V4(addr)->sin_addr = tmp_clock.sin_addr;
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&addr, (struct refclockstat *)0, &clock_stat);
+
+ ic->clockadr = tmp_clock.sin_addr.s_addr;
+ ic->type = clock_stat.type;
+ ic->flags = clock_stat.flags;
+ ic->lastevent = clock_stat.lastevent;
+ ic->currentstatus = clock_stat.currentstatus;
+ ic->polls = htonl((u_int32)clock_stat.polls);
+ ic->noresponse = htonl((u_int32)clock_stat.noresponse);
+ ic->badformat = htonl((u_int32)clock_stat.badformat);
+ ic->baddata = htonl((u_int32)clock_stat.baddata);
+ ic->timestarted = htonl((u_int32)clock_stat.timereset);
+ DTOLFP(clock_stat.fudgetime1, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime1);
+ DTOLFP(clock_stat.fudgetime2, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime2);
+ ic->fudgeval1 = htonl((u_int32)clock_stat.fudgeval1);
+ ic->fudgeval2 = htonl((u_int32)clock_stat.fudgeval2);
+
+ free_varlist(clock_stat.kv_list);
+
+ ic = (struct info_clock *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * set_clock_fudge - get a clock's fudge factors
+ */
+static void
+set_clock_fudge(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock_stat;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ memset((char *)&clock_stat, 0, sizeof clock_stat);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *) inpkt->data;
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = cf->clockadr;
+ *CAST_V4(addr) = tmp_clock;
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ switch(ntohl(cf->which)) {
+ case FUDGE_TIME1:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime1);
+ clock_stat.haveflags = CLK_HAVETIME1;
+ break;
+ case FUDGE_TIME2:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime2);
+ clock_stat.haveflags = CLK_HAVETIME2;
+ break;
+ case FUDGE_VAL1:
+ clock_stat.fudgeval1 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL1;
+ break;
+ case FUDGE_VAL2:
+ clock_stat.fudgeval2 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL2;
+ break;
+ case FUDGE_FLAGS:
+ clock_stat.flags = (u_char) (ntohl(cf->fudgeval_flags) & 0xf);
+ clock_stat.haveflags =
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4);
+ break;
+ default:
+ msyslog(LOG_ERR, "set_clock_fudge: default!");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ refclock_control(&addr, &clock_stat, (struct refclockstat *)0);
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif
+
+#ifdef REFCLOCK
+/*
+ * get_clkbug_info - get debugging info about a clock
+ */
+static void
+get_clkbug_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register int i;
+ register struct info_clkbug *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockbug bug;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (u_int32 *) inpkt->data;
+
+ ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clkbug));
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = *clkaddr++;
+ GET_INADDR(addr) = tmp_clock.sin_addr.s_addr;
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&bug, 0, sizeof bug);
+ refclock_buginfo(&addr, &bug);
+ if (bug.nvalues == 0 && bug.ntimes == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ic->clockadr = tmp_clock.sin_addr.s_addr;
+ i = bug.nvalues;
+ if (i > NUMCBUGVALUES)
+ i = NUMCBUGVALUES;
+ ic->nvalues = (u_char)i;
+ ic->svalues = htons((u_short) (bug.svalues & ((1<<i)-1)));
+ while (--i >= 0)
+ ic->values[i] = htonl(bug.values[i]);
+
+ i = bug.ntimes;
+ if (i > NUMCBUGTIMES)
+ i = NUMCBUGTIMES;
+ ic->ntimes = (u_char)i;
+ ic->stimes = htonl(bug.stimes);
+ while (--i >= 0) {
+ HTONL_FP(&bug.times[i], &ic->times[i]);
+ }
+
+ ic = (struct info_clkbug *)more_pkt();
+ }
+ flush_pkt();
+}
+#endif
+
+/*
+ * receiver of interface structures
+ */
+static void
+fill_info_if_stats(void *data, interface_info_t *interface_info)
+{
+ struct info_if_stats **ifsp = (struct info_if_stats **)data;
+ struct info_if_stats *ifs = *ifsp;
+ struct interface *interface = interface_info->interface;
+
+ memset((char*)ifs, 0, sizeof(*ifs));
+
+ if (interface->sin.ss_family == AF_INET6) {
+ if (!client_v6_capable) {
+ return;
+ }
+ ifs->v6_flag = 1;
+ memcpy((char *)&ifs->unaddr.addr6, (char *)&CAST_V6(interface->sin)->sin6_addr, sizeof(struct in6_addr));
+ memcpy((char *)&ifs->unbcast.addr6, (char *)&CAST_V6(interface->bcast)->sin6_addr, sizeof(struct in6_addr));
+ memcpy((char *)&ifs->unmask.addr6, (char *)&CAST_V6(interface->mask)->sin6_addr, sizeof(struct in6_addr));
+ } else {
+ ifs->v6_flag = 0;
+ memcpy((char *)&ifs->unaddr.addr, (char *)&CAST_V4(interface->sin)->sin_addr, sizeof(struct in_addr));
+ memcpy((char *)&ifs->unbcast.addr, (char *)&CAST_V4(interface->bcast)->sin_addr, sizeof(struct in_addr));
+ memcpy((char *)&ifs->unmask.addr, (char *)&CAST_V4(interface->mask)->sin_addr, sizeof(struct in_addr));
+ }
+ ifs->v6_flag = htonl(ifs->v6_flag);
+ strcpy(ifs->name, interface->name);
+ ifs->family = htons(interface->family);
+ ifs->flags = htonl(interface->flags);
+ ifs->last_ttl = htonl(interface->last_ttl);
+ ifs->num_mcast = htonl(interface->num_mcast);
+ ifs->received = htonl(interface->received);
+ ifs->sent = htonl(interface->sent);
+ ifs->notsent = htonl(interface->notsent);
+ ifs->scopeid = htonl(interface->scopeid);
+ ifs->ifindex = htonl(interface->ifindex);
+ ifs->ifnum = htonl(interface->ifnum);
+ ifs->uptime = htonl(current_time - interface->starttime);
+ ifs->ignore_packets = interface->ignore_packets;
+ ifs->peercnt = htonl(interface->peercnt);
+ ifs->action = interface_info->action;
+
+ *ifsp = (struct info_if_stats *)more_pkt();
+}
+
+/*
+ * get_if_stats - get interface statistics
+ */
+static void
+get_if_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_if_stats *ifs;
+
+ DPRINTF(3, ("wants interface statistics\n"));
+
+ ifs = (struct info_if_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_if_stats));
+
+ interface_enumerate(fill_info_if_stats, &ifs);
+
+ flush_pkt();
+}
+
+static void
+do_if_reload(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ struct info_if_stats *ifs;
+
+ DPRINTF(3, ("wants interface reload\n"));
+
+ ifs = (struct info_if_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_if_stats));
+
+ interface_update(fill_info_if_stats, &ifs);
+
+ flush_pkt();
+}
+
diff --git a/contrib/ntp/ntpd/ntp_restrict.c b/contrib/ntp/ntpd/ntp_restrict.c
new file mode 100644
index 0000000..473e2ce
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_restrict.c
@@ -0,0 +1,607 @@
+/*
+ * ntp_restrict.c - determine host restrictions
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This code keeps a simple address-and-mask list of hosts we want
+ * to place restrictions on (or remove them from). The restrictions
+ * are implemented as a set of flags which tell you what the host
+ * can't do. There is a subroutine entry to return the flags. The
+ * list is kept sorted to reduce the average number of comparisons
+ * and make sure you get the set of restrictions most specific to
+ * the address.
+ *
+ * The algorithm is that, when looking up a host, it is first assumed
+ * that the default set of restrictions will apply. It then searches
+ * down through the list. Whenever it finds a match it adopts the
+ * match's flags instead. When you hit the point where the sorted
+ * address is greater than the target, you return with the last set of
+ * flags you found. Because of the ordering of the list, the most
+ * specific match will provide the final set of flags.
+ *
+ * This was originally intended to restrict you from sync'ing to your
+ * own broadcasts when you are doing that, by restricting yourself from
+ * your own interfaces. It was also thought it would sometimes be useful
+ * to keep a misbehaving host or two from abusing your primary clock. It
+ * has been expanded, however, to suit the needs of those with more
+ * restrictive access policies.
+ */
+/*
+ * We will use two lists, one for IPv4 addresses and one for IPv6
+ * addresses. This is not protocol-independant but for now I can't
+ * find a way to respect this. We'll check this later... JFB 07/2001
+ */
+#define SET_IPV6_ADDR_MASK(dst, src, msk) \
+ do { \
+ int idx; \
+ for (idx = 0; idx < 16; idx++) { \
+ (dst)->s6_addr[idx] = \
+ (u_char) ((src)->s6_addr[idx] & (msk)->s6_addr[idx]); \
+ } \
+ } while (0)
+
+/*
+ * Memory allocation parameters. We allocate INITRESLIST entries
+ * initially, and add INCRESLIST entries to the free list whenever
+ * we run out.
+ */
+#define INITRESLIST 10
+#define INCRESLIST 5
+
+#define RES_AVG 8. /* interpacket averaging factor */
+
+/*
+ * The restriction list
+ */
+struct restrictlist *restrictlist;
+struct restrictlist6 *restrictlist6;
+static int restrictcount; /* count of entries in the res list */
+static int restrictcount6; /* count of entries in the res list 2*/
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static struct restrictlist *resfree;
+static struct restrictlist6 *resfree6;
+static int numresfree; /* number of structures on free list */
+static int numresfree6; /* number of structures on free list 2 */
+
+static u_long res_calls;
+static u_long res_found;
+static u_long res_not_found;
+
+/*
+ * Parameters of the RES_LIMITED restriction option.
+ */
+u_long res_avg_interval = 5; /* min average interpacket interval */
+u_long res_min_interval = 1; /* min interpacket interval */
+
+/*
+ * Count number of restriction entries referring to RES_LIMITED controls
+ * activation/deactivation of monitoring (with respect to RES_LIMITED
+ * control)
+ */
+static u_long res_limited_refcnt;
+static u_long res_limited_refcnt6;
+
+/*
+ * Our initial allocation of lists entries.
+ */
+static struct restrictlist resinit[INITRESLIST];
+static struct restrictlist6 resinit6[INITRESLIST];
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict(void)
+{
+ register int i;
+
+ /*
+ * Zero the list and put all but one on the free list
+ */
+ resfree = NULL;
+ memset((char *)resinit, 0, sizeof resinit);
+ resfree6 = NULL;
+ memset((char *)resinit6, 0, sizeof resinit6);
+ for (i = 1; i < INITRESLIST; i++) {
+ resinit[i].next = resfree;
+ resinit6[i].next = resfree6;
+ resfree = &resinit[i];
+ resfree6 = &resinit6[i];
+ }
+ numresfree = INITRESLIST-1;
+ numresfree6 = INITRESLIST-1;
+
+ /*
+ * Put the remaining item at the head of the list as our default
+ * entry. Everything in here should be zero for now.
+ */
+ resinit[0].addr = htonl(INADDR_ANY);
+ resinit[0].mask = 0;
+ memset(&resinit6[0].addr6, 0, sizeof(struct in6_addr));
+ memset(&resinit6[0].mask6, 0, sizeof(struct in6_addr));
+ restrictlist = &resinit[0];
+ restrictlist6 = &resinit6[0];
+ restrictcount = 1;
+ restrictcount = 2;
+
+ /*
+ * fix up stat counters
+ */
+ res_calls = 0;
+ res_found = 0;
+ res_not_found = 0;
+
+ /*
+ * set default values for RES_LIMIT functionality
+ */
+ res_limited_refcnt = 0;
+ res_limited_refcnt6 = 0;
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+int
+restrictions(
+ struct sockaddr_storage *srcadr,
+ int at_listhead
+ )
+{
+ struct restrictlist *rl;
+ struct restrictlist *match = NULL;
+ struct restrictlist6 *rl6;
+ struct restrictlist6 *match6 = NULL;
+ struct in6_addr hostaddr6;
+ struct in6_addr hostservaddr6;
+ u_int32 hostaddr;
+ int flags = 0;
+ int isntpport;
+
+ res_calls++;
+ if (srcadr->ss_family == AF_INET) {
+ /*
+ * We need the host address in host order. Also need to
+ * know whether this is from the ntp port or not.
+ */
+ hostaddr = SRCADR(srcadr);
+ isntpport = (SRCPORT(srcadr) == NTP_PORT);
+
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * later!)
+ */
+ if (IN_CLASSD(SRCADR(srcadr)))
+ return (int)RES_IGNORE;
+
+ /*
+ * Set match to first entry, which is default entry.
+ * Work our way down from there.
+ */
+ match = restrictlist;
+ for (rl = match->next; rl != NULL && rl->addr <= hostaddr;
+ rl = rl->next)
+ if ((hostaddr & rl->mask) == rl->addr) {
+ if ((rl->mflags & RESM_NTPONLY) &&
+ !isntpport)
+ continue;
+ match = rl;
+ }
+ match->count++;
+ if (match == restrictlist)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match->flags;
+ }
+
+ /* IPv6 source address */
+ if (srcadr->ss_family == AF_INET6) {
+ /*
+ * Need to know whether this is from the ntp port or
+ * not.
+ */
+ hostaddr6 = GET_INADDR6(*srcadr);
+ isntpport = (ntohs((
+ (struct sockaddr_in6 *)srcadr)->sin6_port) ==
+ NTP_PORT);
+
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * later!)
+ */
+ if (IN6_IS_ADDR_MULTICAST(&hostaddr6))
+ return (int)RES_IGNORE;
+
+ /*
+ * Set match to first entry, which is default entry.
+ * Work our way down from there.
+ */
+ match6 = restrictlist6;
+ for (rl6 = match6->next; rl6 != NULL &&
+ (memcmp(&(rl6->addr6), &hostaddr6,
+ sizeof(hostaddr6)) <= 0); rl6 = rl6->next) {
+ SET_IPV6_ADDR_MASK(&hostservaddr6, &hostaddr6,
+ &rl6->mask6);
+ if (memcmp(&hostservaddr6, &(rl6->addr6),
+ sizeof(hostservaddr6)) == 0) {
+ if ((rl6->mflags & RESM_NTPONLY) &&
+ !isntpport)
+ continue;
+ match6 = rl6;
+ }
+ }
+ match6->count++;
+ if (match6 == restrictlist6)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match6->flags;
+ }
+
+ /*
+ * The following implements a generalized call gap facility.
+ * Douse the RES_LIMITED bit only if the interval since the last
+ * packet is greater than res_min_interval and the average is
+ * greater thatn res_avg_interval.
+ */
+ if (!at_listhead || mon_enabled == MON_OFF) {
+ flags &= ~RES_LIMITED;
+ } else {
+ struct mon_data *md;
+
+ /*
+ * At this poin the most recent arrival is first in the
+ * MRU list. Let the first 10 packets in for free until
+ * the average stabilizes.
+ */
+ md = mon_mru_list.mru_next;
+ if (md->avg_interval == 0)
+ md->avg_interval = md->drop_count;
+ else
+ md->avg_interval += (md->drop_count -
+ md->avg_interval) / RES_AVG;
+ if (md->count < 10 || (md->drop_count >
+ res_min_interval && md->avg_interval >
+ res_avg_interval))
+ flags &= ~RES_LIMITED;
+ md->drop_count = flags;
+ }
+ return (flags);
+}
+
+
+/*
+ * hack_restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+hack_restrict(
+ int op,
+ struct sockaddr_storage *resaddr,
+ struct sockaddr_storage *resmask,
+ int mflags,
+ int flags
+ )
+{
+ register u_int32 addr = 0;
+ register u_int32 mask = 0;
+ struct in6_addr addr6;
+ struct in6_addr mask6;
+ register struct restrictlist *rl = NULL;
+ register struct restrictlist *rlprev = NULL;
+ register struct restrictlist6 *rl6 = NULL;
+ register struct restrictlist6 *rlprev6 = NULL;
+ int i, addr_cmp, mask_cmp;
+ memset(&addr6, 0, sizeof(struct in6_addr));
+ memset(&mask6, 0, sizeof(struct in6_addr));
+
+ if (resaddr->ss_family == AF_INET) {
+ /*
+ * Get address and mask in host byte order
+ */
+ addr = SRCADR(resaddr);
+ mask = SRCADR(resmask);
+ addr &= mask; /* make sure low bits zero */
+
+ /*
+ * If this is the default address, point at first on
+ * list. Else go searching for it.
+ */
+ if (addr == 0) {
+ rlprev = NULL;
+ rl = restrictlist;
+ } else {
+ rlprev = restrictlist;
+ rl = rlprev->next;
+ while (rl != NULL) {
+ if (rl->addr > addr) {
+ rl = NULL;
+ break;
+ } else if (rl->addr == addr) {
+ if (rl->mask == mask) {
+ if ((mflags &
+ RESM_NTPONLY) ==
+ (rl->mflags &
+ RESM_NTPONLY))
+ break;
+
+ if (!(mflags &
+ RESM_NTPONLY)) {
+ rl = NULL;
+ break;
+ }
+ } else if (rl->mask > mask) {
+ rl = NULL;
+ break;
+ }
+ }
+ rlprev = rl;
+ rl = rl->next;
+ }
+ }
+ }
+
+ if (resaddr->ss_family == AF_INET6) {
+ mask6 = GET_INADDR6(*resmask);
+ SET_IPV6_ADDR_MASK(&addr6,
+ &GET_INADDR6(*resaddr), &mask6);
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr6)) {
+ rlprev6 = NULL;
+ rl6 = restrictlist6;
+ } else {
+ rlprev6 = restrictlist6;
+ rl6 = rlprev6->next;
+ while (rl6 != NULL) {
+ addr_cmp = memcmp(&rl6->addr6, &addr6,
+ sizeof(addr6));
+ if (addr_cmp > 0) {
+ rl6 = NULL;
+ break;
+ } else if (addr_cmp == 0) {
+ mask_cmp = memcmp(&rl6->mask6,
+ &mask6, sizeof(mask6));
+ if (mask_cmp == 0) {
+ if ((mflags &
+ RESM_NTPONLY) ==
+ (rl6->mflags &
+ RESM_NTPONLY))
+ break;
+
+ if (!(mflags &
+ RESM_NTPONLY)) {
+ rl6 = NULL;
+ break;
+ }
+ } else if (mask_cmp > 0) {
+ rl6 = NULL;
+ break;
+ }
+ }
+ rlprev6 = rl6;
+ rl6 = rl6->next;
+ }
+ }
+ }
+
+ /*
+ * In case the above wasn't clear :-), either rl now points
+ * at the entry this call refers to, or rl is zero and rlprev
+ * points to the entry prior to where this one should go in
+ * the sort.
+ */
+
+ /*
+ * Switch based on operation
+ */
+ if (resaddr->ss_family == AF_INET) {
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a
+ * new restriction add it.
+ */
+ if (rl == NULL) {
+ if (resfree == NULL) {
+ rl = (struct restrictlist *)
+ emalloc(INCRESLIST *
+ sizeof(struct
+ restrictlist));
+ memset((char *)rl, 0,
+ INCRESLIST * sizeof(struct
+ restrictlist));
+ for (i = 0; i < INCRESLIST; i++) {
+ rl->next = resfree;
+ resfree = rl;
+ rl++;
+ }
+ numresfree = INCRESLIST;
+ }
+
+ rl = resfree;
+ resfree = rl->next;
+ numresfree--;
+
+ rl->addr = addr;
+ rl->mask = mask;
+ rl->mflags = (u_short)mflags;
+
+ if (rlprev == NULL) {
+ rl->next = restrictlist;
+ restrictlist = rl;
+ } else {
+ rl->next = rlprev->next;
+ rlprev->next = rl;
+ }
+ restrictcount++;
+ }
+ if ((rl->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt++;
+ mon_start(MON_RES);
+ }
+ rl->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl != NULL) {
+ if ((rl->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ rl->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ case RESTRICT_REMOVEIF:
+ /*
+ * Remove an entry from the table entirely if we
+ * found one. Don't remove the default entry and
+ * don't remove an interface entry.
+ */
+ if (rl != NULL
+ && rl->addr != htonl(INADDR_ANY)
+ && !(rl->mflags & RESM_INTERFACE && op != RESTRICT_REMOVEIF)) {
+ if (rlprev != NULL) {
+ rlprev->next = rl->next;
+ } else {
+ restrictlist = rl->next;
+ }
+ restrictcount--;
+ if (rl->flags & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl, 0,
+ sizeof(struct restrictlist));
+
+ rl->next = resfree;
+ resfree = rl;
+ numresfree++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (resaddr->ss_family == AF_INET6) {
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a
+ * new restriction add it.
+ */
+ if (rl6 == NULL) {
+ if (resfree6 == NULL) {
+ rl6 = (struct
+ restrictlist6 *)emalloc(
+ INCRESLIST * sizeof(struct
+ restrictlist6));
+ memset((char *)rl6, 0,
+ INCRESLIST * sizeof(struct
+ restrictlist6));
+
+ for (i = 0; i < INCRESLIST;
+ i++) {
+ rl6->next = resfree6;
+ resfree6 = rl6;
+ rl6++;
+ }
+ numresfree6 = INCRESLIST;
+ }
+ rl6 = resfree6;
+ resfree6 = rl6->next;
+ numresfree6--;
+ rl6->addr6 = addr6;
+ rl6->mask6 = mask6;
+ rl6->mflags = (u_short)mflags;
+ if (rlprev6 != NULL) {
+ rl6->next = rlprev6->next;
+ rlprev6->next = rl6;
+ } else {
+ rl6->next = restrictlist6;
+ restrictlist6 = rl6;
+ }
+ restrictcount6++;
+ }
+ if ((rl6->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt6++;
+ mon_start(MON_RES);
+ }
+ rl6->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl6 != NULL) {
+ if ((rl6->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt6--;
+ if (res_limited_refcnt6 == 0)
+ mon_stop(MON_RES);
+ }
+ rl6->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ case RESTRICT_REMOVEIF:
+ /*
+ * Remove an entry from the table entirely if we
+ * found one. Don't remove the default entry and
+ * don't remove an interface entry.
+ */
+ if (rl6 != NULL &&
+ !IN6_IS_ADDR_UNSPECIFIED(&rl6->addr6)
+ && !(rl6->mflags & RESM_INTERFACE && op != RESTRICT_REMOVEIF)) {
+ if (rlprev6 != NULL) {
+ rlprev6->next = rl6->next;
+ } else {
+ restrictlist6 = rl6->next;
+ }
+ restrictcount6--;
+ if (rl6->flags & RES_LIMITED) {
+ res_limited_refcnt6--;
+ if (res_limited_refcnt6 == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl6, 0,
+ sizeof(struct restrictlist6));
+ rl6->next = resfree6;
+ resfree6 = rl6;
+ numresfree6++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
diff --git a/contrib/ntp/ntpd/ntp_timer.c b/contrib/ntp/ntpd/ntp_timer.c
new file mode 100644
index 0000000..fb6de70
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_timer.c
@@ -0,0 +1,409 @@
+/*
+ * ntp_timer.c - event timer support routines
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+# include "ntp_iocompletionport.h"
+# include "ntp_timer.h"
+#endif
+
+/*
+ * These routines provide support for the event timer. The timer is
+ * implemented by an interrupt routine which sets a flag once every
+ * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
+ * is called when the mainline code gets around to seeing the flag.
+ * The timer routine dispatches the clock adjustment code if its time
+ * has come, then searches the timer queue for expiries which are
+ * dispatched to the transmit procedure. Finally, we call the hourly
+ * procedure to do cleanup and print a message.
+ */
+
+volatile int interface_interval = 300; /* update interface every 5 minutes as default */
+
+/*
+ * Alarm flag. The mainline code imports this.
+ */
+volatile int alarm_flag;
+
+/*
+ * The counters
+ */
+static u_long adjust_timer; /* second timer */
+static u_long keys_timer; /* minute timer */
+static u_long stats_timer; /* stats timer */
+static u_long huffpuff_timer; /* huff-n'-puff timer */
+static u_long interface_timer; /* interface update timer */
+#ifdef OPENSSL
+static u_long revoke_timer; /* keys revoke timer */
+u_char sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
+#endif /* OPENSSL */
+
+/*
+ * Statistics counter for the interested.
+ */
+volatile u_long alarm_overflow;
+
+#define MINUTE 60
+#define HOUR (60*60)
+
+u_long current_time;
+
+/*
+ * Stats. Number of overflows and number of calls to transmit().
+ */
+u_long timer_timereset;
+u_long timer_overflows;
+u_long timer_xmtcalls;
+
+#if defined(VMS)
+static int vmstimer[2]; /* time for next timer AST */
+static int vmsinc[2]; /* timer increment */
+#endif /* VMS */
+
+#if defined SYS_WINNT
+static HANDLE WaitableTimerHandle = NULL;
+#else
+static RETSIGTYPE alarming P((int));
+#endif /* SYS_WINNT */
+
+#if !defined(VMS)
+# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
+# ifndef HAVE_TIMER_SETTIME
+ struct itimerval itimer;
+# else
+ static timer_t ntpd_timerid;
+ struct itimerspec itimer;
+# endif /* HAVE_TIMER_SETTIME */
+# endif /* SYS_WINNT */
+#endif /* VMS */
+
+/*
+ * reinit_timer - reinitialize interval timer.
+ */
+void
+reinit_timer(void)
+{
+#if !defined(SYS_WINNT) && !defined(VMS)
+# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
+ timer_gettime(ntpd_timerid, &itimer);
+ if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ }
+ if (itimer.it_value.tv_nsec < 0 ) {
+ itimer.it_value.tv_nsec = 0;
+ }
+ if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_nsec == 0) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_value.tv_nsec = 0;
+ }
+ itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_nsec = 0;
+ timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
+# else
+ getitimer(ITIMER_REAL, &itimer);
+ if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ }
+ if (itimer.it_value.tv_usec < 0 ) {
+ itimer.it_value.tv_usec = 0;
+ }
+ if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_usec == 0) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_value.tv_usec = 0;
+ }
+ itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+# endif
+# endif /* VMS */
+}
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer(void)
+{
+# if defined SYS_WINNT & !defined(SYS_CYGWIN32)
+ HANDLE hToken = INVALID_HANDLE_VALUE;
+ TOKEN_PRIVILEGES tkp;
+# endif /* SYS_WINNT */
+
+ /*
+ * Initialize...
+ */
+ alarm_flag = 0;
+ alarm_overflow = 0;
+ adjust_timer = 1;
+ stats_timer = 0;
+ huffpuff_timer = 0;
+ interface_timer = 0;
+ current_time = 0;
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = 0;
+
+#if !defined(SYS_WINNT)
+ /*
+ * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT
+ * seconds from now and they continue on every 2**EVENT_TIMEOUT
+ * seconds.
+ */
+# if !defined(VMS)
+# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
+ if (timer_create (CLOCK_REALTIME, NULL, &ntpd_timerid) ==
+# ifdef SYS_VXWORKS
+ ERROR
+# else
+ -1
+# endif
+ )
+ {
+ fprintf (stderr, "timer create FAILED\n");
+ exit (0);
+ }
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0;
+ timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
+# else
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+# endif
+# else /* VMS */
+ vmsinc[0] = 10000000; /* 1 sec */
+ vmsinc[1] = 0;
+ lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
+
+ sys$gettim(&vmstimer); /* that's "now" as abstime */
+
+ lib$addx(&vmsinc, &vmstimer, &vmstimer);
+ sys$setimr(0, &vmstimer, alarming, alarming, 0);
+# endif /* VMS */
+#else /* SYS_WINNT */
+ _tzset();
+
+ /*
+ * Get privileges needed for fiddling with the clock
+ */
+
+ /* get the current process token handle */
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
+ msyslog(LOG_ERR, "OpenProcessToken failed: %m");
+ exit(1);
+ }
+ /* get the LUID for system-time privilege. */
+ LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
+ tkp.PrivilegeCount = 1; /* one privilege to set */
+ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ /* get set-time privilege for this process. */
+ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
+ /* cannot test return value of AdjustTokenPrivileges. */
+ if (GetLastError() != ERROR_SUCCESS) {
+ msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
+ }
+
+ /*
+ * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
+ * Under Windows/NT,
+ */
+
+ WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
+ if (WaitableTimerHandle == NULL) {
+ msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
+ exit(1);
+ }
+ else {
+ DWORD Period = (1<<EVENT_TIMEOUT) * 1000;
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = Period * 10000i64;
+ if (!SetWaitableTimer(WaitableTimerHandle, &DueTime, Period, NULL, NULL, FALSE) != NO_ERROR) {
+ msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
+ exit(1);
+ }
+ }
+
+#endif /* SYS_WINNT */
+}
+
+#if defined(SYS_WINNT)
+extern HANDLE
+get_timer_handle(void)
+{
+ return WaitableTimerHandle;
+}
+#endif
+
+/*
+ * timer - dispatch anyone who needs to be
+ */
+void
+timer(void)
+{
+ register struct peer *peer, *next_peer;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+#endif /* OPENSSL */
+ u_int n;
+
+ current_time += (1<<EVENT_TIMEOUT);
+
+ /*
+ * Adjustment timeout first.
+ */
+ if (adjust_timer <= current_time) {
+ adjust_timer += 1;
+ adj_host_clock();
+ kod_proto();
+#ifdef REFCLOCK
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (peer->flags & FLAG_REFCLOCK)
+ refclock_timer(peer);
+ }
+ }
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * Now dispatch any peers whose event timer has expired. Be careful
+ * here, since the peer structure might go away as the result of
+ * the call.
+ */
+ for (n = 0; n < NTP_HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (peer->action && peer->nextaction <= current_time)
+ peer->action(peer);
+ if (peer->nextdate <= current_time) {
+#ifdef REFCLOCK
+ if (peer->flags & FLAG_REFCLOCK)
+ refclock_transmit(peer);
+ else
+ transmit(peer);
+#else /* REFCLOCK */
+ transmit(peer);
+#endif /* REFCLOCK */
+ }
+ }
+ }
+
+ /*
+ * Garbage collect expired keys.
+ */
+ if (keys_timer <= current_time) {
+ keys_timer += MINUTE;
+ auth_agekeys();
+ }
+
+ /*
+ * Huff-n'-puff filter
+ */
+ if (huffpuff_timer <= current_time) {
+ huffpuff_timer += HUFFPUFF;
+ huffpuff();
+ }
+
+#ifdef OPENSSL
+ /*
+ * Garbage collect old keys and generate new private value
+ */
+ if (revoke_timer <= current_time) {
+ revoke_timer += RANDPOLL(sys_revoke);
+ expire_all();
+ sprintf(statstr, "refresh ts %u", ntohl(hostval.tstamp));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("timer: %s\n", statstr);
+#endif
+ }
+#endif /* OPENSSL */
+
+ /*
+ * interface update timer
+ */
+ if (interface_interval && interface_timer <= current_time) {
+ timer_interfacetimeout(current_time + interface_interval);
+#ifdef DEBUG
+ if (debug)
+ printf("timer: interface update\n");
+#endif
+ interface_update(NULL, NULL);
+ }
+
+ /*
+ * Finally, periodically write stats.
+ */
+ if (stats_timer <= current_time) {
+ if (stats_timer != 0)
+ write_stats();
+ stats_timer += stats_write_period;
+ }
+}
+
+
+#ifndef SYS_WINNT
+/*
+ * alarming - tell the world we've been alarmed
+ */
+static RETSIGTYPE
+alarming(
+ int sig
+ )
+{
+#if !defined(VMS)
+ if (initializing)
+ return;
+ if (alarm_flag)
+ alarm_overflow++;
+ else
+ alarm_flag++;
+#else /* VMS AST routine */
+ if (!initializing) {
+ if (alarm_flag) alarm_overflow++;
+ else alarm_flag = 1; /* increment is no good */
+ }
+ lib$addx(&vmsinc,&vmstimer,&vmstimer);
+ sys$setimr(0,&vmstimer,alarming,alarming,0);
+#endif /* VMS */
+}
+#endif /* SYS_WINNT */
+
+void
+timer_interfacetimeout(u_long timeout)
+{
+ interface_timer = timeout;
+}
+
+
+/*
+ * timer_clr_stats - clear timer module stat counters
+ */
+void
+timer_clr_stats(void)
+{
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = current_time;
+}
+
diff --git a/contrib/ntp/ntpd/ntp_util.c b/contrib/ntp/ntpd/ntp_util.c
new file mode 100644
index 0000000..91ff8a6
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_util.c
@@ -0,0 +1,801 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+#ifdef DOSYNCTODR
+# if !defined(VMS)
+# include <sys/resource.h>
+# endif /* VMS */
+#endif
+
+#if defined(VMS)
+# include <descrip.h>
+#endif /* VMS */
+
+/*
+ * This contains odds and ends. Right now the only thing you'll find
+ * in here is the hourly stats printer and some code to support
+ * rereading the keys file, but I may eventually put other things in
+ * here such as code to do something with the leap bits.
+ */
+/*
+ * Name of the keys file
+ */
+static char *key_file_name;
+
+/*
+ * The name of the drift_comp file and the temporary.
+ */
+static char *stats_drift_file;
+static char *stats_temp_file;
+int stats_write_period = 3600; /* # of seconds between writes. */
+double stats_write_tolerance = 0;
+static double prev_drift_comp = 99999.;
+
+/*
+ * Statistics file stuff
+ */
+#ifndef NTP_VAR
+# ifndef SYS_WINNT
+# define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
+# else
+# define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */
+# endif /* SYS_WINNT */
+#endif
+
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 256
+#endif
+
+static char statsdir[MAXPATHLEN] = NTP_VAR;
+
+static FILEGEN peerstats;
+static FILEGEN loopstats;
+static FILEGEN clockstats;
+static FILEGEN rawstats;
+static FILEGEN sysstats;
+#ifdef DEBUG_TIMING
+static FILEGEN timingstats;
+#endif
+#ifdef OPENSSL
+static FILEGEN cryptostats;
+#endif /* OPENSSL */
+
+/*
+ * This controls whether stats are written to the fileset. Provided
+ * so that ntpdc can turn off stats when the file system fills up.
+ */
+int stats_control;
+
+/*
+ * Initial frequency offset later passed to the loopfilter.
+ */
+double old_drift;
+
+/*
+ * init_util - initialize the utilities
+ */
+void
+init_util(void)
+{
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ key_file_name = 0;
+
+ filegen_register(&statsdir[0], "peerstats", &peerstats);
+
+ filegen_register(&statsdir[0], "loopstats", &loopstats);
+
+ filegen_register(&statsdir[0], "clockstats", &clockstats);
+
+ filegen_register(&statsdir[0], "rawstats", &rawstats);
+
+ filegen_register(&statsdir[0], "sysstats", &sysstats);
+
+#ifdef OPENSSL
+ filegen_register(&statsdir[0], "cryptostats", &cryptostats);
+#endif /* OPENSSL */
+
+#ifdef DEBUG_TIMING
+ filegen_register(&statsdir[0], "timingstats", &timingstats);
+#endif
+}
+
+
+/*
+ * hourly_stats - print some interesting stats
+ */
+void
+write_stats(void)
+{
+ FILE *fp;
+
+#ifdef DOSYNCTODR
+ struct timeval tv;
+#if !defined(VMS)
+ int prio_set;
+#endif
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ int o_prio;
+
+ /*
+ * Sometimes having a Sun can be a drag.
+ *
+ * The kernel variable dosynctodr controls whether the system's
+ * soft clock is kept in sync with the battery clock. If it
+ * is zero, then the soft clock is not synced, and the battery
+ * clock is simply left to rot. That means that when the system
+ * reboots, the battery clock (which has probably gone wacky)
+ * sets the soft clock. That means ntpd starts off with a very
+ * confused idea of what time it is. It then takes a large
+ * amount of time to figure out just how wacky the battery clock
+ * has made things drift, etc, etc. The solution is to make the
+ * battery clock sync up to system time. The way to do THAT is
+ * to simply set the time of day to the current time of day, but
+ * as quickly as possible. This may, or may not be a sensible
+ * thing to do.
+ *
+ * CAVEAT: settimeofday() steps the sun clock by about 800 us,
+ * so setting DOSYNCTODR seems a bad idea in the
+ * case of us resolution
+ */
+
+#if !defined(VMS)
+ /* (prr) getpriority returns -1 on error, but -1 is also a valid
+ * return value (!), so instead we have to zero errno before the
+ * call and check it for non-zero afterwards.
+ */
+ errno = 0;
+ prio_set = 0;
+ o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
+
+ /*
+ * (prr) if getpriority succeeded, call setpriority to raise
+ * scheduling priority as high as possible. If that succeeds
+ * as well, set the prio_set flag so we remember to reset
+ * priority to its previous value below. Note that on Solaris
+ * 2.6 (and beyond?), both getpriority and setpriority will fail
+ * with ESRCH, because sched_setscheduler (called from main) put
+ * us in the real-time scheduling class which setpriority
+ * doesn't know about. Being in the real-time class is better
+ * than anything setpriority can do, anyhow, so this error is
+ * silently ignored.
+ */
+ if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
+ prio_set = 1; /* overdrive */
+#endif /* VMS */
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+#else /* not HAVE_GETCLOCK */
+ GETTIMEOFDAY(&tv,(struct timezone *)NULL);
+#endif /* not HAVE_GETCLOCK */
+ if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) {
+ msyslog(LOG_ERR, "can't sync battery time: %m");
+ }
+#if !defined(VMS)
+ if (prio_set)
+ setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
+#endif /* VMS */
+#endif /* DOSYNCTODR */
+
+ NLOG(NLOG_SYSSTATIST)
+ msyslog(LOG_INFO,
+ "offset %.6f sec freq %.3f ppm error %.6f poll %d",
+ last_offset, drift_comp * 1e6, sys_jitter,
+ sys_poll);
+
+
+ record_sys_stats();
+ if ((u_long)(fabs(prev_drift_comp - drift_comp) * 1e9) <=
+ (u_long)(fabs(stats_write_tolerance * drift_comp) * 1e9)) {
+ return;
+ }
+ prev_drift_comp = drift_comp;
+ if (stats_drift_file != 0) {
+ if ((fp = fopen(stats_temp_file, "w")) == NULL) {
+ msyslog(LOG_ERR, "can't open %s: %m",
+ stats_temp_file);
+ return;
+ }
+ fprintf(fp, "%.3f\n", drift_comp * 1e6);
+ (void)fclose(fp);
+ /* atomic */
+#ifdef SYS_WINNT
+ (void) _unlink(stats_drift_file); /* rename semantics differ under NT */
+#endif /* SYS_WINNT */
+
+#ifndef NO_RENAME
+ (void) rename(stats_temp_file, stats_drift_file);
+#else
+ /* we have no rename NFS of ftp in use */
+ if ((fp = fopen(stats_drift_file, "w")) == NULL) {
+ msyslog(LOG_ERR, "can't open %s: %m",
+ stats_drift_file);
+ return;
+ }
+
+#endif
+
+#if defined(VMS)
+ /* PURGE */
+ {
+ $DESCRIPTOR(oldvers,";-1");
+ struct dsc$descriptor driftdsc = {
+ strlen(stats_drift_file),0,0,stats_drift_file };
+
+ while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
+ }
+#endif
+ }
+}
+
+
+/*
+ * stats_config - configure the stats operation
+ */
+void
+stats_config(
+ int item,
+ const char *invalue /* only one type so far */
+ )
+{
+ FILE *fp;
+ const char *value;
+ int len;
+
+ /*
+ * Expand environment strings under Windows NT, since the
+ * command interpreter doesn't do this, the program must.
+ */
+#ifdef SYS_WINNT
+ char newvalue[MAX_PATH], parameter[MAX_PATH];
+
+ if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
+ switch(item) {
+ case STATS_FREQ_FILE:
+ strcpy(parameter,"STATS_FREQ_FILE");
+ break;
+ case STATS_STATSDIR:
+ strcpy(parameter,"STATS_STATSDIR");
+ break;
+ case STATS_PID_FILE:
+ strcpy(parameter,"STATS_PID_FILE");
+ break;
+ default:
+ strcpy(parameter,"UNKNOWN");
+ break;
+ }
+ value = invalue;
+
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(%s) failed: %m\n", parameter);
+ } else {
+ value = newvalue;
+ }
+#else
+ value = invalue;
+#endif /* SYS_WINNT */
+
+ switch(item) {
+ case STATS_FREQ_FILE:
+ if (stats_drift_file != 0) {
+ (void) free(stats_drift_file);
+ (void) free(stats_temp_file);
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ }
+
+ if (value == 0 || (len = strlen(value)) == 0)
+ break;
+
+ stats_drift_file = (char*)emalloc((u_int)(len + 1));
+#if !defined(VMS)
+ stats_temp_file = (char*)emalloc((u_int)(len +
+ sizeof(".TEMP")));
+#else
+ stats_temp_file = (char*)emalloc((u_int)(len +
+ sizeof("-TEMP")));
+#endif /* VMS */
+ memmove(stats_drift_file, value, (unsigned)(len+1));
+ memmove(stats_temp_file, value, (unsigned)len);
+#if !defined(VMS)
+ memmove(stats_temp_file + len, ".TEMP",
+ sizeof(".TEMP"));
+#else
+ memmove(stats_temp_file + len, "-TEMP",
+ sizeof("-TEMP"));
+#endif /* VMS */
+
+ /*
+ * Open drift file and read frequency. If the file is
+ * missing or contains errors, tell the loop to reset.
+ */
+ if ((fp = fopen(stats_drift_file, "r")) == NULL) {
+ old_drift = 1e9;
+ break;
+ }
+ if (fscanf(fp, "%lf", &old_drift) != 1) {
+ msyslog(LOG_ERR, "Frequency format error in %s",
+ stats_drift_file);
+ old_drift = 1e9;
+ fclose(fp);
+ break;
+ }
+ fclose(fp);
+ prev_drift_comp = old_drift / 1e6;
+ msyslog(LOG_INFO,
+ "frequency initialized %.3f PPM from %s",
+ old_drift, stats_drift_file);
+ break;
+
+ case STATS_STATSDIR:
+ if (strlen(value) >= sizeof(statsdir)) {
+ msyslog(LOG_ERR,
+ "value for statsdir too long (>%d, sigh)",
+ (int)sizeof(statsdir)-1);
+ } else {
+ l_fp now;
+
+ get_systime(&now);
+ strcpy(statsdir,value);
+ if(peerstats.prefix == &statsdir[0] &&
+ peerstats.fp != NULL) {
+ fclose(peerstats.fp);
+ peerstats.fp = NULL;
+ filegen_setup(&peerstats, now.l_ui);
+ }
+ if(loopstats.prefix == &statsdir[0] &&
+ loopstats.fp != NULL) {
+ fclose(loopstats.fp);
+ loopstats.fp = NULL;
+ filegen_setup(&loopstats, now.l_ui);
+ }
+ if(clockstats.prefix == &statsdir[0] &&
+ clockstats.fp != NULL) {
+ fclose(clockstats.fp);
+ clockstats.fp = NULL;
+ filegen_setup(&clockstats, now.l_ui);
+ }
+ if(rawstats.prefix == &statsdir[0] &&
+ rawstats.fp != NULL) {
+ fclose(rawstats.fp);
+ rawstats.fp = NULL;
+ filegen_setup(&rawstats, now.l_ui);
+ }
+ if(sysstats.prefix == &statsdir[0] &&
+ sysstats.fp != NULL) {
+ fclose(sysstats.fp);
+ sysstats.fp = NULL;
+ filegen_setup(&sysstats, now.l_ui);
+ }
+#ifdef OPENSSL
+ if(cryptostats.prefix == &statsdir[0] &&
+ cryptostats.fp != NULL) {
+ fclose(cryptostats.fp);
+ cryptostats.fp = NULL;
+ filegen_setup(&cryptostats, now.l_ui);
+ }
+#endif /* OPENSSL */
+ }
+ break;
+
+ case STATS_PID_FILE:
+ if ((fp = fopen(value, "w")) == NULL) {
+ msyslog(LOG_ERR, "Can't open %s: %m", value);
+ break;
+ }
+ fprintf(fp, "%d", (int) getpid());
+ fclose(fp);;
+ break;
+
+ default:
+ /* oh well */
+ break;
+ }
+}
+
+/*
+ * record_peer_stats - write peer statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past UTC midnight)
+ * peer (ip address)
+ * peer status word (hex)
+ * peer offset (s)
+ * peer delay (s)
+ * peer error bound (s)
+ * peer error (s)
+*/
+void
+record_peer_stats(
+ struct sockaddr_storage *addr,
+ int status,
+ double offset,
+ double delay,
+ double dispersion,
+ double skew
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&peerstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp,
+ "%lu %s %s %x %.9f %.9f %.9f %.9f\n",
+ day, ulfptoa(&now, 3), stoa(addr), status, offset,
+ delay, dispersion, skew);
+ fflush(peerstats.fp);
+ }
+}
+
+/*
+ * record_loop_stats - write loop filter statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * offset (s)
+ * frequency (approx ppm)
+ * time constant (log base 2)
+ */
+void
+record_loop_stats(
+ double offset,
+ double freq,
+ double jitter,
+ double stability,
+ int spoll
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&loopstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
+ day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
+ stability * 1e6, spoll);
+ fflush(loopstats.fp);
+ }
+}
+
+/*
+ * record_clock_stats - write clock statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_clock_stats(
+ struct sockaddr_storage *addr,
+ const char *text
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&clockstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(addr), text);
+ fflush(clockstats.fp);
+ }
+}
+
+/*
+ * record_raw_stats - write raw timestamps to file
+ *
+ *
+ * file format
+ * time (s past midnight)
+ * peer ip address
+ * local ip address
+ * t1 t2 t3 t4 timestamps
+ */
+void
+record_raw_stats(
+ struct sockaddr_storage *srcadr,
+ struct sockaddr_storage *dstadr,
+ l_fp *t1,
+ l_fp *t2,
+ l_fp *t3,
+ l_fp *t4
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&rawstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (rawstats.fp != NULL) {
+ fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(srcadr), dstadr ? stoa(dstadr) : "-",
+ ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
+ ulfptoa(t4, 9));
+ fflush(rawstats.fp);
+ }
+}
+
+
+/*
+ * record_sys_stats - write system statistics to file
+ *
+ * file format
+ * time (s past midnight)
+ * time since startup (hr)
+ * packets recieved
+ * packets processed
+ * current version
+ * previous version
+ * bad version
+ * access denied
+ * bad length or format
+ * bad authentication
+ * rate exceeded
+ */
+void
+record_sys_stats(void)
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&sysstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (sysstats.fp != NULL) {
+ fprintf(sysstats.fp,
+ "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ day, ulfptoa(&now, 3), sys_stattime / 3600,
+ sys_received, sys_processed, sys_newversionpkt,
+ sys_oldversionpkt, sys_unknownversion,
+ sys_restricted, sys_badlength, sys_badauth,
+ sys_limitrejected);
+ fflush(sysstats.fp);
+ proto_clr_stats();
+ }
+}
+
+
+#ifdef OPENSSL
+/*
+ * record_crypto_stats - write crypto statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_crypto_stats(
+ struct sockaddr_storage *addr,
+ const char *text
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&cryptostats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (cryptostats.fp != NULL) {
+ if (addr == NULL)
+ fprintf(cryptostats.fp, "%lu %s %s\n",
+ day, ulfptoa(&now, 3), text);
+ else
+ fprintf(cryptostats.fp, "%lu %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(addr), text);
+ fflush(cryptostats.fp);
+ }
+}
+#endif /* OPENSSL */
+
+#ifdef DEBUG_TIMING
+/*
+ * record_crypto_stats - write crypto statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * text message
+ */
+void
+record_timing_stats(
+ const char *text
+ )
+{
+ static unsigned int flshcnt;
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&timingstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (timingstats.fp != NULL) {
+ fprintf(timingstats.fp, "%lu %s %s\n",
+ day, lfptoa(&now, 3), text);
+ if (++flshcnt % 100 == 0)
+ fflush(timingstats.fp);
+ }
+}
+#endif
+/*
+ * getauthkeys - read the authentication keys from the specified file
+ */
+void
+getauthkeys(
+ const char *keyfile
+ )
+{
+ int len;
+
+ len = strlen(keyfile);
+ if (len == 0)
+ return;
+
+ if (key_file_name != 0) {
+ if (len > (int)strlen(key_file_name)) {
+ (void) free(key_file_name);
+ key_file_name = 0;
+ }
+ }
+
+ if (key_file_name == 0) {
+#ifndef SYS_WINNT
+ key_file_name = (char*)emalloc((u_int) (len + 1));
+#else
+ key_file_name = (char*)emalloc((u_int) (MAXPATHLEN));
+#endif
+ }
+#ifndef SYS_WINNT
+ memmove(key_file_name, keyfile, (unsigned)(len+1));
+#else
+ if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN))
+ {
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
+ }
+#endif /* SYS_WINNT */
+
+ authreadkeys(key_file_name);
+}
+
+
+/*
+ * rereadkeys - read the authentication key file over again.
+ */
+void
+rereadkeys(void)
+{
+ if (key_file_name != 0)
+ authreadkeys(key_file_name);
+}
+
+/*
+ * sock_hash - hash an sockaddr_storage structure
+ */
+int
+sock_hash(
+ struct sockaddr_storage *addr
+ )
+{
+ int hashVal;
+ int i;
+ int len;
+ char *ch;
+
+ hashVal = 0;
+ len = 0;
+ /*
+ * We can't just hash the whole thing because there are hidden
+ * fields in sockaddr_in6 that might be filled in by recvfrom(),
+ * so just use the family, port and address.
+ */
+ ch = (char *)&addr->ss_family;
+ hashVal = 37 * hashVal + (int)*ch;
+ if (sizeof(addr->ss_family) > 1) {
+ ch++;
+ hashVal = 37 * hashVal + (int)*ch;
+ }
+ switch(addr->ss_family) {
+ case AF_INET:
+ ch = (char *)&((struct sockaddr_in *)addr)->sin_addr;
+ len = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr;
+ len = sizeof(struct in6_addr);
+ break;
+ }
+
+ for (i = 0; i < len ; i++)
+ hashVal = 37 * hashVal + (int)*(ch + i);
+
+ hashVal = hashVal % 128; /* % MON_HASH_SIZE hardcoded */
+
+ if (hashVal < 0)
+ hashVal += 128;
+
+ return hashVal;
+}
+
+#if notyet
+/*
+ * ntp_exit - document explicitly that ntpd has exited
+ */
+void
+ntp_exit(int retval)
+{
+ msyslog(LOG_ERR, "EXITING with return code %d", retval);
+ exit(retval);
+}
+#endif
diff --git a/contrib/ntp/ntpd/ntpd-opts.c b/contrib/ntp/ntpd/ntpd-opts.c
new file mode 100644
index 0000000..eae28cb
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd-opts.c
@@ -0,0 +1,1021 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpd-opts.c)
+ *
+ * It has been AutoGen-ed Sunday August 17, 2008 at 05:20:10 AM EDT
+ * From the definitions ntpd-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 29:0:4 templates.
+ */
+
+/*
+ * This file was produced by an AutoOpts template. AutoOpts is a
+ * copyrighted work. This source file is not encumbered by AutoOpts
+ * licensing, but is provided under the licensing terms chosen by the
+ * ntpd author or copyright holder. AutoOpts is licensed under
+ * the terms of the LGPL. The redistributable library (``libopts'') is
+ * licensed under the terms of either the LGPL or, at the users discretion,
+ * the BSD license. See the AutoOpts and/or libopts sources for details.
+ *
+ * This source file is copyrighted and licensed under the following terms:
+ *
+ * ntpd copyright 1970-2008 David L. Mills and/or others - all rights reserved
+ *
+ * see html/copyright.html
+ */
+
+
+#include <limits.h>
+
+#define OPTION_CODE_COMPILE 1
+#include "ntpd-opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+tSCC zCopyright[] =
+ "ntpd copyright (c) 1970-2008 David L. Mills and/or others, all rights reserved";
+tSCC zCopyrightNotice[] =
+
+/* extracted from ../include/copyright.def near line 8 */
+"see html/copyright.html";
+extern tUsageProc optionUsage;
+
+/*
+ * global included definitions
+ */
+#ifdef __windows
+ extern int atoi(const char*);
+#else
+# include <stdlib.h>
+#endif
+
+#ifndef NULL
+# define NULL 0
+#endif
+#ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+/*
+ * Ipv4 option description:
+ */
+tSCC zIpv4Text[] =
+ "Force IPv4 DNS name resolution";
+tSCC zIpv4_NAME[] = "IPV4";
+tSCC zIpv4_Name[] = "ipv4";
+#define IPV4_FLAGS (OPTST_DISABLED)
+
+/*
+ * Ipv6 option description:
+ */
+tSCC zIpv6Text[] =
+ "Force IPv6 DNS name resolution";
+tSCC zIpv6_NAME[] = "IPV6";
+tSCC zIpv6_Name[] = "ipv6";
+#define IPV6_FLAGS (OPTST_DISABLED)
+
+/*
+ * Authreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+tSCC zAuthreqText[] =
+ "Require crypto authentication";
+tSCC zAuthreq_NAME[] = "AUTHREQ";
+tSCC zAuthreq_Name[] = "authreq";
+static const int
+ aAuthreqCantList[] = {
+ INDEX_OPT_AUTHNOREQ, NO_EQUIVALENT };
+#define AUTHREQ_FLAGS (OPTST_DISABLED)
+
+/*
+ * Authnoreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+tSCC zAuthnoreqText[] =
+ "Do not require crypto authentication";
+tSCC zAuthnoreq_NAME[] = "AUTHNOREQ";
+tSCC zAuthnoreq_Name[] = "authnoreq";
+static const int
+ aAuthnoreqCantList[] = {
+ INDEX_OPT_AUTHREQ, NO_EQUIVALENT };
+#define AUTHNOREQ_FLAGS (OPTST_DISABLED)
+
+/*
+ * Bcastsync option description:
+ */
+tSCC zBcastsyncText[] =
+ "Allow us to sync to broadcast servers";
+tSCC zBcastsync_NAME[] = "BCASTSYNC";
+tSCC zBcastsync_Name[] = "bcastsync";
+#define BCASTSYNC_FLAGS (OPTST_DISABLED)
+
+/*
+ * Configfile option description:
+ */
+tSCC zConfigfileText[] =
+ "configuration file name";
+tSCC zConfigfile_NAME[] = "CONFIGFILE";
+tSCC zConfigfile_Name[] = "configfile";
+#define CONFIGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Debug_Level option description:
+ */
+#ifdef DEBUG
+tSCC zDebug_LevelText[] =
+ "Increase output debug message level";
+tSCC zDebug_Level_NAME[] = "DEBUG_LEVEL";
+tSCC zDebug_Level_Name[] = "debug-level";
+#define DEBUG_LEVEL_FLAGS (OPTST_DISABLED)
+
+#else /* disable Debug_Level */
+#define VALUE_OPT_DEBUG_LEVEL NO_EQUIVALENT
+#define DEBUG_LEVEL_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zDebug_LevelText NULL
+#define zDebug_Level_NAME NULL
+#define zDebug_Level_Name NULL
+#endif /* DEBUG */
+
+/*
+ * Set_Debug_Level option description:
+ */
+#ifdef DEBUG
+tSCC zSet_Debug_LevelText[] =
+ "Set the output debug message level";
+tSCC zSet_Debug_Level_NAME[] = "SET_DEBUG_LEVEL";
+tSCC zSet_Debug_Level_Name[] = "set-debug-level";
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable Set_Debug_Level */
+#define VALUE_OPT_SET_DEBUG_LEVEL NO_EQUIVALENT
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zSet_Debug_LevelText NULL
+#define zSet_Debug_Level_NAME NULL
+#define zSet_Debug_Level_Name NULL
+#endif /* DEBUG */
+
+/*
+ * Driftfile option description:
+ */
+tSCC zDriftfileText[] =
+ "frequency drift file name";
+tSCC zDriftfile_NAME[] = "DRIFTFILE";
+tSCC zDriftfile_Name[] = "driftfile";
+#define DRIFTFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Panicgate option description:
+ */
+tSCC zPanicgateText[] =
+ "Allow the first adjustment to be Big";
+tSCC zPanicgate_NAME[] = "PANICGATE";
+tSCC zPanicgate_Name[] = "panicgate";
+#define PANICGATE_FLAGS (OPTST_DISABLED)
+
+/*
+ * Jaildir option description:
+ */
+tSCC zJaildirText[] =
+ "Jail directory";
+tSCC zJaildir_NAME[] = "JAILDIR";
+tSCC zJaildir_Name[] = "jaildir";
+#define JAILDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Interface option description:
+ */
+tSCC zInterfaceText[] =
+ "Listen on interface";
+tSCC zInterface_NAME[] = "INTERFACE";
+tSCC zInterface_Name[] = "interface";
+#define INTERFACE_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Keyfile option description:
+ */
+tSCC zKeyfileText[] =
+ "path to symmetric keys";
+tSCC zKeyfile_NAME[] = "KEYFILE";
+tSCC zKeyfile_Name[] = "keyfile";
+#define KEYFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Logfile option description:
+ */
+tSCC zLogfileText[] =
+ "path to the log file";
+tSCC zLogfile_NAME[] = "LOGFILE";
+tSCC zLogfile_Name[] = "logfile";
+#define LOGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Novirtualips option description:
+ */
+tSCC zNovirtualipsText[] =
+ "Do not listen to virtual IPs";
+tSCC zNovirtualips_NAME[] = "NOVIRTUALIPS";
+tSCC zNovirtualips_Name[] = "novirtualips";
+#define NOVIRTUALIPS_FLAGS (OPTST_DISABLED)
+
+/*
+ * Modifymmtimer option description:
+ */
+#ifdef SYS_WINNT
+tSCC zModifymmtimerText[] =
+ "Modify Multimedia Timer (Windows only)";
+tSCC zModifymmtimer_NAME[] = "MODIFYMMTIMER";
+tSCC zModifymmtimer_Name[] = "modifymmtimer";
+#define MODIFYMMTIMER_FLAGS (OPTST_DISABLED)
+
+#else /* disable Modifymmtimer */
+#define VALUE_OPT_MODIFYMMTIMER NO_EQUIVALENT
+#define MODIFYMMTIMER_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zModifymmtimerText NULL
+#define zModifymmtimer_NAME NULL
+#define zModifymmtimer_Name NULL
+#endif /* SYS_WINNT */
+
+/*
+ * Nofork option description:
+ */
+tSCC zNoforkText[] =
+ "Do not fork";
+tSCC zNofork_NAME[] = "NOFORK";
+tSCC zNofork_Name[] = "nofork";
+#define NOFORK_FLAGS (OPTST_DISABLED)
+
+/*
+ * Nice option description:
+ */
+tSCC zNiceText[] =
+ "Run at high priority";
+tSCC zNice_NAME[] = "NICE";
+tSCC zNice_Name[] = "nice";
+#define NICE_FLAGS (OPTST_DISABLED)
+
+/*
+ * Pidfile option description:
+ */
+tSCC zPidfileText[] =
+ "path to the PID file";
+tSCC zPidfile_NAME[] = "PIDFILE";
+tSCC zPidfile_Name[] = "pidfile";
+#define PIDFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Priority option description:
+ */
+tSCC zPriorityText[] =
+ "Process priority";
+tSCC zPriority_NAME[] = "PRIORITY";
+tSCC zPriority_Name[] = "priority";
+#define PRIORITY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/*
+ * Quit option description:
+ */
+tSCC zQuitText[] =
+ "Set the time and quit";
+tSCC zQuit_NAME[] = "QUIT";
+tSCC zQuit_Name[] = "quit";
+#define QUIT_FLAGS (OPTST_DISABLED)
+
+/*
+ * Propagationdelay option description:
+ */
+tSCC zPropagationdelayText[] =
+ "Broadcast/propagation delay";
+tSCC zPropagationdelay_NAME[] = "PROPAGATIONDELAY";
+tSCC zPropagationdelay_Name[] = "propagationdelay";
+#define PROPAGATIONDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Updateinterval option description:
+ */
+tSCC zUpdateintervalText[] =
+ "interval in seconds between scans for new or dropped interfaces";
+tSCC zUpdateinterval_NAME[] = "UPDATEINTERVAL";
+tSCC zUpdateinterval_Name[] = "updateinterval";
+#define UPDATEINTERVAL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/*
+ * Statsdir option description:
+ */
+tSCC zStatsdirText[] =
+ "Statistics file location";
+tSCC zStatsdir_NAME[] = "STATSDIR";
+tSCC zStatsdir_Name[] = "statsdir";
+#define STATSDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Trustedkey option description:
+ */
+tSCC zTrustedkeyText[] =
+ "Trusted key number";
+tSCC zTrustedkey_NAME[] = "TRUSTEDKEY";
+tSCC zTrustedkey_Name[] = "trustedkey";
+#define TRUSTEDKEY_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * User option description:
+ */
+tSCC zUserText[] =
+ "Run as userid (or userid:groupid)";
+tSCC zUser_NAME[] = "USER";
+tSCC zUser_Name[] = "user";
+#define USER_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Var option description:
+ */
+tSCC zVarText[] =
+ "make ARG an ntp variable (RW)";
+tSCC zVar_NAME[] = "VAR";
+tSCC zVar_Name[] = "var";
+#define VAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Dvar option description:
+ */
+tSCC zDvarText[] =
+ "make ARG an ntp variable (RW|DEF)";
+tSCC zDvar_NAME[] = "DVAR";
+tSCC zDvar_Name[] = "dvar";
+#define DVAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Slew option description:
+ */
+tSCC zSlewText[] =
+ "Slew up to 600 seconds";
+tSCC zSlew_NAME[] = "SLEW";
+tSCC zSlew_Name[] = "slew";
+#define SLEW_FLAGS (OPTST_DISABLED)
+
+/*
+ * Help/More_Help/Version option descriptions:
+ */
+tSCC zHelpText[] = "Display usage information and exit";
+tSCC zHelp_Name[] = "help";
+
+tSCC zMore_HelpText[] = "Extended usage information passed thru pager";
+tSCC zMore_Help_Name[] = "more-help";
+
+tSCC zVersionText[] = "Output version information and exit";
+tSCC zVersion_Name[] = "version";
+/*
+ * Declare option callback procedures
+ */
+#ifdef DEBUG
+ static tOptProc doOptSet_Debug_Level;
+#else /* not DEBUG */
+# define doOptSet_Debug_Level NULL
+#endif /* def/not DEBUG */
+#if defined(TEST_NTPD_OPTS)
+/*
+ * Under test, omit argument processing, or call optionStackArg,
+ * if multiple copies are allowed.
+ */
+extern tOptProc
+ optionNumericVal, optionPagedUsage, optionStackArg,
+ optionVersionStderr;
+static tOptProc
+ doUsageOpt;
+
+/*
+ * #define map the "normal" callout procs to the test ones...
+ */
+#define SET_DEBUG_LEVEL_OPT_PROC optionStackArg
+
+
+#else /* NOT defined TEST_NTPD_OPTS */
+/*
+ * When not under test, there are different procs to use
+ */
+extern tOptProc
+ optionNumericVal, optionPagedUsage, optionPrintVersion, optionStackArg;
+static tOptProc
+ doUsageOpt;
+
+/*
+ * #define map the "normal" callout procs
+ */
+#define SET_DEBUG_LEVEL_OPT_PROC doOptSet_Debug_Level
+
+#define SET_DEBUG_LEVEL_OPT_PROC doOptSet_Debug_Level
+#endif /* defined(TEST_NTPD_OPTS) */
+#ifdef TEST_NTPD_OPTS
+# define DOVERPROC optionVersionStderr
+#else
+# define DOVERPROC optionPrintVersion
+#endif /* TEST_NTPD_OPTS */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Define the Ntpd Option Descriptions.
+ */
+static tOptDesc optDesc[ OPTION_CT ] = {
+ { /* entry idx, value */ 0, VALUE_OPT_IPV4,
+ /* equiv idx, value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV4_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zIpv4Text, zIpv4_NAME, zIpv4_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 1, VALUE_OPT_IPV6,
+ /* equiv idx, value */ NOLIMIT, NOLIMIT,
+ /* equivalenced to */ INDEX_OPT_IPV4,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV6_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zIpv6Text, zIpv6_NAME, zIpv6_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equiv idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zAuthreqText, zAuthreq_NAME, zAuthreq_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equiv idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHNOREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthnoreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zAuthnoreqText, zAuthnoreq_NAME, zAuthnoreq_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equiv idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ BCASTSYNC_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zBcastsyncText, zBcastsync_NAME, zBcastsync_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 5, VALUE_OPT_CONFIGFILE,
+ /* equiv idx, value */ 5, VALUE_OPT_CONFIGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ CONFIGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zConfigfileText, zConfigfile_NAME, zConfigfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 6, VALUE_OPT_DEBUG_LEVEL,
+ /* equiv idx, value */ 6, VALUE_OPT_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zDebug_LevelText, zDebug_Level_NAME, zDebug_Level_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 7, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equiv idx, value */ 7, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SET_DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ SET_DEBUG_LEVEL_OPT_PROC,
+ /* desc, NAME, name */ zSet_Debug_LevelText, zSet_Debug_Level_NAME, zSet_Debug_Level_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 8, VALUE_OPT_DRIFTFILE,
+ /* equiv idx, value */ 8, VALUE_OPT_DRIFTFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ DRIFTFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zDriftfileText, zDriftfile_NAME, zDriftfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 9, VALUE_OPT_PANICGATE,
+ /* equiv idx, value */ 9, VALUE_OPT_PANICGATE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PANICGATE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPanicgateText, zPanicgate_NAME, zPanicgate_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 10, VALUE_OPT_JAILDIR,
+ /* equiv idx, value */ 10, VALUE_OPT_JAILDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ JAILDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zJaildirText, zJaildir_NAME, zJaildir_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 11, VALUE_OPT_INTERFACE,
+ /* equiv idx, value */ 11, VALUE_OPT_INTERFACE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ INTERFACE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zInterfaceText, zInterface_NAME, zInterface_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 12, VALUE_OPT_KEYFILE,
+ /* equiv idx, value */ 12, VALUE_OPT_KEYFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ KEYFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zKeyfileText, zKeyfile_NAME, zKeyfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 13, VALUE_OPT_LOGFILE,
+ /* equiv idx, value */ 13, VALUE_OPT_LOGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zLogfileText, zLogfile_NAME, zLogfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 14, VALUE_OPT_NOVIRTUALIPS,
+ /* equiv idx, value */ 14, VALUE_OPT_NOVIRTUALIPS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOVIRTUALIPS_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNovirtualipsText, zNovirtualips_NAME, zNovirtualips_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 15, VALUE_OPT_MODIFYMMTIMER,
+ /* equiv idx, value */ 15, VALUE_OPT_MODIFYMMTIMER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MODIFYMMTIMER_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zModifymmtimerText, zModifymmtimer_NAME, zModifymmtimer_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 16, VALUE_OPT_NOFORK,
+ /* equiv idx, value */ 16, VALUE_OPT_NOFORK,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOFORK_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNoforkText, zNofork_NAME, zNofork_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 17, VALUE_OPT_NICE,
+ /* equiv idx, value */ 17, VALUE_OPT_NICE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NICE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNiceText, zNice_NAME, zNice_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 18, VALUE_OPT_PIDFILE,
+ /* equiv idx, value */ 18, VALUE_OPT_PIDFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PIDFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPidfileText, zPidfile_NAME, zPidfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 19, VALUE_OPT_PRIORITY,
+ /* equiv idx, value */ 19, VALUE_OPT_PRIORITY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PRIORITY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ zPriorityText, zPriority_NAME, zPriority_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 20, VALUE_OPT_QUIT,
+ /* equiv idx, value */ 20, VALUE_OPT_QUIT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ QUIT_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zQuitText, zQuit_NAME, zQuit_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 21, VALUE_OPT_PROPAGATIONDELAY,
+ /* equiv idx, value */ 21, VALUE_OPT_PROPAGATIONDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PROPAGATIONDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPropagationdelayText, zPropagationdelay_NAME, zPropagationdelay_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 22, VALUE_OPT_UPDATEINTERVAL,
+ /* equiv idx, value */ 22, VALUE_OPT_UPDATEINTERVAL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ UPDATEINTERVAL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ zUpdateintervalText, zUpdateinterval_NAME, zUpdateinterval_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 23, VALUE_OPT_STATSDIR,
+ /* equiv idx, value */ 23, VALUE_OPT_STATSDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ STATSDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zStatsdirText, zStatsdir_NAME, zStatsdir_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 24, VALUE_OPT_TRUSTEDKEY,
+ /* equiv idx, value */ 24, VALUE_OPT_TRUSTEDKEY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ TRUSTEDKEY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zTrustedkeyText, zTrustedkey_NAME, zTrustedkey_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 25, VALUE_OPT_USER,
+ /* equiv idx, value */ 25, VALUE_OPT_USER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ USER_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zUserText, zUser_NAME, zUser_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 26, VALUE_OPT_VAR,
+ /* equiv idx, value */ 26, VALUE_OPT_VAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ VAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zVarText, zVar_NAME, zVar_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 27, VALUE_OPT_DVAR,
+ /* equiv idx, value */ 27, VALUE_OPT_DVAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DVAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zDvarText, zDvar_NAME, zDvar_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 28, VALUE_OPT_SLEW,
+ /* equiv idx, value */ 28, VALUE_OPT_SLEW,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SLEW_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zSlewText, zSlew_NAME, zSlew_Name,
+ /* disablement strs */ NULL, NULL },
+
+#ifdef NO_OPTIONAL_OPT_ARGS
+# define VERSION_OPT_FLAGS OPTST_IMM | OPTST_NO_INIT
+#else
+# define VERSION_OPT_FLAGS OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | \
+ OPTST_ARG_OPTIONAL | OPTST_IMM | OPTST_NO_INIT
+#endif
+
+ { /* entry idx, value */ INDEX_OPT_VERSION, VALUE_OPT_VERSION,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ VERSION_OPT_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ DOVERPROC,
+ /* desc, NAME, name */ zVersionText, NULL, zVersion_Name,
+ /* disablement strs */ NULL, NULL },
+
+#undef VERSION_OPT_FLAGS
+
+
+ { /* entry idx, value */ INDEX_OPT_HELP, VALUE_OPT_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ zHelpText, NULL, zHelp_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_MORE_HELP, VALUE_OPT_MORE_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionPagedUsage,
+ /* desc, NAME, name */ zMore_HelpText, NULL, zMore_Help_Name,
+ /* disablement strs */ NULL, NULL }
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Define the Ntpd Option Environment
+ */
+tSCC zPROGNAME[] = "NTPD";
+tSCC zUsageTitle[] =
+"ntpd - NTP daemon program - Ver. 4.2.4p5\n\
+USAGE: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]...\n";
+#define zRcName NULL
+#define apzHomeList NULL
+
+tSCC zBugsAddr[] = "http://bugs.ntp.isc.org, bugs@ntp.org";
+#define zExplain NULL
+tSCC zDetail[] = "\n\n";
+tSCC zFullVersion[] = NTPD_FULL_VERSION;
+/* extracted from /usr/local/gnu/share/autogen/optcode.tpl near line 408 */
+
+#if defined(ENABLE_NLS)
+# define OPTPROC_BASE OPTPROC_TRANSLATE
+ static tOptionXlateProc translate_option_strings;
+#else
+# define OPTPROC_BASE OPTPROC_NONE
+# define translate_option_strings NULL
+#endif /* ENABLE_NLS */
+
+tOptions ntpdOptions = {
+ OPTIONS_STRUCT_VERSION,
+ 0, NULL, /* original argc + argv */
+ ( OPTPROC_BASE
+ + OPTPROC_ERRSTOP
+ + OPTPROC_SHORTOPT
+ + OPTPROC_LONGOPT
+ + OPTPROC_NO_REQ_OPT
+ + OPTPROC_ENVIRON
+ + OPTPROC_NO_ARGS ),
+ 0, NULL, /* current option index, current option */
+ NULL, NULL, zPROGNAME,
+ zRcName, zCopyright, zCopyrightNotice,
+ zFullVersion, apzHomeList, zUsageTitle,
+ zExplain, zDetail, optDesc,
+ zBugsAddr, /* address to send bugs to */
+ NULL, NULL, /* extensions/saved state */
+ optionUsage, /* usage procedure */
+ translate_option_strings, /* translation procedure */
+ /*
+ * Indexes to special options
+ */
+ { INDEX_OPT_MORE_HELP,
+ 0 /* no option state saving */,
+ NO_EQUIVALENT /* index of '-#' option */,
+ NO_EQUIVALENT /* index of default opt */
+ },
+ 32 /* full option count */, 29 /* user option count */
+};
+
+/*
+ * Create the static procedure(s) declared above.
+ */
+static void
+doUsageOpt(
+ tOptions* pOptions,
+ tOptDesc* pOptDesc )
+{
+ USAGE( EXIT_SUCCESS );
+}
+
+#if ! defined(TEST_NTPD_OPTS)
+
+/* * * * * * *
+ *
+ * For the set-debug-level option, when DEBUG is #define-d.
+ */
+#ifdef DEBUG
+static void
+doOptSet_Debug_Level(
+ tOptions* pOptions,
+ tOptDesc* pOptDesc )
+{
+ /* extracted from ../include/debug-opt.def, line 29 */
+DESC(DEBUG_LEVEL).optOccCt = atoi( pOptDesc->pzLastArg );
+}
+#endif /* defined DEBUG */
+
+#endif /* defined(TEST_NTPD_OPTS) */
+
+/* extracted from /usr/local/gnu/share/autogen/optmain.tpl near line 92 */
+
+#if defined(TEST_NTPD_OPTS) /* TEST MAIN PROCEDURE: */
+
+int
+main( int argc, char** argv )
+{
+ int res = EXIT_SUCCESS;
+ (void)optionProcess( &ntpdOptions, argc, argv );
+ {
+ void optionPutShell( tOptions* );
+ optionPutShell( &ntpdOptions );
+ }
+ return res;
+}
+#endif /* defined TEST_NTPD_OPTS */
+/* extracted from /usr/local/gnu/share/autogen/optcode.tpl near line 514 */
+
+#if ENABLE_NLS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <autoopts/usage-txt.h>
+
+static char* AO_gettext( char const* pz );
+static void coerce_it(void** s);
+
+static char*
+AO_gettext( char const* pz )
+{
+ char* pzRes;
+ if (pz == NULL)
+ return NULL;
+ pzRes = _(pz);
+ if (pzRes == pz)
+ return pzRes;
+ pzRes = strdup( pzRes );
+ if (pzRes == NULL) {
+ fputs( _("No memory for duping translated strings\n"), stderr );
+ exit( EXIT_FAILURE );
+ }
+ return pzRes;
+}
+
+static void coerce_it(void** s) { *s = AO_gettext(*s); }
+#define COERSION(_f) \
+ coerce_it((void*)&(ntpdOptions._f))
+
+/*
+ * This invokes the translation code (e.g. gettext(3)).
+ */
+static void
+translate_option_strings( void )
+{
+ /*
+ * Guard against re-translation. It won't work. The strings will have
+ * been changed by the first pass through this code. One shot only.
+ */
+ if (option_usage_text.field_ct == 0)
+ return;
+ /*
+ * Do the translations. The first pointer follows the field count field.
+ * The field count field is the size of a pointer.
+ */
+ {
+ char** ppz = (char**)(void*)&(option_usage_text);
+ int ix = option_usage_text.field_ct;
+
+ do {
+ ppz++;
+ *ppz = AO_gettext(*ppz);
+ } while (--ix > 0);
+ }
+ option_usage_text.field_ct = 0;
+
+ {
+ tOptDesc* pOD = ntpdOptions.pOptDesc;
+ int ix = ntpdOptions.optCt;
+
+ for (;;) {
+ pOD->pzText = AO_gettext(pOD->pzText);
+ pOD->pz_NAME = AO_gettext(pOD->pz_NAME);
+ pOD->pz_Name = AO_gettext(pOD->pz_Name);
+ pOD->pz_DisableName = AO_gettext(pOD->pz_DisableName);
+ pOD->pz_DisablePfx = AO_gettext(pOD->pz_DisablePfx);
+ if (--ix <= 0)
+ break;
+ pOD++;
+ }
+ }
+ COERSION(pzCopyright);
+ COERSION(pzCopyNotice);
+ COERSION(pzFullVersion);
+ COERSION(pzUsageTitle);
+ COERSION(pzExplain);
+ COERSION(pzDetail);
+}
+
+#endif /* ENABLE_NLS */
+
+#ifdef __cplusplus
+}
+#endif
+/* ntpd-opts.c ends here */
diff --git a/contrib/ntp/ntpd/ntpd-opts.def b/contrib/ntp/ntpd/ntpd-opts.def
new file mode 100644
index 0000000..7266e1b
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd-opts.def
@@ -0,0 +1,13 @@
+/* -*- Mode: Text -*- */
+
+autogen definitions options;
+
+#include copyright.def
+
+prog-name = "ntpd";
+prog-title = "NTP daemon program";
+
+#include ntpdbase-opts.def
+
+detail = <<- _END_DETAIL
+ _END_DETAIL;
diff --git a/contrib/ntp/ntpd/ntpd-opts.h b/contrib/ntp/ntpd/ntpd-opts.h
new file mode 100644
index 0000000..45d0de5
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd-opts.h
@@ -0,0 +1,351 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpd-opts.h)
+ *
+ * It has been AutoGen-ed Sunday August 17, 2008 at 05:20:09 AM EDT
+ * From the definitions ntpd-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 29:0:4 templates.
+ */
+
+/*
+ * This file was produced by an AutoOpts template. AutoOpts is a
+ * copyrighted work. This header file is not encumbered by AutoOpts
+ * licensing, but is provided under the licensing terms chosen by the
+ * ntpd author or copyright holder. AutoOpts is licensed under
+ * the terms of the LGPL. The redistributable library (``libopts'') is
+ * licensed under the terms of either the LGPL or, at the users discretion,
+ * the BSD license. See the AutoOpts and/or libopts sources for details.
+ *
+ * This source file is copyrighted and licensed under the following terms:
+ *
+ * ntpd copyright 1970-2008 David L. Mills and/or others - all rights reserved
+ *
+ * see html/copyright.html
+ */
+/*
+ * This file contains the programmatic interface to the Automated
+ * Options generated for the ntpd program.
+ * These macros are documented in the AutoGen info file in the
+ * "AutoOpts" chapter. Please refer to that doc for usage help.
+ */
+#ifndef AUTOOPTS_NTPD_OPTS_H_GUARD
+#define AUTOOPTS_NTPD_OPTS_H_GUARD
+#include "config.h"
+#include <autoopts/options.h>
+
+/*
+ * Ensure that the library used for compiling this generated header is at
+ * least as new as the version current when the header template was released
+ * (not counting patch version increments). Also ensure that the oldest
+ * tolerable version is at least as old as what was current when the header
+ * template was released.
+ */
+#define AO_TEMPLATE_VERSION 118784
+#if (AO_TEMPLATE_VERSION < OPTIONS_MINIMUM_VERSION) \
+ || (AO_TEMPLATE_VERSION > OPTIONS_STRUCT_VERSION)
+# error option template version mismatches autoopts/options.h header
+ Choke Me.
+#endif
+
+/*
+ * Enumeration of each option:
+ */
+typedef enum {
+ INDEX_OPT_IPV4 = 0,
+ INDEX_OPT_IPV6 = 1,
+ INDEX_OPT_AUTHREQ = 2,
+ INDEX_OPT_AUTHNOREQ = 3,
+ INDEX_OPT_BCASTSYNC = 4,
+ INDEX_OPT_CONFIGFILE = 5,
+ INDEX_OPT_DEBUG_LEVEL = 6,
+ INDEX_OPT_SET_DEBUG_LEVEL = 7,
+ INDEX_OPT_DRIFTFILE = 8,
+ INDEX_OPT_PANICGATE = 9,
+ INDEX_OPT_JAILDIR = 10,
+ INDEX_OPT_INTERFACE = 11,
+ INDEX_OPT_KEYFILE = 12,
+ INDEX_OPT_LOGFILE = 13,
+ INDEX_OPT_NOVIRTUALIPS = 14,
+ INDEX_OPT_MODIFYMMTIMER = 15,
+ INDEX_OPT_NOFORK = 16,
+ INDEX_OPT_NICE = 17,
+ INDEX_OPT_PIDFILE = 18,
+ INDEX_OPT_PRIORITY = 19,
+ INDEX_OPT_QUIT = 20,
+ INDEX_OPT_PROPAGATIONDELAY = 21,
+ INDEX_OPT_UPDATEINTERVAL = 22,
+ INDEX_OPT_STATSDIR = 23,
+ INDEX_OPT_TRUSTEDKEY = 24,
+ INDEX_OPT_USER = 25,
+ INDEX_OPT_VAR = 26,
+ INDEX_OPT_DVAR = 27,
+ INDEX_OPT_SLEW = 28,
+ INDEX_OPT_VERSION = 29,
+ INDEX_OPT_HELP = 30,
+ INDEX_OPT_MORE_HELP = 31
+} teOptIndex;
+
+#define OPTION_CT 32
+#define NTPD_VERSION "4.2.4p5"
+#define NTPD_FULL_VERSION "ntpd - NTP daemon program - Ver. 4.2.4p5"
+
+/*
+ * Interface defines for all options. Replace "n" with the UPPER_CASED
+ * option name (as in the teOptIndex enumeration above).
+ * e.g. HAVE_OPT( IPV4 )
+ */
+#define DESC(n) (ntpdOptions.pOptDesc[INDEX_OPT_## n])
+#define HAVE_OPT(n) (! UNUSED_OPT(& DESC(n)))
+#define OPT_ARG(n) (DESC(n).optArg.argString)
+#define STATE_OPT(n) (DESC(n).fOptState & OPTST_SET_MASK)
+#define COUNT_OPT(n) (DESC(n).optOccCt)
+#define ISSEL_OPT(n) (SELECTED_OPT(&DESC(n)))
+#define ISUNUSED_OPT(n) (UNUSED_OPT(& DESC(n)))
+#define ENABLED_OPT(n) (! DISABLED_OPT(& DESC(n)))
+#define STACKCT_OPT(n) (((tArgList*)(DESC(n).optCookie))->useCt)
+#define STACKLST_OPT(n) (((tArgList*)(DESC(n).optCookie))->apzArgs)
+#define CLEAR_OPT(n) STMTS( \
+ DESC(n).fOptState &= OPTST_PERSISTENT_MASK; \
+ if ( (DESC(n).fOptState & OPTST_INITENABLED) == 0) \
+ DESC(n).fOptState |= OPTST_DISABLED; \
+ DESC(n).optCookie = NULL )
+
+/*
+ * Make sure there are no #define name conflicts with the option names
+ */
+#ifndef NO_OPTION_NAME_WARNINGS
+# ifdef IPV4
+# warning undefining IPV4 due to option name conflict
+# undef IPV4
+# endif
+# ifdef IPV6
+# warning undefining IPV6 due to option name conflict
+# undef IPV6
+# endif
+# ifdef AUTHREQ
+# warning undefining AUTHREQ due to option name conflict
+# undef AUTHREQ
+# endif
+# ifdef AUTHNOREQ
+# warning undefining AUTHNOREQ due to option name conflict
+# undef AUTHNOREQ
+# endif
+# ifdef BCASTSYNC
+# warning undefining BCASTSYNC due to option name conflict
+# undef BCASTSYNC
+# endif
+# ifdef CONFIGFILE
+# warning undefining CONFIGFILE due to option name conflict
+# undef CONFIGFILE
+# endif
+# ifdef DEBUG_LEVEL
+# warning undefining DEBUG_LEVEL due to option name conflict
+# undef DEBUG_LEVEL
+# endif
+# ifdef SET_DEBUG_LEVEL
+# warning undefining SET_DEBUG_LEVEL due to option name conflict
+# undef SET_DEBUG_LEVEL
+# endif
+# ifdef DRIFTFILE
+# warning undefining DRIFTFILE due to option name conflict
+# undef DRIFTFILE
+# endif
+# ifdef PANICGATE
+# warning undefining PANICGATE due to option name conflict
+# undef PANICGATE
+# endif
+# ifdef JAILDIR
+# warning undefining JAILDIR due to option name conflict
+# undef JAILDIR
+# endif
+# ifdef INTERFACE
+# warning undefining INTERFACE due to option name conflict
+# undef INTERFACE
+# endif
+# ifdef KEYFILE
+# warning undefining KEYFILE due to option name conflict
+# undef KEYFILE
+# endif
+# ifdef LOGFILE
+# warning undefining LOGFILE due to option name conflict
+# undef LOGFILE
+# endif
+# ifdef NOVIRTUALIPS
+# warning undefining NOVIRTUALIPS due to option name conflict
+# undef NOVIRTUALIPS
+# endif
+# ifdef MODIFYMMTIMER
+# warning undefining MODIFYMMTIMER due to option name conflict
+# undef MODIFYMMTIMER
+# endif
+# ifdef NOFORK
+# warning undefining NOFORK due to option name conflict
+# undef NOFORK
+# endif
+# ifdef NICE
+# warning undefining NICE due to option name conflict
+# undef NICE
+# endif
+# ifdef PIDFILE
+# warning undefining PIDFILE due to option name conflict
+# undef PIDFILE
+# endif
+# ifdef PRIORITY
+# warning undefining PRIORITY due to option name conflict
+# undef PRIORITY
+# endif
+# ifdef QUIT
+# warning undefining QUIT due to option name conflict
+# undef QUIT
+# endif
+# ifdef PROPAGATIONDELAY
+# warning undefining PROPAGATIONDELAY due to option name conflict
+# undef PROPAGATIONDELAY
+# endif
+# ifdef UPDATEINTERVAL
+# warning undefining UPDATEINTERVAL due to option name conflict
+# undef UPDATEINTERVAL
+# endif
+# ifdef STATSDIR
+# warning undefining STATSDIR due to option name conflict
+# undef STATSDIR
+# endif
+# ifdef TRUSTEDKEY
+# warning undefining TRUSTEDKEY due to option name conflict
+# undef TRUSTEDKEY
+# endif
+# ifdef USER
+# warning undefining USER due to option name conflict
+# undef USER
+# endif
+# ifdef VAR
+# warning undefining VAR due to option name conflict
+# undef VAR
+# endif
+# ifdef DVAR
+# warning undefining DVAR due to option name conflict
+# undef DVAR
+# endif
+# ifdef SLEW
+# warning undefining SLEW due to option name conflict
+# undef SLEW
+# endif
+#else /* NO_OPTION_NAME_WARNINGS */
+# undef IPV4
+# undef IPV6
+# undef AUTHREQ
+# undef AUTHNOREQ
+# undef BCASTSYNC
+# undef CONFIGFILE
+# undef DEBUG_LEVEL
+# undef SET_DEBUG_LEVEL
+# undef DRIFTFILE
+# undef PANICGATE
+# undef JAILDIR
+# undef INTERFACE
+# undef KEYFILE
+# undef LOGFILE
+# undef NOVIRTUALIPS
+# undef MODIFYMMTIMER
+# undef NOFORK
+# undef NICE
+# undef PIDFILE
+# undef PRIORITY
+# undef QUIT
+# undef PROPAGATIONDELAY
+# undef UPDATEINTERVAL
+# undef STATSDIR
+# undef TRUSTEDKEY
+# undef USER
+# undef VAR
+# undef DVAR
+# undef SLEW
+#endif /* NO_OPTION_NAME_WARNINGS */
+
+/*
+ * Interface defines for specific options.
+ */
+#define VALUE_OPT_IPV4 '4'
+#define WHICH_OPT_IPV4 (DESC(IPV4).optActualValue)
+#define WHICH_IDX_IPV4 (DESC(IPV4).optActualIndex)
+#define VALUE_OPT_IPV6 '6'
+#define VALUE_OPT_AUTHREQ 'a'
+#define VALUE_OPT_AUTHNOREQ 'A'
+#define VALUE_OPT_BCASTSYNC 'b'
+#define VALUE_OPT_CONFIGFILE 'c'
+#ifdef DEBUG
+#define VALUE_OPT_DEBUG_LEVEL 'd'
+#endif /* DEBUG */
+#ifdef DEBUG
+#define VALUE_OPT_SET_DEBUG_LEVEL 'D'
+#endif /* DEBUG */
+#define VALUE_OPT_DRIFTFILE 'f'
+#define VALUE_OPT_PANICGATE 'g'
+#define VALUE_OPT_JAILDIR 'i'
+#define VALUE_OPT_INTERFACE 'I'
+#define VALUE_OPT_KEYFILE 'k'
+#define VALUE_OPT_LOGFILE 'l'
+#define VALUE_OPT_NOVIRTUALIPS 'L'
+#ifdef SYS_WINNT
+#define VALUE_OPT_MODIFYMMTIMER 'M'
+#endif /* SYS_WINNT */
+#define VALUE_OPT_NOFORK 'n'
+#define VALUE_OPT_NICE 'N'
+#define VALUE_OPT_PIDFILE 'p'
+#define VALUE_OPT_PRIORITY 'P'
+#define OPT_VALUE_PRIORITY (DESC(PRIORITY).optArg.argInt)
+#define VALUE_OPT_QUIT 'q'
+#define VALUE_OPT_PROPAGATIONDELAY 'r'
+#define VALUE_OPT_UPDATEINTERVAL 'U'
+#define OPT_VALUE_UPDATEINTERVAL (DESC(UPDATEINTERVAL).optArg.argInt)
+#define VALUE_OPT_STATSDIR 's'
+#define VALUE_OPT_TRUSTEDKEY 't'
+#define VALUE_OPT_USER 'u'
+#define VALUE_OPT_VAR 'v'
+#define VALUE_OPT_DVAR 'V'
+#define VALUE_OPT_SLEW 'x'
+
+#define VALUE_OPT_VERSION 'v'
+#define VALUE_OPT_HELP '?'
+#define VALUE_OPT_MORE_HELP '!'
+/*
+ * Interface defines not associated with particular options
+ */
+#define ERRSKIP_OPTERR STMTS( ntpdOptions.fOptSet &= ~OPTPROC_ERRSTOP )
+#define ERRSTOP_OPTERR STMTS( ntpdOptions.fOptSet |= OPTPROC_ERRSTOP )
+#define RESTART_OPT(n) STMTS( \
+ ntpdOptions.curOptIdx = (n); \
+ ntpdOptions.pzCurOpt = NULL )
+#define START_OPT RESTART_OPT(1)
+#define USAGE(c) (*ntpdOptions.pUsageProc)( &ntpdOptions, c )
+/* extracted from /usr/local/gnu/share/autogen/opthead.tpl near line 360 */
+
+/* * * * * *
+ *
+ * Declare the ntpd option descriptor.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern tOptions ntpdOptions;
+
+#ifndef _
+# if ENABLE_NLS
+# include <stdio.h>
+ static inline char* aoGetsText( char const* pz ) {
+ if (pz == NULL) return NULL;
+ return (char*)gettext( pz );
+ }
+# define _(s) aoGetsText(s)
+# else /* ENABLE_NLS */
+# define _(s) s
+# endif /* ENABLE_NLS */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* AUTOOPTS_NTPD_OPTS_H_GUARD */
+/* ntpd-opts.h ends here */
diff --git a/contrib/ntp/ntpd/ntpd-opts.menu b/contrib/ntp/ntpd/ntpd-opts.menu
new file mode 100644
index 0000000..3425d82
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd-opts.menu
@@ -0,0 +1 @@
+* ntpd Invocation:: Invoking ntpd
diff --git a/contrib/ntp/ntpd/ntpd-opts.texi b/contrib/ntp/ntpd/ntpd-opts.texi
new file mode 100644
index 0000000..5a99b58
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd-opts.texi
@@ -0,0 +1,489 @@
+@node ntpd Invocation
+@section Invoking ntpd
+@pindex ntpd
+@cindex NTP daemon program
+@ignore
+#
+# EDIT THIS FILE WITH CAUTION (ntpd-opts.texi)
+#
+# It has been AutoGen-ed Sunday August 17, 2008 at 05:20:12 AM EDT
+# From the definitions ntpd-opts.def
+# and the template file aginfo.tpl
+@end ignore
+This program has no explanation.
+
+
+
+This section was generated by @strong{AutoGen},
+the aginfo template and the option descriptions for the @command{ntpd} program. It documents the ntpd usage text and option meanings.
+
+This software is released under a specialized copyright license.
+
+@menu
+* ntpd usage:: ntpd usage help (-?)
+* ntpd authnoreq:: authnoreq option (-A)
+* ntpd authreq:: authreq option (-a)
+* ntpd bcastsync:: bcastsync option (-b)
+* ntpd configfile:: configfile option (-c)
+* ntpd debug-level:: debug-level option (-d)
+* ntpd driftfile:: driftfile option (-f)
+* ntpd dvar:: dvar option (-V)
+* ntpd interface:: interface option (-I)
+* ntpd ipv4:: ipv4 option (-4)
+* ntpd ipv6:: ipv6 option (-6)
+* ntpd jaildir:: jaildir option (-i)
+* ntpd keyfile:: keyfile option (-k)
+* ntpd logfile:: logfile option (-l)
+* ntpd modifymmtimer:: modifymmtimer option (-M)
+* ntpd nice:: nice option (-N)
+* ntpd nofork:: nofork option (-n)
+* ntpd novirtualips:: novirtualips option (-L)
+* ntpd panicgate:: panicgate option (-g)
+* ntpd pidfile:: pidfile option (-p)
+* ntpd priority:: priority option (-P)
+* ntpd propagationdelay:: propagationdelay option (-r)
+* ntpd quit:: quit option (-q)
+* ntpd set-debug-level:: set-debug-level option (-D)
+* ntpd slew:: slew option (-x)
+* ntpd statsdir:: statsdir option (-s)
+* ntpd trustedkey:: trustedkey option (-t)
+* ntpd updateinterval:: updateinterval option (-U)
+* ntpd user:: user option (-u)
+* ntpd var:: var option (-v)
+@end menu
+
+@node ntpd usage
+@subsection ntpd usage help (-?)
+@cindex ntpd usage
+
+This is the automatically generated usage text for ntpd:
+
+@exampleindent 0
+@example
+ntpd - NTP daemon program - Ver. 4.2.5p113
+USAGE: ntpd [ -<flag> [<val>] | --<name>[@{=| @}<val>] ]...
+ Flg Arg Option-Name Description
+ -4 no ipv4 Force IPv4 DNS name resolution
+ -6 no ipv6 Force IPv6 DNS name resolution
+ - an alternate for ipv4
+ -a no authreq Require crypto authentication
+ - prohibits these options:
+ authnoreq
+ -A no authnoreq Do not require crypto authentication
+ - prohibits these options:
+ authreq
+ -b no bcastsync Allow us to sync to broadcast servers
+ -c Str configfile configuration file name
+ -d no debug-level Increase output debug message level
+ - may appear multiple times
+ -D Str set-debug-level Set the output debug message level
+ - may appear multiple times
+ -f Str driftfile frequency drift file name
+ -g no panicgate Allow the first adjustment to be Big
+ -I Str interface Listen on the specified interface or IP
+ - may appear multiple times
+ -k Str keyfile path to symmetric keys
+ -l Str logfile path to the log file
+ -L no novirtualips Do not listen to virtual IPs
+ -n no nofork Do not fork
+ -N no nice Run at high priority
+ -p Str pidfile path to the PID file
+ -P Num priority Process priority
+ -q no quit Set the time and quit
+ -r Str propagationdelay Broadcast/propagation delay
+ -s Str statsdir Statistics file location
+ -t Str trustedkey Trusted key number
+ - may appear multiple times
+ -U Num updateinterval interval in seconds between scans for new or dropped interfaces
+ Str var make ARG an ntp variable (RW)
+ - may appear multiple times
+ Str dvar make ARG an ntp variable (RW|DEF)
+ - may appear multiple times
+ -x no slew Slew up to 600 seconds
+ opt version Output version information and exit
+ -? no help Display usage information and exit
+ -! no more-help Extended usage information passed thru pager
+
+Options are specified by doubled hyphens and their name
+or by a single hyphen and the flag character.
+
+The following option preset mechanisms are supported:
+ - examining environment variables named NTPD_*
+
+
+
+please send bug reports to: http://bugs.ntp.org, bugs@@ntp.org
+@end example
+@exampleindent 4
+
+@node ntpd ipv4
+@subsection ipv4 option (-4)
+@cindex ntpd-ipv4
+
+This is the ``force ipv4 dns name resolution'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+is a member of the ipv4 class of options.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+
+@node ntpd ipv6
+@subsection ipv6 option (-6)
+@cindex ntpd-ipv6
+
+This is the ``force ipv6 dns name resolution'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+is a member of the ipv4 class of options.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+
+@node ntpd authreq
+@subsection authreq option (-a)
+@cindex ntpd-authreq
+
+This is the ``require crypto authentication'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authnoreq.
+@end itemize
+
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+
+@node ntpd authnoreq
+@subsection authnoreq option (-A)
+@cindex ntpd-authnoreq
+
+This is the ``do not require crypto authentication'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authreq.
+@end itemize
+
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+
+@node ntpd bcastsync
+@subsection bcastsync option (-b)
+@cindex ntpd-bcastsync
+
+This is the ``allow us to sync to broadcast servers'' option.
+
+
+@node ntpd configfile
+@subsection configfile option (-c)
+@cindex ntpd-configfile
+
+This is the ``configuration file name'' option.
+The name and path of the configuration file,
+/etc/ntp.conf
+by default.
+
+@node ntpd debug-level
+@subsection debug-level option (-d)
+@cindex ntpd-debug-level
+
+This is the ``increase output debug message level'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+must be compiled in by defining @code{DEBUG} during the compilation.
+@end itemize
+
+Increase the debugging message output level.
+
+@node ntpd set-debug-level
+@subsection set-debug-level option (-D)
+@cindex ntpd-set-debug-level
+
+This is the ``set the output debug message level'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+must be compiled in by defining @code{DEBUG} during the compilation.
+@end itemize
+
+Set the output debugging level. Can be supplied multiple times,
+but each overrides the previous value(s).
+
+@node ntpd driftfile
+@subsection driftfile option (-f)
+@cindex ntpd-driftfile
+
+This is the ``frequency drift file name'' option.
+The name and path of the frequency file,
+/etc/ntp.drift
+by default.
+This is the same operation as the
+driftfile driftfile
+configuration specification in the
+/etc/ntp.conf
+file.
+
+@node ntpd panicgate
+@subsection panicgate option (-g)
+@cindex ntpd-panicgate
+
+This is the ``allow the first adjustment to be big'' option.
+Normally,
+ntpd
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ntpd
+will exit with a message to the system log. This option can be used with the
+-q
+and
+-x
+options.
+See the
+tinker
+configuration file directive for other options.
+
+@node ntpd jaildir
+@subsection jaildir option (-i)
+@cindex ntpd-jaildir
+
+This is the ``jail directory'' option.
+Chroot the server to the directory
+jaildir
+.
+This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges.
+You may need to also specify a
+-u
+option.
+
+@node ntpd interface
+@subsection interface option (-I)
+@cindex ntpd-interface
+
+This is the ``listen on interface'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpd keyfile
+@subsection keyfile option (-k)
+@cindex ntpd-keyfile
+
+This is the ``path to symmetric keys'' option.
+Specify the name and path of the symmetric key file.
+/etc/ntp.keys
+is the default.
+This is the same operation as the
+keys keyfile
+configuration file directive.
+
+@node ntpd logfile
+@subsection logfile option (-l)
+@cindex ntpd-logfile
+
+This is the ``path to the log file'' option.
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+logfile logfile
+configuration file directive.
+
+@node ntpd novirtualips
+@subsection novirtualips option (-L)
+@cindex ntpd-novirtualips
+
+This is the ``do not listen to virtual ips'' option.
+Do not listen to virtual IPs. The default is to listen.
+
+@node ntpd modifymmtimer
+@subsection modifymmtimer option (-M)
+@cindex ntpd-modifymmtimer
+
+This is the ``modify multimedia timer (windows only)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SYS_WINNT} during the compilation.
+@end itemize
+
+Set the Windows Multimedia Timer to highest resolution.
+
+@node ntpd nofork
+@subsection nofork option (-n)
+@cindex ntpd-nofork
+
+This is the ``do not fork'' option.
+
+
+@node ntpd nice
+@subsection nice option (-N)
+@cindex ntpd-nice
+
+This is the ``run at high priority'' option.
+To the extent permitted by the operating system, run
+ntpd
+at the highest priority.
+
+@node ntpd pidfile
+@subsection pidfile option (-p)
+@cindex ntpd-pidfile
+
+This is the ``path to the pid file'' option.
+Specify the name and path of the file used to record
+ntpd's
+process ID.
+This is the same operation as the
+pidfile pidfile
+configuration file directive.
+
+@node ntpd priority
+@subsection priority option (-P)
+@cindex ntpd-priority
+
+This is the ``process priority'' option.
+To the extent permitted by the operating system, run
+ntpd
+at the specified
+sched_setscheduler(SCHED_FIFO)
+priority.
+
+@node ntpd quit
+@subsection quit option (-q)
+@cindex ntpd-quit
+
+This is the ``set the time and quit'' option.
+ntpd
+will exit just after the first time the clock is set. This behavior mimics that of the
+ntpdate
+program, which is to be retired.
+The
+-g
+and
+-x
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+
+@node ntpd propagationdelay
+@subsection propagationdelay option (-r)
+@cindex ntpd-propagationdelay
+
+This is the ``broadcast/propagation delay'' option.
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+
+@node ntpd updateinterval
+@subsection updateinterval option (-U)
+@cindex ntpd-updateinterval
+
+This is the ``interval in seconds between scans for new or dropped interfaces'' option.
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning.
+
+@node ntpd statsdir
+@subsection statsdir option (-s)
+@cindex ntpd-statsdir
+
+This is the ``statistics file location'' option.
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+statsdir statsdir
+configuration file directive.
+
+@node ntpd trustedkey
+@subsection trustedkey option (-t)
+@cindex ntpd-trustedkey
+
+This is the ``trusted key number'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Add a key number to the trusted key list.
+
+@node ntpd user
+@subsection user option (-u)
+@cindex ntpd-user
+
+This is the ``run as userid (or userid:groupid)'' option.
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports to run the server without full root privileges.
+Currently, this option is supported under NetBSD (configure with
+--enable-clockctl
+) and Linux (configure with
+--enable-linuxcaps
+).
+
+@node ntpd var
+@subsection var option (-v)
+@cindex ntpd-var
+
+This is the ``make arg an ntp variable (rw)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpd dvar
+@subsection dvar option (-V)
+@cindex ntpd-dvar
+
+This is the ``make arg an ntp variable (rw|def)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpd slew
+@subsection slew option (-x)
+@cindex ntpd-slew
+
+This is the ``slew up to 600 seconds'' option.
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+-g
+and
+-q
+options.
+See the
+tinker
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
diff --git a/contrib/ntp/ntpd/ntpd.1 b/contrib/ntp/ntpd/ntpd.1
new file mode 100644
index 0000000..25aee41
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd.1
@@ -0,0 +1,296 @@
+.TH NTPD 1 2008-08-17 "( 4.2.4p5)" "Programmer's Manual"
+.\" EDIT THIS FILE WITH CAUTION (ntpd.1)
+.\"
+.\" It has been AutoGen-ed Sunday August 17, 2008 at 05:20:11 AM EDT
+.\" From the definitions ntpd-opts.def
+.\" and the template file agman1.tpl
+.\"
+.SH NAME
+ntpd \- NTP daemon program
+.SH SYNOPSIS
+.B ntpd
+.\" Mixture of short (flag) options and long options
+.RB [ \-\fIflag\fP " [\fIvalue\fP]]... [" \--\fIopt-name\fP " [[=| ]\fIvalue\fP]]..."
+.PP
+All arguments must be options.
+.SH "DESCRIPTION"
+This manual page documents, briefly, the \fBntpd\fP command.
+
+.SH OPTIONS
+.TP
+.BR \-4 ", " \--ipv4
+Force IPv4 DNS name resolution.
+This option is a member of the ipv4 class of options.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.TP
+.BR \-6 ", " \--ipv6
+Force IPv6 DNS name resolution.
+This option is a member of the ipv4 class of options.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.TP
+.BR \-a ", " \--authreq
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.TP
+.BR \-A ", " \--authnoreq
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.TP
+.BR \-b ", " \--bcastsync
+Allow us to sync to broadcast servers.
+.sp
+
+.TP
+.BR \-c " \fIstring\fP, " \--configfile "=" \fIstring\fP
+configuration file name.
+.sp
+The name and path of the configuration file,
+/etc/ntp.conf
+by default.
+.TP
+.BR \-d ", " \--debug-level
+Increase output debug message level.
+This option may appear an unlimited number of times.
+.sp
+Increase the debugging message output level.
+.TP
+.BR \-D " \fIstring\fP, " \--set-debug-level "=" \fIstring\fP
+Set the output debug message level.
+This option may appear an unlimited number of times.
+.sp
+Set the output debugging level. Can be supplied multiple times,
+but each overrides the previous value(s).
+.TP
+.BR \-f " \fIstring\fP, " \--driftfile "=" \fIstring\fP
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+/etc/ntp.drift
+by default.
+This is the same operation as the
+driftfile driftfile
+configuration specification in the
+/etc/ntp.conf
+file.
+.TP
+.BR \-g ", " \--panicgate
+Allow the first adjustment to be Big.
+.sp
+Normally,
+ntpd
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ntpd
+will exit with a message to the system log. This option can be used with the
+-q
+and
+-x
+options.
+See the
+tinker
+configuration file directive for other options.
+.TP
+.BR \-i " \fIstring\fP, " \--jaildir "=" \fIstring\fP
+Jail directory.
+.sp
+Chroot the server to the directory
+jaildir
+.
+This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges.
+You may need to also specify a
+-u
+option.
+.TP
+.BR \-I " \fIiface\fP, " \--interface "=" \fIiface\fP
+Listen on interface.
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-k " \fIstring\fP, " \--keyfile "=" \fIstring\fP
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+/etc/ntp.keys
+is the default.
+This is the same operation as the
+keys keyfile
+configuration file directive.
+.TP
+.BR \-l " \fIstring\fP, " \--logfile "=" \fIstring\fP
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+logfile logfile
+configuration file directive.
+.TP
+.BR \-L ", " \--novirtualips
+Do not listen to virtual IPs.
+.sp
+Do not listen to virtual IPs. The default is to listen.
+.TP
+.BR \-M ", " \--modifymmtimer
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution.
+.TP
+.BR \-n ", " \--nofork
+Do not fork.
+.sp
+
+.TP
+.BR \-N ", " \--nice
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+ntpd
+at the highest priority.
+.TP
+.BR \-p " \fIstring\fP, " \--pidfile "=" \fIstring\fP
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+ntpd's
+process ID.
+This is the same operation as the
+pidfile pidfile
+configuration file directive.
+.TP
+.BR \-P " \fInumber\fP, " \--priority "=" \fInumber\fP
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+ntpd
+at the specified
+sched_setscheduler(SCHED_FIFO)
+priority.
+.TP
+.BR \-q ", " \--quit
+Set the time and quit.
+.sp
+ntpd
+will exit just after the first time the clock is set. This behavior mimics that of the
+ntpdate
+program, which is to be retired.
+The
+-g
+and
+-x
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.BR \-r " \fIstring\fP, " \--propagationdelay "=" \fIstring\fP
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.TP
+.BR \-U " \fInumber\fP, " \--updateinterval "=" \fInumber\fP
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning.
+.TP
+.BR \-s " \fIstring\fP, " \--statsdir "=" \fIstring\fP
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+statsdir statsdir
+configuration file directive.
+.TP
+.BR \-t " \fItkey\fP, " \--trustedkey "=" \fItkey\fP
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add a key number to the trusted key list.
+.TP
+.BR \-u " \fIstring\fP, " \--user "=" \fIstring\fP
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports to run the server without full root privileges.
+Currently, this option is supported under NetBSD (configure with
+--enable-clockctl
+) and Linux (configure with
+--enable-linuxcaps
+).
+.TP
+.BR \-v " \fInvar\fP, " \--var "=" \fInvar\fP
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-V " \fIndvar\fP, " \--dvar "=" \fIndvar\fP
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-x ", " \--slew
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+-g
+and
+-q
+options.
+See the
+tinker
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.BR \-? , " \--help"
+Display usage information and exit.
+.TP
+.BR \-! , " \--more-help"
+Extended usage information passed thru pager.
+.TP
+.BR \-v " [{\fIv|c|n\fP}]," " \--version" "[=\fI{v|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.
+.SH OPTION PRESETS
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from environment variables named:
+.nf
+ \fBNTPD_<option-name>\fP or \fBNTPD\fP
+.fi
+.aj
+.SH AUTHOR
+David L. Mills and/or others
+.br
+Please send bug reports to: http://bugs.ntp.isc.org, bugs@ntp.org
+
+.PP
+.nf
+.na
+see html/copyright.html
+.fi
+.ad
+.PP
+This manual page was \fIAutoGen\fP-erated from the \fBntpd\fP
+option definitions.
diff --git a/contrib/ntp/ntpd/ntpd.c b/contrib/ntp/ntpd/ntpd.c
new file mode 100644
index 0000000..04e0d07
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd.c
@@ -0,0 +1,1231 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+#include <ntp_random.h>
+
+#ifdef SIM
+# include "ntpsim.h"
+# include "ntpdsim-opts.h"
+#else
+# include "ntpd-opts.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#ifndef SYS_WINNT
+# if !defined(VMS) /*wjm*/
+# ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# endif /* VMS */
+# ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# else
+# include <signal.h>
+# endif
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif /* HAVE_SYS_RESOURCE_H */
+#else
+# include <signal.h>
+# include <process.h>
+# include <io.h>
+# include <clockstuff.h>
+#include "ntp_iocompletionport.h"
+#endif /* SYS_WINNT */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+#include "recvbuff.h"
+#include "ntp_cmdargs.h"
+
+#if 0 /* HMS: I don't think we need this. 961223 */
+#ifdef LOCK_PROCESS
+# ifdef SYS_SOLARIS
+# include <sys/mman.h>
+# else
+# include <sys/lock.h>
+# endif
+#endif
+#endif
+
+#ifdef _AIX
+# include <ulimit.h>
+#endif /* _AIX */
+
+#ifdef SCO5_CLOCK
+# include <sys/ci/ciioctl.h>
+#endif
+
+#ifdef HAVE_DROPROOT
+# include <ctype.h>
+# include <grp.h>
+# include <pwd.h>
+#ifdef HAVE_LINUX_CAPABILITIES
+# include <sys/capability.h>
+# include <sys/prctl.h>
+#endif
+#endif
+
+/*
+ * Signals we catch for debugging. If not debugging we ignore them.
+ */
+#define MOREDEBUGSIG SIGUSR1
+#define LESSDEBUGSIG SIGUSR2
+
+/*
+ * Signals which terminate us gracefully.
+ */
+#ifndef SYS_WINNT
+# define SIGDIE1 SIGHUP
+# define SIGDIE3 SIGQUIT
+# define SIGDIE2 SIGINT
+# define SIGDIE4 SIGTERM
+#endif /* SYS_WINNT */
+
+#ifdef HAVE_DNSREGISTRATION
+#include <dns_sd.h>
+DNSServiceRef mdns;
+#endif
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPD_PRIO (-12)
+
+int priority_done = 2; /* 0 - Set priority */
+ /* 1 - priority is OK where it is */
+ /* 2 - Don't set priority */
+ /* 1 and 2 are pretty much the same */
+
+#ifdef DEBUG
+/*
+ * Debugging flag
+ */
+volatile int debug = 0; /* No debugging by default */
+#endif
+
+int listen_to_virtual_ips = 1;
+const char *specific_interface = NULL; /* interface name or IP address to bind to */
+
+/*
+ * No-fork flag. If set, we do not become a background daemon.
+ */
+int nofork = 0; /* Fork by default */
+
+#ifdef HAVE_DROPROOT
+int droproot = 0;
+char *user = NULL; /* User to switch to */
+char *group = NULL; /* group to switch to */
+char *chrootdir = NULL; /* directory to chroot to */
+int sw_uid;
+int sw_gid;
+char *endp;
+struct group *gr;
+struct passwd *pw;
+#endif /* HAVE_DROPROOT */
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing;
+
+/*
+ * Version declaration
+ */
+extern const char *Version;
+
+char const *progname;
+
+int was_alarmed;
+
+#ifdef DECL_SYSCALL
+/*
+ * We put this here, since the argument profile is syscall-specific
+ */
+extern int syscall P((int, ...));
+#endif /* DECL_SYSCALL */
+
+
+#ifdef SIGDIE2
+static RETSIGTYPE finish P((int));
+#endif /* SIGDIE2 */
+
+#ifdef DEBUG
+#ifndef SYS_WINNT
+static RETSIGTYPE moredebug P((int));
+static RETSIGTYPE lessdebug P((int));
+#endif
+#else /* not DEBUG */
+static RETSIGTYPE no_debug P((int));
+#endif /* not DEBUG */
+
+int ntpdmain P((int, char **));
+static void set_process_priority P((void));
+static void init_logging P((char const *));
+static void setup_logfile P((void));
+
+/*
+ * Initialize the logging
+ */
+void
+init_logging(char const *name)
+{
+ const char *cp;
+
+ /*
+ * Logging. This may actually work on the gizmo board. Find a name
+ * to log with by using the basename
+ */
+ cp = strrchr(name, '/');
+ if (cp == 0)
+ cp = name;
+ else
+ cp++;
+
+#if !defined(VMS)
+
+# ifndef LOG_DAEMON
+ openlog(cp, LOG_PID);
+# else /* LOG_DAEMON */
+
+# ifndef LOG_NTP
+# define LOG_NTP LOG_DAEMON
+# endif
+ openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP);
+# ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+# endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+# endif /* LOG_DAEMON */
+#endif /* !SYS_WINNT && !VMS */
+
+ NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "%s", Version);
+}
+
+
+/*
+ * See if we should redirect the logfile
+ */
+
+void
+setup_logfile(
+ void
+ )
+{
+ if (HAVE_OPT( LOGFILE )) {
+ const char *my_optarg = OPT_ARG( LOGFILE );
+ FILE *new_file;
+
+ if(strcmp(my_optarg, "stderr") == 0)
+ new_file = stderr;
+ else if(strcmp(my_optarg, "stdout") == 0)
+ new_file = stdout;
+ else
+ new_file = fopen(my_optarg, "a");
+ if (new_file != NULL) {
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE, "logging to file %s", my_optarg);
+ if (syslog_file != NULL &&
+ fileno(syslog_file) != fileno(new_file))
+ (void)fclose(syslog_file);
+
+ syslog_file = new_file;
+ syslogit = 0;
+ }
+ else
+ msyslog(LOG_ERR,
+ "Cannot open log file %s",
+ my_optarg);
+ }
+}
+
+#ifdef SIM
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpsim(argc, argv);
+}
+#else /* SIM */
+#ifdef NO_MAIN_ALLOWED
+CALL(ntpd,"ntpd",ntpdmain);
+#else
+#ifndef SYS_WINNT
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpdmain(argc, argv);
+}
+#endif /* SYS_WINNT */
+#endif /* NO_MAIN_ALLOWED */
+#endif /* SIM */
+
+#ifdef _AIX
+/*
+ * OK. AIX is different than solaris in how it implements plock().
+ * If you do NOT adjust the stack limit, you will get the MAXIMUM
+ * stack size allocated and PINNED with you program. To check the
+ * value, use ulimit -a.
+ *
+ * To fix this, we create an automatic variable and set our stack limit
+ * to that PLUS 32KB of extra space (we need some headroom).
+ *
+ * This subroutine gets the stack address.
+ *
+ * Grover Davidson and Matt Ladendorf
+ *
+ */
+static char *
+get_aix_stack(void)
+{
+ char ch;
+ return (&ch);
+}
+
+/*
+ * Signal handler for SIGDANGER.
+ */
+static void
+catch_danger(int signo)
+{
+ msyslog(LOG_INFO, "ntpd: setpgid(): %m");
+ /* Make the system believe we'll free something, but don't do it! */
+ return;
+}
+#endif /* _AIX */
+
+/*
+ * Set the process priority
+ */
+static void
+set_process_priority(void)
+{
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_DEBUG, "set_process_priority: %s: priority_done is <%d>",
+ ((priority_done)
+ ? "Leave priority alone"
+ : "Attempt to set priority"
+ ),
+ priority_done);
+#endif /* DEBUG */
+
+#ifdef SYS_WINNT
+ priority_done += NT_set_process_priority();
+#endif
+
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ if (!priority_done) {
+ extern int config_priority_override, config_priority;
+ int pmax, pmin;
+ struct sched_param sched;
+
+ pmax = sched_get_priority_max(SCHED_FIFO);
+ sched.sched_priority = pmax;
+ if ( config_priority_override ) {
+ pmin = sched_get_priority_min(SCHED_FIFO);
+ if ( config_priority > pmax )
+ sched.sched_priority = pmax;
+ else if ( config_priority < pmin )
+ sched.sched_priority = pmin;
+ else
+ sched.sched_priority = config_priority;
+ }
+ if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 )
+ msyslog(LOG_ERR, "sched_setscheduler(): %m");
+ else
+ ++priority_done;
+ }
+#endif /* HAVE_SCHED_SETSCHEDULER */
+#if defined(HAVE_RTPRIO)
+# ifdef RTP_SET
+ if (!priority_done) {
+ struct rtprio srtp;
+
+ srtp.type = RTP_PRIO_REALTIME; /* was: RTP_PRIO_NORMAL */
+ srtp.prio = 0; /* 0 (hi) -> RTP_PRIO_MAX (31,lo) */
+
+ if (rtprio(RTP_SET, getpid(), &srtp) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# else /* not RTP_SET */
+ if (!priority_done) {
+ if (rtprio(0, 120) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* not RTP_SET */
+#endif /* HAVE_RTPRIO */
+#if defined(NTPD_PRIO) && NTPD_PRIO != 0
+# ifdef HAVE_ATT_NICE
+ if (!priority_done) {
+ errno = 0;
+ if (-1 == nice (NTPD_PRIO) && errno != 0)
+ msyslog(LOG_ERR, "nice() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_ATT_NICE */
+# ifdef HAVE_BSD_NICE
+ if (!priority_done) {
+ if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO))
+ msyslog(LOG_ERR, "setpriority() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_BSD_NICE */
+#endif /* NTPD_PRIO && NTPD_PRIO != 0 */
+ if (!priority_done)
+ msyslog(LOG_ERR, "set_process_priority: No way found to improve our priority");
+}
+
+
+/*
+ * Main program. Initialize us, disconnect us from the tty if necessary,
+ * and loop waiting for I/O and/or timer expiries.
+ */
+int
+ntpdmain(
+ int argc,
+ char *argv[]
+ )
+{
+ l_fp now;
+ struct recvbuf *rbuf;
+#ifdef _AIX /* HMS: ifdef SIGDANGER? */
+ struct sigaction sa;
+#endif
+
+ progname = argv[0];
+
+ initializing = 1; /* mark that we are initializing */
+
+ {
+ int optct = optionProcess(
+#ifdef SIM
+ &ntpdsimOptions
+#else
+ &ntpdOptions
+#endif
+ , argc, argv);
+ argc -= optct;
+ argv += optct;
+ }
+
+ /* HMS: is this lame? Should we process -l first? */
+
+ init_logging(progname); /* Open the log file */
+
+#ifdef HAVE_UMASK
+ {
+ mode_t uv;
+
+ uv = umask(0);
+ if(uv)
+ (void) umask(uv);
+ else
+ (void) umask(022);
+ }
+#endif
+
+#if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */
+ {
+ uid_t uid;
+
+ uid = getuid();
+ if (uid)
+ {
+ msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid);
+ printf("must be run as root, not uid %ld", (long)uid);
+ exit(1);
+ }
+ }
+#endif
+
+#ifdef OPENSSL
+ if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) {
+ msyslog(LOG_ERR,
+ "ntpd: OpenSSL version mismatch. Built against %lx, you have %lx\n",
+ OPENSSL_VERSION_NUMBER, SSLeay());
+ exit(1);
+ }
+#endif
+
+ /* getstartup(argc, argv); / * startup configuration, may set debug */
+
+#ifdef DEBUG
+ debug = DESC(DEBUG_LEVEL).optOccCt;
+ if (debug)
+ printf("%s\n", Version);
+#endif
+
+/*
+ * Enable the Multi-Media Timer for Windows?
+ */
+#ifdef SYS_WINNT
+ if (HAVE_OPT( MODIFYMMTIMER ))
+ set_mm_timer(MM_TIMER_HIRES);
+#endif
+
+ if (HAVE_OPT( NOFORK ) || HAVE_OPT( QUIT ))
+ nofork = 1;
+
+ if (HAVE_OPT( NOVIRTUALIPS ))
+ listen_to_virtual_ips = 0;
+
+ if (HAVE_OPT( INTERFACE )) {
+#if 0
+ int ifacect = STACKCT_OPT( INTERFACE );
+ char** ifaces = STACKLST_OPT( INTERFACE );
+
+ /* malloc space for the array of names */
+ while (ifacect-- > 0) {
+ next_iface = *ifaces++;
+ }
+#else
+ specific_interface = OPT_ARG( INTERFACE );
+#endif
+ }
+
+ if (HAVE_OPT( NICE ))
+ priority_done = 0;
+
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ if (HAVE_OPT( PRIORITY )) {
+ config_priority = OPT_VALUE_PRIORITY;
+ config_priority_override = 1;
+ priority_done = 0;
+ }
+#endif
+
+#ifdef SYS_WINNT
+ /*
+ * Initialize the time structures and variables
+ */
+ init_winnt_time();
+#endif
+
+ setup_logfile();
+
+ /*
+ * Initialize random generator and public key pair
+ */
+ get_systime(&now);
+
+ ntp_srandom((int)(now.l_i * now.l_uf));
+
+#ifdef HAVE_DNSREGISTRATION
+ /* HMS: does this have to happen this early? */
+ msyslog(LOG_INFO, "Attemping to register mDNS");
+ if ( DNSServiceRegister (&mdns, 0, 0, NULL, "_ntp._udp", NULL, NULL, htons(NTP_PORT), 0, NULL, NULL, NULL) != kDNSServiceErr_NoError ) {
+ msyslog(LOG_ERR, "Unable to register mDNS");
+ }
+#endif
+
+#if !defined(VMS)
+# ifndef NODETACH
+ /*
+ * Detach us from the terminal. May need an #ifndef GIZMO.
+ */
+ if (
+# ifdef DEBUG
+ !debug &&
+# endif /* DEBUG */
+ !nofork)
+ {
+# ifndef SYS_WINNT
+# ifdef HAVE_DAEMON
+ daemon(0, 0);
+# else /* not HAVE_DAEMON */
+ if (fork()) /* HMS: What about a -1? */
+ exit(0);
+
+ {
+#if !defined(F_CLOSEM)
+ u_long s;
+ int max_fd;
+#endif /* not F_CLOSEM */
+
+#if defined(F_CLOSEM)
+ /*
+ * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
+ * by Eric Agar (saves us from doing 32767 system
+ * calls)
+ */
+ if (fcntl(0, F_CLOSEM, 0) == -1)
+ msyslog(LOG_ERR, "ntpd: failed to close open files(): %m");
+#else /* not F_CLOSEM */
+
+# if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ max_fd = sysconf(_SC_OPEN_MAX);
+# else /* HAVE_SYSCONF && _SC_OPEN_MAX */
+ max_fd = getdtablesize();
+# endif /* HAVE_SYSCONF && _SC_OPEN_MAX */
+ for (s = 0; s < max_fd; s++)
+ (void) close((int)s);
+#endif /* not F_CLOSEM */
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+#ifdef SYS_DOMAINOS
+ {
+ uid_$t puid;
+ status_$t st;
+
+ proc2_$who_am_i(&puid);
+ proc2_$make_server(&puid, &st);
+ }
+#endif /* SYS_DOMAINOS */
+#if defined(HAVE_SETPGID) || defined(HAVE_SETSID)
+# ifdef HAVE_SETSID
+ if (setsid() == (pid_t)-1)
+ msyslog(LOG_ERR, "ntpd: setsid(): %m");
+# else
+ if (setpgid(0, 0) == -1)
+ msyslog(LOG_ERR, "ntpd: setpgid(): %m");
+# endif
+#else /* HAVE_SETPGID || HAVE_SETSID */
+ {
+# if defined(TIOCNOTTY)
+ int fid;
+
+ fid = open("/dev/tty", 2);
+ if (fid >= 0)
+ {
+ (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0);
+ (void) close(fid);
+ }
+# endif /* defined(TIOCNOTTY) */
+# ifdef HAVE_SETPGRP_0
+ (void) setpgrp();
+# else /* HAVE_SETPGRP_0 */
+ (void) setpgrp(0, getpid());
+# endif /* HAVE_SETPGRP_0 */
+ }
+#endif /* HAVE_SETPGID || HAVE_SETSID */
+#ifdef _AIX
+ /* Don't get killed by low-on-memory signal. */
+ sa.sa_handler = catch_danger;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ (void) sigaction(SIGDANGER, &sa, NULL);
+#endif /* _AIX */
+ }
+# endif /* not HAVE_DAEMON */
+# endif /* SYS_WINNT */
+ }
+# endif /* NODETACH */
+#endif /* VMS */
+
+ setup_logfile(); /* We lost any redirect when we daemonized */
+
+#ifdef SCO5_CLOCK
+ /*
+ * SCO OpenServer's system clock offers much more precise timekeeping
+ * on the base CPU than the other CPUs (for multiprocessor systems),
+ * so we must lock to the base CPU.
+ */
+ {
+ int fd = open("/dev/at1", O_RDONLY);
+ if (fd >= 0) {
+ int zero = 0;
+ if (ioctl(fd, ACPU_LOCK, &zero) < 0)
+ msyslog(LOG_ERR, "cannot lock to base CPU: %m");
+ close( fd );
+ } /* else ...
+ * If we can't open the device, this probably just isn't
+ * a multiprocessor system, so we're A-OK.
+ */
+ }
+#endif
+
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined(MCL_FUTURE)
+# ifdef HAVE_SETRLIMIT
+ /*
+ * Set the stack limit to something smaller, so that we don't lock a lot
+ * of unused stack memory.
+ */
+ {
+ struct rlimit rl;
+
+ /* HMS: must make the rlim_cur amount configurable */
+ if (getrlimit(RLIMIT_STACK, &rl) != -1
+ && (rl.rlim_cur = 50 * 4096) < rl.rlim_max)
+ {
+ if (setrlimit(RLIMIT_STACK, &rl) == -1)
+ {
+ msyslog(LOG_ERR,
+ "Cannot adjust stack limit for mlockall: %m");
+ }
+ }
+# ifdef RLIMIT_MEMLOCK
+ /*
+ * The default RLIMIT_MEMLOCK is very low on Linux systems.
+ * Unless we increase this limit malloc calls are likely to
+ * fail if we drop root privlege. To be useful the value
+ * has to be larger than the largest ntpd resident set size.
+ */
+ rl.rlim_cur = rl.rlim_max = 32*1024*1024;
+ if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1) {
+ msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m");
+ }
+# endif /* RLIMIT_MEMLOCK */
+ }
+# endif /* HAVE_SETRLIMIT */
+ /*
+ * lock the process into memory
+ */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0)
+ msyslog(LOG_ERR, "mlockall(): %m");
+#else /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
+# ifdef HAVE_PLOCK
+# ifdef PROCLOCK
+# ifdef _AIX
+ /*
+ * set the stack limit for AIX for plock().
+ * see get_aix_stack() for more info.
+ */
+ if (ulimit(SET_STACKLIM, (get_aix_stack() - 8*4096)) < 0)
+ {
+ msyslog(LOG_ERR,"Cannot adjust stack limit for plock on AIX: %m");
+ }
+# endif /* _AIX */
+ /*
+ * lock the process into memory
+ */
+ if (plock(PROCLOCK) < 0)
+ msyslog(LOG_ERR, "plock(PROCLOCK): %m");
+# else /* not PROCLOCK */
+# ifdef TXTLOCK
+ /*
+ * Lock text into ram
+ */
+ if (plock(TXTLOCK) < 0)
+ msyslog(LOG_ERR, "plock(TXTLOCK) error: %m");
+# else /* not TXTLOCK */
+ msyslog(LOG_ERR, "plock() - don't know what to lock!");
+# endif /* not TXTLOCK */
+# endif /* not PROCLOCK */
+# endif /* HAVE_PLOCK */
+#endif /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
+
+ /*
+ * Set up signals we pay attention to locally.
+ */
+#ifdef SIGDIE1
+ (void) signal_no_reset(SIGDIE1, finish);
+#endif /* SIGDIE1 */
+#ifdef SIGDIE2
+ (void) signal_no_reset(SIGDIE2, finish);
+#endif /* SIGDIE2 */
+#ifdef SIGDIE3
+ (void) signal_no_reset(SIGDIE3, finish);
+#endif /* SIGDIE3 */
+#ifdef SIGDIE4
+ (void) signal_no_reset(SIGDIE4, finish);
+#endif /* SIGDIE4 */
+
+#ifdef SIGBUS
+ (void) signal_no_reset(SIGBUS, finish);
+#endif /* SIGBUS */
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+# ifdef DEBUG
+ (void) signal_no_reset(MOREDEBUGSIG, moredebug);
+ (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
+# else
+ (void) signal_no_reset(MOREDEBUGSIG, no_debug);
+ (void) signal_no_reset(LESSDEBUGSIG, no_debug);
+# endif /* DEBUG */
+#endif /* !SYS_WINNT && !VMS */
+
+ /*
+ * Set up signals we should never pay attention to.
+ */
+#if defined SIGPIPE
+ (void) signal_no_reset(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+ /*
+ * Call the init_ routines to initialize the data structures.
+ *
+ * Exactly what command-line options are we expecting here?
+ */
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+#if defined (HAVE_IO_COMPLETION_PORT)
+ init_io_completion_port();
+#endif
+ init_lib();
+ init_request();
+ init_control();
+ init_peer();
+#ifdef REFCLOCK
+ init_refclock();
+#endif
+ set_process_priority();
+ init_proto(); /* Call at high priority */
+ init_io();
+ init_loopfilter();
+ mon_start(MON_ON); /* monitor on by default now */
+ /* turn off in config if unwanted */
+
+ /*
+ * Get the configuration. This is done in a separate module
+ * since this will definitely be different for the gizmo board.
+ */
+
+ getconfig(argc, argv);
+
+ loop_config(LOOP_DRIFTCOMP, old_drift / 1e6);
+#ifdef OPENSSL
+ crypto_setup();
+#endif /* OPENSSL */
+ initializing = 0;
+
+#ifdef HAVE_DROPROOT
+ if( droproot ) {
+ /* Drop super-user privileges and chroot now if the OS supports this */
+
+#ifdef HAVE_LINUX_CAPABILITIES
+ /* set flag: keep privileges accross setuid() call (we only really need cap_sys_time): */
+ if( prctl( PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L ) == -1 ) {
+ msyslog( LOG_ERR, "prctl( PR_SET_KEEPCAPS, 1L ) failed: %m" );
+ exit(-1);
+ }
+#else
+ /* we need a user to switch to */
+ if( user == NULL ) {
+ msyslog(LOG_ERR, "Need user name to drop root privileges (see -u flag!)" );
+ exit(-1);
+ }
+#endif /* HAVE_LINUX_CAPABILITIES */
+
+ if (user != NULL) {
+ if (isdigit((unsigned char)*user)) {
+ sw_uid = (uid_t)strtoul(user, &endp, 0);
+ if (*endp != '\0')
+ goto getuser;
+ } else {
+getuser:
+ if ((pw = getpwnam(user)) != NULL) {
+ sw_uid = pw->pw_uid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find user `%s'", user);
+ exit (-1);
+ }
+ }
+ }
+ if (group != NULL) {
+ if (isdigit((unsigned char)*group)) {
+ sw_gid = (gid_t)strtoul(group, &endp, 0);
+ if (*endp != '\0')
+ goto getgroup;
+ } else {
+getgroup:
+ if ((gr = getgrnam(group)) != NULL) {
+ sw_gid = gr->gr_gid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find group `%s'", group);
+ exit (-1);
+ }
+ }
+ }
+
+ if( chrootdir ) {
+ /* make sure cwd is inside the jail: */
+ if( chdir(chrootdir) ) {
+ msyslog(LOG_ERR, "Cannot chdir() to `%s': %m", chrootdir);
+ exit (-1);
+ }
+ if( chroot(chrootdir) ) {
+ msyslog(LOG_ERR, "Cannot chroot() to `%s': %m", chrootdir);
+ exit (-1);
+ }
+ }
+ if (group && setgid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (group && setegid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (user && setuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user);
+ exit (-1);
+ }
+ if (user && seteuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user);
+ exit (-1);
+ }
+
+#ifndef HAVE_LINUX_CAPABILITIES
+ /*
+ * for now assume that the privilege to bind to privileged ports
+ * is associated with running with uid 0 - should be refined on
+ * ports that allow binding to NTP_PORT with uid != 0
+ */
+ disable_dynamic_updates |= (sw_uid != 0); /* also notifies routing message listener */
+#endif
+
+ if (disable_dynamic_updates && interface_interval) {
+ interface_interval = 0;
+ msyslog(LOG_INFO, "running in unprivileged mode disables dynamic interface tracking");
+ }
+
+#ifdef HAVE_LINUX_CAPABILITIES
+ do {
+ /*
+ * We may be running under non-root uid now, but we still hold full root privileges!
+ * We drop all of them, except for the crucial one or two: cap_sys_time and
+ * cap_net_bind_service if doing dynamic interface tracking.
+ */
+ cap_t caps;
+ char *captext = interface_interval ?
+ "cap_sys_time,cap_net_bind_service=ipe" :
+ "cap_sys_time=ipe";
+ if( ! ( caps = cap_from_text( captext ) ) ) {
+ msyslog( LOG_ERR, "cap_from_text() failed: %m" );
+ exit(-1);
+ }
+ if( cap_set_proc( caps ) == -1 ) {
+ msyslog( LOG_ERR, "cap_set_proc() failed to drop root privileges: %m" );
+ exit(-1);
+ }
+ cap_free( caps );
+ } while(0);
+#endif /* HAVE_LINUX_CAPABILITIES */
+
+ } /* if( droproot ) */
+#endif /* HAVE_DROPROOT */
+
+ /*
+ * Report that we're up to any trappers
+ */
+ report_event(EVNT_SYSRESTART, (struct peer *)0);
+
+ /*
+ * Use select() on all on all input fd's for unlimited
+ * time. select() will terminate on SIGALARM or on the
+ * reception of input. Using select() means we can't do
+ * robust signal handling and we get a potential race
+ * between checking for alarms and doing the select().
+ * Mostly harmless, I think.
+ */
+ /* On VMS, I suspect that select() can't be interrupted
+ * by a "signal" either, so I take the easy way out and
+ * have select() time out after one second.
+ * System clock updates really aren't time-critical,
+ * and - lacking a hardware reference clock - I have
+ * yet to learn about anything else that is.
+ */
+#if defined(HAVE_IO_COMPLETION_PORT)
+
+ for (;;) {
+ int tot_full_recvbufs = GetReceivedBuffers();
+#else /* normal I/O */
+
+ BLOCK_IO_AND_ALARM();
+ was_alarmed = 0;
+ for (;;)
+ {
+# if !defined(HAVE_SIGNALED_IO)
+ extern fd_set activefds;
+ extern int maxactivefd;
+
+ fd_set rdfdes;
+ int nfound;
+# endif
+
+ if (alarm_flag) /* alarmed? */
+ {
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+
+ if (!was_alarmed && has_full_recv_buffer() == ISC_FALSE)
+ {
+ /*
+ * Nothing to do. Wait for something.
+ */
+# ifndef HAVE_SIGNALED_IO
+ rdfdes = activefds;
+# if defined(VMS) || defined(SYS_VXWORKS)
+ /* make select() wake up after one second */
+ {
+ struct timeval t1;
+
+ t1.tv_sec = 1; t1.tv_usec = 0;
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, &t1);
+ }
+# else
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+# endif /* VMS */
+ if (nfound > 0)
+ {
+ l_fp ts;
+
+ get_systime(&ts);
+
+ (void)input_handler(&ts);
+ }
+ else if (nfound == -1 && errno != EINTR)
+ netsyslog(LOG_ERR, "select() error: %m");
+# ifdef DEBUG
+ else if (debug > 5)
+ netsyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
+# endif /* DEBUG */
+# else /* HAVE_SIGNALED_IO */
+
+ wait_for_signal();
+# endif /* HAVE_SIGNALED_IO */
+ if (alarm_flag) /* alarmed? */
+ {
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ }
+
+ if (was_alarmed)
+ {
+ UNBLOCK_IO_AND_ALARM();
+ /*
+ * Out here, signals are unblocked. Call timer routine
+ * to process expiry.
+ */
+ timer();
+ was_alarmed = 0;
+ BLOCK_IO_AND_ALARM();
+ }
+
+#endif /* HAVE_IO_COMPLETION_PORT */
+
+#ifdef DEBUG_TIMING
+ {
+ l_fp pts;
+ l_fp tsa, tsb;
+ int bufcount = 0;
+
+ get_systime(&pts);
+ tsa = pts;
+#endif
+ rbuf = get_full_recv_buffer();
+ while (rbuf != NULL)
+ {
+ if (alarm_flag)
+ {
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ UNBLOCK_IO_AND_ALARM();
+
+ if (was_alarmed)
+ { /* avoid timer starvation during lengthy I/O handling */
+ timer();
+ was_alarmed = 0;
+ }
+
+ /*
+ * Call the data procedure to handle each received
+ * packet.
+ */
+ if (rbuf->receiver != NULL) /* This should always be true */
+ {
+#ifdef DEBUG_TIMING
+ l_fp dts = pts;
+
+ L_SUB(&dts, &rbuf->recv_time);
+ DPRINTF(2, ("processing timestamp delta %s (with prec. fuzz)\n", lfptoa(&dts, 9)));
+ collect_timing(rbuf, "buffer processing delay", 1, &dts);
+ bufcount++;
+#endif
+ (rbuf->receiver)(rbuf);
+ } else {
+ msyslog(LOG_ERR, "receive buffer corruption - receiver found to be NULL - ABORTING");
+ abort();
+ }
+
+ BLOCK_IO_AND_ALARM();
+ freerecvbuf(rbuf);
+ rbuf = get_full_recv_buffer();
+ }
+#ifdef DEBUG_TIMING
+ get_systime(&tsb);
+ L_SUB(&tsb, &tsa);
+ if (bufcount) {
+ collect_timing(NULL, "processing", bufcount, &tsb);
+ DPRINTF(2, ("processing time for %d buffers %s\n", bufcount, lfptoa(&tsb, 9)));
+ }
+ }
+#endif
+
+ /*
+ * Go around again
+ */
+ }
+ UNBLOCK_IO_AND_ALARM();
+ return 1;
+}
+
+
+#ifdef SIGDIE2
+/*
+ * finish - exit gracefully
+ */
+static RETSIGTYPE
+finish(
+ int sig
+ )
+{
+
+ msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig);
+ write_stats();
+#ifdef HAVE_DNSREGISTRATION
+ if (mdns != NULL)
+ DNSServiceRefDeallocate(mdns);
+#endif
+
+ switch (sig)
+ {
+# ifdef SIGBUS
+ case SIGBUS:
+ printf("\nfinish(SIGBUS)\n");
+ exit(0);
+# endif
+ case 0: /* Should never happen... */
+ return;
+ default:
+ exit(0);
+ }
+}
+#endif /* SIGDIE2 */
+
+
+#ifdef DEBUG
+#ifndef SYS_WINNT
+/*
+ * moredebug - increase debugging verbosity
+ */
+static RETSIGTYPE
+moredebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug < 255)
+ {
+ debug++;
+ msyslog(LOG_DEBUG, "debug raised to %d", debug);
+ }
+ errno = saved_errno;
+}
+
+/*
+ * lessdebug - decrease debugging verbosity
+ */
+static RETSIGTYPE
+lessdebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug > 0)
+ {
+ debug--;
+ msyslog(LOG_DEBUG, "debug lowered to %d", debug);
+ }
+ errno = saved_errno;
+}
+#endif
+#else /* not DEBUG */
+#ifndef SYS_WINNT
+/*
+ * no_debug - We don't do the debug here.
+ */
+static RETSIGTYPE
+no_debug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ msyslog(LOG_DEBUG, "ntpd not compiled for debugging (signal %d)", sig);
+ errno = saved_errno;
+}
+#endif /* not SYS_WINNT */
+#endif /* not DEBUG */
diff --git a/contrib/ntp/ntpd/ntpdbase-opts.def b/contrib/ntp/ntpd/ntpdbase-opts.def
new file mode 100644
index 0000000..852af45
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdbase-opts.def
@@ -0,0 +1,459 @@
+#include autogen-version.def
+
+test-main;
+
+flag = {
+ name = ipv4;
+ value = 4;
+ equivalence = ipv4;
+ descrip = "Force IPv4 DNS name resolution";
+ doc = <<- _EndOfDoc_
+ Force DNS resolution of following host names on the command line
+ to the IPv4 namespace.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = ipv6;
+ value = 6;
+ equivalence = ipv4;
+ descrip = "Force IPv6 DNS name resolution";
+ doc = <<- _EndOfDoc_
+ Force DNS resolution of following host names on the command line
+ to the IPv6 namespace.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = authreq;
+ value = a;
+ descrip = "Require crypto authentication";
+ flags-cant = authnoreq;
+ doc = <<- _EndOfDoc_
+ Require cryptographic authentication for broadcast client,
+ multicast client and symmetric passive associations.
+ This is the default.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = authnoreq;
+ value = A;
+ descrip = "Do not require crypto authentication";
+ flags-cant = authreq;
+ doc = <<- _EndOfDoc_
+ Do not require cryptographic authentication for broadcast client,
+ multicast client and symmetric passive associations.
+ This is almost never a good idea.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = bcastsync;
+ value = b;
+ descrip = "Allow us to sync to broadcast servers";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = simbroadcastdelay;
+ value = B;
+ arg-type = string;
+ descrip = "Simulator broadcast delay";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+flag = {
+ name = configfile;
+ value = c;
+ arg-type = string;
+ descrip = "configuration file name";
+ doc = <<- _EndOfDoc_
+ The name and path of the configuration file,
+ /etc/ntp.conf
+ by default.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = phasenoise;
+ value = C;
+ arg-type = string;
+ descrip = "Phase noise level";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+#include debug-opt.def
+
+flag = {
+ name = driftfile;
+ value = f;
+ arg-type = string;
+ descrip = "frequency drift file name";
+ doc = <<- _EndOfDoc_
+ The name and path of the frequency file,
+ /etc/ntp.drift
+ by default.
+ This is the same operation as the
+ driftfile driftfile
+ configuration specification in the
+ /etc/ntp.conf
+ file.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = panicgate;
+ value = g;
+ descrip = "Allow the first adjustment to be Big";
+ doc = <<- _EndOfDoc_
+ Normally,
+ ntpd
+ exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ ntpd
+ will exit with a message to the system log. This option can be used with the
+ -q
+ and
+ -x
+ options.
+ See the
+ tinker
+ configuration file directive for other options.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = simslew;
+ value = H;
+ arg-type = string;
+ descrip = "Simuator slew";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+flag = {
+ name = jaildir;
+ value = i;
+ arg-type = string;
+ descrip = "Jail directory";
+ doc = <<- _EndOfDoc_
+ Chroot the server to the directory
+ jaildir
+ .
+ This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges.
+ You may need to also specify a
+ -u
+ option.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = interface;
+ value = I;
+ arg-type = string;
+ descrip = "Listen on interface";
+ max = NOLIMIT;
+ arg-name = iface;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = keyfile;
+ value = k;
+ arg-type = string;
+ descrip = "path to symmetric keys";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the symmetric key file.
+ /etc/ntp.keys
+ is the default.
+ This is the same operation as the
+ keys keyfile
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = logfile;
+ value = l;
+ arg-type = string;
+ descrip = "path to the log file";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the log file.
+ The default is the system log file.
+ This is the same operation as the
+ logfile logfile
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = novirtualips;
+ value = L;
+ descrip = "Do not listen to virtual IPs";
+ doc = <<- _EndOfDoc_
+ Do not listen to virtual IPs. The default is to listen.
+ _EndOfDoc_;
+};
+
+flag = {
+ ifdef = SYS_WINNT;
+ name = modifymmtimer;
+ value = M;
+ descrip = "Modify Multimedia Timer (Windows only)";
+ doc = <<- _EndOfDoc_
+ Set the Windows Multimedia Timer to highest resolution.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = nofork;
+ value = n;
+ descrip = "Do not fork";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = nice;
+ value = N;
+ descrip = "Run at high priority";
+ doc = <<- _EndOfDoc_
+ To the extent permitted by the operating system, run
+ ntpd
+ at the highest priority.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = servertime;
+ value = O;
+ arg-type = string;
+ descrip = "Server time";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+flag = {
+ name = pidfile;
+ value = p;
+ arg-type = string;
+ descrip = "path to the PID file";
+ doc = <<- _EndOfDoc_
+ Specify the name and path of the file used to record
+ ntpd's
+ process ID.
+ This is the same operation as the
+ pidfile pidfile
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = priority;
+ value = P;
+ arg-type = number;
+ descrip = "Process priority";
+ doc = <<- _EndOfDoc_
+ To the extent permitted by the operating system, run
+ ntpd
+ at the specified
+ sched_setscheduler(SCHED_FIFO)
+ priority.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = quit;
+ value = q;
+ descrip = "Set the time and quit";
+ doc = <<- _EndOfDoc_
+ ntpd
+ will exit just after the first time the clock is set. This behavior mimics that of the
+ ntpdate
+ program, which is to be retired.
+ The
+ -g
+ and
+ -x
+ options can be used with this option.
+ Note: The kernel time discipline is disabled with this option.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = propagationdelay;
+ value = r;
+ arg-type = string;
+ descrip = "Broadcast/propagation delay";
+ doc = <<- _EndOfDoc_
+ Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = updateinterval;
+ value = U;
+ arg-type = number;
+ descrip = "interval in seconds between scans for new or dropped interfaces";
+ doc = <<- _EndOfDoc_
+ Give the time in seconds between two scans for new or dropped interfaces.
+ For systems with routing socket support the scans will be performed shortly after the interface change
+ has been detected by the system.
+ Use 0 to disable scanning.
+ _EndOfDoc_;
+};
+
+flag = {
+ name = statsdir;
+ value = s;
+ arg-type = string;
+ descrip = "Statistics file location";
+ doc = <<- _EndOfDoc_
+ Specify the directory path for files created by the statistics facility.
+ This is the same operation as the
+ statsdir statsdir
+ configuration file directive.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = endsimtime;
+ value = S;
+ arg-type = string;
+ descrip = "Simulation end time";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+flag = {
+ name = trustedkey;
+ value = t;
+ arg-type = string;
+ descrip = "Trusted key number";
+ max = NOLIMIT;
+ arg-name = tkey;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ Add a key number to the trusted key list.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = freqerr;
+ value = T;
+ arg-type = string;
+ descrip = "Simulation frequency error";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+#ifdef SIM
+flag = {
+ name = walknoise;
+ value = W;
+ arg-type = string;
+ descrip = "Simulation random walk noise";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+flag = {
+ name = user;
+ value = u;
+ arg-type = string;
+ descrip = "Run as userid (or userid:groupid)";
+ doc = <<- _EndOfDoc_
+ Specify a user, and optionally a group, to switch to.
+ This option is only available if the OS supports to run the server without full root privileges.
+ Currently, this option is supported under NetBSD (configure with
+ --enable-clockctl
+ ) and Linux (configure with
+ --enable-linuxcaps
+ ).
+ _EndOfDoc_;
+};
+
+flag = {
+ name = var;
+ value = v;
+ arg-type = string;
+ descrip = "make ARG an ntp variable (RW)";
+ max = NOLIMIT;
+ arg-name = nvar;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = dvar;
+ value = V;
+ arg-type = string;
+ descrip = "make ARG an ntp variable (RW|DEF)";
+ max = NOLIMIT;
+ arg-name = ndvar;
+ stack-arg;
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+
+flag = {
+ name = slew;
+ value = x;
+ descrip = "Slew up to 600 seconds";
+ doc = <<- _EndOfDoc_
+ Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+ This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+ Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+ Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+ This option can be used with the
+ -g
+ and
+ -q
+ options.
+ See the
+ tinker
+ configuration file directive for other options.
+ Note: The kernel time discipline is disabled with this option.
+ _EndOfDoc_;
+};
+
+#ifdef SIM
+flag = {
+ name = ndelay;
+ value = Y;
+ arg-type = string;
+ descrip = "Simulation network delay";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
+
+#ifdef SIM
+flag = {
+ name = pdelay;
+ value = Z;
+ arg-type = string;
+ descrip = "Simulation processing delay";
+ doc = <<- _EndOfDoc_
+ _EndOfDoc_;
+};
+#endif
diff --git a/contrib/ntp/ntpd/ntpdsim-opts.c b/contrib/ntp/ntpd/ntpdsim-opts.c
new file mode 100644
index 0000000..4f09c0f
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim-opts.c
@@ -0,0 +1,1262 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpdsim-opts.c)
+ *
+ * It has been AutoGen-ed Sunday August 17, 2008 at 05:20:14 AM EDT
+ * From the definitions ntpdsim-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 29:0:4 templates.
+ */
+
+/*
+ * This file was produced by an AutoOpts template. AutoOpts is a
+ * copyrighted work. This source file is not encumbered by AutoOpts
+ * licensing, but is provided under the licensing terms chosen by the
+ * ntpdsim author or copyright holder. AutoOpts is licensed under
+ * the terms of the LGPL. The redistributable library (``libopts'') is
+ * licensed under the terms of either the LGPL or, at the users discretion,
+ * the BSD license. See the AutoOpts and/or libopts sources for details.
+ *
+ * This source file is copyrighted and licensed under the following terms:
+ *
+ * ntpdsim copyright 1970-2008 David L. Mills and/or others - all rights reserved
+ *
+ * see html/copyright.html
+ */
+
+
+#include <limits.h>
+
+#define OPTION_CODE_COMPILE 1
+#include "ntpdsim-opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+tSCC zCopyright[] =
+ "ntpdsim copyright (c) 1970-2008 David L. Mills and/or others, all rights reserved";
+tSCC zCopyrightNotice[] =
+
+/* extracted from ../include/copyright.def near line 8 */
+"see html/copyright.html";
+extern tUsageProc optionUsage;
+
+/*
+ * global included definitions
+ */
+#ifdef __windows
+ extern int atoi(const char*);
+#else
+# include <stdlib.h>
+#endif
+
+#ifndef NULL
+# define NULL 0
+#endif
+#ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+/*
+ * Ipv4 option description:
+ */
+tSCC zIpv4Text[] =
+ "Force IPv4 DNS name resolution";
+tSCC zIpv4_NAME[] = "IPV4";
+tSCC zIpv4_Name[] = "ipv4";
+#define IPV4_FLAGS (OPTST_DISABLED)
+
+/*
+ * Ipv6 option description:
+ */
+tSCC zIpv6Text[] =
+ "Force IPv6 DNS name resolution";
+tSCC zIpv6_NAME[] = "IPV6";
+tSCC zIpv6_Name[] = "ipv6";
+#define IPV6_FLAGS (OPTST_DISABLED)
+
+/*
+ * Authreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+tSCC zAuthreqText[] =
+ "Require crypto authentication";
+tSCC zAuthreq_NAME[] = "AUTHREQ";
+tSCC zAuthreq_Name[] = "authreq";
+static const int
+ aAuthreqCantList[] = {
+ INDEX_OPT_AUTHNOREQ, NO_EQUIVALENT };
+#define AUTHREQ_FLAGS (OPTST_DISABLED)
+
+/*
+ * Authnoreq option description with
+ * "Must also have options" and "Incompatible options":
+ */
+tSCC zAuthnoreqText[] =
+ "Do not require crypto authentication";
+tSCC zAuthnoreq_NAME[] = "AUTHNOREQ";
+tSCC zAuthnoreq_Name[] = "authnoreq";
+static const int
+ aAuthnoreqCantList[] = {
+ INDEX_OPT_AUTHREQ, NO_EQUIVALENT };
+#define AUTHNOREQ_FLAGS (OPTST_DISABLED)
+
+/*
+ * Bcastsync option description:
+ */
+tSCC zBcastsyncText[] =
+ "Allow us to sync to broadcast servers";
+tSCC zBcastsync_NAME[] = "BCASTSYNC";
+tSCC zBcastsync_Name[] = "bcastsync";
+#define BCASTSYNC_FLAGS (OPTST_DISABLED)
+
+/*
+ * Simbroadcastdelay option description:
+ */
+tSCC zSimbroadcastdelayText[] =
+ "Simulator broadcast delay";
+tSCC zSimbroadcastdelay_NAME[] = "SIMBROADCASTDELAY";
+tSCC zSimbroadcastdelay_Name[] = "simbroadcastdelay";
+#define SIMBROADCASTDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Configfile option description:
+ */
+tSCC zConfigfileText[] =
+ "configuration file name";
+tSCC zConfigfile_NAME[] = "CONFIGFILE";
+tSCC zConfigfile_Name[] = "configfile";
+#define CONFIGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Phasenoise option description:
+ */
+tSCC zPhasenoiseText[] =
+ "Phase noise level";
+tSCC zPhasenoise_NAME[] = "PHASENOISE";
+tSCC zPhasenoise_Name[] = "phasenoise";
+#define PHASENOISE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Debug_Level option description:
+ */
+#ifdef DEBUG
+tSCC zDebug_LevelText[] =
+ "Increase output debug message level";
+tSCC zDebug_Level_NAME[] = "DEBUG_LEVEL";
+tSCC zDebug_Level_Name[] = "debug-level";
+#define DEBUG_LEVEL_FLAGS (OPTST_DISABLED)
+
+#else /* disable Debug_Level */
+#define VALUE_OPT_DEBUG_LEVEL NO_EQUIVALENT
+#define DEBUG_LEVEL_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zDebug_LevelText NULL
+#define zDebug_Level_NAME NULL
+#define zDebug_Level_Name NULL
+#endif /* DEBUG */
+
+/*
+ * Set_Debug_Level option description:
+ */
+#ifdef DEBUG
+tSCC zSet_Debug_LevelText[] =
+ "Set the output debug message level";
+tSCC zSet_Debug_Level_NAME[] = "SET_DEBUG_LEVEL";
+tSCC zSet_Debug_Level_Name[] = "set-debug-level";
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+#else /* disable Set_Debug_Level */
+#define VALUE_OPT_SET_DEBUG_LEVEL NO_EQUIVALENT
+#define SET_DEBUG_LEVEL_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zSet_Debug_LevelText NULL
+#define zSet_Debug_Level_NAME NULL
+#define zSet_Debug_Level_Name NULL
+#endif /* DEBUG */
+
+/*
+ * Driftfile option description:
+ */
+tSCC zDriftfileText[] =
+ "frequency drift file name";
+tSCC zDriftfile_NAME[] = "DRIFTFILE";
+tSCC zDriftfile_Name[] = "driftfile";
+#define DRIFTFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Panicgate option description:
+ */
+tSCC zPanicgateText[] =
+ "Allow the first adjustment to be Big";
+tSCC zPanicgate_NAME[] = "PANICGATE";
+tSCC zPanicgate_Name[] = "panicgate";
+#define PANICGATE_FLAGS (OPTST_DISABLED)
+
+/*
+ * Simslew option description:
+ */
+tSCC zSimslewText[] =
+ "Simuator slew";
+tSCC zSimslew_NAME[] = "SIMSLEW";
+tSCC zSimslew_Name[] = "simslew";
+#define SIMSLEW_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Jaildir option description:
+ */
+tSCC zJaildirText[] =
+ "Jail directory";
+tSCC zJaildir_NAME[] = "JAILDIR";
+tSCC zJaildir_Name[] = "jaildir";
+#define JAILDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Interface option description:
+ */
+tSCC zInterfaceText[] =
+ "Listen on interface";
+tSCC zInterface_NAME[] = "INTERFACE";
+tSCC zInterface_Name[] = "interface";
+#define INTERFACE_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Keyfile option description:
+ */
+tSCC zKeyfileText[] =
+ "path to symmetric keys";
+tSCC zKeyfile_NAME[] = "KEYFILE";
+tSCC zKeyfile_Name[] = "keyfile";
+#define KEYFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Logfile option description:
+ */
+tSCC zLogfileText[] =
+ "path to the log file";
+tSCC zLogfile_NAME[] = "LOGFILE";
+tSCC zLogfile_Name[] = "logfile";
+#define LOGFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Novirtualips option description:
+ */
+tSCC zNovirtualipsText[] =
+ "Do not listen to virtual IPs";
+tSCC zNovirtualips_NAME[] = "NOVIRTUALIPS";
+tSCC zNovirtualips_Name[] = "novirtualips";
+#define NOVIRTUALIPS_FLAGS (OPTST_DISABLED)
+
+/*
+ * Modifymmtimer option description:
+ */
+#ifdef SYS_WINNT
+tSCC zModifymmtimerText[] =
+ "Modify Multimedia Timer (Windows only)";
+tSCC zModifymmtimer_NAME[] = "MODIFYMMTIMER";
+tSCC zModifymmtimer_Name[] = "modifymmtimer";
+#define MODIFYMMTIMER_FLAGS (OPTST_DISABLED)
+
+#else /* disable Modifymmtimer */
+#define VALUE_OPT_MODIFYMMTIMER NO_EQUIVALENT
+#define MODIFYMMTIMER_FLAGS (OPTST_OMITTED | OPTST_NO_INIT)
+#define zModifymmtimerText NULL
+#define zModifymmtimer_NAME NULL
+#define zModifymmtimer_Name NULL
+#endif /* SYS_WINNT */
+
+/*
+ * Nofork option description:
+ */
+tSCC zNoforkText[] =
+ "Do not fork";
+tSCC zNofork_NAME[] = "NOFORK";
+tSCC zNofork_Name[] = "nofork";
+#define NOFORK_FLAGS (OPTST_DISABLED)
+
+/*
+ * Nice option description:
+ */
+tSCC zNiceText[] =
+ "Run at high priority";
+tSCC zNice_NAME[] = "NICE";
+tSCC zNice_Name[] = "nice";
+#define NICE_FLAGS (OPTST_DISABLED)
+
+/*
+ * Servertime option description:
+ */
+tSCC zServertimeText[] =
+ "Server time";
+tSCC zServertime_NAME[] = "SERVERTIME";
+tSCC zServertime_Name[] = "servertime";
+#define SERVERTIME_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Pidfile option description:
+ */
+tSCC zPidfileText[] =
+ "path to the PID file";
+tSCC zPidfile_NAME[] = "PIDFILE";
+tSCC zPidfile_Name[] = "pidfile";
+#define PIDFILE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Priority option description:
+ */
+tSCC zPriorityText[] =
+ "Process priority";
+tSCC zPriority_NAME[] = "PRIORITY";
+tSCC zPriority_Name[] = "priority";
+#define PRIORITY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/*
+ * Quit option description:
+ */
+tSCC zQuitText[] =
+ "Set the time and quit";
+tSCC zQuit_NAME[] = "QUIT";
+tSCC zQuit_Name[] = "quit";
+#define QUIT_FLAGS (OPTST_DISABLED)
+
+/*
+ * Propagationdelay option description:
+ */
+tSCC zPropagationdelayText[] =
+ "Broadcast/propagation delay";
+tSCC zPropagationdelay_NAME[] = "PROPAGATIONDELAY";
+tSCC zPropagationdelay_Name[] = "propagationdelay";
+#define PROPAGATIONDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Updateinterval option description:
+ */
+tSCC zUpdateintervalText[] =
+ "interval in seconds between scans for new or dropped interfaces";
+tSCC zUpdateinterval_NAME[] = "UPDATEINTERVAL";
+tSCC zUpdateinterval_Name[] = "updateinterval";
+#define UPDATEINTERVAL_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_NUMERIC))
+
+/*
+ * Statsdir option description:
+ */
+tSCC zStatsdirText[] =
+ "Statistics file location";
+tSCC zStatsdir_NAME[] = "STATSDIR";
+tSCC zStatsdir_Name[] = "statsdir";
+#define STATSDIR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Endsimtime option description:
+ */
+tSCC zEndsimtimeText[] =
+ "Simulation end time";
+tSCC zEndsimtime_NAME[] = "ENDSIMTIME";
+tSCC zEndsimtime_Name[] = "endsimtime";
+#define ENDSIMTIME_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Trustedkey option description:
+ */
+tSCC zTrustedkeyText[] =
+ "Trusted key number";
+tSCC zTrustedkey_NAME[] = "TRUSTEDKEY";
+tSCC zTrustedkey_Name[] = "trustedkey";
+#define TRUSTEDKEY_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Freqerr option description:
+ */
+tSCC zFreqerrText[] =
+ "Simulation frequency error";
+tSCC zFreqerr_NAME[] = "FREQERR";
+tSCC zFreqerr_Name[] = "freqerr";
+#define FREQERR_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Walknoise option description:
+ */
+tSCC zWalknoiseText[] =
+ "Simulation random walk noise";
+tSCC zWalknoise_NAME[] = "WALKNOISE";
+tSCC zWalknoise_Name[] = "walknoise";
+#define WALKNOISE_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * User option description:
+ */
+tSCC zUserText[] =
+ "Run as userid (or userid:groupid)";
+tSCC zUser_NAME[] = "USER";
+tSCC zUser_Name[] = "user";
+#define USER_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Var option description:
+ */
+tSCC zVarText[] =
+ "make ARG an ntp variable (RW)";
+tSCC zVar_NAME[] = "VAR";
+tSCC zVar_Name[] = "var";
+#define VAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Dvar option description:
+ */
+tSCC zDvarText[] =
+ "make ARG an ntp variable (RW|DEF)";
+tSCC zDvar_NAME[] = "DVAR";
+tSCC zDvar_Name[] = "dvar";
+#define DVAR_FLAGS (OPTST_DISABLED | OPTST_STACKED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Slew option description:
+ */
+tSCC zSlewText[] =
+ "Slew up to 600 seconds";
+tSCC zSlew_NAME[] = "SLEW";
+tSCC zSlew_Name[] = "slew";
+#define SLEW_FLAGS (OPTST_DISABLED)
+
+/*
+ * Ndelay option description:
+ */
+tSCC zNdelayText[] =
+ "Simulation network delay";
+tSCC zNdelay_NAME[] = "NDELAY";
+tSCC zNdelay_Name[] = "ndelay";
+#define NDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Pdelay option description:
+ */
+tSCC zPdelayText[] =
+ "Simulation processing delay";
+tSCC zPdelay_NAME[] = "PDELAY";
+tSCC zPdelay_Name[] = "pdelay";
+#define PDELAY_FLAGS (OPTST_DISABLED \
+ | OPTST_SET_ARGTYPE(OPARG_TYPE_STRING))
+
+/*
+ * Help/More_Help/Version option descriptions:
+ */
+tSCC zHelpText[] = "Display usage information and exit";
+tSCC zHelp_Name[] = "help";
+
+tSCC zMore_HelpText[] = "Extended usage information passed thru pager";
+tSCC zMore_Help_Name[] = "more-help";
+
+tSCC zVersionText[] = "Output version information and exit";
+tSCC zVersion_Name[] = "version";
+
+/*
+ * Save/Load_Opts option description:
+ */
+tSCC zSave_OptsText[] = "Save the option state to a config file";
+tSCC zSave_Opts_Name[] = "save-opts";
+
+tSCC zLoad_OptsText[] = "Load options from a config file";
+tSCC zLoad_Opts_NAME[] = "LOAD_OPTS";
+
+tSCC zNotLoad_Opts_Name[] = "no-load-opts";
+tSCC zNotLoad_Opts_Pfx[] = "no";
+#define zLoad_Opts_Name (zNotLoad_Opts_Name + 3)
+/*
+ * Declare option callback procedures
+ */
+#ifdef DEBUG
+ static tOptProc doOptSet_Debug_Level;
+#else /* not DEBUG */
+# define doOptSet_Debug_Level NULL
+#endif /* def/not DEBUG */
+#if defined(TEST_NTPDSIM_OPTS)
+/*
+ * Under test, omit argument processing, or call optionStackArg,
+ * if multiple copies are allowed.
+ */
+extern tOptProc
+ optionNumericVal, optionPagedUsage, optionStackArg,
+ optionVersionStderr;
+static tOptProc
+ doUsageOpt;
+
+/*
+ * #define map the "normal" callout procs to the test ones...
+ */
+#define SET_DEBUG_LEVEL_OPT_PROC optionStackArg
+
+
+#else /* NOT defined TEST_NTPDSIM_OPTS */
+/*
+ * When not under test, there are different procs to use
+ */
+extern tOptProc
+ optionNumericVal, optionPagedUsage, optionPrintVersion, optionStackArg;
+static tOptProc
+ doUsageOpt;
+
+/*
+ * #define map the "normal" callout procs
+ */
+#define SET_DEBUG_LEVEL_OPT_PROC doOptSet_Debug_Level
+
+#define SET_DEBUG_LEVEL_OPT_PROC doOptSet_Debug_Level
+#endif /* defined(TEST_NTPDSIM_OPTS) */
+#ifdef TEST_NTPDSIM_OPTS
+# define DOVERPROC optionVersionStderr
+#else
+# define DOVERPROC optionPrintVersion
+#endif /* TEST_NTPDSIM_OPTS */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Define the Ntpdsim Option Descriptions.
+ */
+static tOptDesc optDesc[ OPTION_CT ] = {
+ { /* entry idx, value */ 0, VALUE_OPT_IPV4,
+ /* equiv idx, value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV4_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zIpv4Text, zIpv4_NAME, zIpv4_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 1, VALUE_OPT_IPV6,
+ /* equiv idx, value */ NOLIMIT, NOLIMIT,
+ /* equivalenced to */ INDEX_OPT_IPV4,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ IPV6_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zIpv6Text, zIpv6_NAME, zIpv6_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equiv idx, value */ 2, VALUE_OPT_AUTHREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zAuthreqText, zAuthreq_NAME, zAuthreq_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equiv idx, value */ 3, VALUE_OPT_AUTHNOREQ,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ AUTHNOREQ_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, aAuthnoreqCantList,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zAuthnoreqText, zAuthnoreq_NAME, zAuthnoreq_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equiv idx, value */ 4, VALUE_OPT_BCASTSYNC,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ BCASTSYNC_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zBcastsyncText, zBcastsync_NAME, zBcastsync_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 5, VALUE_OPT_SIMBROADCASTDELAY,
+ /* equiv idx, value */ 5, VALUE_OPT_SIMBROADCASTDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SIMBROADCASTDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zSimbroadcastdelayText, zSimbroadcastdelay_NAME, zSimbroadcastdelay_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 6, VALUE_OPT_CONFIGFILE,
+ /* equiv idx, value */ 6, VALUE_OPT_CONFIGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ CONFIGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zConfigfileText, zConfigfile_NAME, zConfigfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 7, VALUE_OPT_PHASENOISE,
+ /* equiv idx, value */ 7, VALUE_OPT_PHASENOISE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PHASENOISE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPhasenoiseText, zPhasenoise_NAME, zPhasenoise_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 8, VALUE_OPT_DEBUG_LEVEL,
+ /* equiv idx, value */ 8, VALUE_OPT_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zDebug_LevelText, zDebug_Level_NAME, zDebug_Level_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 9, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equiv idx, value */ 9, VALUE_OPT_SET_DEBUG_LEVEL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ SET_DEBUG_LEVEL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ SET_DEBUG_LEVEL_OPT_PROC,
+ /* desc, NAME, name */ zSet_Debug_LevelText, zSet_Debug_Level_NAME, zSet_Debug_Level_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 10, VALUE_OPT_DRIFTFILE,
+ /* equiv idx, value */ 10, VALUE_OPT_DRIFTFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ DRIFTFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zDriftfileText, zDriftfile_NAME, zDriftfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 11, VALUE_OPT_PANICGATE,
+ /* equiv idx, value */ 11, VALUE_OPT_PANICGATE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PANICGATE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPanicgateText, zPanicgate_NAME, zPanicgate_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 12, VALUE_OPT_SIMSLEW,
+ /* equiv idx, value */ 12, VALUE_OPT_SIMSLEW,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SIMSLEW_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zSimslewText, zSimslew_NAME, zSimslew_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 13, VALUE_OPT_JAILDIR,
+ /* equiv idx, value */ 13, VALUE_OPT_JAILDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ JAILDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zJaildirText, zJaildir_NAME, zJaildir_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 14, VALUE_OPT_INTERFACE,
+ /* equiv idx, value */ 14, VALUE_OPT_INTERFACE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ INTERFACE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zInterfaceText, zInterface_NAME, zInterface_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 15, VALUE_OPT_KEYFILE,
+ /* equiv idx, value */ 15, VALUE_OPT_KEYFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ KEYFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zKeyfileText, zKeyfile_NAME, zKeyfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 16, VALUE_OPT_LOGFILE,
+ /* equiv idx, value */ 16, VALUE_OPT_LOGFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ LOGFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zLogfileText, zLogfile_NAME, zLogfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 17, VALUE_OPT_NOVIRTUALIPS,
+ /* equiv idx, value */ 17, VALUE_OPT_NOVIRTUALIPS,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOVIRTUALIPS_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNovirtualipsText, zNovirtualips_NAME, zNovirtualips_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 18, VALUE_OPT_MODIFYMMTIMER,
+ /* equiv idx, value */ 18, VALUE_OPT_MODIFYMMTIMER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ MODIFYMMTIMER_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zModifymmtimerText, zModifymmtimer_NAME, zModifymmtimer_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 19, VALUE_OPT_NOFORK,
+ /* equiv idx, value */ 19, VALUE_OPT_NOFORK,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NOFORK_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNoforkText, zNofork_NAME, zNofork_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 20, VALUE_OPT_NICE,
+ /* equiv idx, value */ 20, VALUE_OPT_NICE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NICE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNiceText, zNice_NAME, zNice_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 21, VALUE_OPT_SERVERTIME,
+ /* equiv idx, value */ 21, VALUE_OPT_SERVERTIME,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SERVERTIME_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zServertimeText, zServertime_NAME, zServertime_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 22, VALUE_OPT_PIDFILE,
+ /* equiv idx, value */ 22, VALUE_OPT_PIDFILE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PIDFILE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPidfileText, zPidfile_NAME, zPidfile_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 23, VALUE_OPT_PRIORITY,
+ /* equiv idx, value */ 23, VALUE_OPT_PRIORITY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PRIORITY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ zPriorityText, zPriority_NAME, zPriority_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 24, VALUE_OPT_QUIT,
+ /* equiv idx, value */ 24, VALUE_OPT_QUIT,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ QUIT_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zQuitText, zQuit_NAME, zQuit_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 25, VALUE_OPT_PROPAGATIONDELAY,
+ /* equiv idx, value */ 25, VALUE_OPT_PROPAGATIONDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PROPAGATIONDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPropagationdelayText, zPropagationdelay_NAME, zPropagationdelay_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 26, VALUE_OPT_UPDATEINTERVAL,
+ /* equiv idx, value */ 26, VALUE_OPT_UPDATEINTERVAL,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ UPDATEINTERVAL_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionNumericVal,
+ /* desc, NAME, name */ zUpdateintervalText, zUpdateinterval_NAME, zUpdateinterval_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 27, VALUE_OPT_STATSDIR,
+ /* equiv idx, value */ 27, VALUE_OPT_STATSDIR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ STATSDIR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zStatsdirText, zStatsdir_NAME, zStatsdir_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 28, VALUE_OPT_ENDSIMTIME,
+ /* equiv idx, value */ 28, VALUE_OPT_ENDSIMTIME,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ ENDSIMTIME_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zEndsimtimeText, zEndsimtime_NAME, zEndsimtime_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 29, VALUE_OPT_TRUSTEDKEY,
+ /* equiv idx, value */ 29, VALUE_OPT_TRUSTEDKEY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ TRUSTEDKEY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zTrustedkeyText, zTrustedkey_NAME, zTrustedkey_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 30, VALUE_OPT_FREQERR,
+ /* equiv idx, value */ 30, VALUE_OPT_FREQERR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ FREQERR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zFreqerrText, zFreqerr_NAME, zFreqerr_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 31, VALUE_OPT_WALKNOISE,
+ /* equiv idx, value */ 31, VALUE_OPT_WALKNOISE,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ WALKNOISE_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zWalknoiseText, zWalknoise_NAME, zWalknoise_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 32, VALUE_OPT_USER,
+ /* equiv idx, value */ 32, VALUE_OPT_USER,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ USER_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zUserText, zUser_NAME, zUser_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 33, VALUE_OPT_VAR,
+ /* equiv idx, value */ 33, VALUE_OPT_VAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ VAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zVarText, zVar_NAME, zVar_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 34, VALUE_OPT_DVAR,
+ /* equiv idx, value */ 34, VALUE_OPT_DVAR,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ DVAR_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionStackArg,
+ /* desc, NAME, name */ zDvarText, zDvar_NAME, zDvar_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 35, VALUE_OPT_SLEW,
+ /* equiv idx, value */ 35, VALUE_OPT_SLEW,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ SLEW_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zSlewText, zSlew_NAME, zSlew_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 36, VALUE_OPT_NDELAY,
+ /* equiv idx, value */ 36, VALUE_OPT_NDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ NDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zNdelayText, zNdelay_NAME, zNdelay_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ 37, VALUE_OPT_PDELAY,
+ /* equiv idx, value */ 37, VALUE_OPT_PDELAY,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ PDELAY_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zPdelayText, zPdelay_NAME, zPdelay_Name,
+ /* disablement strs */ NULL, NULL },
+
+#ifdef NO_OPTIONAL_OPT_ARGS
+# define VERSION_OPT_FLAGS OPTST_IMM | OPTST_NO_INIT
+#else
+# define VERSION_OPT_FLAGS OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) | \
+ OPTST_ARG_OPTIONAL | OPTST_IMM | OPTST_NO_INIT
+#endif
+
+ { /* entry idx, value */ INDEX_OPT_VERSION, VALUE_OPT_VERSION,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ VERSION_OPT_FLAGS, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ DOVERPROC,
+ /* desc, NAME, name */ zVersionText, NULL, zVersion_Name,
+ /* disablement strs */ NULL, NULL },
+
+#undef VERSION_OPT_FLAGS
+
+
+ { /* entry idx, value */ INDEX_OPT_HELP, VALUE_OPT_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ doUsageOpt,
+ /* desc, NAME, name */ zHelpText, NULL, zHelp_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_MORE_HELP, VALUE_OPT_MORE_HELP,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_IMM | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionPagedUsage,
+ /* desc, NAME, name */ zMore_HelpText, NULL, zMore_Help_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_SAVE_OPTS, VALUE_OPT_SAVE_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, 1, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING)
+ | OPTST_ARG_OPTIONAL | OPTST_NO_INIT, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ NULL,
+ /* desc, NAME, name */ zSave_OptsText, NULL, zSave_Opts_Name,
+ /* disablement strs */ NULL, NULL },
+
+ { /* entry idx, value */ INDEX_OPT_LOAD_OPTS, VALUE_OPT_LOAD_OPTS,
+ /* equiv idx value */ NO_EQUIVALENT, 0,
+ /* equivalenced to */ NO_EQUIVALENT,
+ /* min, max, act ct */ 0, NOLIMIT, 0,
+ /* opt state flags */ OPTST_SET_ARGTYPE(OPARG_TYPE_STRING) \
+ | OPTST_DISABLE_IMM, 0,
+ /* last opt argumnt */ { NULL },
+ /* arg list/cookie */ NULL,
+ /* must/cannot opts */ NULL, NULL,
+ /* option proc */ optionLoadOpt,
+ /* desc, NAME, name */ zLoad_OptsText, zLoad_Opts_NAME, zLoad_Opts_Name,
+ /* disablement strs */ zNotLoad_Opts_Name, zNotLoad_Opts_Pfx }
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Define the Ntpdsim Option Environment
+ */
+tSCC zPROGNAME[] = "NTPDSIM";
+tSCC zUsageTitle[] =
+"ntpdsim - NTP daemon simulation program - Ver. 4.2.4p5\n\
+USAGE: %s [ -<flag> [<val>] | --<name>[{=| }<val>] ]...\n";
+tSCC zRcName[] = ".ntprc";
+tSCC* apzHomeList[] = {
+ "$HOME",
+ ".",
+ NULL };
+
+tSCC zBugsAddr[] = "http://bugs.ntp.isc.org, bugs@ntp.org";
+#define zExplain NULL
+tSCC zDetail[] = "\n\n";
+tSCC zFullVersion[] = NTPDSIM_FULL_VERSION;
+/* extracted from /usr/local/gnu/share/autogen/optcode.tpl near line 408 */
+
+#if defined(ENABLE_NLS)
+# define OPTPROC_BASE OPTPROC_TRANSLATE
+ static tOptionXlateProc translate_option_strings;
+#else
+# define OPTPROC_BASE OPTPROC_NONE
+# define translate_option_strings NULL
+#endif /* ENABLE_NLS */
+
+tOptions ntpdsimOptions = {
+ OPTIONS_STRUCT_VERSION,
+ 0, NULL, /* original argc + argv */
+ ( OPTPROC_BASE
+ + OPTPROC_ERRSTOP
+ + OPTPROC_SHORTOPT
+ + OPTPROC_LONGOPT
+ + OPTPROC_NO_REQ_OPT
+ + OPTPROC_ENVIRON
+ + OPTPROC_NO_ARGS
+ + OPTPROC_HAS_IMMED ),
+ 0, NULL, /* current option index, current option */
+ NULL, NULL, zPROGNAME,
+ zRcName, zCopyright, zCopyrightNotice,
+ zFullVersion, apzHomeList, zUsageTitle,
+ zExplain, zDetail, optDesc,
+ zBugsAddr, /* address to send bugs to */
+ NULL, NULL, /* extensions/saved state */
+ optionUsage, /* usage procedure */
+ translate_option_strings, /* translation procedure */
+ /*
+ * Indexes to special options
+ */
+ { INDEX_OPT_MORE_HELP,
+ INDEX_OPT_SAVE_OPTS,
+ NO_EQUIVALENT /* index of '-#' option */,
+ NO_EQUIVALENT /* index of default opt */
+ },
+ 43 /* full option count */, 38 /* user option count */
+};
+
+/*
+ * Create the static procedure(s) declared above.
+ */
+static void
+doUsageOpt(
+ tOptions* pOptions,
+ tOptDesc* pOptDesc )
+{
+ USAGE( EXIT_SUCCESS );
+}
+
+#if ! defined(TEST_NTPDSIM_OPTS)
+
+/* * * * * * *
+ *
+ * For the set-debug-level option, when DEBUG is #define-d.
+ */
+#ifdef DEBUG
+static void
+doOptSet_Debug_Level(
+ tOptions* pOptions,
+ tOptDesc* pOptDesc )
+{
+ /* extracted from ../include/debug-opt.def, line 29 */
+DESC(DEBUG_LEVEL).optOccCt = atoi( pOptDesc->pzLastArg );
+}
+#endif /* defined DEBUG */
+
+#endif /* defined(TEST_NTPDSIM_OPTS) */
+
+/* extracted from /usr/local/gnu/share/autogen/optmain.tpl near line 92 */
+
+#if defined(TEST_NTPDSIM_OPTS) /* TEST MAIN PROCEDURE: */
+
+int
+main( int argc, char** argv )
+{
+ int res = EXIT_SUCCESS;
+ (void)optionProcess( &ntpdsimOptions, argc, argv );
+ {
+ void optionPutShell( tOptions* );
+ optionPutShell( &ntpdsimOptions );
+ }
+ return res;
+}
+#endif /* defined TEST_NTPDSIM_OPTS */
+/* extracted from /usr/local/gnu/share/autogen/optcode.tpl near line 514 */
+
+#if ENABLE_NLS
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <autoopts/usage-txt.h>
+
+static char* AO_gettext( char const* pz );
+static void coerce_it(void** s);
+
+static char*
+AO_gettext( char const* pz )
+{
+ char* pzRes;
+ if (pz == NULL)
+ return NULL;
+ pzRes = _(pz);
+ if (pzRes == pz)
+ return pzRes;
+ pzRes = strdup( pzRes );
+ if (pzRes == NULL) {
+ fputs( _("No memory for duping translated strings\n"), stderr );
+ exit( EXIT_FAILURE );
+ }
+ return pzRes;
+}
+
+static void coerce_it(void** s) { *s = AO_gettext(*s); }
+#define COERSION(_f) \
+ coerce_it((void*)&(ntpdsimOptions._f))
+
+/*
+ * This invokes the translation code (e.g. gettext(3)).
+ */
+static void
+translate_option_strings( void )
+{
+ /*
+ * Guard against re-translation. It won't work. The strings will have
+ * been changed by the first pass through this code. One shot only.
+ */
+ if (option_usage_text.field_ct == 0)
+ return;
+ /*
+ * Do the translations. The first pointer follows the field count field.
+ * The field count field is the size of a pointer.
+ */
+ {
+ char** ppz = (char**)(void*)&(option_usage_text);
+ int ix = option_usage_text.field_ct;
+
+ do {
+ ppz++;
+ *ppz = AO_gettext(*ppz);
+ } while (--ix > 0);
+ }
+ option_usage_text.field_ct = 0;
+
+ {
+ tOptDesc* pOD = ntpdsimOptions.pOptDesc;
+ int ix = ntpdsimOptions.optCt;
+
+ for (;;) {
+ pOD->pzText = AO_gettext(pOD->pzText);
+ pOD->pz_NAME = AO_gettext(pOD->pz_NAME);
+ pOD->pz_Name = AO_gettext(pOD->pz_Name);
+ pOD->pz_DisableName = AO_gettext(pOD->pz_DisableName);
+ pOD->pz_DisablePfx = AO_gettext(pOD->pz_DisablePfx);
+ if (--ix <= 0)
+ break;
+ pOD++;
+ }
+ }
+ COERSION(pzCopyright);
+ COERSION(pzCopyNotice);
+ COERSION(pzFullVersion);
+ COERSION(pzUsageTitle);
+ COERSION(pzExplain);
+ COERSION(pzDetail);
+}
+
+#endif /* ENABLE_NLS */
+
+#ifdef __cplusplus
+}
+#endif
+/* ntpdsim-opts.c ends here */
diff --git a/contrib/ntp/ntpd/ntpdsim-opts.def b/contrib/ntp/ntpd/ntpdsim-opts.def
new file mode 100644
index 0000000..093c325
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim-opts.def
@@ -0,0 +1,15 @@
+/* -*- Mode: Text -*- */
+
+autogen definitions options;
+
+#include copyright.def
+#include homerc.def
+
+prog-name = "ntpdsim";
+prog-title = "NTP daemon simulation program";
+
+#define SIM
+#include ntpdbase-opts.def
+
+detail = <<- _END_DETAIL
+ _END_DETAIL;
diff --git a/contrib/ntp/ntpd/ntpdsim-opts.h b/contrib/ntp/ntpd/ntpdsim-opts.h
new file mode 100644
index 0000000..1c63654
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim-opts.h
@@ -0,0 +1,422 @@
+/*
+ * EDIT THIS FILE WITH CAUTION (ntpdsim-opts.h)
+ *
+ * It has been AutoGen-ed Sunday August 17, 2008 at 05:20:13 AM EDT
+ * From the definitions ntpdsim-opts.def
+ * and the template file options
+ *
+ * Generated from AutoOpts 29:0:4 templates.
+ */
+
+/*
+ * This file was produced by an AutoOpts template. AutoOpts is a
+ * copyrighted work. This header file is not encumbered by AutoOpts
+ * licensing, but is provided under the licensing terms chosen by the
+ * ntpdsim author or copyright holder. AutoOpts is licensed under
+ * the terms of the LGPL. The redistributable library (``libopts'') is
+ * licensed under the terms of either the LGPL or, at the users discretion,
+ * the BSD license. See the AutoOpts and/or libopts sources for details.
+ *
+ * This source file is copyrighted and licensed under the following terms:
+ *
+ * ntpdsim copyright 1970-2008 David L. Mills and/or others - all rights reserved
+ *
+ * see html/copyright.html
+ */
+/*
+ * This file contains the programmatic interface to the Automated
+ * Options generated for the ntpdsim program.
+ * These macros are documented in the AutoGen info file in the
+ * "AutoOpts" chapter. Please refer to that doc for usage help.
+ */
+#ifndef AUTOOPTS_NTPDSIM_OPTS_H_GUARD
+#define AUTOOPTS_NTPDSIM_OPTS_H_GUARD
+#include "config.h"
+#include <autoopts/options.h>
+
+/*
+ * Ensure that the library used for compiling this generated header is at
+ * least as new as the version current when the header template was released
+ * (not counting patch version increments). Also ensure that the oldest
+ * tolerable version is at least as old as what was current when the header
+ * template was released.
+ */
+#define AO_TEMPLATE_VERSION 118784
+#if (AO_TEMPLATE_VERSION < OPTIONS_MINIMUM_VERSION) \
+ || (AO_TEMPLATE_VERSION > OPTIONS_STRUCT_VERSION)
+# error option template version mismatches autoopts/options.h header
+ Choke Me.
+#endif
+
+/*
+ * Enumeration of each option:
+ */
+typedef enum {
+ INDEX_OPT_IPV4 = 0,
+ INDEX_OPT_IPV6 = 1,
+ INDEX_OPT_AUTHREQ = 2,
+ INDEX_OPT_AUTHNOREQ = 3,
+ INDEX_OPT_BCASTSYNC = 4,
+ INDEX_OPT_SIMBROADCASTDELAY = 5,
+ INDEX_OPT_CONFIGFILE = 6,
+ INDEX_OPT_PHASENOISE = 7,
+ INDEX_OPT_DEBUG_LEVEL = 8,
+ INDEX_OPT_SET_DEBUG_LEVEL = 9,
+ INDEX_OPT_DRIFTFILE = 10,
+ INDEX_OPT_PANICGATE = 11,
+ INDEX_OPT_SIMSLEW = 12,
+ INDEX_OPT_JAILDIR = 13,
+ INDEX_OPT_INTERFACE = 14,
+ INDEX_OPT_KEYFILE = 15,
+ INDEX_OPT_LOGFILE = 16,
+ INDEX_OPT_NOVIRTUALIPS = 17,
+ INDEX_OPT_MODIFYMMTIMER = 18,
+ INDEX_OPT_NOFORK = 19,
+ INDEX_OPT_NICE = 20,
+ INDEX_OPT_SERVERTIME = 21,
+ INDEX_OPT_PIDFILE = 22,
+ INDEX_OPT_PRIORITY = 23,
+ INDEX_OPT_QUIT = 24,
+ INDEX_OPT_PROPAGATIONDELAY = 25,
+ INDEX_OPT_UPDATEINTERVAL = 26,
+ INDEX_OPT_STATSDIR = 27,
+ INDEX_OPT_ENDSIMTIME = 28,
+ INDEX_OPT_TRUSTEDKEY = 29,
+ INDEX_OPT_FREQERR = 30,
+ INDEX_OPT_WALKNOISE = 31,
+ INDEX_OPT_USER = 32,
+ INDEX_OPT_VAR = 33,
+ INDEX_OPT_DVAR = 34,
+ INDEX_OPT_SLEW = 35,
+ INDEX_OPT_NDELAY = 36,
+ INDEX_OPT_PDELAY = 37,
+ INDEX_OPT_VERSION = 38,
+ INDEX_OPT_HELP = 39,
+ INDEX_OPT_MORE_HELP = 40,
+ INDEX_OPT_SAVE_OPTS = 41,
+ INDEX_OPT_LOAD_OPTS = 42
+} teOptIndex;
+
+#define OPTION_CT 43
+#define NTPDSIM_VERSION "4.2.4p5"
+#define NTPDSIM_FULL_VERSION "ntpdsim - NTP daemon simulation program - Ver. 4.2.4p5"
+
+/*
+ * Interface defines for all options. Replace "n" with the UPPER_CASED
+ * option name (as in the teOptIndex enumeration above).
+ * e.g. HAVE_OPT( IPV4 )
+ */
+#define DESC(n) (ntpdsimOptions.pOptDesc[INDEX_OPT_## n])
+#define HAVE_OPT(n) (! UNUSED_OPT(& DESC(n)))
+#define OPT_ARG(n) (DESC(n).optArg.argString)
+#define STATE_OPT(n) (DESC(n).fOptState & OPTST_SET_MASK)
+#define COUNT_OPT(n) (DESC(n).optOccCt)
+#define ISSEL_OPT(n) (SELECTED_OPT(&DESC(n)))
+#define ISUNUSED_OPT(n) (UNUSED_OPT(& DESC(n)))
+#define ENABLED_OPT(n) (! DISABLED_OPT(& DESC(n)))
+#define STACKCT_OPT(n) (((tArgList*)(DESC(n).optCookie))->useCt)
+#define STACKLST_OPT(n) (((tArgList*)(DESC(n).optCookie))->apzArgs)
+#define CLEAR_OPT(n) STMTS( \
+ DESC(n).fOptState &= OPTST_PERSISTENT_MASK; \
+ if ( (DESC(n).fOptState & OPTST_INITENABLED) == 0) \
+ DESC(n).fOptState |= OPTST_DISABLED; \
+ DESC(n).optCookie = NULL )
+
+/*
+ * Make sure there are no #define name conflicts with the option names
+ */
+#ifndef NO_OPTION_NAME_WARNINGS
+# ifdef IPV4
+# warning undefining IPV4 due to option name conflict
+# undef IPV4
+# endif
+# ifdef IPV6
+# warning undefining IPV6 due to option name conflict
+# undef IPV6
+# endif
+# ifdef AUTHREQ
+# warning undefining AUTHREQ due to option name conflict
+# undef AUTHREQ
+# endif
+# ifdef AUTHNOREQ
+# warning undefining AUTHNOREQ due to option name conflict
+# undef AUTHNOREQ
+# endif
+# ifdef BCASTSYNC
+# warning undefining BCASTSYNC due to option name conflict
+# undef BCASTSYNC
+# endif
+# ifdef SIMBROADCASTDELAY
+# warning undefining SIMBROADCASTDELAY due to option name conflict
+# undef SIMBROADCASTDELAY
+# endif
+# ifdef CONFIGFILE
+# warning undefining CONFIGFILE due to option name conflict
+# undef CONFIGFILE
+# endif
+# ifdef PHASENOISE
+# warning undefining PHASENOISE due to option name conflict
+# undef PHASENOISE
+# endif
+# ifdef DEBUG_LEVEL
+# warning undefining DEBUG_LEVEL due to option name conflict
+# undef DEBUG_LEVEL
+# endif
+# ifdef SET_DEBUG_LEVEL
+# warning undefining SET_DEBUG_LEVEL due to option name conflict
+# undef SET_DEBUG_LEVEL
+# endif
+# ifdef DRIFTFILE
+# warning undefining DRIFTFILE due to option name conflict
+# undef DRIFTFILE
+# endif
+# ifdef PANICGATE
+# warning undefining PANICGATE due to option name conflict
+# undef PANICGATE
+# endif
+# ifdef SIMSLEW
+# warning undefining SIMSLEW due to option name conflict
+# undef SIMSLEW
+# endif
+# ifdef JAILDIR
+# warning undefining JAILDIR due to option name conflict
+# undef JAILDIR
+# endif
+# ifdef INTERFACE
+# warning undefining INTERFACE due to option name conflict
+# undef INTERFACE
+# endif
+# ifdef KEYFILE
+# warning undefining KEYFILE due to option name conflict
+# undef KEYFILE
+# endif
+# ifdef LOGFILE
+# warning undefining LOGFILE due to option name conflict
+# undef LOGFILE
+# endif
+# ifdef NOVIRTUALIPS
+# warning undefining NOVIRTUALIPS due to option name conflict
+# undef NOVIRTUALIPS
+# endif
+# ifdef MODIFYMMTIMER
+# warning undefining MODIFYMMTIMER due to option name conflict
+# undef MODIFYMMTIMER
+# endif
+# ifdef NOFORK
+# warning undefining NOFORK due to option name conflict
+# undef NOFORK
+# endif
+# ifdef NICE
+# warning undefining NICE due to option name conflict
+# undef NICE
+# endif
+# ifdef SERVERTIME
+# warning undefining SERVERTIME due to option name conflict
+# undef SERVERTIME
+# endif
+# ifdef PIDFILE
+# warning undefining PIDFILE due to option name conflict
+# undef PIDFILE
+# endif
+# ifdef PRIORITY
+# warning undefining PRIORITY due to option name conflict
+# undef PRIORITY
+# endif
+# ifdef QUIT
+# warning undefining QUIT due to option name conflict
+# undef QUIT
+# endif
+# ifdef PROPAGATIONDELAY
+# warning undefining PROPAGATIONDELAY due to option name conflict
+# undef PROPAGATIONDELAY
+# endif
+# ifdef UPDATEINTERVAL
+# warning undefining UPDATEINTERVAL due to option name conflict
+# undef UPDATEINTERVAL
+# endif
+# ifdef STATSDIR
+# warning undefining STATSDIR due to option name conflict
+# undef STATSDIR
+# endif
+# ifdef ENDSIMTIME
+# warning undefining ENDSIMTIME due to option name conflict
+# undef ENDSIMTIME
+# endif
+# ifdef TRUSTEDKEY
+# warning undefining TRUSTEDKEY due to option name conflict
+# undef TRUSTEDKEY
+# endif
+# ifdef FREQERR
+# warning undefining FREQERR due to option name conflict
+# undef FREQERR
+# endif
+# ifdef WALKNOISE
+# warning undefining WALKNOISE due to option name conflict
+# undef WALKNOISE
+# endif
+# ifdef USER
+# warning undefining USER due to option name conflict
+# undef USER
+# endif
+# ifdef VAR
+# warning undefining VAR due to option name conflict
+# undef VAR
+# endif
+# ifdef DVAR
+# warning undefining DVAR due to option name conflict
+# undef DVAR
+# endif
+# ifdef SLEW
+# warning undefining SLEW due to option name conflict
+# undef SLEW
+# endif
+# ifdef NDELAY
+# warning undefining NDELAY due to option name conflict
+# undef NDELAY
+# endif
+# ifdef PDELAY
+# warning undefining PDELAY due to option name conflict
+# undef PDELAY
+# endif
+#else /* NO_OPTION_NAME_WARNINGS */
+# undef IPV4
+# undef IPV6
+# undef AUTHREQ
+# undef AUTHNOREQ
+# undef BCASTSYNC
+# undef SIMBROADCASTDELAY
+# undef CONFIGFILE
+# undef PHASENOISE
+# undef DEBUG_LEVEL
+# undef SET_DEBUG_LEVEL
+# undef DRIFTFILE
+# undef PANICGATE
+# undef SIMSLEW
+# undef JAILDIR
+# undef INTERFACE
+# undef KEYFILE
+# undef LOGFILE
+# undef NOVIRTUALIPS
+# undef MODIFYMMTIMER
+# undef NOFORK
+# undef NICE
+# undef SERVERTIME
+# undef PIDFILE
+# undef PRIORITY
+# undef QUIT
+# undef PROPAGATIONDELAY
+# undef UPDATEINTERVAL
+# undef STATSDIR
+# undef ENDSIMTIME
+# undef TRUSTEDKEY
+# undef FREQERR
+# undef WALKNOISE
+# undef USER
+# undef VAR
+# undef DVAR
+# undef SLEW
+# undef NDELAY
+# undef PDELAY
+#endif /* NO_OPTION_NAME_WARNINGS */
+
+/*
+ * Interface defines for specific options.
+ */
+#define VALUE_OPT_IPV4 '4'
+#define WHICH_OPT_IPV4 (DESC(IPV4).optActualValue)
+#define WHICH_IDX_IPV4 (DESC(IPV4).optActualIndex)
+#define VALUE_OPT_IPV6 '6'
+#define VALUE_OPT_AUTHREQ 'a'
+#define VALUE_OPT_AUTHNOREQ 'A'
+#define VALUE_OPT_BCASTSYNC 'b'
+#define VALUE_OPT_SIMBROADCASTDELAY 'B'
+#define VALUE_OPT_CONFIGFILE 'c'
+#define VALUE_OPT_PHASENOISE 'C'
+#ifdef DEBUG
+#define VALUE_OPT_DEBUG_LEVEL 'd'
+#endif /* DEBUG */
+#ifdef DEBUG
+#define VALUE_OPT_SET_DEBUG_LEVEL 'D'
+#endif /* DEBUG */
+#define VALUE_OPT_DRIFTFILE 'f'
+#define VALUE_OPT_PANICGATE 'g'
+#define VALUE_OPT_SIMSLEW 'H'
+#define VALUE_OPT_JAILDIR 'i'
+#define VALUE_OPT_INTERFACE 'I'
+#define VALUE_OPT_KEYFILE 'k'
+#define VALUE_OPT_LOGFILE 'l'
+#define VALUE_OPT_NOVIRTUALIPS 'L'
+#ifdef SYS_WINNT
+#define VALUE_OPT_MODIFYMMTIMER 'M'
+#endif /* SYS_WINNT */
+#define VALUE_OPT_NOFORK 'n'
+#define VALUE_OPT_NICE 'N'
+#define VALUE_OPT_SERVERTIME 'O'
+#define VALUE_OPT_PIDFILE 'p'
+#define VALUE_OPT_PRIORITY 'P'
+#define OPT_VALUE_PRIORITY (DESC(PRIORITY).optArg.argInt)
+#define VALUE_OPT_QUIT 'q'
+#define VALUE_OPT_PROPAGATIONDELAY 'r'
+#define VALUE_OPT_UPDATEINTERVAL 'U'
+#define OPT_VALUE_UPDATEINTERVAL (DESC(UPDATEINTERVAL).optArg.argInt)
+#define VALUE_OPT_STATSDIR 's'
+#define VALUE_OPT_ENDSIMTIME 'S'
+#define VALUE_OPT_TRUSTEDKEY 't'
+#define VALUE_OPT_FREQERR 'T'
+#define VALUE_OPT_WALKNOISE 'W'
+#define VALUE_OPT_USER 'u'
+#define VALUE_OPT_VAR 'v'
+#define VALUE_OPT_DVAR 'V'
+#define VALUE_OPT_SLEW 'x'
+#define VALUE_OPT_NDELAY 'Y'
+#define VALUE_OPT_PDELAY 'Z'
+
+#define VALUE_OPT_VERSION 'v'
+#define VALUE_OPT_HELP '?'
+#define VALUE_OPT_MORE_HELP '!'
+#define VALUE_OPT_SAVE_OPTS '>'
+#define VALUE_OPT_LOAD_OPTS '<'
+#define SET_OPT_SAVE_OPTS(a) STMTS( \
+ DESC(SAVE_OPTS).fOptState &= OPTST_PERSISTENT_MASK; \
+ DESC(SAVE_OPTS).fOptState |= OPTST_SET; \
+ DESC(SAVE_OPTS).optArg.argString = (char const*)(a) )
+/*
+ * Interface defines not associated with particular options
+ */
+#define ERRSKIP_OPTERR STMTS( ntpdsimOptions.fOptSet &= ~OPTPROC_ERRSTOP )
+#define ERRSTOP_OPTERR STMTS( ntpdsimOptions.fOptSet |= OPTPROC_ERRSTOP )
+#define RESTART_OPT(n) STMTS( \
+ ntpdsimOptions.curOptIdx = (n); \
+ ntpdsimOptions.pzCurOpt = NULL )
+#define START_OPT RESTART_OPT(1)
+#define USAGE(c) (*ntpdsimOptions.pUsageProc)( &ntpdsimOptions, c )
+/* extracted from /usr/local/gnu/share/autogen/opthead.tpl near line 360 */
+
+/* * * * * *
+ *
+ * Declare the ntpdsim option descriptor.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern tOptions ntpdsimOptions;
+
+#ifndef _
+# if ENABLE_NLS
+# include <stdio.h>
+ static inline char* aoGetsText( char const* pz ) {
+ if (pz == NULL) return NULL;
+ return (char*)gettext( pz );
+ }
+# define _(s) aoGetsText(s)
+# else /* ENABLE_NLS */
+# define _(s) s
+# endif /* ENABLE_NLS */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* AUTOOPTS_NTPDSIM_OPTS_H_GUARD */
+/* ntpdsim-opts.h ends here */
diff --git a/contrib/ntp/ntpd/ntpdsim-opts.menu b/contrib/ntp/ntpd/ntpdsim-opts.menu
new file mode 100644
index 0000000..c787458
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim-opts.menu
@@ -0,0 +1 @@
+* ntpdsim Invocation:: Invoking ntpdsim
diff --git a/contrib/ntp/ntpd/ntpdsim-opts.texi b/contrib/ntp/ntpd/ntpdsim-opts.texi
new file mode 100644
index 0000000..49b5439
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim-opts.texi
@@ -0,0 +1,509 @@
+@node ntpdsim Invocation
+@section Invoking ntpdsim
+@pindex ntpdsim
+@cindex NTP daemon simulation program
+@ignore
+#
+# EDIT THIS FILE WITH CAUTION (ntpdsim-opts.texi)
+#
+# It has been AutoGen-ed Sunday August 17, 2008 at 05:20:17 AM EDT
+# From the definitions ntpdsim-opts.def
+# and the template file aginfo.tpl
+@end ignore
+This program has no explanation.
+
+
+
+This section was generated by @strong{AutoGen},
+the aginfo template and the option descriptions for the @command{ntpdsim} program. It documents the ntpdsim usage text and option meanings.
+
+This software is released under a specialized copyright license.
+
+@menu
+* ntpdsim usage:: ntpdsim usage help (-?)
+* ntpdsim authnoreq:: authnoreq option (-A)
+* ntpdsim authreq:: authreq option (-a)
+* ntpdsim bcastsync:: bcastsync option (-b)
+* ntpdsim configfile:: configfile option (-c)
+* ntpdsim debug-level:: debug-level option (-d)
+* ntpdsim driftfile:: driftfile option (-f)
+* ntpdsim dvar:: dvar option (-V)
+* ntpdsim endsimtime:: endsimtime option (-S)
+* ntpdsim freqerr:: freqerr option (-T)
+* ntpdsim interface:: interface option (-I)
+* ntpdsim ipv4:: ipv4 option (-4)
+* ntpdsim ipv6:: ipv6 option (-6)
+* ntpdsim jaildir:: jaildir option (-i)
+* ntpdsim keyfile:: keyfile option (-k)
+* ntpdsim logfile:: logfile option (-l)
+* ntpdsim modifymmtimer:: modifymmtimer option (-M)
+* ntpdsim ndelay:: ndelay option (-Y)
+* ntpdsim nice:: nice option (-N)
+* ntpdsim nofork:: nofork option (-n)
+* ntpdsim novirtualips:: novirtualips option (-L)
+* ntpdsim panicgate:: panicgate option (-g)
+* ntpdsim pdelay:: pdelay option (-Z)
+* ntpdsim phasenoise:: phasenoise option (-C)
+* ntpdsim pidfile:: pidfile option (-p)
+* ntpdsim priority:: priority option (-P)
+* ntpdsim propagationdelay:: propagationdelay option (-r)
+* ntpdsim quit:: quit option (-q)
+* ntpdsim servertime:: servertime option (-O)
+* ntpdsim set-debug-level:: set-debug-level option (-D)
+* ntpdsim simbroadcastdelay:: simbroadcastdelay option (-B)
+* ntpdsim simslew:: simslew option (-H)
+* ntpdsim slew:: slew option (-x)
+* ntpdsim statsdir:: statsdir option (-s)
+* ntpdsim trustedkey:: trustedkey option (-t)
+* ntpdsim updateinterval:: updateinterval option (-U)
+* ntpdsim user:: user option (-u)
+* ntpdsim var:: var option (-v)
+* ntpdsim walknoise:: walknoise option (-W)
+@end menu
+
+@node ntpdsim usage
+@subsection ntpdsim usage help (-?)
+@cindex ntpdsim usage
+
+This is the automatically generated usage text for ntpdsim:
+
+@exampleindent 0
+@example
+ntpdsim is unavailable - no --help
+@end example
+@exampleindent 4
+
+@node ntpdsim ipv4
+@subsection ipv4 option (-4)
+@cindex ntpdsim-ipv4
+
+This is the ``force ipv4 dns name resolution'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+is a member of the ipv4 class of options.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+
+@node ntpdsim ipv6
+@subsection ipv6 option (-6)
+@cindex ntpdsim-ipv6
+
+This is the ``force ipv6 dns name resolution'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+is a member of the ipv4 class of options.
+@end itemize
+
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+
+@node ntpdsim authreq
+@subsection authreq option (-a)
+@cindex ntpdsim-authreq
+
+This is the ``require crypto authentication'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authnoreq.
+@end itemize
+
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+
+@node ntpdsim authnoreq
+@subsection authnoreq option (-A)
+@cindex ntpdsim-authnoreq
+
+This is the ``do not require crypto authentication'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must not appear in combination with any of the following options:
+authreq.
+@end itemize
+
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+
+@node ntpdsim bcastsync
+@subsection bcastsync option (-b)
+@cindex ntpdsim-bcastsync
+
+This is the ``allow us to sync to broadcast servers'' option.
+
+
+@node ntpdsim simbroadcastdelay
+@subsection simbroadcastdelay option (-B)
+@cindex ntpdsim-simbroadcastdelay
+
+This is the ``simulator broadcast delay'' option.
+
+
+@node ntpdsim configfile
+@subsection configfile option (-c)
+@cindex ntpdsim-configfile
+
+This is the ``configuration file name'' option.
+The name and path of the configuration file,
+/etc/ntp.conf
+by default.
+
+@node ntpdsim phasenoise
+@subsection phasenoise option (-C)
+@cindex ntpdsim-phasenoise
+
+This is the ``phase noise level'' option.
+
+
+@node ntpdsim debug-level
+@subsection debug-level option (-d)
+@cindex ntpdsim-debug-level
+
+This is the ``increase output debug message level'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+must be compiled in by defining @code{DEBUG} during the compilation.
+@end itemize
+
+Increase the debugging message output level.
+
+@node ntpdsim set-debug-level
+@subsection set-debug-level option (-D)
+@cindex ntpdsim-set-debug-level
+
+This is the ``set the output debug message level'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@item
+must be compiled in by defining @code{DEBUG} during the compilation.
+@end itemize
+
+Set the output debugging level. Can be supplied multiple times,
+but each overrides the previous value(s).
+
+@node ntpdsim driftfile
+@subsection driftfile option (-f)
+@cindex ntpdsim-driftfile
+
+This is the ``frequency drift file name'' option.
+The name and path of the frequency file,
+/etc/ntp.drift
+by default.
+This is the same operation as the
+driftfile driftfile
+configuration specification in the
+/etc/ntp.conf
+file.
+
+@node ntpdsim panicgate
+@subsection panicgate option (-g)
+@cindex ntpdsim-panicgate
+
+This is the ``allow the first adjustment to be big'' option.
+Normally,
+ntpd
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ntpd
+will exit with a message to the system log. This option can be used with the
+-q
+and
+-x
+options.
+See the
+tinker
+configuration file directive for other options.
+
+@node ntpdsim simslew
+@subsection simslew option (-H)
+@cindex ntpdsim-simslew
+
+This is the ``simuator slew'' option.
+
+
+@node ntpdsim jaildir
+@subsection jaildir option (-i)
+@cindex ntpdsim-jaildir
+
+This is the ``jail directory'' option.
+Chroot the server to the directory
+jaildir
+.
+This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges.
+You may need to also specify a
+-u
+option.
+
+@node ntpdsim interface
+@subsection interface option (-I)
+@cindex ntpdsim-interface
+
+This is the ``listen on interface'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpdsim keyfile
+@subsection keyfile option (-k)
+@cindex ntpdsim-keyfile
+
+This is the ``path to symmetric keys'' option.
+Specify the name and path of the symmetric key file.
+/etc/ntp.keys
+is the default.
+This is the same operation as the
+keys keyfile
+configuration file directive.
+
+@node ntpdsim logfile
+@subsection logfile option (-l)
+@cindex ntpdsim-logfile
+
+This is the ``path to the log file'' option.
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+logfile logfile
+configuration file directive.
+
+@node ntpdsim novirtualips
+@subsection novirtualips option (-L)
+@cindex ntpdsim-novirtualips
+
+This is the ``do not listen to virtual ips'' option.
+Do not listen to virtual IPs. The default is to listen.
+
+@node ntpdsim modifymmtimer
+@subsection modifymmtimer option (-M)
+@cindex ntpdsim-modifymmtimer
+
+This is the ``modify multimedia timer (windows only)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+must be compiled in by defining @code{SYS_WINNT} during the compilation.
+@end itemize
+
+Set the Windows Multimedia Timer to highest resolution.
+
+@node ntpdsim nofork
+@subsection nofork option (-n)
+@cindex ntpdsim-nofork
+
+This is the ``do not fork'' option.
+
+
+@node ntpdsim nice
+@subsection nice option (-N)
+@cindex ntpdsim-nice
+
+This is the ``run at high priority'' option.
+To the extent permitted by the operating system, run
+ntpd
+at the highest priority.
+
+@node ntpdsim servertime
+@subsection servertime option (-O)
+@cindex ntpdsim-servertime
+
+This is the ``server time'' option.
+
+
+@node ntpdsim pidfile
+@subsection pidfile option (-p)
+@cindex ntpdsim-pidfile
+
+This is the ``path to the pid file'' option.
+Specify the name and path of the file used to record
+ntpd's
+process ID.
+This is the same operation as the
+pidfile pidfile
+configuration file directive.
+
+@node ntpdsim priority
+@subsection priority option (-P)
+@cindex ntpdsim-priority
+
+This is the ``process priority'' option.
+To the extent permitted by the operating system, run
+ntpd
+at the specified
+sched_setscheduler(SCHED_FIFO)
+priority.
+
+@node ntpdsim quit
+@subsection quit option (-q)
+@cindex ntpdsim-quit
+
+This is the ``set the time and quit'' option.
+ntpd
+will exit just after the first time the clock is set. This behavior mimics that of the
+ntpdate
+program, which is to be retired.
+The
+-g
+and
+-x
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+
+@node ntpdsim propagationdelay
+@subsection propagationdelay option (-r)
+@cindex ntpdsim-propagationdelay
+
+This is the ``broadcast/propagation delay'' option.
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+
+@node ntpdsim updateinterval
+@subsection updateinterval option (-U)
+@cindex ntpdsim-updateinterval
+
+This is the ``interval in seconds between scans for new or dropped interfaces'' option.
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning.
+
+@node ntpdsim statsdir
+@subsection statsdir option (-s)
+@cindex ntpdsim-statsdir
+
+This is the ``statistics file location'' option.
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+statsdir statsdir
+configuration file directive.
+
+@node ntpdsim endsimtime
+@subsection endsimtime option (-S)
+@cindex ntpdsim-endsimtime
+
+This is the ``simulation end time'' option.
+
+
+@node ntpdsim trustedkey
+@subsection trustedkey option (-t)
+@cindex ntpdsim-trustedkey
+
+This is the ``trusted key number'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+Add a key number to the trusted key list.
+
+@node ntpdsim freqerr
+@subsection freqerr option (-T)
+@cindex ntpdsim-freqerr
+
+This is the ``simulation frequency error'' option.
+
+
+@node ntpdsim walknoise
+@subsection walknoise option (-W)
+@cindex ntpdsim-walknoise
+
+This is the ``simulation random walk noise'' option.
+
+
+@node ntpdsim user
+@subsection user option (-u)
+@cindex ntpdsim-user
+
+This is the ``run as userid (or userid:groupid)'' option.
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports to run the server without full root privileges.
+Currently, this option is supported under NetBSD (configure with
+--enable-clockctl
+) and Linux (configure with
+--enable-linuxcaps
+).
+
+@node ntpdsim var
+@subsection var option (-v)
+@cindex ntpdsim-var
+
+This is the ``make arg an ntp variable (rw)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpdsim dvar
+@subsection dvar option (-V)
+@cindex ntpdsim-dvar
+
+This is the ``make arg an ntp variable (rw|def)'' option.
+
+This option has some usage constraints. It:
+@itemize @bullet
+@item
+may appear an unlimited number of times.
+@end itemize
+
+
+
+@node ntpdsim slew
+@subsection slew option (-x)
+@cindex ntpdsim-slew
+
+This is the ``slew up to 600 seconds'' option.
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+-g
+and
+-q
+options.
+See the
+tinker
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+
+@node ntpdsim ndelay
+@subsection ndelay option (-Y)
+@cindex ntpdsim-ndelay
+
+This is the ``simulation network delay'' option.
+
+
+@node ntpdsim pdelay
+@subsection pdelay option (-Z)
+@cindex ntpdsim-pdelay
+
+This is the ``simulation processing delay'' option.
+
diff --git a/contrib/ntp/ntpd/ntpdsim.1 b/contrib/ntp/ntpd/ntpdsim.1
new file mode 100644
index 0000000..2a7ea46
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpdsim.1
@@ -0,0 +1,357 @@
+.TH NTPDSIM 1 2008-08-17 "( 4.2.4p5)" "Programmer's Manual"
+.\" EDIT THIS FILE WITH CAUTION (ntpdsim.1)
+.\"
+.\" It has been AutoGen-ed Sunday August 17, 2008 at 05:20:16 AM EDT
+.\" From the definitions ntpdsim-opts.def
+.\" and the template file agman1.tpl
+.\"
+.SH NAME
+ntpdsim \- NTP daemon simulation program
+.SH SYNOPSIS
+.B ntpdsim
+.\" Mixture of short (flag) options and long options
+.RB [ \-\fIflag\fP " [\fIvalue\fP]]... [" \--\fIopt-name\fP " [[=| ]\fIvalue\fP]]..."
+.PP
+All arguments must be options.
+.SH "DESCRIPTION"
+This manual page documents, briefly, the \fBntpdsim\fP command.
+
+.SH OPTIONS
+.TP
+.BR \-4 ", " \--ipv4
+Force IPv4 DNS name resolution.
+This option is a member of the ipv4 class of options.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv4 namespace.
+.TP
+.BR \-6 ", " \--ipv6
+Force IPv6 DNS name resolution.
+This option is a member of the ipv4 class of options.
+.sp
+Force DNS resolution of following host names on the command line
+to the IPv6 namespace.
+.TP
+.BR \-a ", " \--authreq
+Require crypto authentication.
+This option must not appear in combination with any of the following options:
+authnoreq.
+.sp
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.TP
+.BR \-A ", " \--authnoreq
+Do not require crypto authentication.
+This option must not appear in combination with any of the following options:
+authreq.
+.sp
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.TP
+.BR \-b ", " \--bcastsync
+Allow us to sync to broadcast servers.
+.sp
+
+.TP
+.BR \-B " \fIstring\fP, " \--simbroadcastdelay "=" \fIstring\fP
+Simulator broadcast delay.
+.sp
+
+.TP
+.BR \-c " \fIstring\fP, " \--configfile "=" \fIstring\fP
+configuration file name.
+.sp
+The name and path of the configuration file,
+/etc/ntp.conf
+by default.
+.TP
+.BR \-C " \fIstring\fP, " \--phasenoise "=" \fIstring\fP
+Phase noise level.
+.sp
+
+.TP
+.BR \-d ", " \--debug-level
+Increase output debug message level.
+This option may appear an unlimited number of times.
+.sp
+Increase the debugging message output level.
+.TP
+.BR \-D " \fIstring\fP, " \--set-debug-level "=" \fIstring\fP
+Set the output debug message level.
+This option may appear an unlimited number of times.
+.sp
+Set the output debugging level. Can be supplied multiple times,
+but each overrides the previous value(s).
+.TP
+.BR \-f " \fIstring\fP, " \--driftfile "=" \fIstring\fP
+frequency drift file name.
+.sp
+The name and path of the frequency file,
+/etc/ntp.drift
+by default.
+This is the same operation as the
+driftfile driftfile
+configuration specification in the
+/etc/ntp.conf
+file.
+.TP
+.BR \-g ", " \--panicgate
+Allow the first adjustment to be Big.
+.sp
+Normally,
+ntpd
+exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default. This option allows the time to be set to any value without restriction; however, this can happen only once. If the threshold is exceeded after that,
+ntpd
+will exit with a message to the system log. This option can be used with the
+-q
+and
+-x
+options.
+See the
+tinker
+configuration file directive for other options.
+.TP
+.BR \-H " \fIstring\fP, " \--simslew "=" \fIstring\fP
+Simuator slew.
+.sp
+
+.TP
+.BR \-i " \fIstring\fP, " \--jaildir "=" \fIstring\fP
+Jail directory.
+.sp
+Chroot the server to the directory
+jaildir
+.
+This option also implies that the server attempts to drop root privileges at startup (otherwise, chroot gives very little additional security), and it is only available if the OS supports to run the server without full root privileges.
+You may need to also specify a
+-u
+option.
+.TP
+.BR \-I " \fIiface\fP, " \--interface "=" \fIiface\fP
+Listen on interface.
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-k " \fIstring\fP, " \--keyfile "=" \fIstring\fP
+path to symmetric keys.
+.sp
+Specify the name and path of the symmetric key file.
+/etc/ntp.keys
+is the default.
+This is the same operation as the
+keys keyfile
+configuration file directive.
+.TP
+.BR \-l " \fIstring\fP, " \--logfile "=" \fIstring\fP
+path to the log file.
+.sp
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+logfile logfile
+configuration file directive.
+.TP
+.BR \-L ", " \--novirtualips
+Do not listen to virtual IPs.
+.sp
+Do not listen to virtual IPs. The default is to listen.
+.TP
+.BR \-M ", " \--modifymmtimer
+Modify Multimedia Timer (Windows only).
+.sp
+Set the Windows Multimedia Timer to highest resolution.
+.TP
+.BR \-n ", " \--nofork
+Do not fork.
+.sp
+
+.TP
+.BR \-N ", " \--nice
+Run at high priority.
+.sp
+To the extent permitted by the operating system, run
+ntpd
+at the highest priority.
+.TP
+.BR \-O " \fIstring\fP, " \--servertime "=" \fIstring\fP
+Server time.
+.sp
+
+.TP
+.BR \-p " \fIstring\fP, " \--pidfile "=" \fIstring\fP
+path to the PID file.
+.sp
+Specify the name and path of the file used to record
+ntpd's
+process ID.
+This is the same operation as the
+pidfile pidfile
+configuration file directive.
+.TP
+.BR \-P " \fInumber\fP, " \--priority "=" \fInumber\fP
+Process priority.
+This option takes an integer number as its argument.
+.sp
+To the extent permitted by the operating system, run
+ntpd
+at the specified
+sched_setscheduler(SCHED_FIFO)
+priority.
+.TP
+.BR \-q ", " \--quit
+Set the time and quit.
+.sp
+ntpd
+will exit just after the first time the clock is set. This behavior mimics that of the
+ntpdate
+program, which is to be retired.
+The
+-g
+and
+-x
+options can be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.BR \-r " \fIstring\fP, " \--propagationdelay "=" \fIstring\fP
+Broadcast/propagation delay.
+.sp
+Specify the default propagation delay from the broadcast/multicast server to this client. This is necessary only if the delay cannot be computed automatically by the protocol.
+.TP
+.BR \-U " \fInumber\fP, " \--updateinterval "=" \fInumber\fP
+interval in seconds between scans for new or dropped interfaces.
+This option takes an integer number as its argument.
+.sp
+Give the time in seconds between two scans for new or dropped interfaces.
+For systems with routing socket support the scans will be performed shortly after the interface change
+has been detected by the system.
+Use 0 to disable scanning.
+.TP
+.BR \-s " \fIstring\fP, " \--statsdir "=" \fIstring\fP
+Statistics file location.
+.sp
+Specify the directory path for files created by the statistics facility.
+This is the same operation as the
+statsdir statsdir
+configuration file directive.
+.TP
+.BR \-S " \fIstring\fP, " \--endsimtime "=" \fIstring\fP
+Simulation end time.
+.sp
+
+.TP
+.BR \-t " \fItkey\fP, " \--trustedkey "=" \fItkey\fP
+Trusted key number.
+This option may appear an unlimited number of times.
+.sp
+Add a key number to the trusted key list.
+.TP
+.BR \-T " \fIstring\fP, " \--freqerr "=" \fIstring\fP
+Simulation frequency error.
+.sp
+
+.TP
+.BR \-W " \fIstring\fP, " \--walknoise "=" \fIstring\fP
+Simulation random walk noise.
+.sp
+
+.TP
+.BR \-u " \fIstring\fP, " \--user "=" \fIstring\fP
+Run as userid (or userid:groupid).
+.sp
+Specify a user, and optionally a group, to switch to.
+This option is only available if the OS supports to run the server without full root privileges.
+Currently, this option is supported under NetBSD (configure with
+--enable-clockctl
+) and Linux (configure with
+--enable-linuxcaps
+).
+.TP
+.BR \-v " \fInvar\fP, " \--var "=" \fInvar\fP
+make ARG an ntp variable (RW).
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-V " \fIndvar\fP, " \--dvar "=" \fIndvar\fP
+make ARG an ntp variable (RW|DEF).
+This option may appear an unlimited number of times.
+.sp
+
+.TP
+.BR \-x ", " \--slew
+Slew up to 600 seconds.
+.sp
+Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold.
+This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s, each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+-g
+and
+-q
+options.
+See the
+tinker
+configuration file directive for other options.
+Note: The kernel time discipline is disabled with this option.
+.TP
+.BR \-Y " \fIstring\fP, " \--ndelay "=" \fIstring\fP
+Simulation network delay.
+.sp
+
+.TP
+.BR \-Z " \fIstring\fP, " \--pdelay "=" \fIstring\fP
+Simulation processing delay.
+.sp
+
+.TP
+.BR \-? , " \--help"
+Display usage information and exit.
+.TP
+.BR \-! , " \--more-help"
+Extended usage information passed thru pager.
+.TP
+.BR \-> " [\fIrcfile\fP]," " \--save-opts" "[=\fIrcfile\fP]"
+Save the option state to \fIrcfile\fP. The default is the \fIlast\fP
+configuration file listed in the \fBOPTION PRESETS\fP section, below.
+.TP
+.BR \-< " \fIrcfile\fP," " \--load-opts" "=\fIrcfile\fP," " \--no-load-opts"
+Load options from \fIrcfile\fP.
+The \fIno-load-opts\fP form will disable the loading
+of earlier RC/INI files. \fI--no-load-opts\fP is handled early,
+out of order.
+.TP
+.BR \-v " [{\fIv|c|n\fP}]," " \--version" "[=\fI{v|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.
+.SH OPTION PRESETS
+Any option that is not marked as \fInot presettable\fP may be preset
+by loading values from configuration ("RC" or ".INI") file(s) and values from
+environment variables named:
+.nf
+ \fBNTPDSIM_<option-name>\fP or \fBNTPDSIM\fP
+.fi
+.aj
+The environmental presets take precedence (are processed later than)
+the configuration files.
+The \fIhomerc\fP files are "\fI$HOME\fP", and "\fI.\fP".
+If any of these are directories, then the file \fI.ntprc\fP
+is searched for within those directories.
+.SH AUTHOR
+David L. Mills and/or others
+.br
+Please send bug reports to: http://bugs.ntp.isc.org, bugs@ntp.org
+
+.PP
+.nf
+.na
+see html/copyright.html
+.fi
+.ad
+.PP
+This manual page was \fIAutoGen\fP-erated from the \fBntpdsim\fP
+option definitions.
diff --git a/contrib/ntp/ntpd/ntpsim.c b/contrib/ntp/ntpd/ntpsim.c
new file mode 100644
index 0000000..d5ed587
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpsim.c
@@ -0,0 +1,371 @@
+/*
+ * NTP simulator engine - Harish Nair
+ * University of Delaware, 2001
+ */
+#include "ntpd.h"
+#include "ntpsim.h"
+#include "ntpdsim-opts.h"
+
+/*
+ * Defines...
+ */
+#define SIM_TIME 86400 /* end simulation time */
+#define NET_DLY .001 /* network delay */
+#define PROC_DLY .001 /* processing delay */
+#define BEEP_DLY 3600 /* beep interval (s) */
+#define SLEW 500e-6 /* correction rate (PPM) */
+
+/*
+ * Function pointers
+ */
+void (*funcPtr[]) (Node *, Event) = {
+ &ndbeep, &ndeclk, &ntptmr, &netpkt
+};
+
+
+/*
+ * ntpsim - initialize global variables and event queue and start
+ */
+int
+ntpsim(
+ int argc,
+ char *argv[]
+ )
+{
+ Event e;
+ double maxtime;
+ struct timeval seed;
+
+ /*
+ * Initialize the global node
+ */
+ ntp_node.time = 0; /* simulation time */
+ ntp_node.sim_time = SIM_TIME; /* end simulation time (-S) */
+ ntp_node.ntp_time = 0; /* client disciplined time */
+ ntp_node.adj = 0; /* remaining time correction */
+ ntp_node.slew = SLEW; /* correction rate (-H) */
+
+ ntp_node.clk_time = 0; /* server time (-O) */
+ ntp_node.ferr = 0; /* frequency error (-T) */
+ ntp_node.fnse = 0; /* random walk noise (-W) */
+ ntp_node.ndly = NET_DLY; /* network delay (-Y) */
+ ntp_node.snse = 0; /* phase noise (-C) */
+ ntp_node.pdly = PROC_DLY; /* processing delay (-Z) */
+ ntp_node.bdly = BEEP_DLY; /* beep interval (-B) */
+
+ ntp_node.events = NULL;
+ ntp_node.rbuflist = NULL;
+
+ /*
+ * Initialize ntp variables
+ */
+ initializing = 1;
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_lib();
+ init_request();
+ init_control();
+ init_peer();
+ init_proto();
+ init_io();
+ init_loopfilter();
+ mon_start(MON_OFF);
+
+ {
+ int optct = optionProcess(&ntpdsimOptions, argc, argv);
+ argc -= optct;
+ argv += optct;
+ }
+
+ getconfig(argc, argv);
+
+ initializing = 0;
+ loop_config(LOOP_DRIFTCOMP, old_drift / 1e6);
+
+ /*
+ * Watch out here, we want the real time, not the silly stuff.
+ */
+ gettimeofday(&seed, NULL);
+ ntp_srandom(seed.tv_usec);
+
+ /*
+ * Push a beep and timer interrupt on the queue
+ */
+ push(event(0, BEEP), &ntp_node.events);
+ push(event(ntp_node.time + 1.0, TIMER), &ntp_node.events);
+
+ /*
+ * Pop the queue until nothing is left or time is exceeded
+ */
+ maxtime = ntp_node.time + ntp_node.sim_time;
+ while (ntp_node.time <= maxtime && ntp_node.events != NULL ) {
+ e = pop(&ntp_node.events);
+ ndeclk(&ntp_node, e);
+ funcPtr[e.function](&ntp_node, e);
+ }
+ return (0);
+}
+
+
+/*
+ * Return an event
+ */
+Event
+event(
+ double t,
+ funcTkn f
+ )
+{
+ Event e;
+
+ e.time = t;
+ e.function = f;
+ return (e);
+}
+
+/*
+ * Create an event queue
+ */
+Queue
+queue(
+ Event e,
+ Queue q
+ )
+{
+ Queue ret;
+
+ if ((ret = (Queue)malloc(sizeof(struct List))) == NULL)
+ abortsim("queue-malloc");
+ ret->event = e;
+ ret->next = q;
+ return (ret);
+}
+
+
+/*
+ * Push an event into the event queue
+ */
+void push(
+ Event e,
+ Queue *qp
+ )
+{
+ Queue *tmp = qp;
+
+ while (*tmp != NULL && ((*tmp)->event.time < e.time))
+ tmp = &((*tmp)->next);
+ *tmp = queue(e, (*tmp));
+}
+
+
+/*
+ * Pop the first event from the event queue
+ */
+Event
+pop(
+ Queue *qp
+ )
+{
+ Event ret;
+ Queue tmp;
+
+ tmp = *qp;
+ if (tmp == NULL)
+ abortsim("pop - empty queue");
+ ret = tmp->event;
+ *qp = tmp->next;
+ free(tmp);
+ return (ret);
+}
+
+
+/*
+ * Update clocks
+ */
+void
+ndeclk(
+ Node *n,
+ Event e
+ )
+{
+ node_clock(n, e.time);
+}
+
+
+/*
+ * Timer interrupt. Eventually, this results in calling the
+ * srvr_rplyi() routine below.
+ */
+void
+ntptmr(
+ Node *n,
+ Event e
+ )
+{
+ struct recvbuf *rbuf;
+
+ timer();
+
+ /*
+ * Process buffers received. They had better be in order by
+ * receive timestamp. Note that there are no additional buffers
+ * in the current implementation of ntpsim.
+ */
+ while (n->rbuflist != NULL) {
+ rbuf = n->rbuflist;
+ n->rbuflist = NULL;
+ (rbuf->receiver)(rbuf);
+ free(rbuf);
+ }
+
+ /*
+ * Arm the next timer interrupt.
+ */
+ push(event(e.time + (1 << EVENT_TIMEOUT), TIMER), &n->events);
+}
+
+
+/*
+ * srvr_rply() - send packet
+ */
+int srvr_rply(
+ Node *n,
+ struct sockaddr_storage *dest,
+ struct interface *inter, struct pkt *rpkt
+ )
+{
+ struct pkt xpkt;
+ struct recvbuf rbuf;
+ Event xvnt;
+ double dtemp, etemp;
+
+ /*
+ * Insert packet header values. We make this look like a
+ * stratum-1 server with a GPS clock, but nobody will ever
+ * notice that.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
+ MODE_SERVER);
+ xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
+ memcpy(&xpkt.refid, "GPS", 4);
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = rpkt->precision;
+ xpkt.rootdelay = 0;
+ xpkt.rootdispersion = 0;
+
+ /*
+ * Insert the timestamps.
+ */
+ xpkt.org = rpkt->xmt;
+ dtemp = poisson(n->ndly, n->snse); /* client->server delay */
+ DTOLFP(dtemp + n->clk_time, &xpkt.rec);
+ dtemp += poisson(n->pdly, 0); /* server delay */
+ DTOLFP(dtemp + n->clk_time, &xpkt.xmt);
+ xpkt.reftime = xpkt.xmt;
+ dtemp += poisson(n->ndly, n->snse); /* server->client delay */
+
+ /*
+ * Insert the I/O stuff.
+ */
+ rbuf.receiver = receive;
+ get_systime(&rbuf.recv_time);
+ rbuf.recv_length = LEN_PKT_NOMAC;
+ rbuf.recv_pkt = xpkt;
+ memcpy(&rbuf.srcadr, dest, sizeof(struct sockaddr_storage));
+ memcpy(&rbuf.recv_srcadr, dest,
+ sizeof(struct sockaddr_storage));
+ if ((rbuf.dstadr = malloc(sizeof(struct interface))) == NULL)
+ abortsim("server-malloc");
+ memcpy(rbuf.dstadr, inter, sizeof(struct interface));
+
+ /*
+ * Very carefully predict the time of arrival for the received
+ * packet.
+ */
+ LFPTOD(&xpkt.org, etemp);
+ etemp += dtemp;
+ xvnt = event(etemp, PACKET);
+ xvnt.rcv_buf = rbuf;
+ push(xvnt, &n->events);
+ return (0);
+}
+
+
+/*
+ * netpkt() - receive packet
+ */
+void
+netpkt(
+ Node *n,
+ Event e
+ )
+{
+ struct recvbuf *rbuf;
+ struct recvbuf *obuf;
+
+ /*
+ * Insert the packet on the receive queue and record the arrival
+ * time.
+ */
+ if ((rbuf = malloc(sizeof(struct recvbuf))) == NULL)
+ abortsim("ntprcv-malloc");
+ memcpy(rbuf, &e.rcv_buf, sizeof(struct recvbuf));
+ rbuf->receiver = receive;
+ DTOLFP(n->ntp_time, &rbuf->recv_time);
+ obuf = n->rbuflist;
+
+ /*
+ * In the present incarnation, no more than one buffer can be on
+ * the queue;
+ */
+ if (obuf == NULL) {
+ n->rbuflist = rbuf;
+ }
+}
+
+
+/*
+ * ndbeep() - progress indicator
+ */
+void
+ndbeep(
+ Node *n,
+ Event e
+ )
+{
+ static int first_time = 1;
+ char *dash = "-----------------";
+
+ if(n->bdly > 0) {
+ if (first_time) {
+ printf(
+ "\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n", ' ', ' ', ' ', ' ',' ');
+ printf("\t%s\t%s\t%s\n", dash, dash, dash);
+ first_time = 0;
+ push(event(n->bdly, BEEP), &n->events);
+ push(event(n->sim_time, BEEP), &n->events);
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ return;
+ }
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ push(event(e.time + n->bdly, BEEP), &n->events);
+ }
+}
+
+
+/*
+ * Abort simulation
+ */
+void
+abortsim(
+ char *errmsg
+ )
+{
+ perror(errmsg);
+ exit(1);
+}
diff --git a/contrib/ntp/ntpd/ppsapi_timepps.h b/contrib/ntp/ntpd/ppsapi_timepps.h
new file mode 100644
index 0000000..8adaf62
--- /dev/null
+++ b/contrib/ntp/ntpd/ppsapi_timepps.h
@@ -0,0 +1,26 @@
+/* ppsapi_timepps.h */
+
+/*
+ * This logic first tries to get the timepps.h file from a standard
+ * location, and then from our include/ subdirectory.
+ */
+
+#ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+#else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# else
+# ifdef HAVE_CIOGETEV
+# include "timepps-SunOS.h"
+# else
+# ifdef HAVE_TIOCGPPSEV
+# include "timepps-Solaris.h"
+# else
+# ifdef TIOCDCDTIMESTAMP
+# include "timepps-SCO.h"
+# endif
+# endif
+# endif
+# endif
+#endif
diff --git a/contrib/ntp/ntpd/refclock_acts.c b/contrib/ntp/ntpd/refclock_acts.c
new file mode 100644
index 0000000..57f2ca7
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_acts.c
@@ -0,0 +1,931 @@
+/*
+ * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
+ * Services
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+/*
+ * This driver supports the US (NIST, USNO) and European (PTB, NPL,
+ * etc.) modem time services, as well as Spectracom GPS and WWVB
+ * receivers connected via a modem. The driver periodically dials a
+ * number from a telephone list, receives the timecode data and
+ * calculates the local clock correction. It is designed primarily for
+ * use as backup when neither a radio clock nor connectivity to Internet
+ * time servers is available.
+ *
+ * This driver requires a modem with a Hayes-compatible command set and
+ * control over the modem data terminal ready (DTR) control line. The
+ * modem setup string is hard-coded in the driver and may require
+ * changes for nonstandard modems or special circumstances. For reasons
+ * unrelated to this driver, the data set ready (DSR) control line
+ * should not be set when this driver is first started.
+ *
+ * The calling program is initiated by setting fudge flag1, either
+ * manually or automatically. When flag1 is set, the calling program
+ * dials the first number in the phone command of the configuration
+ * file. If that call fails, the calling program dials the second number
+ * and so on. The number is specified by the Hayes ATDT prefix followed
+ * by the number itself, including the prefix and long-distance digits
+ * and delay code, if necessary. The flag1 is reset and the calling
+ * program terminated if (a) a valid clock update has been determined,
+ * (b) no more numbers remain in the list, (c) a device fault or timeout
+ * occurs or (d) fudge flag1 is reset manually.
+ *
+ * The driver is transparent to each of the modem time services and
+ * Spectracom radios. It selects the parsing algorithm depending on the
+ * message length. There is some hazard should the message be corrupted.
+ * However, the data format is checked carefully and only if all checks
+ * succeed is the message accepted. Corrupted lines are discarded
+ * without complaint.
+ *
+ * Fudge controls
+ *
+ * flag1 force a call in manual mode
+ * flag2 enable port locking (not verified)
+ * flag3 no modem; port is directly connected to device
+ * flag4 not used
+ *
+ * time1 offset adjustment (s)
+ *
+ * Ordinarily, the serial port is connected to a modem; however, it can
+ * be connected directly to a device or another computer for testing and
+ * calibration. In this case set fudge flag3 and the driver will send a
+ * single character 'T' at each poll event. In principle, fudge flag2
+ * enables port locking, allowing the modem to be shared when not in use
+ * by this driver. At least on Solaris with the current NTP I/O
+ * routines, this results only in lots of ugly error messages.
+ */
+/*
+ * National Institute of Science and Technology (NIST)
+ *
+ * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
+ *
+ * Data Format
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
+ * ...
+ *
+ * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
+ * the on-time markers echoed by the driver and used by NIST to measure
+ * and correct for the propagation delay.
+ *
+ * US Naval Observatory (USNO)
+ *
+ * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
+ *
+ * Data Format (two lines, repeating at one-second intervals)
+ *
+ * jjjjj nnn hhmmss UTC<CR><LF>
+ * *<CR><LF>
+ *
+ * jjjjj modified Julian day number (not used)
+ * nnn day of year
+ * hhmmss second of day
+ * * on-time marker for previous timecode
+ * ...
+ *
+ * USNO does not correct for the propagation delay. A fudge time1 of
+ * about .06 s is advisable.
+ *
+ * European Services (PTB, NPL, etc.)
+ *
+ * PTB: +49 531 512038 (Germany)
+ * NPL: 0906 851 6333 (UK only)
+ *
+ * Data format (see the documentation for phone numbers and formats.)
+ *
+ * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
+ *
+ * Spectracom GPS and WWVB Receivers
+ *
+ * If a modem is connected to a Spectracom receiver, this driver will
+ * call it up and retrieve the time in one of two formats. As this
+ * driver does not send anything, the radio will have to either be
+ * configured in continuous mode or be polled by another local driver.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define LOCKFILE "/var/spool/locks/LCK..cua%d"
+#define DESCRIPTION "Automated Computer Time Service" /* WRU */
+#define REFID "NONE" /* default reference ID */
+#define MSGCNT 20 /* max message count */
+#define SMAX 256 /* max clockstats line length */
+
+/*
+ * Calling program modes
+ */
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+/*
+ * Service identifiers.
+ */
+#define REFACTS "NIST" /* NIST reference ID */
+#define LENACTS 50 /* NIST format */
+#define REFUSNO "USNO" /* USNO reference ID */
+#define LENUSNO 20 /* USNO */
+#define REFPTB "PTB\0" /* PTB/NPL reference ID */
+#define LENPTB 78 /* PTB/NPL format */
+#define REFWWVB "WWVB" /* WWVB reference ID */
+#define LENWWVB0 22 /* WWVB format 0 */
+#define LENWWVB2 24 /* WWVB format 2 */
+#define LF 0x0a /* ASCII LF */
+
+/*
+ * Modem setup strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 US answer tone
+ * &C0 disable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled until carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
+#define MODEM_HANGUP "ATH\r" /* modem disconnect */
+
+/*
+ * Timeouts (all in seconds)
+ */
+#define SETUP 3 /* setup timeout */
+#define DTR 1 /* DTR timeout */
+#define ANSWER 60 /* answer timeout */
+#define CONNECT 20 /* first valid message timeout */
+#define TIMECODE 30 /* all valid messages timeout */
+
+/*
+ * State machine codes
+ */
+#define S_IDLE 0 /* wait for poll */
+#define S_OK 1 /* wait for modem setup */
+#define S_DTR 2 /* wait for modem DTR */
+#define S_CONNECT 3 /* wait for answer*/
+#define S_FIRST 4 /* wait for first valid message */
+#define S_MSG 5 /* wait for all messages */
+#define S_CLOSE 6 /* wait after sending disconnect */
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ int unit; /* unit number */
+ int state; /* the first one was Delaware */
+ int timer; /* timeout counter */
+ int retry; /* retry index */
+ int msgcnt; /* count of messages received */
+ l_fp tstamp; /* on-time timestamp */
+ char *bufptr; /* buffer pointer */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_message P((struct peer *));
+static void acts_timecode P((struct peer *, char *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static void acts_timer P((int, struct peer *));
+
+/*
+ * Transfer vector (conditional structure name)
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ acts_timer /* housekeeping timer */
+};
+
+struct refclock refclock_ptb;
+
+/*
+ * Initialize data for processing
+ */
+static int
+acts_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = emalloc(sizeof(struct actsunit));
+ if (up == NULL)
+ return (0);
+
+ memset(up, 0, sizeof(struct actsunit));
+ up->unit = unit;
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->sstclktype = CTL_SST_TS_TELEPHONE;
+ peer->flags &= ~FLAG_FIXPOLL;
+ up->bufptr = pp->a_lastcode;
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Warning: do this only when a call is not in progress.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive (
+ struct recvbuf *rbufp
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char tbuf[BMAX];
+ char *tptr;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. Note
+ * we are in raw mode and victim of whatever the terminal
+ * interface kicks up; so, we have to reassemble messages from
+ * arbitrary fragments. Capture the timecode at the beginning of
+ * the message and at the '*' and '#' on-time characters.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
+ pp->a_lastcode), &pp->lastrec);
+ for (tptr = tbuf; *tptr != '\0'; tptr++) {
+ if (*tptr == LF) {
+ if (up->bufptr == pp->a_lastcode) {
+ up->tstamp = pp->lastrec;
+ continue;
+
+ } else {
+ *up->bufptr = '\0';
+ acts_message(peer);
+ up->bufptr = pp->a_lastcode;
+ }
+ } else if (!iscntrl(*tptr)) {
+ *up->bufptr++ = *tptr;
+ if (*tptr == '*' || *tptr == '#') {
+ up->tstamp = pp->lastrec;
+ write(pp->io.fd, tptr, 1);
+ }
+ }
+ }
+}
+
+
+/*
+ * acts_message - process message
+ */
+void
+acts_message(
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+ char tbuf[SMAX];
+#ifdef DEBUG
+ u_int modem;
+#endif
+
+ /*
+ * What to do depends on the state and the first token in the
+ * message. A NO token sends the message to the clockstats.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+#ifdef DEBUG
+ ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
+ sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state,
+ up->timer, strlen(pp->a_lastcode), pp->a_lastcode);
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ strncpy(tbuf, pp->a_lastcode, SMAX);
+ strtok(tbuf, " ");
+ if (strcmp(tbuf, "NO") == 0)
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ switch(up->state) {
+
+ /*
+ * We are waiting for the OK response to the modem setup
+ * command. When this happens, raise DTR and dial the number
+ * followed by \r.
+ */
+ case S_OK:
+ if (strcmp(tbuf, "OK") != 0) {
+ msyslog(LOG_ERR, "acts: setup error %s",
+ pp->a_lastcode);
+ acts_disc(peer);
+ return;
+ }
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ up->state = S_DTR;
+ up->timer = DTR;
+ return;
+
+ /*
+ * We are waiting for the call to be answered. All we care about
+ * here is token CONNECT. Send the message to the clockstats.
+ */
+ case S_CONNECT:
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ if (strcmp(tbuf, "CONNECT") != 0) {
+ acts_disc(peer);
+ return;
+ }
+ up->state = S_FIRST;
+ up->timer = CONNECT;
+ return;
+
+ /*
+ * We are waiting for a timecode. Pass it to the parser.
+ */
+ case S_FIRST:
+ case S_MSG:
+ acts_timecode(peer, pp->a_lastcode);
+ break;
+ }
+}
+
+/*
+ * acts_timecode - identify the service and parse the timecode message
+ */
+void
+acts_timecode(
+ struct peer *peer, /* peer structure pointer */
+ char *str /* timecode string */
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ double dut1; /* DUT adjustment */
+
+ u_int dst; /* ACTS daylight/standard time */
+ u_int leap; /* ACTS leap indicator */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* ACTS timescale */
+ char flag; /* ACTS on-time character (* or #) */
+
+ char synchar; /* WWVB synchronized indicator */
+ char qualchar; /* WWVB quality indicator */
+ char leapchar; /* WWVB leap indicator */
+ char dstchar; /* WWVB daylight/savings indicator */
+ int tz; /* WWVB timezone */
+
+ u_int leapmonth; /* PTB/NPL month of leap */
+ char leapdir; /* PTB/NPL leap direction */
+
+ /*
+ * The parser selects the modem format based on the message
+ * length. Since the data are checked carefully, occasional
+ * errors due noise are forgivable.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->nsec = 0;
+ switch(strlen(str)) {
+
+ /*
+ * For USNO format on-time character '*', which is on a line by
+ * itself. Be sure a timecode has been received.
+ */
+ case 1:
+ if (*str == '*' && up->msgcnt > 0)
+ break;
+
+ return;
+
+ /*
+ * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
+ * UTC(NIST) *"
+ */
+ case LENACTS:
+ if (sscanf(str,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &pp->second, &dst, &leap, &dut1,
+ &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Wait until ACTS has calculated the roundtrip delay.
+ * We don't need to do anything, as ACTS adjusts the
+ * on-time epoch.
+ */
+ if (flag != '#')
+ return;
+
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->leap = LEAP_NOWARNING;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ memcpy(&pp->refid, REFACTS, 4);
+ if (up->msgcnt == 0)
+ record_clock_stats(&peer->srcadr, str);
+ up->msgcnt++;
+ break;
+
+ /*
+ * USNO format: "jjjjj nnn hhmmss UTC"
+ */
+ case LENUSNO:
+ if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
+ &mjd, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, utc) != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Wait for the on-time character, which follows in a
+ * separate message. There is no provision for leap
+ * warning.
+ */
+ pp->leap = LEAP_NOWARNING;
+ memcpy(&pp->refid, REFUSNO, 4);
+ if (up->msgcnt == 0)
+ record_clock_stats(&peer->srcadr, str);
+ up->msgcnt++;
+ return;
+
+ /*
+ * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
+ */
+ case LENPTB:
+ if (sscanf(str,
+ "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
+ &pp->second, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
+ &msADV, &flag) != 12) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->leap = LEAP_NOWARNING;
+ if (leapmonth == month) {
+ if (leapdir == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapdir == '-')
+ pp->leap = LEAP_DELSECOND;
+ }
+ pp->day = ymd2yd(pp->year, month, day);
+ memcpy(&pp->refid, REFPTB, 4);
+ if (up->msgcnt == 0)
+ record_clock_stats(&peer->srcadr, str);
+ up->msgcnt++;
+ break;
+
+
+ /*
+ * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
+ */
+ case LENWWVB0:
+ if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
+ &synchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &dstchar, &tz) != 7) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->leap = LEAP_NOWARNING;
+ if (synchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ memcpy(&pp->refid, REFWWVB, 4);
+ if (up->msgcnt == 0)
+ record_clock_stats(&peer->srcadr, str);
+ up->msgcnt++;
+ break;
+
+ /*
+ * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
+ */
+ case LENWWVB2:
+ if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
+ &synchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &dstchar, &leapchar, &dstchar) != 11) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+ pp->leap = LEAP_NOWARNING;
+ if (synchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ memcpy(&pp->refid, REFWWVB, 4);
+ if (up->msgcnt == 0)
+ record_clock_stats(&peer->srcadr, str);
+ up->msgcnt++;
+ break;
+
+ /*
+ * None of the above. Just forget about it and wait for the next
+ * message or timeout.
+ */
+ default:
+ return;
+ }
+
+ /*
+ * We have a valid timecode. The fudge time1 value is added to
+ * each sample by the main line routines. Note that in current
+ * telephone networks the propatation time can be different for
+ * each call and can reach 200 ms for some calls.
+ */
+ peer->refid = pp->refid;
+ pp->lastrec = up->tstamp;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+ if (up->state != S_MSG) {
+ up->state = S_MSG;
+ up->timer = TIMECODE;
+ }
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * This routine is called at every system poll. All it does is
+ * set flag1 under certain conditions. The real work is done by
+ * the timeout routine and state machine.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the calling program is activated by the ntpdc
+ * program using the enable flag (fudge flag1), either manually
+ * or by a cron job.
+ */
+ case MODE_MANUAL:
+ /* fall through */
+ break;
+
+ /*
+ * In automatic mode the calling program runs continuously at
+ * intervals determined by the poll event or specified timeout.
+ */
+ case MODE_AUTO:
+ pp->sloppyclockflag |= CLK_FLAG1;
+ break;
+
+ /*
+ * In backup mode the calling program runs continuously as long
+ * as either no peers are available or this peer is selected.
+ */
+ case MODE_BACKUP:
+ if (sys_peer == NULL || sys_peer == peer)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ break;
+ }
+}
+
+
+/*
+ * acts_timer - called at one-second intervals
+ */
+static void
+acts_timer(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * This routine implments a timeout which runs for a programmed
+ * interval. The counter is initialized by the state machine and
+ * counts down to zero. Upon reaching zero, the state machine is
+ * called. If flag1 is set while in S_IDLE state, force a
+ * timeout.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) {
+ acts_timeout(peer);
+ return;
+ }
+ if (up->timer == 0)
+ return;
+
+ up->timer--;
+ if (up->timer == 0)
+ acts_timeout(peer);
+}
+
+
+/*
+ * acts_timeout - called on timeout
+ */
+static void
+acts_timeout(
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ char lockfile[128], pidbuf[8];
+ char tbuf[BMAX];
+
+ /*
+ * The state machine is driven by messages from the modem, when
+ * first stated and at timeout.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag &
+ CLK_FLAG3)) {
+ msyslog(LOG_ERR, "acts: no phones");
+ return;
+ }
+ switch(up->state) {
+
+ /*
+ * System poll event. Lock the modem port and open the device.
+ */
+ case S_IDLE:
+
+ /*
+ * Lock the modem port. If busy, retry later. Note: if
+ * something fails between here and the close, the lock
+ * file may not be removed.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ sprintf(lockfile, LOCKFILE, up->unit);
+ fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
+ 0644);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "acts: port busy");
+ return;
+ }
+ sprintf(pidbuf, "%d\n", (u_int)getpid());
+ write(fd, pidbuf, strlen(pidbuf));
+ close(fd);
+ }
+
+ /*
+ * Open the device in raw mode and link the I/O.
+ */
+ if (!pp->io.fd) {
+ sprintf(device, DEVICE, up->unit);
+ fd = refclock_open(device, SPEED232,
+ LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
+ if (fd == 0) {
+ return;
+ }
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ msyslog(LOG_ERR,
+ "acts: addclock fails");
+ close(fd);
+ pp->io.fd = 0;
+ return;
+ }
+ }
+
+ /*
+ * If the port is directly connected to the device, skip
+ * the modem business and send 'T' for Spectrabum.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG3) {
+ if (write(pp->io.fd, "T", 1) < 0) {
+ msyslog(LOG_ERR, "acts: write %m");
+ return;
+ }
+ up->state = S_FIRST;
+ up->timer = CONNECT;
+ return;
+ }
+
+ /*
+ * Initialize the modem. This works with Hayes commands.
+ */
+#ifdef DEBUG
+ if (debug)
+ printf("acts: setup %s\n", MODEM_SETUP);
+#endif
+ if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
+ 0) {
+ msyslog(LOG_ERR, "acts: write %m");
+ return;
+ }
+ up->state = S_OK;
+ up->timer = SETUP;
+ return;
+
+ /*
+ * In OK state the modem did not respond to setup.
+ */
+ case S_OK:
+ msyslog(LOG_ERR, "acts: no modem");
+ break;
+
+ /*
+ * In DTR state we are waiting for the modem to settle down
+ * before hammering it with a dial command.
+ */
+ case S_DTR:
+ sprintf(tbuf, "DIAL #%d %s", up->retry,
+ sys_phone[up->retry]);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ write(pp->io.fd, sys_phone[up->retry],
+ strlen(sys_phone[up->retry]));
+ write(pp->io.fd, "\r", 1);
+ up->state = S_CONNECT;
+ up->timer = ANSWER;
+ return;
+
+ /*
+ * In CONNECT state the call did not complete.
+ */
+ case S_CONNECT:
+ msyslog(LOG_ERR, "acts: no answer");
+ break;
+
+ /*
+ * In FIRST state no messages were received.
+ */
+ case S_FIRST:
+ msyslog(LOG_ERR, "acts: no messages");
+ break;
+
+ /*
+ * In CLOSE state hangup is complete. Close the doors and
+ * windows and get some air.
+ */
+ case S_CLOSE:
+
+ /*
+ * Close the device and unlock a shared modem.
+ */
+ if (pp->io.fd) {
+ io_closeclock(&pp->io);
+ close(pp->io.fd);
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ sprintf(lockfile, LOCKFILE, up->unit);
+ unlink(lockfile);
+ }
+ pp->io.fd = 0;
+ }
+
+ /*
+ * If messages were received, fold the tent and wait for
+ * the next poll. If no messages and there are more
+ * numbers to dial, retry after a short wait.
+ */
+ up->bufptr = pp->a_lastcode;
+ up->timer = 0;
+ up->state = S_IDLE;
+ if ( up->msgcnt == 0) {
+ up->retry++;
+ if (sys_phone[up->retry] == NULL)
+ up->retry = 0;
+ else
+ up->timer = SETUP;
+ } else {
+ up->retry = 0;
+ }
+ up->msgcnt = 0;
+ return;
+ }
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_disc - disconnect the call and clean the place up.
+ */
+static void
+acts_disc (
+ struct peer *peer
+ )
+{
+ struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We get here if the call terminated successfully or if an
+ * error occured. If the median filter has something in it,feed
+ * the data to the clock filter. If a modem port, drop DTR to
+ * force command mode and send modem hangup.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->msgcnt > 0)
+ refclock_receive(peer);
+ if (!(pp->sloppyclockflag & CLK_FLAG3)) {
+ ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
+ }
+ up->timer = SETUP;
+ up->state = S_CLOSE;
+}
+
+#else
+int refclock_acts_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_arbiter.c b/contrib/ntp/ntpd/refclock_arbiter.c
new file mode 100644
index 0000000..88a3225
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_arbiter.c
@@ -0,0 +1,453 @@
+/*
+ * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite
+ * Controlled Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARBITER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Arbiter 1088A/B Satellite Controlled Clock.
+ * The claimed accuracy of this clock is 100 ns relative to the PPS
+ * output when receiving four or more satellites.
+ *
+ * The receiver should be configured before starting the NTP daemon, in
+ * order to establish reliable position and operating conditions. It
+ * does not initiate surveying or hold mode. For use with NTP, the
+ * daylight savings time feature should be disables (D0 command) and the
+ * broadcast mode set to operate in UTC (BU command).
+ *
+ * The timecode format supported by this driver is selected by the poll
+ * sequence "B5", which initiates a line in the following format to be
+ * repeated once per second until turned off by the "B0" poll sequence.
+ *
+ * Format B5 (24 ASCII printing characters):
+ *
+ * <cr><lf>i yy ddd hh:mm:ss.000bbb
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = locked, '?' = unlocked)
+ * yy = year of century
+ * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * .000 = fraction of second (not used)
+ * bbb = tailing spaces for fill
+ *
+ * The alarm condition is indicated by a '?' at i, which indicates the
+ * receiver is not synchronized. In normal operation, a line consisting
+ * of the timecode followed by the time quality character (TQ) followed
+ * by the receiver status string (SR) is written to the clockstats file.
+ * The time quality character is encoded in IEEE P1344 standard:
+ *
+ * Format TQ (IEEE P1344 estimated worst-case time quality)
+ *
+ * 0 clock locked, maximum accuracy
+ * F clock failure, time not reliable
+ * 4 clock unlocked, accuracy < 1 us
+ * 5 clock unlocked, accuracy < 10 us
+ * 6 clock unlocked, accuracy < 100 us
+ * 7 clock unlocked, accuracy < 1 ms
+ * 8 clock unlocked, accuracy < 10 ms
+ * 9 clock unlocked, accuracy < 100 ms
+ * A clock unlocked, accuracy < 1 s
+ * B clock unlocked, accuracy < 10 s
+ *
+ * The status string is encoded as follows:
+ *
+ * Format SR (25 ASCII printing characters)
+ *
+ * V=vv S=ss T=t P=pdop E=ee
+ *
+ * vv = satellites visible
+ * ss = relative signal strength
+ * t = satellites tracked
+ * pdop = position dilution of precision (meters)
+ * ee = hardware errors
+ *
+ * If flag4 is set, an additional line consisting of the receiver
+ * latitude (LA), longitude (LO), elevation (LH) (meters), and data
+ * buffer (DB) is written to this file. If channel B is enabled for
+ * deviation mode and connected to a 1-PPS signal, the last two numbers
+ * on the line are the deviation and standard deviation averaged over
+ * the last 15 seconds.
+ *
+ * PPS calibration fudge time1 .001240
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS " /* reference ID */
+#define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */
+#define LENARB 24 /* format B5 timecode length */
+#define MAXSTA 40 /* max length of status string */
+#define MAXPOS 80 /* max length of position string */
+
+/*
+ * ARB unit control structure
+ */
+struct arbunit {
+ l_fp laststamp; /* last receive timestamp */
+ int tcswitch; /* timecode switch/counter */
+ char qualchar; /* IEEE P1344 quality (TQ command) */
+ char status[MAXSTA]; /* receiver status (SR command) */
+ char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */
+};
+
+/*
+ * Function prototypes
+ */
+static int arb_start P((int, struct peer *));
+static void arb_shutdown P((int, struct peer *));
+static void arb_receive P((struct recvbuf *));
+static void arb_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arbiter = {
+ arb_start, /* start up driver */
+ arb_shutdown, /* shut down driver */
+ arb_poll, /* transmit poll message */
+ noentry, /* not used (old arb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * arb_start - open the devices and initialize data for processing
+ */
+static int
+arb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct arbunit *)emalloc(sizeof(struct arbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct arbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = arb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ write(pp->io.fd, "B0", 2);
+ return (1);
+}
+
+
+/*
+ * arb_shutdown - shut down the clock
+ */
+static void
+arb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * arb_receive - receive data from the serial interface
+ */
+static void
+arb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int temp;
+ u_char syncchar; /* synch indicator */
+ char tbuf[BMAX]; /* temp buffer */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, tbuf, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. The program first
+ * sends a TQ and expects the echo followed by the time quality
+ * character. It then sends a B5 starting the timecode broadcast
+ * and expects the echo followed some time later by the on-time
+ * character <cr> and then the <lf> beginning the timecode
+ * itself. Finally, at the <cr> beginning the next timecode at
+ * the next second, the program sends a B0 shutting down the
+ * timecode broadcast.
+ *
+ * If flag4 is set, the program snatches the latitude, longitude
+ * and elevation and writes it to the clockstats file.
+ */
+ if (temp == 0)
+ return;
+
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ if (temp < 3)
+ return;
+
+ if (up->tcswitch == 0) {
+
+ /*
+ * Collect statistics. If nothing is recogized, just
+ * ignore; sometimes the clock doesn't stop spewing
+ * timecodes for awhile after the B0 command.
+ *
+ * If flag4 is not set, send TQ, SR, B5. If flag4 is
+ * sset, send TQ, SR, LA, LO, LH, DB, B5. When the
+ * median filter is full, send B0.
+ */
+ if (!strncmp(tbuf, "TQ", 2)) {
+ up->qualchar = tbuf[2];
+ write(pp->io.fd, "SR", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "SR", 2)) {
+ strcpy(up->status, tbuf + 2);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ write(pp->io.fd, "LA", 2);
+ else
+ write(pp->io.fd, "B5", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LA", 2)) {
+ strcpy(up->latlon, tbuf + 2);
+ write(pp->io.fd, "LO", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LO", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, tbuf + 2);
+ write(pp->io.fd, "LH", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "LH", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, tbuf + 2);
+ write(pp->io.fd, "DB", 2);
+ return;
+
+ } else if (!strncmp(tbuf, "DB", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, tbuf + 2);
+ record_clock_stats(&peer->srcadr, up->latlon);
+#ifdef DEBUG
+ if (debug)
+ printf("arbiter: %s\n", up->latlon);
+#endif
+ write(pp->io.fd, "B5", 2);
+ }
+ }
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has valid length, but not in
+ * proper format, we declare bad format and exit. If the
+ * timecode has invalid length, which sometimes occurs when the
+ * B0 amputates the broadcast, we just quietly steal away. Note
+ * that the time quality character and receiver status string is
+ * tacked on the end for clockstats display.
+ */
+ up->tcswitch++;
+ if (up->tcswitch <= 1 || temp < LENARB)
+ return;
+
+ /*
+ * Timecode format B5: "i yy ddd hh:mm:ss.000 "
+ */
+ strncpy(pp->a_lastcode, tbuf, BMAX);
+ pp->a_lastcode[LENARB - 2] = up->qualchar;
+ strcat(pp->a_lastcode, up->status);
+ pp->lencode = strlen(pp->a_lastcode);
+ syncchar = ' ';
+ if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d",
+ &syncchar, &pp->year, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, "B0", 2);
+ return;
+ }
+
+ /*
+ * We decode the clock dispersion from the time quality
+ * character.
+ */
+ switch (up->qualchar) {
+
+ case '0': /* locked, max accuracy */
+ pp->disp = 1e-7;
+ pp->lastref = pp->lastrec;
+ break;
+
+ case '4': /* unlock accuracy < 1 us */
+ pp->disp = 1e-6;
+ break;
+
+ case '5': /* unlock accuracy < 10 us */
+ pp->disp = 1e-5;
+ break;
+
+ case '6': /* unlock accuracy < 100 us */
+ pp->disp = 1e-4;
+ break;
+
+ case '7': /* unlock accuracy < 1 ms */
+ pp->disp = .001;
+ break;
+
+ case '8': /* unlock accuracy < 10 ms */
+ pp->disp = .01;
+ break;
+
+ case '9': /* unlock accuracy < 100 ms */
+ pp->disp = .1;
+ break;
+
+ case 'A': /* unlock accuracy < 1 s */
+ pp->disp = 1;
+ break;
+
+ case 'B': /* unlock accuracy < 10 s */
+ pp->disp = 10;
+ break;
+
+ case 'F': /* clock failure */
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_FAULT);
+ write(pp->io.fd, "B0", 2);
+ return;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, "B0", 2);
+ return;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ else if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+
+ if (up->tcswitch >= MAXSTAGE) {
+ write(pp->io.fd, "B0", 2);
+ }
+}
+
+
+/*
+ * arb_poll - called by the transmit procedure
+ */
+static void
+arb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The Arbiter clock responds to a "B5"
+ * by returning a timecode in the format specified above.
+ * Transmission occurs once per second, unless turned off by a
+ * "B0". Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in.
+ */
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ pp->polls++;
+ up->tcswitch = 0;
+ if (write(pp->io.fd, "TQ", 2) != 2)
+ refclock_report(peer, CEVNT_FAULT);
+
+ /*
+ * Process median filter samples. If none received, declare a
+ * timeout and keep going.
+ */
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("arbiter: timecode %d %s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+}
+
+#else
+int refclock_arbiter_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_arc.c b/contrib/ntp/ntpd/refclock_arc.c
new file mode 100644
index 0000000..af80621
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_arc.c
@@ -0,0 +1,1569 @@
+/*
+ * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
+
+static const char arc_version[] = { "V1.3 2003/02/21" };
+
+/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
+ to 4.1.0 */
+#undef PRE_NTP420
+
+#ifndef ARCRON_NOT_KEEN
+#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
+#endif
+
+#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
+#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
+#endif
+
+#ifndef ARCRON_NOT_LEAPSECOND_KEEN
+#ifndef ARCRON_LEAPSECOND_KEEN
+#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
+#endif
+#endif
+
+/*
+Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
+Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
+Modifications by Paul Alfille, <palfille@partners.org>, 2003.
+Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
+Modifications by Nigel Roles <nigel@9fs.org>, 2003.
+
+
+THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT
+YOUR OWN RISK.
+
+Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
+
+Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
+
+This code may be freely copied and used and incorporated in other
+systems providing the disclaimer and notice of authorship are
+reproduced.
+
+-------------------------------------------------------------------------------
+
+Nigel's notes:
+
+1) Called tcgetattr() before modifying, so that fields correctly initialised
+ for all operating systems
+
+2) Altered parsing of timestamp line so that it copes with fields which are
+ not always ASCII digits (e.g. status field when battery low)
+
+-------------------------------------------------------------------------------
+
+Christopher's notes:
+
+MAJOR CHANGES SINCE V1.2
+========================
+ 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
+ 2001-02-17 comp.protocols.time.ntp
+
+ 2) Added WWVB support via clock mode command, localtime/UTC time configured
+ via flag1=(0=UTC, 1=localtime)
+
+ 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
+
+ 4) Added simplified conversion from localtime to UTC with dst/bst translation
+
+ 5) Added average signal quality poll
+
+ 6) Fixed a badformat error when no code is available due to stripping
+ \n & \r's
+
+ 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
+ routine
+
+ 8) Lots of code cleanup, including standardized DEBUG macros and removal
+ of unused code
+
+-------------------------------------------------------------------------------
+
+Author's original note:
+
+I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
+
+It works (after a fashion) on both Solaris-1 and Solaris-2.
+
+I am currently using ntp3-5.85. I have been running the code for
+about 7 months without any problems. Even coped with the change to BST!
+
+I had to do some funky things to read from the clock because it uses the
+power from the receive lines to drive the transmit lines. This makes the
+code look a bit stupid but it works. I also had to put in some delays to
+allow for the turnaround time from receive to transmit. These delays
+are between characters when requesting a time stamp so that shouldn't affect
+the results too drastically.
+
+...
+
+The bottom line is that it works but could easily be improved. You are
+free to do what you will with the code. I haven't been able to determine
+how good the clock is. I think that this requires a known good clock
+to compare it against.
+
+-------------------------------------------------------------------------------
+
+Damon's notes for adjustments:
+
+MAJOR CHANGES SINCE V1.0
+========================
+ 1) Removal of pollcnt variable that made the clock go permanently
+ off-line once two time polls failed to gain responses.
+
+ 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
+ terminal of the process when we do a low-level open().
+
+ 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
+ defined) to try to resync quickly after a potential leap-second
+ insertion or deletion.
+
+ 4) Code significantly slimmer at run-time than V1.0.
+
+
+GENERAL
+=======
+
+ 1) The C preprocessor symbol to have the clock built has been changed
+ from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
+ possiblity of clashes with other symbols in the future.
+
+ 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
+
+ a) The ARC documentation claims the internal clock is (only)
+ accurate to about 20ms relative to Rugby (plus there must be
+ noticable drift and delay in the ms range due to transmission
+ delays and changing atmospheric effects). This clock is not
+ designed for ms accuracy as NTP has spoilt us all to expect.
+
+ b) The clock oscillator looks like a simple uncompensated quartz
+ crystal of the sort used in digital watches (ie 32768Hz) which
+ can have large temperature coefficients and drifts; it is not
+ clear if this oscillator is properly disciplined to the MSF
+ transmission, but as the default is to resync only once per
+ *day*, we can imagine that it is not, and is free-running. We
+ can minimise drift by resyncing more often (at the cost of
+ reduced battery life), but drift/wander may still be
+ significant.
+
+ c) Note that the bit time of 3.3ms adds to the potential error in
+ the the clock timestamp, since the bit clock of the serial link
+ may effectively be free-running with respect to the host clock
+ and the MSF clock. Actually, the error is probably 1/16th of
+ the above, since the input data is probably sampled at at least
+ 16x the bit rate.
+
+ By keeping the clock marked as not very precise, it will have a
+ fairly large dispersion, and thus will tend to be used as a
+ `backup' time source and sanity checker, which this clock is
+ probably ideal for. For an isolated network without other time
+ sources, this clock can probably be expected to provide *much*
+ better than 1s accuracy, which will be fine.
+
+ By default, PRECISION is set to -4, but experience, especially at a
+ particular geographic location with a particular clock, may allow
+ this to be altered to -5. (Note that skews of +/- 10ms are to be
+ expected from the clock from time-to-time.) This improvement of
+ reported precision can be instigated by setting flag3 to 1, though
+ the PRECISION will revert to the normal value while the clock
+ signal quality is unknown whatever the flag3 setting.
+
+ IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
+ ANY RESIDUAL SKEW, eg:
+
+ server 127.127.27.0 # ARCRON MSF radio clock unit 0.
+ # Fudge timestamps by about 20ms.
+ fudge 127.127.27.0 time1 0.020
+
+ You will need to observe your system's behaviour, assuming you have
+ some other NTP source to compare it with, to work out what the
+ fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with
+ my MSF clock with my distance from the MSF transmitter, +20ms
+ seemed about right, after some observation.
+
+ 3) REFID has been made "MSFa" to reflect the MSF time source and the
+ ARCRON receiver.
+
+ 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
+ forcing a resync since the last attempt. This is picked to give a
+ little less than an hour between resyncs and to try to avoid
+ clashing with any regular event at a regular time-past-the-hour
+ which might cause systematic errors.
+
+ The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
+ running down its batteries unnecesarily if ntpd is going to crash
+ or be killed or reconfigured quickly. If ARCRON_KEEN is defined
+ then this period is long enough for (with normal polling rates)
+ enough time samples to have been taken to allow ntpd to sync to
+ the clock before the interruption for the clock to resync to MSF.
+ This avoids ntpd syncing to another peer first and then
+ almost immediately hopping to the MSF clock.
+
+ The RETRY_RESYNC_TIME is used before rescheduling a resync after a
+ resync failed to reveal a statisfatory signal quality (too low or
+ unknown).
+
+ 5) The clock seems quite jittery, so I have increased the
+ median-filter size from the typical (previous) value of 3. I
+ discard up to half the results in the filter. It looks like maybe
+ 1 sample in 10 or so (maybe less) is a spike, so allow the median
+ filter to discard at least 10% of its entries or 1 entry, whichever
+ is greater.
+
+ 6) Sleeping *before* each character sent to the unit to allow required
+ inter-character time but without introducting jitter and delay in
+ handling the response if possible.
+
+ 7) If the flag ARCRON_KEEN is defined, take time samples whenever
+ possible, even while resyncing, etc. We rely, in this case, on the
+ clock always giving us a reasonable time or else telling us in the
+ status byte at the end of the timestamp that it failed to sync to
+ MSF---thus we should never end up syncing to completely the wrong
+ time.
+
+ 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
+ refclock median-filter routines to get round small bug in 3-5.90
+ code which does not return the median offset. XXX Removed this
+ bit due NTP Version 4 upgrade - dlm.
+
+ 9) We would appear to have a year-2000 problem with this clock since
+ it returns only the two least-significant digits of the year. But
+ ntpd ignores the year and uses the local-system year instead, so
+ this is in fact not a problem. Nevertheless, we attempt to do a
+ sensible thing with the dates, wrapping them into a 100-year
+ window.
+
+ 10)Logs stats information that can be used by Derek's Tcl/Tk utility
+ to show the status of the clock.
+
+ 11)The clock documentation insists that the number of bits per
+ character to be sent to the clock, and sent by it, is 11, including
+ one start bit and two stop bits. The data format is either 7+even
+ or 8+none.
+
+
+TO-DO LIST
+==========
+
+ * Eliminate use of scanf(), and maybe sprintf().
+
+ * Allow user setting of resync interval to trade battery life for
+ accuracy; maybe could be done via fudge factor or unit number.
+
+ * Possibly note the time since the last resync of the MSF clock to
+ MSF as the age of the last reference timestamp, ie trust the
+ clock's oscillator not very much...
+
+ * Add very slow auto-adjustment up to a value of +/- time2 to correct
+ for long-term errors in the clock value (time2 defaults to 0 so the
+ correction would be disabled by default).
+
+ * Consider trying to use the tty_clk/ppsclock support.
+
+ * Possibly use average or maximum signal quality reported during
+ resync, rather than just the last one, which may be atypical.
+
+*/
+
+
+/* Notes for HKW Elektronik GmBH Radio clock driver */
+/* Author Lyndon David, Sentinet Ltd, Feb 1997 */
+/* These notes seem also to apply usefully to the ARCRON clock. */
+
+/* The HKW clock module is a radio receiver tuned into the Rugby */
+/* MSF time signal tranmitted on 60 kHz. The clock module connects */
+/* to the computer via a serial line and transmits the time encoded */
+/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
+
+/* Clock communications, from the datasheet */
+/* All characters sent to the clock are echoed back to the controlling */
+/* device. */
+/* Transmit time/date information */
+/* syntax ASCII o<cr> */
+/* Character o may be replaced if neccesary by a character whose code */
+/* contains the lowest four bits f(hex) eg */
+/* syntax binary: xxxx1111 00001101 */
+
+/* DHD note:
+You have to wait for character echo + 10ms before sending next character.
+*/
+
+/* The clock replies to this command with a sequence of 15 characters */
+/* which contain the complete time and a final <cr> making 16 characters */
+/* in total. */
+/* The RC computer clock will not reply immediately to this command because */
+/* the start bit edge of the first reply character marks the beginning of */
+/* the second. So the RC Computer Clock will reply to this command at the */
+/* start of the next second */
+/* The characters have the following meaning */
+/* 1. hours tens */
+/* 2. hours units */
+/* 3. minutes tens */
+/* 4. minutes units */
+/* 5. seconds tens */
+/* 6. seconds units */
+/* 7. day of week 1-monday 7-sunday */
+/* 8. day of month tens */
+/* 9. day of month units */
+/* 10. month tens */
+/* 11. month units */
+/* 12. year tens */
+/* 13. year units */
+/* 14. BST/UTC status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 always 0 */
+/* bit 2 =1 if UTC is in effect, complementary to the BST bit */
+/* bit 1 =1 if BST is in effect, according to the BST bit */
+/* bit 0 BST/UTC change impending bit=1 in case of change impending */
+/* 15. status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 =1 if low battery is detected */
+/* bit 2 =1 if the very last reception attempt failed and a valid */
+/* time information already exists (bit0=1) */
+/* =0 if the last reception attempt was successful */
+/* bit 1 =1 if at least one reception since 2:30 am was successful */
+/* =0 if no reception attempt since 2:30 am was successful */
+/* bit 0 =1 if the RC Computer Clock contains valid time information */
+/* This bit is zero after reset and one after the first */
+/* successful reception attempt */
+
+/* DHD note:
+Also note g<cr> command which confirms that a resync is in progress, and
+if so what signal quality (0--5) is available.
+Also note h<cr> command which starts a resync to MSF signal.
+*/
+
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+/*
+ * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/arc%d" /* Device name and unit. */
+#define SPEED B300 /* UART speed (300 baud) */
+#define PRECISION (-4) /* Precision (~63 ms). */
+#define HIGHPRECISION (-5) /* If things are going well... */
+#define REFID "MSFa" /* Reference ID. */
+#define REFID_MSF "MSF" /* Reference ID. */
+#define REFID_DCF77 "DCF" /* Reference ID. */
+#define REFID_WWVB "WWVB" /* Reference ID. */
+#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver"
+
+#ifdef PRE_NTP420
+#define MODE ttlmax
+#else
+#define MODE ttl
+#endif
+
+#define LENARC 16 /* Format `o' timecode length. */
+
+#define BITSPERCHAR 11 /* Bits per character. */
+#define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */
+#define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */
+#define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */
+#define CHARTIME /* Time for char at 300bps. */ \
+( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
+ (BITSPERCHAR * BITTIME) ) )
+
+ /* Allow for UART to accept char half-way through final stop bit. */
+#define INITIALOFFSET (u_int32)(-BITTIME/2)
+
+ /*
+ charoffsets[x] is the time after the start of the second that byte
+ x (with the first byte being byte 1) is received by the UART,
+ assuming that the initial edge of the start bit of the first byte
+ is on-time. The values are represented as the fractional part of
+ an l_fp.
+
+ We store enough values to have the offset of each byte including
+ the trailing \r, on the assumption that the bytes follow one
+ another without gaps.
+ */
+ static const u_int32 charoffsets[LENARC+1] = {
+#if BITSPERCHAR == 11 /* Usual case. */
+ /* Offsets computed as accurately as possible... */
+ 0,
+ INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */
+ INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */
+ INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */
+ INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */
+ INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */
+ INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */
+ INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */
+ INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */
+ INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */
+ INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
+ INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
+ INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
+ INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
+ INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
+ INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
+ INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */
+#else
+ /* Offsets computed with a small rounding error... */
+ 0,
+ INITIALOFFSET + 1 * CHARTIME,
+ INITIALOFFSET + 2 * CHARTIME,
+ INITIALOFFSET + 3 * CHARTIME,
+ INITIALOFFSET + 4 * CHARTIME,
+ INITIALOFFSET + 5 * CHARTIME,
+ INITIALOFFSET + 6 * CHARTIME,
+ INITIALOFFSET + 7 * CHARTIME,
+ INITIALOFFSET + 8 * CHARTIME,
+ INITIALOFFSET + 9 * CHARTIME,
+ INITIALOFFSET + 10 * CHARTIME,
+ INITIALOFFSET + 11 * CHARTIME,
+ INITIALOFFSET + 12 * CHARTIME,
+ INITIALOFFSET + 13 * CHARTIME,
+ INITIALOFFSET + 14 * CHARTIME,
+ INITIALOFFSET + 15 * CHARTIME,
+ INITIALOFFSET + 16 * CHARTIME
+#endif
+ };
+
+#define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */
+#define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */
+#ifdef ARCRON_KEEN
+#define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */
+#else
+#define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */
+#endif
+
+ static const int moff[12] =
+{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+/* Flags for a raw open() of the clock serial device. */
+#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
+#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
+#else /* Oh well, it may not matter... */
+#define OPEN_FLAGS (O_RDWR)
+#endif
+
+
+/* Length of queue of command bytes to be sent. */
+#define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */
+/* Queue tick time; interval in seconds between chars taken off queue. */
+/* Must be >= 2 to allow o\r response to come back uninterrupted. */
+#define QUEUETICK 2 /* Allow o\r reply to finish. */
+
+/*
+ * ARC unit control structure
+ */
+struct arcunit {
+ l_fp lastrec; /* Time tag for the receive time (system). */
+ int status; /* Clock status. */
+
+ int quality; /* Quality of reception 0--5 for unit. */
+ /* We may also use the values -1 or 6 internally. */
+ u_long quality_stamp; /* Next time to reset quality average. */
+
+ u_long next_resync; /* Next resync time (s) compared to current_time. */
+ int resyncing; /* Resync in progress if true. */
+
+ /* In the outgoing queue, cmdqueue[0] is next to be sent. */
+ char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
+
+ u_long saved_flags; /* Saved fudge flags. */
+};
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+/* The flag `possible_leap' is set non-zero when any MSF unit
+ thinks a leap-second may have happened.
+
+ Set whenever we receive a valid time sample in the first hour of
+ the first day of the first/seventh months.
+
+ Outside the special hour this value is unconditionally set
+ to zero by the receive routine.
+
+ On finding itself in this timeslot, as long as the value is
+ non-negative, the receive routine sets it to a positive value to
+ indicate a resync to MSF should be performed.
+
+ In the poll routine, if this value is positive and we are not
+ already resyncing (eg from a sync that started just before
+ midnight), start resyncing and set this value negative to
+ indicate that a leap-triggered resync has been started. Having
+ set this negative prevents the receive routine setting it
+ positive and thus prevents multiple resyncs during the witching
+ hour.
+ */
+static int possible_leap = 0; /* No resync required by default. */
+#endif
+
+#if 0
+static void dummy_event_handler P((struct peer *));
+static void arc_event_handler P((struct peer *));
+#endif /* 0 */
+
+#define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */
+#define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */
+#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
+#define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */
+
+/*
+ * Function prototypes
+ */
+static int arc_start P((int, struct peer *));
+static void arc_shutdown P((int, struct peer *));
+static void arc_receive P((struct recvbuf *));
+static void arc_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arc = {
+ arc_start, /* start up driver */
+ arc_shutdown, /* shut down driver */
+ arc_poll, /* transmit poll message */
+ noentry, /* not used (old arc_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arc_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/* Queue us up for the next tick. */
+#define ENQUEUE(up) \
+ do { \
+ peer->nextaction = current_time + QUEUETICK; \
+ } while(0)
+
+/* Placeholder event handler---does nothing safely---soaks up loose tick. */
+static void
+dummy_event_handler(
+ struct peer *peer
+ )
+{
+#ifdef DEBUG
+ if(debug) { printf("arc: dummy_event_handler() called.\n"); }
+#endif
+}
+
+/*
+Normal event handler.
+
+Take first character off queue and send to clock if not a null.
+
+Shift characters down and put a null on the end.
+
+We assume that there is no parallelism so no race condition, but even
+if there is nothing bad will happen except that we might send some bad
+data to the clock once in a while.
+*/
+static void
+arc_event_handler(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = (struct arcunit *)pp->unitptr;
+ int i;
+ char c;
+#ifdef DEBUG
+ if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
+#endif
+
+ c = up->cmdqueue[0]; /* Next char to be sent. */
+ /* Shift down characters, shifting trailing \0 in at end. */
+ for(i = 0; i < CMDQUEUELEN; ++i)
+ { up->cmdqueue[i] = up->cmdqueue[i+1]; }
+
+ /* Don't send '\0' characters. */
+ if(c != '\0') {
+ if(write(pp->io.fd, &c, 1) != 1) {
+ msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
+ }
+#ifdef DEBUG
+ else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
+#endif
+ }
+
+ ENQUEUE(up);
+}
+
+/*
+ * arc_start - open the devices and initialize data for processing
+ */
+static int
+arc_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+ msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
+ }
+#endif
+
+ /* Prevent a ridiculous device number causing overflow of device[]. */
+ if((unit < 0) || (unit > 255)) { return(0); }
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
+ return(0);
+#ifdef DEBUG
+ if(debug) { printf("arc: unit %d using open().\n", unit); }
+#endif
+ fd = open(device, OPEN_FLAGS);
+ if(fd < 0) {
+#ifdef DEBUG
+ if(debug) { printf("arc: failed [open()] to open %s.\n", device); }
+#endif
+ return(0);
+ }
+
+ fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
+#ifdef DEBUG
+ if(debug)
+ { printf("arc: opened RS232 port with file descriptor %d.\n", fd); }
+#endif
+
+#ifdef HAVE_TERMIOS
+
+ tcgetattr(fd, &arg);
+
+ arg.c_iflag = IGNBRK | ISTRIP;
+ arg.c_oflag = 0;
+ arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 1;
+ arg.c_cc[VTIME] = 0;
+
+ tcsetattr(fd, TCSANOW, &arg);
+
+#else
+
+ msyslog(LOG_ERR, "ARCRON: termios not supported in this driver");
+ (void)close(fd);
+
+ return 0;
+
+#endif
+
+ up = (struct arcunit *) emalloc(sizeof(struct arcunit));
+ if(!up) { (void) close(fd); return(0); }
+ /* Set structure to all zeros... */
+ memset((char *)up, 0, sizeof(struct arcunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = arc_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->stratum = 2; /* Default to stratum 2 not 0. */
+ pp->clockdesc = DESCRIPTION;
+ if (peer->MODE > 3) {
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
+ return 0;
+ }
+#ifdef DEBUG
+ if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
+#endif
+ switch (peer->MODE) {
+ case 1:
+ memcpy((char *)&pp->refid, REFID_MSF, 4);
+ break;
+ case 2:
+ memcpy((char *)&pp->refid, REFID_DCF77, 4);
+ break;
+ case 3:
+ memcpy((char *)&pp->refid, REFID_WWVB, 4);
+ break;
+ default:
+ memcpy((char *)&pp->refid, REFID, 4);
+ break;
+ }
+ /* Spread out resyncs so that they should remain separated. */
+ up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
+
+#if 0 /* Not needed because of zeroing of arcunit structure... */
+ up->resyncing = 0; /* Not resyncing yet. */
+ up->saved_flags = 0; /* Default is all flags off. */
+ /* Clear send buffer out... */
+ {
+ int i;
+ for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
+ }
+#endif
+
+#ifdef ARCRON_KEEN
+ up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */
+#else
+ up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
+#endif
+
+ peer->action = arc_event_handler;
+
+ ENQUEUE(up);
+
+ return(1);
+}
+
+
+/*
+ * arc_shutdown - shut down the clock
+ */
+static void
+arc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+
+ peer->action = dummy_event_handler;
+
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+Compute space left in output buffer.
+*/
+static int
+space_left(
+ register struct arcunit *up
+ )
+{
+ int spaceleft;
+
+ /* Compute space left in buffer after any pending output. */
+ for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
+ { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
+ return(spaceleft);
+}
+
+/*
+Send command by copying into command buffer as far forward as possible,
+after any pending output.
+
+Indicate an error by returning 0 if there is not space for the command.
+*/
+static int
+send_slow(
+ register struct arcunit *up,
+ int fd,
+ const char *s
+ )
+{
+ int sl = strlen(s);
+ int spaceleft = space_left(up);
+
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
+#endif
+ if(spaceleft < sl) { /* Should not normally happen... */
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
+ sl, spaceleft);
+#endif
+ return(0); /* FAILED! */
+ }
+
+ /* Copy in the command to be sent. */
+ while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
+
+ return(1);
+}
+
+
+static int
+get2(char *p, int *val)
+{
+ if (!isdigit((int)p[0]) || !isdigit((int)p[1])) return 0;
+ *val = (p[0] - '0') * 10 + p[1] - '0';
+ return 1;
+}
+
+static int
+get1(char *p, int *val)
+{
+ if (!isdigit((int)p[0])) return 0;
+ *val = p[0] - '0';
+ return 1;
+}
+
+/* Macro indicating action we will take for different quality values. */
+#define quality_action(q) \
+(((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \
+ (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
+ "OK, will use clock"))
+
+ /*
+ * arc_receive - receive data from the serial interface
+ */
+ static void
+arc_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char c;
+ int i, n, wday, month, flags, status;
+ int arc_last_offset;
+ static int quality_average = 0;
+ static int quality_sum = 0;
+ static int quality_polls = 0;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+
+
+ /*
+ If the command buffer is empty, and we are resyncing, insert a
+ g\r quality request into it to poll for signal quality again.
+ */
+ if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
+#endif
+ send_slow(up, pp->io.fd, "g\r");
+ }
+
+ /*
+ The `arc_last_offset' is the offset in lastcode[] of the last byte
+ received, and which we assume actually received the input
+ timestamp.
+
+ (When we get round to using tty_clk and it is available, we
+ assume that we will receive the whole timecode with the
+ trailing \r, and that that \r will be timestamped. But this
+ assumption also works if receive the characters one-by-one.)
+ */
+ arc_last_offset = pp->lencode+rbufp->recv_length - 1;
+
+ /*
+ We catch a timestamp iff:
+
+ * The command code is `o' for a timestamp.
+
+ * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
+ exactly char in the buffer (the command code) so that we
+ only sample the first character of the timecode as our
+ `on-time' character.
+
+ * The first character in the buffer is not the echoed `\r'
+ from the `o` command (so if we are to timestamp an `\r' it
+ must not be first in the receive buffer with lencode==1.
+ (Even if we had other characters following it, we probably
+ would have a premature timestamp on the '\r'.)
+
+ * We have received at least one character (I cannot imagine
+ how it could be otherwise, but anyway...).
+ */
+ c = rbufp->recv_buffer[0];
+ if((pp->a_lastcode[0] == 'o') &&
+#ifndef ARCRON_MULTIPLE_SAMPLES
+ (pp->lencode == 1) &&
+#endif
+ ((pp->lencode != 1) || (c != '\r')) &&
+ (arc_last_offset >= 1)) {
+ /* Note that the timestamp should be corrected if >1 char rcvd. */
+ l_fp timestamp;
+ timestamp = rbufp->recv_time;
+#ifdef DEBUG
+ if(debug) { /* Show \r as `R', other non-printing char as `?'. */
+ printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
+ ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
+ rbufp->recv_length);
+ }
+#endif
+
+ /*
+ Now correct timestamp by offset of last byte received---we
+ subtract from the receive time the delay implied by the
+ extra characters received.
+
+ Reject the input if the resulting code is too long, but
+ allow for the trailing \r, normally not used but a good
+ handle for tty_clk or somesuch kernel timestamper.
+ */
+ if(arc_last_offset > LENARC) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: input code too long (%d cf %d); rejected.\n",
+ arc_last_offset, LENARC);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
+#ifdef DEBUG
+ if(debug > 1) {
+ printf(
+ "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
+ ((rbufp->recv_length > 1) ? "*** " : ""),
+ rbufp->recv_length,
+ arc_last_offset,
+ mfptoms((unsigned long)0,
+ charoffsets[arc_last_offset],
+ 1));
+ }
+#endif
+
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ /*
+ If taking multiple samples, capture the current adjusted
+ sample iff:
+
+ * No timestamp has yet been captured (it is zero), OR
+
+ * This adjusted timestamp is earlier than the one already
+ captured, on the grounds that this one suffered less
+ delay in being delivered to us and is more accurate.
+
+ */
+ if(L_ISZERO(&(up->lastrec)) ||
+ L_ISGEQ(&(up->lastrec), &timestamp))
+#endif
+ {
+#ifdef DEBUG
+ if(debug > 1) {
+ printf("arc: system timestamp captured.\n");
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ if(!L_ISZERO(&(up->lastrec))) {
+ l_fp diff;
+ diff = up->lastrec;
+ L_SUB(&diff, &timestamp);
+ printf("arc: adjusted timestamp by -%sms.\n",
+ mfptoms(diff.l_i, diff.l_f, 3));
+ }
+#endif
+ }
+#endif
+ up->lastrec = timestamp;
+ }
+
+ }
+
+ /* Just in case we still have lots of rubbish in the buffer... */
+ /* ...and to avoid the same timestamp being reused by mistake, */
+ /* eg on receipt of the \r coming in on its own after the */
+ /* timecode. */
+ if(pp->lencode >= LENARC) {
+#ifdef DEBUG
+ if(debug && (rbufp->recv_buffer[0] != '\r'))
+ { printf("arc: rubbish in pp->a_lastcode[].\n"); }
+#endif
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Append input to code buffer, avoiding overflow. */
+ for(i = 0; i < rbufp->recv_length; i++) {
+ if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
+ c = rbufp->recv_buffer[i];
+
+ /* Drop trailing '\r's and drop `h' command echo totally. */
+ if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
+
+ /*
+ If we've just put an `o' in the lastcode[0], clear the
+ timestamp in anticipation of a timecode arriving soon.
+
+ We would expect to get to process this before any of the
+ timecode arrives.
+ */
+ if((c == 'o') && (pp->lencode == 1)) {
+ L_CLR(&(up->lastrec));
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: clearing timestamp.\n"); }
+#endif
+ }
+ }
+ if (pp->lencode == 0) return;
+
+ /* Handle a quality message. */
+ if(pp->a_lastcode[0] == 'g') {
+ int r, q;
+
+ if(pp->lencode < 3) { return; } /* Need more data... */
+ r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
+ q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
+ if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
+ ((r & 0x70) != 0x30)) {
+ /* Badly formatted response. */
+#ifdef DEBUG
+ if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
+#endif
+ return;
+ }
+ if(r == '3') { /* Only use quality value whilst sync in progress. */
+ if (up->quality_stamp < current_time) {
+ struct calendar cal;
+ l_fp new_stamp;
+
+ get_systime (&new_stamp);
+ caljulian (new_stamp.l_ui, &cal);
+ up->quality_stamp =
+ current_time + 60 - cal.second + 5;
+ quality_sum = 0;
+ quality_polls = 0;
+ }
+ quality_sum += (q & 0xf);
+ quality_polls++;
+ quality_average = (quality_sum / quality_polls);
+#ifdef DEBUG
+ if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
+#endif
+ } else if( /* (r == '2') && */ up->resyncing) {
+ up->quality = quality_average;
+#ifdef DEBUG
+ if(debug)
+ {
+ printf("arc: sync finished, signal quality %d: %s\n",
+ up->quality,
+ quality_action(up->quality));
+ }
+#endif
+ msyslog(LOG_NOTICE,
+ "ARCRON: sync finished, signal quality %d: %s",
+ up->quality,
+ quality_action(up->quality));
+ up->resyncing = 0; /* Resync is over. */
+ quality_average = 0;
+ quality_sum = 0;
+ quality_polls = 0;
+
+#ifdef ARCRON_KEEN
+ /* Clock quality dubious; resync earlier than usual. */
+ if((up->quality == QUALITY_UNKNOWN) ||
+ (up->quality < MIN_CLOCK_QUALITY_OK))
+ { up->next_resync = current_time + RETRY_RESYNC_TIME; }
+#endif
+ }
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Stop now if this is not a timecode message. */
+ if(pp->a_lastcode[0] != 'o') {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* If we don't have enough data, wait for more... */
+ if(pp->lencode < LENARC) { return; }
+
+
+ /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
+#endif
+
+ /* But check that we actually captured a system timestamp on it. */
+ if(L_ISZERO(&(up->lastrec))) {
+#ifdef DEBUG
+ if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ Append a mark of the clock's received signal quality for the
+ benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
+ quality value to `6' for his s/w) and terminate the string for
+ sure. This should not go off the buffer end.
+ */
+ pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
+ '6' : ('0' + up->quality));
+ pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
+
+#ifdef PRE_NTP420
+ /* We don't use the micro-/milli- second part... */
+ pp->usec = 0;
+ pp->msec = 0;
+#else
+ /* We don't use the nano-second part... */
+ pp->nsec = 0;
+#endif
+ /* Validate format and numbers. */
+ if (pp->a_lastcode[0] != 'o'
+ || !get2(pp->a_lastcode + 1, &pp->hour)
+ || !get2(pp->a_lastcode + 3, &pp->minute)
+ || !get2(pp->a_lastcode + 5, &pp->second)
+ || !get1(pp->a_lastcode + 7, &wday)
+ || !get2(pp->a_lastcode + 8, &pp->day)
+ || !get2(pp->a_lastcode + 10, &month)
+ || !get2(pp->a_lastcode + 12, &pp->year)) {
+#ifdef DEBUG
+ /* Would expect to have caught major problems already... */
+ if(debug) { printf("arc: badly formatted data.\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ flags = pp->a_lastcode[14];
+ status = pp->a_lastcode[15];
+#ifdef DEBUG
+ if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
+#endif
+ n = 9;
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (wday < 1) || (wday > 7) ||
+ (pp->day < 1) || (pp->day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ if(peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+ /* Check that BST/UTC bits are the complement of one another. */
+ if(!(bst & 2) == !(bst & 4)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ }
+ if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
+
+ /* Year-2000 alert! */
+ /* Attempt to wrap 2-digit date into sensible window. */
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */
+ pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */
+ /*
+ Attempt to do the right thing by screaming that the code will
+ soon break when we get to the end of its useful life. What a
+ hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
+ */
+ if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */
+ /*This should get attention B^> */
+ msyslog(LOG_NOTICE,
+ "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
+ }
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
+ n,
+ pp->hour, pp->minute, pp->second,
+ pp->day, month, pp->year, flags, status);
+ }
+#endif
+
+ /*
+ The status value tested for is not strictly supported by the
+ clock spec since the value of bit 2 (0x4) is claimed to be
+ undefined for MSF, yet does seem to indicate if the last resync
+ was successful or not.
+ */
+ pp->leap = LEAP_NOWARNING;
+ status &= 0x7;
+ if(status == 0x3) {
+ if(status != up->status)
+ { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
+ } else {
+ if(status != up->status) {
+ msyslog(LOG_NOTICE, "ARCRON: signal lost");
+ pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
+ up->status = status;
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ up->status = status;
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+
+ pp->day += moff[month - 1];
+
+ if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
+
+ /* Convert to UTC if required */
+ if(bst & 2) {
+ pp->hour--;
+ if (pp->hour < 0) {
+ pp->hour = 23;
+ pp->day--;
+ /* If we try to wrap round the year
+ * (BST on 1st Jan), reject.*/
+ if(pp->day < 0) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ }
+ }
+ }
+
+ if(peer->MODE > 0) {
+ if(pp->sloppyclockflag & CLK_FLAG1) {
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+
+ /*
+ * Convert to GMT for sites that distribute localtime.
+ * This means we have to do Y2K conversion on the
+ * 2-digit year; otherwise, we get the time wrong.
+ */
+
+ local.tm_year = pp->year-1900;
+ local.tm_mon = month-1;
+ local.tm_mday = pp->day;
+ local.tm_hour = pp->hour;
+ local.tm_min = pp->minute;
+ local.tm_sec = pp->second;
+ switch (peer->MODE) {
+ case 1:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 2:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 3:
+ switch (flags & 3) {
+ case 0: /* It is unclear exactly when the
+ Arcron changes from DST->ST and
+ ST->DST. Testing has shown this
+ to be irregular. For the time
+ being, let the OS decide. */
+ local.tm_isdst = 0;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 00 (0)\n");
+#endif
+ break;
+ case 1: /* dst->st time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 01 (1)\n");
+#endif
+ break;
+ case 2: /* st->dst time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 10 (2)\n");
+#endif
+ break;
+ case 3: /* dst time */
+ local.tm_isdst = 1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 11 (3)\n");
+#endif
+ break;
+ }
+ break;
+ default:
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
+ peer->MODE);
+ return;
+ break;
+ }
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ pp->lencode = 0;
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ pp->year = gmtp->tm_year+1900;
+ month = gmtp->tm_mon+1;
+ pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
+ /* pp->day = gmtp->tm_yday; */
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ {
+ printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+ }
+#endif
+ } else
+ {
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(pp->year,month,pp->day);
+ }
+ }
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ /* If clock signal quality is
+ * unknown, revert to default PRECISION...*/
+ if(up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else { /* ...else improve precision if flag3 is set... */
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ } else {
+ if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ } else if (up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ }
+
+ /* Notice and log any change (eg from initial defaults) for flags. */
+ if(up->saved_flags != pp->sloppyclockflag) {
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
+ ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
+ /* Note effects of flags changing... */
+ if(debug) {
+ printf("arc: PRECISION = %d.\n", peer->precision);
+ }
+#endif
+ up->saved_flags = pp->sloppyclockflag;
+ }
+
+ /* Note time of last believable timestamp. */
+ pp->lastrec = up->lastrec;
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /* Find out if a leap-second might just have happened...
+ (ie is this the first hour of the first day of Jan or Jul?)
+ */
+ if((pp->hour == 0) &&
+ (pp->day == 1) &&
+ ((month == 1) || (month == 7))) {
+ if(possible_leap >= 0) {
+ /* A leap may have happened, and no resync has started yet...*/
+ possible_leap = 1;
+ }
+ } else {
+ /* Definitely not leap-second territory... */
+ possible_leap = 0;
+ }
+#endif
+
+ if (!refclock_process(pp)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+
+/* request_time() sends a time request to the clock with given peer. */
+/* This automatically reports a fault if necessary. */
+/* No data should be sent after this until arc_poll() returns. */
+static void request_time P((int, struct peer *));
+static void
+request_time(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = (struct arcunit *)pp->unitptr;
+#ifdef DEBUG
+ if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
+#endif
+ if (!send_slow(up, pp->io.fd, "o\r")) {
+#ifdef DEBUG
+ if (debug) {
+ printf("arc: unit %d: problem sending", unit);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->polls++;
+}
+
+/*
+ * arc_poll - called by the transmit procedure
+ */
+static void
+arc_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int resync_needed; /* Should we start a resync? */
+
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+#if 0
+ pp->lencode = 0;
+ memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
+#endif
+
+#if 0
+ /* Flush input. */
+ tcflush(pp->io.fd, TCIFLUSH);
+#endif
+
+ /* Resync if our next scheduled resync time is here or has passed. */
+ resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
+ (up->next_resync <= current_time) );
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /*
+ Try to catch a potential leap-second insertion or deletion quickly.
+
+ In addition to the normal NTP fun of clocks that don't report
+ leap-seconds spooking their hosts, this clock does not even
+ sample the radio sugnal the whole time, so may miss a
+ leap-second insertion or deletion for up to a whole sample
+ time.
+
+ To try to minimise this effect, if in the first few minutes of
+ the day immediately following a leap-second-insertion point
+ (ie in the first hour of the first day of the first and sixth
+ months), and if the last resync was in the previous day, and a
+ resync is not already in progress, resync the clock
+ immediately.
+
+ */
+ if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */
+ (!up->resyncing)) { /* No resync in progress yet. */
+ resync_needed = 1;
+ possible_leap = -1; /* Prevent multiple resyncs. */
+ msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
+ }
+#endif
+
+ /* Do a resync if required... */
+ if(resync_needed) {
+ /* First, reset quality value to `unknown' so we can detect */
+ /* when a quality message has been responded to by this */
+ /* being set to some other value. */
+ up->quality = QUALITY_UNKNOWN;
+
+ /* Note that we are resyncing... */
+ up->resyncing = 1;
+
+ /* Now actually send the resync command and an immediate poll. */
+#ifdef DEBUG
+ if(debug) { printf("arc: sending resync command (h\\r).\n"); }
+#endif
+ msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
+ send_slow(up, pp->io.fd, "h\r");
+
+ /* Schedule our next resync... */
+ up->next_resync = current_time + DEFAULT_RESYNC_TIME;
+
+ /* Drop through to request time if appropriate. */
+ }
+
+ /* If clock quality is too poor to trust, indicate a fault. */
+ /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
+ /* we'll cross our fingers and just hope that the thing */
+ /* synced so quickly we did not catch it---we'll */
+ /* double-check the clock is OK elsewhere. */
+ if(
+#ifdef ARCRON_KEEN
+ (up->quality != QUALITY_UNKNOWN) &&
+#else
+ (up->quality == QUALITY_UNKNOWN) ||
+#endif
+ (up->quality < MIN_CLOCK_QUALITY_OK)) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: clock quality %d too poor.\n", up->quality);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ /* This is the normal case: request a timestamp. */
+ request_time(unit, peer);
+}
+
+#else
+int refclock_arc_bs;
+#endif
diff --git a/contrib/ntp/ntpd/refclock_as2201.c b/contrib/ntp/ntpd/refclock_as2201.c
new file mode 100644
index 0000000..f04d417b
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_as2201.c
@@ -0,0 +1,388 @@
+/*
+ * refclock_as2201 - clock driver for the Austron 2201A GPS
+ * Timing Receiver
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_AS2201)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Austron 2200A/2201A GPS Receiver with
+ * Buffered RS-232-C Interface Module. Note that the original 2200/2201
+ * receivers will not work reliably with this driver, since the older
+ * design cannot accept input commands at any reasonable data rate.
+ *
+ * The program sends a "*toc\r" to the radio and expects a response of
+ * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
+ * day of year, hh:mm:ss = second of day and mmm = millisecond of
+ * second. Then, it sends statistics commands to the radio and expects
+ * a multi-line reply showing the corresponding statistics or other
+ * selected data. Statistics commands are sent in order as determined by
+ * a vector of commands; these might have to be changed with different
+ * radio options. If flag4 of the fudge configuration command is set to
+ * 1, the statistics data are written to the clockstats file for later
+ * processing.
+ *
+ * In order for this code to work, the radio must be placed in non-
+ * interactive mode using the "off" command and with a single <cr>
+ * response using the "term cr" command. The setting of the "echo"
+ * and "df" commands does not matter. The radio should select UTC
+ * timescale using the "ts utc" command.
+ *
+ * There are two modes of operation for this driver. The first with
+ * default configuration is used with stock kernels and serial-line
+ * drivers and works with almost any machine. In this mode the driver
+ * assumes the radio captures a timestamp upon receipt of the "*" that
+ * begins the driver query. Accuracies in this mode are in the order of
+ * a millisecond or two and the receiver can be connected to only one
+ * host.
+ *
+ * The second mode of operation can be used for SunOS kernels that have
+ * been modified with the ppsclock streams module included in this
+ * distribution. The mode is enabled if flag3 of the fudge configuration
+ * command has been set to 1. In this mode a precise timestamp is
+ * available using a gadget box and 1-pps signal from the receiver. This
+ * improves the accuracy to the order of a few tens of microseconds. In
+ * addition, the serial output and 1-pps signal can be bussed to more
+ * than one hosts, but only one of them should be connected to the
+ * radio input data line.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define SMAX 200 /* statistics buffer length */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
+
+#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
+
+/*
+ * AS2201 unit control structure.
+ */
+struct as2201unit {
+ char *lastptr; /* statistics buffer pointer */
+ char stats[SMAX]; /* statistics buffer */
+ int linect; /* count of lines remaining */
+ int index; /* current statistics command */
+};
+
+/*
+ * Radio commands to extract statitistics
+ *
+ * A command consists of an ASCII string terminated by a <cr> (\r). The
+ * command list consist of a sequence of commands terminated by a null
+ * string ("\0"). One command from the list is sent immediately
+ * following each received timecode (*toc\r command) and the ASCII
+ * strings received from the radio are saved along with the timecode in
+ * the clockstats file. Subsequent commands are sent at each timecode,
+ * with the last one in the list followed by the first one. The data
+ * received from the radio consist of ASCII strings, each terminated by
+ * a <cr> (\r) character. The number of strings for each command is
+ * specified as the first line of output as an ASCII-encode number. Note
+ * that the ETF command requires the Input Buffer Module and the LORAN
+ * commands require the LORAN Assist Module. However, if these modules
+ * are not installed, the radio and this driver will continue to operate
+ * successfuly, but no data will be captured for these commands.
+ */
+static char stat_command[][30] = {
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "ID;OPT;VER\r", /* model; options; software version */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "UTC\r", /* UTC leap info */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
+ "\0" /* end of table */
+};
+
+/*
+ * Function prototypes
+ */
+static int as2201_start P((int, struct peer *));
+static void as2201_shutdown P((int, struct peer *));
+static void as2201_receive P((struct recvbuf *));
+static void as2201_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_as2201 = {
+ as2201_start, /* start up driver */
+ as2201_shutdown, /* shut down driver */
+ as2201_poll, /* transmit poll message */
+ noentry, /* not used (old as2201_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old as2201_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * as2201_start - open the devices and initialize data for processing
+ */
+static int
+as2201_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct as2201unit *)
+ emalloc(sizeof(struct as2201unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct as2201unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = as2201_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->lastptr = up->stats;
+ up->index = 0;
+ return (1);
+}
+
+
+/*
+ * as2201_shutdown - shut down the clock
+ */
+static void
+as2201_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * as2201__receive - receive data from the serial interface
+ */
+static void
+as2201_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: timecode %d %d %s\n",
+ up->linect, pp->lencode, pp->a_lastcode);
+#endif
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linect is greater than zero, we must be in the middle of a
+ * statistics operation, so simply tack the received data at the
+ * end of the statistics string. If not, we could either have
+ * just received the timecode itself or a decimal number
+ * indicating the number of following lines of the statistics
+ * reply. In the former case, write the accumulated statistics
+ * data to the clockstats file and continue onward to process
+ * the timecode; in the later case, save the number of lines and
+ * quietly return.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ pp->lastrec = trtmp;
+ if (up->linect > 0) {
+ up->linect--;
+ if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ return;
+ } else {
+ if (pp->lencode == 1) {
+ up->linect = atoi(pp->a_lastcode);
+ return;
+ } else {
+ record_clock_stats(&peer->srcadr, up->stats);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: stat %s\n", up->stats);
+#endif
+ }
+ }
+ up->lastptr = up->stats;
+ *up->lastptr = '\0';
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTOC) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "yy:ddd:hh:mm:ss.mmm"
+ */
+ if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Test for synchronization (this is a temporary crock).
+ */
+ if (pp->a_lastcode[2] != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * If CLK_FLAG4 is set, initialize the statistics buffer and
+ * send the next command. If not, simply write the timecode to
+ * the clockstats file.
+ */
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, stat_command[up->index]);
+ up->lastptr += strlen(stat_command[up->index]);
+ up->lastptr--;
+ *up->lastptr = '\0';
+ (void)write(pp->io.fd, stat_command[up->index],
+ strlen(stat_command[up->index]));
+ up->index++;
+ if (*stat_command[up->index] == '\0')
+ up->index = 0;
+ }
+}
+
+
+/*
+ * as2201_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+as2201_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ /*
+ * Send a "\r*toc\r" to get things going. We go to great pains
+ * to avoid changing state, since there may be more than one
+ * eavesdropper watching the radio.
+ */
+ pp = peer->procptr;
+ if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else {
+ pp->polls++;
+ if (!(pp->sloppyclockflag & CLK_FLAG2))
+ get_systime(&pp->lastrec);
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+#else
+int refclock_as2201_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_atom.c b/contrib/ntp/ntpd/refclock_atom.c
new file mode 100644
index 0000000..2e469ec
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_atom.c
@@ -0,0 +1,517 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_ATOM)
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver furnishes an interface for pulse-per-second (PPS) signals
+ * produced by a cesium clock, timing receiver or related equipment. It
+ * can be used to remove accumulated jitter and retime a secondary
+ * server when synchronized to a primary server over a congested, wide-
+ * area network and before redistributing the time to local clients.
+ *
+ * Before this driver becomes active, the local clock must be set to
+ * within +-500 ms by another means, such as a radio clock or NTP
+ * itself. There are two ways to connect the PPS signal, normally at TTL
+ * levels, to the computer. One is to shift to EIA levels and connect to
+ * pin 8 (DCD) of a serial port. This requires a level converter and
+ * may require a one-shot flipflop to lengthen the pulse. The other is
+ * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
+ * port. These methods are architecture dependent.
+ *
+ * Both methods require a modified device driver and kernel interface
+ * compatible with the Pulse-per-Second API for Unix-like Operating
+ * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
+ * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
+ * present only the Alpha implementation provides the full generality of
+ * the API with multiple PPS drivers and multiple handles per driver. If
+ * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
+ * header file and kernel support specific to each operating system.
+ * However, this driver can operate without this interface if means are
+ * proviced to call the pps_sample() routine from another driver. Please
+ * note; if the PPSAPI interface is present, it must be used.
+ *
+ * In many configurations a single port is used for the radio timecode
+ * and PPS signal. In order to provide for this configuration and others
+ * involving dedicated multiple serial/parallel ports, the driver first
+ * attempts to open the device /dev/pps%d, where %d is the unit number.
+ * If this fails, the driver attempts to open the device specified by
+ * the pps configuration command. If a port is to be shared, the pps
+ * command must be placed before the radio device(s) and the radio
+ * device(s) must be placed before the PPS driver(s) in the
+ * configuration file.
+ *
+ * This driver normally uses the PLL/FLL clock discipline implemented in
+ * the ntpd code. Ordinarily, this is the most accurate means, as the
+ * median filter in the driver interface is much larger than in the
+ * kernel. However, if the systemic clock frequency error is large (tens
+ * to hundreds of PPM), it's better to used the kernel support, if
+ * available.
+ *
+ * Fudge Factors
+ *
+ * If flag2 is dim (default), the on-time epoch is the assert edge of
+ * the PPS signal; if lit, the on-time epoch is the clear edge. If flag2
+ * is lit, the assert edge is used; if flag3 is dim (default), the
+ * kernel PPS support is disabled; if lit it is enabled. The time1
+ * parameter can be used to compensate for miscellaneous device driver
+ * and OS delays.
+ */
+/*
+ * Interface definitions
+ */
+#ifdef HAVE_PPSAPI
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#endif /* HAVE_PPSAPI */
+
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+#define NANOSECOND 1000000000 /* one second (ns) */
+#define RANGEGATE 500000 /* range gate (ns) */
+
+static struct peer *pps_peer; /* atom driver for PPS sources */
+
+#ifdef HAVE_PPSAPI
+/*
+ * PPS unit control structure
+ */
+struct ppsunit {
+ struct timespec ts; /* last timestamp */
+ int fddev; /* pps device descriptor */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+};
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_poll P((int, struct peer *));
+static void atom_shutdown P((int, struct peer *));
+#ifdef HAVE_PPSAPI
+static void atom_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static void atom_timer P((int, struct peer *));
+static int atom_ppsapi P((struct peer *, int));
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Transfer vector
+ */
+#ifdef HAVE_PPSAPI
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ atom_control, /* fudge control */
+ noentry, /* initialize driver (not used) */
+ noentry, /* buginfo (not used) */
+ atom_timer, /* called once per second */
+};
+#else /* HAVE_PPSAPI */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ noentry, /* fudge control (not used) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* buginfo (not used) */
+ NOFLAGS /* not used */
+};
+#endif /* HAVE_PPPSAPI */
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+#ifdef HAVE_PPSAPI
+ register struct ppsunit *up;
+ char device[80];
+ int mode;
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ pps_peer = peer;
+ pp = peer->procptr;
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ pp->stratum = STRATUM_UNSPEC;
+ memcpy((char *)&pp->refid, REFID, 4);
+#ifdef HAVE_PPSAPI
+ up = emalloc(sizeof(struct ppsunit));
+ memset(up, 0, sizeof(struct ppsunit));
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Open PPS device. This can be any serial or parallel port and
+ * not necessarily the port used for the associated radio.
+ */
+ sprintf(device, DEVICE, unit);
+ up->fddev = open(device, O_RDWR, 0777);
+ if (up->fddev <= 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Light off the PPSAPI interface.
+ */
+ if (time_pps_create(up->fddev, &up->handle) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_create failed: %m");
+ return (0);
+ }
+
+ /*
+ * If the mode is nonzero, use that for the time_pps_setparams()
+ * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
+ * flag3 is lit.
+ */
+ mode = peer->ttl;
+ if (mode == 0)
+ mode = PPS_CAPTUREASSERT;
+ return (atom_ppsapi(peer, mode));
+#else /* HAVE_PPSAPI */
+ return (1);
+#endif /* HAVE_PPSAPI */
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ register struct ppsunit *up;
+
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+#ifdef HAVE_PPSAPI
+ if (up->fddev > 0)
+ close(up->fddev);
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+#endif /* HAVE_PPSAPI */
+ if (pps_peer == peer)
+ pps_peer = NULL;
+ free(up);
+}
+
+
+#ifdef HAVE_PPSAPI
+/*
+ * atom_control - fudge control
+ */
+static void
+atom_control(
+ int unit, /* unit (not used */
+ struct refclockstat *in, /* input parameters (not uded) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ int mode;
+
+ pp = peer->procptr;
+ if (peer->ttl != 0) /* all legal modes must be nonzero */
+ return;
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ mode = PPS_CAPTURECLEAR;
+ else
+ mode = PPS_CAPTUREASSERT;
+ atom_ppsapi(peer, mode);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+atom_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int mode /* mode */
+ )
+{
+ struct refclockproc *pp;
+ register struct ppsunit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+ if (up->handle == 0)
+ return (0);
+
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ up->pps_params.api_version = PPS_API_VERS_1;
+ up->pps_params.mode = mode | PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (pp->sloppyclockflag & CLK_FLAG3) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",
+ up->fddev, capability, up->pps_params.api_version,
+ up->pps_params.mode);
+ }
+#endif
+ return (1);
+}
+
+
+/*
+ * atom_timer - called once per second
+ *
+ * This routine is called once per second when the PPSAPI interface is
+ * present. It snatches the PPS timestamp from the kernel and saves the
+ * sign-extended fraction in a circular buffer for processing at the
+ * next poll event.
+ */
+static void
+atom_timer(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ register struct ppsunit *up;
+ struct refclockproc *pp;
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ long sec, nsec;
+ double dtemp;
+ char tbuf[80]; /* monitor buffer */
+
+ /*
+ * Convert the timespec nanoseconds field to signed double and
+ * save in the median filter. for billboards. No harm is done if
+ * previous data are overwritten. If the discipline comes bum or
+ * the data grow stale, just forget it. A range gate rejects new
+ * samples if less than a jiggle time from the next second.
+ */
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+ if (up->handle == 0)
+ return;
+
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ /*
+ * There can be zero, one or two PPS seconds between polls. If
+ * zero, either the poll clock is slightly faster than the PPS
+ * clock or the PPS clock has died. If the PPS clock advanced
+ * once between polls, we make sure the fraction time difference
+ * since the last sample is within the range gate of 5 ms (500
+ * PPM). If the PPS clock advanced twice since the last poll,
+ * the poll bracketed more than one second and the first second
+ * was lost to a slip. Since the interval since the last sample
+ * found is now two seconds, just widen the range gate. If the
+ * PPS clock advanced three or more times, either the signal has
+ * failed for a number of seconds or we have runts, in which
+ * case just ignore them.
+ *
+ * If flag4 is lit, record each second offset to clockstats.
+ * That's so we can make awesome Allan deviation plots.
+ */
+ sec = ts.tv_sec - up->ts.tv_sec;
+ nsec = ts.tv_nsec - up->ts.tv_nsec;
+ up->ts = ts;
+ if (nsec < 0) {
+ sec --;
+ nsec += NANOSECOND;
+ } else if (nsec >= NANOSECOND) {
+ sec++;
+ nsec -= NANOSECOND;
+ }
+ if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)
+ return;
+
+ else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)
+ return;
+
+ pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ if (dtemp >= FRAC)
+ pp->lastrec.l_ui++;
+ pp->lastrec.l_uf = (u_int32)dtemp;
+ if (ts.tv_nsec > NANOSECOND / 2)
+ ts.tv_nsec -= NANOSECOND;
+ dtemp = -(double)ts.tv_nsec / NANOSECOND;
+ SAMPLE(dtemp + pp->fudgetime1);
+ if (pp->sloppyclockflag & CLK_FLAG4){
+ sprintf(tbuf, "%.9f", dtemp);
+ record_clock_stats(&peer->srcadr, tbuf);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf("atom_timer: %lu %f %f\n", current_time,
+ dtemp, pp->fudgetime1);
+#endif
+ return;
+}
+#endif /* HAVE_PPSAPI */
+
+
+/*
+ * pps_sample - receive PPS data from some other clock driver
+ *
+ * This routine is called once per second when the external clock driver
+ * processes PPS information. It processes the PPS timestamp and saves
+ * the sign-extended fraction in a circular buffer for processing at the
+ * next poll event. This works only for a single PPS device.
+ *
+ * The routine should be used by another configured driver ONLY when
+ * this driver is configured as well and the PPSAPI is NOT in use.
+ */
+int
+pps_sample(
+ l_fp *offset /* PPS offset */
+ )
+{
+ register struct peer *peer;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ double doffset;
+
+ peer = pps_peer;
+ if (peer == NULL)
+ return (1);
+
+ pp = peer->procptr;
+
+ /*
+ * Convert the timeval to l_fp and save for billboards. Sign-
+ * extend the fraction and stash in the buffer. No harm is done
+ * if previous data are overwritten. If the discipline comes bum
+ * or the data grow stale, just forget it.
+ */
+ pp->lastrec = *offset;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+ return (0);
+}
+
+
+/*
+ * atom_poll - called by the transmit procedure
+ */
+static void
+atom_poll(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ pp->polls++;
+
+ /*
+ * Valid time is returned only if the prefer peer has survived
+ * the intersection algorithm and within 0.4 s of local time
+ * and not too long ago. This ensures the PPS time is within
+ * 0.5 s of the local time and the seconds numbering is
+ * unambiguous. Note that the leap bits, stratum and refid are
+ * set from the prefer peer, unless overriden by a fudge
+ * command.
+ */
+ if (pp->codeproc == pp->coderecv) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+
+ } else if (sys_prefer == NULL) {
+ pp->codeproc = pp->coderecv;
+ return;
+
+ } else if (fabs(sys_prefer->offset) >= 0.4) {
+ pp->codeproc = pp->coderecv;
+ return;
+ }
+ pp->leap = sys_prefer->leap;
+ if (pp->stratum >= STRATUM_UNSPEC)
+ peer->stratum = sys_prefer->stratum;
+ else
+ peer->stratum = pp->stratum;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_atom_bs;
+int
+pps_sample(
+ l_fp *offset /* PPS offset */
+ )
+{
+ return (1);
+}
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_bancomm.c b/contrib/ntp/ntpd/refclock_bancomm.c
new file mode 100644
index 0000000..9fc8c82
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_bancomm.c
@@ -0,0 +1,467 @@
+/* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME
+ * Time and Frequency Processor. It requires the BANCOMM bc635VME/
+ * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
+ * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
+ * IIi-cEngine running Solaris 2.6.
+ *
+ * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
+ * Ottawa, Canada
+ *
+ * Date: July 1999
+ *
+ * Note(s): The refclock type has been defined as 16.
+ *
+ * This program has been modelled after the Bancomm driver
+ * originally written by R. Schmidt of Time Service, U.S.
+ * Naval Observatory for a HP-UX machine. Since the original
+ * authors no longer plan to maintain this code, all
+ * references to the HP-UX vme2 driver subsystem bave been
+ * removed. Functions vme_report_event(), vme_receive(),
+ * vme_control() and vme_buginfo() have been deleted because
+ * they are no longer being used.
+ *
+ * The time on the bc635 TFP must be set to GMT due to the
+ * fact that NTP makes use of GMT for all its calculations.
+ *
+ * Installation of the Datum/Bancomm driver creates the
+ * device file /dev/btfp0
+ *
+ * 04/28/2005 Rob Neal
+ * Modified to add support for Symmetricom bc637PCI-U Time &
+ * Frequency Processor.
+ * Card bus type (VME/VXI or PCI) and environment are specified via the
+ * "mode" keyword on the server command in ntp.conf.
+ * server 127.127.16.u prefer mode m (...)
+ * Modes currently supported are
+ * 1 : FreeBSD PCI 635/637.
+ * 2 : Linux or Windows PCI 635/637.
+ * not specified, or other number:
+ * : Assumed to be VME/VXI legacy Bancomm card on Solaris.
+ * Linux and Windows platforms require Symmetricoms' proprietary driver
+ * for the TFP card.
+ * Tested on FreeBSD 5.3 with a 637 card.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_BANC)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+
+/* STUFF BY RES */
+struct btfp_time /* Structure for reading 5 time words */
+ /* in one ioctl(2) operation. */
+{
+ unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
+};
+/* SunOS5 ioctl commands definitions.*/
+#define BTFPIOC ( 'b'<< 8 )
+#define IOCIO( l, n ) ( BTFPIOC | n )
+#define IOCIOR( l, n, s ) ( BTFPIOC | n )
+#define IOCIORN( l, n, s ) ( BTFPIOC | n )
+#define IOCIOWN( l, n, s ) ( BTFPIOC | n )
+
+/***** Simple ioctl commands *****/
+#define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
+#define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
+#define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
+/***** Compound ioctl commands *****/
+
+/* Read all 5 time words in one call. */
+#define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
+
+#if defined(__FreeBSD__)
+#undef READTIME
+#define READTIME _IOR('u', 5, struct btfp_time )
+#endif
+
+#define VMEFD "/dev/btfp0"
+
+struct vmedate { /* structure returned by get_vmetime.c */
+ unsigned short year;
+ unsigned short day;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ long frac;
+ unsigned short status;
+};
+
+/* END OF STUFF FROM RES */
+typedef void *SYMMT_PCI_HANDLE;
+
+/*
+ * VME interface parameters.
+ */
+#define VMEPRECISION (-21) /* precision assumed (1 us) */
+#define USNOREFID "BTFP" /* or whatever */
+#define VMEREFID "BTFP" /* reference id */
+#define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
+#define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
+/* clock type 16 is used here */
+#define GMT 0 /* hour offset from Greenwich */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time(s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern volatile int debug; /* global debug flag */
+
+/*
+ * VME unit control structure.
+ * Changes made to vmeunit structure. Most members are now available in the
+ * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
+ */
+struct vmeunit {
+ struct vmedate vmedata; /* data returned from vme read */
+ u_long lasttime; /* last time clock heard from */
+};
+
+/*
+ * Function prototypes
+ */
+static int vme_start (int, struct peer *);
+static void vme_shutdown (int, struct peer *);
+static void vme_receive (struct recvbuf *);
+static void vme_poll (int unit, struct peer *);
+struct vmedate *get_datumtime(struct vmedate *);
+void tvme_fill(struct vmedate *, uint32_t btm[2]);
+/*
+ * Define the bc*() functions as weak so we can compile/link without them.
+ * Only clients with the card will have the proprietary vendor device driver
+ * and interface library needed for use on Linux/Windows platforms.
+ */
+extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
+extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
+extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_bancomm = {
+ vme_start, /* start up driver */
+ vme_shutdown, /* shut down driver */
+ vme_poll, /* transmit poll message */
+ noentry, /* not used (old vme_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old vme_buginfo) */
+ NOFLAGS /* not used */
+};
+
+int fd_vme; /* file descriptor for ioctls */
+int regvalue;
+int tfp_type; /* mode selector, indicate platform and driver interface */
+SYMMT_PCI_HANDLE stfp_handle;
+
+
+/*
+ * vme_start - open the VME device and initialize data for processing
+ */
+static int
+vme_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+ int dummy;
+ char vmedev[20];
+
+ tfp_type = (int)(peer->ttl);
+ switch (tfp_type) {
+ case 1:
+ break;
+ case 2:
+ stfp_handle = bcStartPci(); /* init the card in lin/win */
+ break;
+ default:
+ break;
+ }
+ /*
+ * Open VME device
+ */
+#ifdef DEBUG
+
+ printf("Opening DATUM VME DEVICE \n");
+#endif
+ if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
+ msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
+ return (0);
+ }
+ else {
+ switch (tfp_type) {
+ case 1: break;
+ case 2: break;
+ default:
+ /* Release capture lockout in case it was set before. */
+ if( ioctl( fd_vme, RUNLOCK, &dummy ) )
+ msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
+
+ regvalue = 0; /* More esoteric stuff to do... */
+ if( ioctl( fd_vme, WCR0, &regvalue ) )
+ msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
+ break;
+ }
+ }
+
+ /*
+ * Allocate unit structure
+ */
+ vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
+ bzero((char *)vme, sizeof(struct vmeunit));
+
+
+ /*
+ * Set up the structures
+ */
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t) vme;
+ pp->timestarted = current_time;
+
+ pp->io.clock_recv = vme_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd_vme;
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success. Note that root delay and root dispersion are
+ * always zero for this clock.
+ */
+ peer->precision = VMEPRECISION;
+ memcpy(&pp->refid, USNOREFID,4);
+ return (1);
+}
+
+
+/*
+ * vme_shutdown - shut down a VME clock
+ */
+static void
+vme_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ pp = peer->procptr;
+ vme = (struct vmeunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ pp->unitptr = NULL;
+ free(vme);
+ if (tfp_type == 2) bcStopPci(stfp_handle);
+}
+
+
+/*
+ * vme_receive - receive data from the VME device.
+ *
+ * Note: This interface would be interrupt-driven. We don't use that
+ * now, but include a dummy routine for possible future adventures.
+ */
+static void
+vme_receive(
+ struct recvbuf *rbufp
+ )
+{
+}
+
+
+/*
+ * vme_poll - called by the transmit procedure
+ */
+static void
+vme_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct vmedate *tptr;
+ struct vmeunit *vme;
+ struct refclockproc *pp;
+ time_t tloc;
+ struct tm *tadr;
+
+ pp = peer->procptr;
+ vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */
+
+ tptr = &vme->vmedata;
+ if ((tptr = get_datumtime(tptr)) == NULL ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ vme->lasttime = current_time;
+
+ /*
+ * Get VME time and convert to timestamp format.
+ * The year must come from the system clock.
+ */
+
+ time(&tloc);
+ tadr = gmtime(&tloc);
+ tptr->year = (unsigned short)(tadr->tm_year + 1900);
+
+ sprintf(pp->a_lastcode,
+ "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
+ tptr->day,
+ tptr->hr,
+ tptr->mn,
+ tptr->sec,
+ tptr->frac,
+ tptr->status);
+
+ pp->lencode = (u_short) strlen(pp->a_lastcode);
+
+ pp->day = tptr->day;
+ pp->hour = tptr->hr;
+ pp->minute = tptr->mn;
+ pp->second = tptr->sec;
+ pp->nsec = tptr->frac;
+
+#ifdef DEBUG
+ if (debug)
+ printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
+ pp->day, pp->hour, pp->minute, pp->second,
+ pp->nsec, tptr->status);
+#endif
+ if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present. If an error in conversion to internal
+ * format is found, the program declares bad data and exits.
+ * Note that this code does not yet know how to do the years and
+ * relies on the clock-calendar chip for sanity.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+struct vmedate *
+get_datumtime(struct vmedate *time_vme)
+{
+ char cbuf[7];
+ struct btfp_time vts;
+ uint32_t btm[2];
+ uint8_t dmy;
+
+ if ( time_vme == (struct vmedate *)NULL) {
+ time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
+ }
+
+ switch (tfp_type) {
+ case 1: /* BSD, PCI, 2 32bit time words */
+ if (ioctl(fd_vme, READTIME, &btm)) {
+ msyslog(LOG_ERR, "get_bc63x error: %m");
+ return(NULL);
+ }
+ tvme_fill(time_vme, btm);
+ break;
+
+ case 2: /* Linux/Windows, PCI, 2 32bit time words */
+ if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
+ msyslog(LOG_ERR, "get_datumtime error: %m");
+ return(NULL);
+ }
+ tvme_fill(time_vme, btm);
+ break;
+
+ default: /* legacy bancomm card */
+
+ if (ioctl(fd_vme, READTIME, &vts)) {
+ msyslog(LOG_ERR, "get_datumtime error: %m");
+ return(NULL);
+ }
+ /* Get day */
+ sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
+ ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
+ time_vme->day = (unsigned short)atoi(cbuf);
+
+ /* Get hour */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
+
+ time_vme->hr = (unsigned short)atoi(cbuf);
+
+ /* Get minutes */
+ sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
+ time_vme->mn = (unsigned short)atoi(cbuf);
+
+ /* Get seconds */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
+ time_vme->sec = (unsigned short)atoi(cbuf);
+
+ /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
+ we can use the TVTOTSF function later on...*/
+
+ sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
+ vts.btfp_time[ 4 ]>>8);
+
+ time_vme->frac = (u_long) atoi(cbuf);
+
+ /* Get status bit */
+ time_vme->status = (vts.btfp_time[0] & 0x0010) >>4;
+
+ break;
+ }
+
+ if (time_vme->status)
+ return ((void *)NULL);
+ else
+ return (time_vme);
+}
+/* Assign values to time_vme struct. Mostly for readability */
+void
+tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
+{
+ struct tm maj;
+ uint32_t dmaj, dmin;
+
+ dmaj = btm[1]; /* syntax sugar */
+ dmin = btm[0];
+
+ gmtime_r(&dmaj, &maj);
+ time_vme->day = maj.tm_yday+1;
+ time_vme->hr = maj.tm_hour;
+ time_vme->mn = maj.tm_min;
+ time_vme->sec = maj.tm_sec;
+ time_vme->frac = (dmin & 0x000fffff) * 1000;
+ time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
+ time_vme->status = (dmin & 0x01000000) >> 24;
+ return;
+}
+
+#else
+int refclock_bancomm_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_chronolog.c b/contrib/ntp/ntpd/refclock_chronolog.c
new file mode 100644
index 0000000..a1d131e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_chronolog.c
@@ -0,0 +1,341 @@
+/*
+ * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Chronolog K-series WWVB receiver.
+ *
+ * Input format:
+ *
+ * Y YY/MM/DD<cr><lf>
+ * Z hh:mm:ss<cr><lf>
+ *
+ * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
+ * timestamp.
+ * hh:mm:ss -- what you'd expect. We take time on the <cr>.
+ *
+ * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
+ * otherwise. The clock seems to appear every 60 seconds, which doesn't make
+ * for good statistics collection.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/chronolog%d" /* device name and unit */
+#define SPEED232 B2400 /* uart speed (2400 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "chronolog" /* reference ID */
+#define DESCRIPTION "Chrono-log K" /* WRU */
+
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * Chrono-log unit control structure
+ */
+struct chronolog_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ int year; /* Y2K-adjusted year */
+ int day; /* day-of-month */
+ int month; /* month-of-year */
+};
+
+/*
+ * Function prototypes
+ */
+static int chronolog_start P((int, struct peer *));
+static void chronolog_shutdown P((int, struct peer *));
+static void chronolog_receive P((struct recvbuf *));
+static void chronolog_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chronolog = {
+ chronolog_start, /* start up driver */
+ chronolog_shutdown, /* shut down driver */
+ chronolog_poll, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chronolog_start - open the devices and initialize data for processing
+ */
+static int
+chronolog_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Chronolog with device %s\n",device);
+#endif
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chronolog_unit *)
+ emalloc(sizeof(struct chronolog_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chronolog_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = chronolog_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * chronolog_shutdown - shut down the clock
+ */
+static void
+chronolog_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * chronolog_receive - receive data from the serial interface
+ */
+static void
+chronolog_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct chronolog_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("chronolog: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format and decode
+ * its contents. This code uses the first character to see whether
+ * we're looking at a date or a time. We store data data across
+ * calls since it is transmitted a few seconds ahead of the
+ * timestamp.
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
+ {
+ /*
+ * Y2K convert the 2-digit year
+ */
+ up->year = up->year >= 69 ? up->year : up->year + 100;
+ return;
+ }
+ if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+#ifdef GET_LOCALTIME
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+ int adjyear;
+ int adjmon;
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to do Y2K conversion on the 2-digit year;
+ * otherwise, we get the time wrong.
+ */
+
+ local.tm_year = up->year;
+ local.tm_mon = up->month-1;
+ local.tm_mday = up->day;
+ local.tm_hour = hours;
+ local.tm_min = minutes;
+ local.tm_sec = seconds;
+ local.tm_isdst = -1;
+
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+#else
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(year+1900,month,day);
+ pp->hour = hours;
+ pp->minute = minutes;
+ pp->second = seconds;
+
+#endif
+ got_good=1;
+ }
+
+ if (!got_good)
+ return;
+
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = pp->hour;
+}
+
+
+/*
+ * chronolog_poll - called by the transmit procedure
+ */
+static void
+chronolog_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ if (peer->burst == 0 && peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+
+#else
+int refclock_chronolog_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_chu.c b/contrib/ntp/ntpd/refclock_chu.c
new file mode 100644
index 0000000..843e9aaa
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_chu.c
@@ -0,0 +1,1687 @@
+/*
+ * refclock_chu - clock driver for Canadian CHU time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHU)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef HAVE_AUDIO
+#include "audio.h"
+#endif /* HAVE_AUDIO */
+
+#define ICOM 1 /* undefine to suppress ICOM code */
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+
+/*
+ * Audio CHU demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from Canadian time/frequency station CHU in
+ * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
+ * 7335 kHz and 14670 kHz in upper sideband, compatible AM mode. An
+ * ordinary shortwave receiver can be tuned manually to one of these
+ * frequencies or, in the case of ICOM receivers, the receiver can be
+ * tuned automatically using this program as propagation conditions
+ * change throughout the day and night.
+ *
+ * The driver receives, demodulates and decodes the radio signals when
+ * connected to the audio codec of a suported workstation hardware and
+ * operating system. These include Solaris, SunOS, FreeBSD, NetBSD and
+ * Linux. In this implementation, only one audio driver and codec can be
+ * supported on a single machine.
+ *
+ * The driver can be compiled to use a Bell 103 compatible modem or
+ * modem chip to receive the radio signal and demodulate the data.
+ * Alternatively, the driver can be compiled to use the audio codec of
+ * the Sun workstation or another with compatible audio drivers. In the
+ * latter case, the driver implements the modem using DSP routines, so
+ * the radio can be connected directly to either the microphone on line
+ * input port. In either case, the driver decodes the data using a
+ * maximum likelihood technique which exploits the considerable degree
+ * of redundancy available to maximize accuracy and minimize errors.
+ *
+ * The CHU time broadcast includes an audio signal compatible with the
+ * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). It consist
+ * of nine, ten-character bursts transmitted at 300 bps and beginning
+ * each second from second 31 to second 39 of the minute. Each character
+ * consists of eight data bits plus one start bit and two stop bits to
+ * encode two hex digits. The burst data consist of five characters (ten
+ * hex digits) followed by a repeat of these characters. In format A,
+ * the characters are repeated in the same polarity; in format B, the
+ * characters are repeated in the opposite polarity.
+ *
+ * Format A bursts are sent at seconds 32 through 39 of the minute in
+ * hex digits
+ *
+ * 6dddhhmmss6dddhhmmss
+ *
+ * The first ten digits encode a frame marker (6) followed by the day
+ * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
+ * format A bursts are sent during the third decade of seconds the tens
+ * digit of ss is always 3. The driver uses this to determine correct
+ * burst synchronization. These digits are then repeated with the same
+ * polarity.
+ *
+ * Format B bursts are sent at second 31 of the minute in hex digits
+ *
+ * xdyyyyttaaxdyyyyttaa
+ *
+ * The first ten digits encode a code (x described below) followed by
+ * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
+ * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
+ * digits are then repeated with inverted polarity.
+ *
+ * The x is coded
+ *
+ * 1 Sign of DUT (0 = +)
+ * 2 Leap second warning. One second will be added.
+ * 4 Leap second warning. One second will be subtracted.
+ * 8 Even parity bit for this nibble.
+ *
+ * By design, the last stop bit of the last character in the burst
+ * coincides with 0.5 second. Since characters have 11 bits and are
+ * transmitted at 300 bps, the last stop bit of the first character
+ * coincides with 0.5 - 10 * 11/300 = 0.133 second. Depending on the
+ * UART, character interrupts can vary somewhere between the beginning
+ * of bit 9 and end of bit 11. These eccentricities can be corrected
+ * along with the radio propagation delay using fudge time 1.
+ *
+ * Debugging aids
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the radio signal and serial
+ * connections. With debugging enabled (-d on the ntpd command line),
+ * the driver produces one line for each burst in two formats
+ * corresponding to format A and B. Following is format A:
+ *
+ * n b f s m code
+ *
+ * where n is the number of characters in the burst (0-11), b the burst
+ * distance (0-40), f the field alignment (-1, 0, 1), s the
+ * synchronization distance (0-16), m the burst number (2-9) and code
+ * the burst characters as received. Note that the hex digits in each
+ * character are reversed, so the burst
+ *
+ * 10 38 0 16 9 06851292930685129293
+ *
+ * is interpreted as containing 11 characters with burst distance 38,
+ * field alignment 0, synchronization distance 16 and burst number 9.
+ * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
+ * second 39.
+ *
+ * When the audio driver is compiled, format A is preceded by
+ * the current gain (0-255) and relative signal level (0-9999). The
+ * receiver folume control should be set so that the gain is somewhere
+ * near the middle of the range 0-255, which results in a signal level
+ * near 1000.
+ *
+ * Following is format B:
+ *
+ * n b s code
+ *
+ * where n is the number of characters in the burst (0-11), b the burst
+ * distance (0-40), s the synchronization distance (0-40) and code the
+ * burst characters as received. Note that the hex digits in each
+ * character are reversed and the last ten digits inverted, so the burst
+ *
+ * 11 40 1091891300ef6e76ecff
+ *
+ * is interpreted as containing 11 characters with burst distance 40.
+ * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
+ * - UTC 31 seconds.
+ *
+ * In addition to the above, the reference timecode is updated and
+ * written to the clockstats file and debug score after the last burst
+ * received in the minute. The format is
+ *
+ * qq yyyy ddd hh:mm:ss nn dd tt
+ *
+ * where qq are the error flags, as described below, yyyy is the year,
+ * ddd the day, hh:mm:ss the time of day, nn the number of format A
+ * bursts received during the previous minute, dd the decoding distance
+ * and tt the number of timestamps. The error flags are cleared after
+ * every update.
+ *
+ * Fudge factors
+ *
+ * For accuracies better than the low millisceconds, fudge time1 can be
+ * set to the radio propagation delay from CHU to the receiver. This can
+ * be done conviently using the minimuf program.
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. When the audio driver is compiled,
+ * fudge flag2 selects the audio input port, where 0 is the mike port
+ * (default) and 1 is the line-in port. It does not seem useful to
+ * select the compact disc player port. Fudge flag3 enables audio
+ * monitoring of the input signal. For this purpose, the monitor gain is
+ * set to a default value.
+ *
+ * The audio codec code is normally compiled in the driver if the
+ * architecture supports it (HAVE_AUDIO defined), but is used only if
+ * the link /dev/chu_audio is defined and valid. The serial port code is
+ * always compiled in the driver, but is used only if the autdio codec
+ * is not available and the link /dev/chu%d is defined and valid.
+ *
+ * The ICOM code is normally compiled in the driver if selected (ICOM
+ * defined), but is used only if the link /dev/icom%d is defined and
+ * valid and the mode keyword on the server configuration command
+ * specifies a nonzero mode (ICOM ID select code). The C-IV speed is
+ * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps
+ * if one. The C-IV trace is turned on if the debug level is greater
+ * than one.
+ */
+/*
+ * Interface definitions
+ */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "CHU" /* reference ID */
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* UART speed (300 baud) */
+#ifdef ICOM
+#define TUNE .001 /* offset for narrow filter (kHz) */
+#define DWELL 5 /* minutes in a probe cycle */
+#define NCHAN 3 /* number of channels */
+#define ISTAGE 3 /* number of integrator stages */
+#endif /* ICOM */
+
+#ifdef HAVE_AUDIO
+/*
+ * Audio demodulator definitions
+ */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 300 /* modulation rate (bps) */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXAMP 6000. /* maximum signal level */
+#define MAXCLP 100 /* max clips above reference per s */
+#define LIMIT 1000. /* soft limiter threshold */
+#define AGAIN 6. /* baseband gain */
+#define LAG 10 /* discriminator lag */
+#define DEVICE_AUDIO "/dev/audio" /* device name */
+#define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */
+#define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */
+#else
+#define DESCRIPTION "CHU Modem Receiver" /* WRU */
+#endif /* HAVE_AUDIO */
+
+/*
+ * Decoder definitions
+ */
+#define CHAR (11. / 300.) /* character time (s) */
+#define FUDGE .185 /* offset to first stop bit (s) */
+#define BURST 11 /* max characters per burst */
+#define MINCHAR 9 /* min characters per burst */
+#define MINDIST 28 /* min burst distance (of 40) */
+#define MINBURST 4 /* min bursts in minute */
+#define MINSYNC 8 /* min sync distance (of 16) */
+#define MINSTAMP 20 /* min timestamps (of 60) */
+#define METRIC 50. /* min channel metric */
+#define PANIC 1440 /* panic timeout (m) */
+#define HOLD 30 /* reach hold (m) */
+
+/*
+ * Hex extension codes (>= 16)
+ */
+#define HEX_MISS 16 /* miss _ */
+#define HEX_SOFT 17 /* soft error * */
+#define HEX_HARD 18 /* hard error = */
+
+/*
+ * Status bits (status)
+ */
+#define RUNT 0x0001 /* runt burst */
+#define NOISE 0x0002 /* noise burst */
+#define BFRAME 0x0004 /* invalid format B frame sync */
+#define BFORMAT 0x0008 /* invalid format B data */
+#define AFRAME 0x0010 /* invalid format A frame sync */
+#define AFORMAT 0x0020 /* invalid format A data */
+#define DECODE 0x0040 /* invalid data decode */
+#define STAMP 0x0080 /* too few timestamps */
+#define AVALID 0x0100 /* valid A frame */
+#define BVALID 0x0200 /* valid B frame */
+#define INSYNC 0x0400 /* clock synchronized */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These alarms are set at the end of a minute in which at least one
+ * burst was received. SYNERR is raised if the AFRAME or BFRAME status
+ * bits are set during the minute, FMTERR is raised if the AFORMAT or
+ * BFORMAT status bits are set, DECERR is raised if the DECODE status
+ * bit is set and TSPERR is raised if the STAMP status bit is set.
+ */
+#define SYNERR 0x01 /* frame sync error */
+#define FMTERR 0x02 /* data format error */
+#define DECERR 0x04 /* data decoding error */
+#define TSPERR 0x08 /* insufficient data */
+
+#ifdef HAVE_AUDIO
+/*
+ * Maximum likelihood UART structure. There are eight of these
+ * corresponding to the number of phases.
+ */
+struct surv {
+ double shift[12]; /* mark register */
+ double es_max, es_min; /* max/min envelope signals */
+ double dist; /* sample distance */
+ int uart; /* decoded character */
+};
+#endif /* HAVE_AUDIO */
+
+#ifdef ICOM
+/*
+ * CHU station structure. There are three of these corresponding to the
+ * three frequencies.
+ */
+struct xmtr {
+ double integ[ISTAGE]; /* circular integrator */
+ double metric; /* integrator sum */
+ int iptr; /* integrator pointer */
+ int probe; /* dwells since last probe */
+};
+#endif /* ICOM */
+
+/*
+ * CHU unit control structure
+ */
+struct chuunit {
+ u_char decode[20][16]; /* maximum likelihood decoding matrix */
+ l_fp cstamp[BURST]; /* character timestamps */
+ l_fp tstamp[MAXSTAGE]; /* timestamp samples */
+ l_fp timestamp; /* current buffer timestamp */
+ l_fp laststamp; /* last buffer timestamp */
+ l_fp charstamp; /* character time as a l_fp */
+ int errflg; /* error flags */
+ int status; /* status bits */
+ char ident[5]; /* station ID and channel */
+#ifdef ICOM
+ int fd_icom; /* ICOM file descriptor */
+ int chan; /* data channel */
+ int achan; /* active channel */
+ int dwell; /* dwell cycle */
+ struct xmtr xmtr[NCHAN]; /* station metric */
+#endif /* ICOM */
+
+ /*
+ * Character burst variables
+ */
+ int cbuf[BURST]; /* character buffer */
+ int ntstamp; /* number of timestamp samples */
+ int ndx; /* buffer start index */
+ int prevsec; /* previous burst second */
+ int burdist; /* burst distance */
+ int syndist; /* sync distance */
+ int burstcnt; /* format A bursts this minute */
+
+ /*
+ * Format particulars
+ */
+ int leap; /* leap/dut code */
+ int dut; /* UTC1 correction */
+ int tai; /* TAI - UTC correction */
+ int dst; /* Canadian DST code */
+
+#ifdef HAVE_AUDIO
+ /*
+ * Audio codec variables
+ */
+ int fd_audio; /* audio port file descriptor */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clip count */
+ int seccnt; /* second interval counter */
+
+ /*
+ * Modem variables
+ */
+ l_fp tick; /* audio sample increment */
+ double bpf[9]; /* IIR bandpass filter */
+ double disc[LAG]; /* discriminator shift register */
+ double lpf[27]; /* FIR lowpass filter */
+ double monitor; /* audio monitor */
+ double maxsignal; /* signal level */
+ int discptr; /* discriminator pointer */
+
+ /*
+ * Maximum likelihood UART variables
+ */
+ double baud; /* baud interval */
+ struct surv surv[8]; /* UART survivor structures */
+ int decptr; /* decode pointer */
+ int dbrk; /* holdoff counter */
+#endif /* HAVE_AUDIO */
+};
+
+/*
+ * Function prototypes
+ */
+static int chu_start P((int, struct peer *));
+static void chu_shutdown P((int, struct peer *));
+static void chu_receive P((struct recvbuf *));
+static void chu_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void chu_decode P((struct peer *, int));
+static void chu_burst P((struct peer *));
+static void chu_clear P((struct peer *));
+static void chu_a P((struct peer *, int));
+static void chu_b P((struct peer *, int));
+static int chu_dist P((int, int));
+static double chu_major P((struct peer *));
+#ifdef HAVE_AUDIO
+static void chu_uart P((struct surv *, double));
+static void chu_rf P((struct peer *, double));
+static void chu_gain P((struct peer *));
+static void chu_audio_receive P((struct recvbuf *rbufp));
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+static int chu_newchan P((struct peer *, double));
+#endif /* ICOM */
+static void chu_serial_receive P((struct recvbuf *rbufp));
+
+/*
+ * Global variables
+ */
+static char hexchar[] = "0123456789abcdef_*=";
+
+#ifdef ICOM
+/*
+ * Note the tuned frequencies are 1 kHz higher than the carrier. CHU
+ * transmits on USB with carrier so we can use AM and the narrow SSB
+ * filter.
+ */
+static double qsy[NCHAN] = {3.330, 7.335, 14.670}; /* freq (MHz) */
+#endif /* ICOM */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chu = {
+ chu_start, /* start up driver */
+ chu_shutdown, /* shut down driver */
+ chu_poll, /* transmit poll message */
+ noentry, /* not used (old chu_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old chu_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chu_start - open the devices and initialize data for processing
+ */
+static int
+chu_start(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ char device[20]; /* device name */
+ int fd; /* file descriptor */
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+#ifdef HAVE_AUDIO
+ int fd_audio; /* audio port file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device.
+ */
+ fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+#ifdef DEBUG
+ if (fd_audio > 0 && debug)
+ audio_show();
+#endif
+
+ /*
+ * Open serial port in raw mode.
+ */
+ if (fd_audio > 0) {
+ fd = fd_audio;
+ } else {
+ sprintf(device, DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+ }
+#else /* HAVE_AUDIO */
+
+ /*
+ * Open serial port in raw mode.
+ */
+ sprintf(device, DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+#endif /* HAVE_AUDIO */
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chuunit *)
+ emalloc(sizeof(struct chuunit)))) {
+ close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chuunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = chu_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ strcpy(up->ident, "CHU");
+ memcpy(&peer->refid, up->ident, 4);
+ DTOLFP(CHAR, &up->charstamp);
+#ifdef HAVE_AUDIO
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed. We do
+ * this even if the audio codec is not available. C'est la lazy.
+ */
+ up->fd_audio = fd_audio;
+ up->gain = 127;
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif
+ if (peer->ttl > 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ }
+ if (up->fd_icom > 0) {
+ if (chu_newchan(peer, 0) != 0) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: radio not found");
+ up->errflg = CEVNT_FAULT;
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+ return (1);
+}
+
+
+/*
+ * chu_shutdown - shut down the clock
+ */
+static void
+chu_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (up == NULL)
+ return;
+
+ io_closeclock(&pp->io);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+#endif /* ICOM */
+ free(up);
+}
+
+
+/*
+ * chu_receive - receive data from the audio or serial device
+ */
+static void
+chu_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+#ifdef HAVE_AUDIO
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * If the audio codec is warmed up, the buffer contains codec
+ * samples which need to be demodulated and decoded into CHU
+ * characters using the software UART. Otherwise, the buffer
+ * contains CHU characters from the serial port, so the software
+ * UART is bypassed. In this case the CPU will probably run a
+ * few degrees cooler.
+ */
+ if (up->fd_audio > 0)
+ chu_audio_receive(rbufp);
+ else
+ chu_serial_receive(rbufp);
+#else
+ chu_serial_receive(rbufp);
+#endif /* HAVE_AUDIO */
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_audio_receive - receive data from the audio device
+ */
+static void
+chu_audio_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXAMP. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXAMP) {
+ sample = MAXAMP;
+ up->clipcnt++;
+ } else if (sample < -MAXAMP) {
+ sample = -MAXAMP;
+ up->clipcnt++;
+ }
+ chu_rf(peer, sample);
+ L_ADD(&up->timestamp, &up->tick);
+
+ /*
+ * Once each second ride gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ pp->second = (pp->second + 1) % 60;
+ chu_gain(peer);
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * chu_rf - filter and demodulate the FSK signal
+ *
+ * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
+ * and space 2025 Hz. It uses a bandpass filter followed by a soft
+ * limiter, FM discriminator and lowpass filter. A maximum likelihood
+ * decoder samples the baseband signal at eight times the baud rate and
+ * detects the start bit of each character.
+ *
+ * The filters are built for speed, which explains the rather clumsy
+ * code. Hopefully, the compiler will efficiently implement the move-
+ * and-muiltiply-and-add operations.
+ */
+static void
+chu_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* analog sample */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ struct surv *sp;
+
+ /*
+ * Local variables
+ */
+ double signal; /* bandpass signal */
+ double limit; /* limiter signal */
+ double disc; /* discriminator signal */
+ double lpf; /* lowpass signal */
+ double span; /* UART signal span */
+ double dist; /* UART signal distance */
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
+ * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB.
+ */
+ signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
+ signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
+ signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
+ signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
+ signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
+ signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
+ signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
+ signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
+ up->bpf[0] = sample - signal;
+ signal = up->bpf[0] * 6.176213e-03
+ + up->bpf[1] * 3.156599e-03
+ + up->bpf[2] * 7.567487e-03
+ + up->bpf[3] * 4.344580e-03
+ + up->bpf[4] * 1.190128e-02
+ + up->bpf[5] * 4.344580e-03
+ + up->bpf[6] * 7.567487e-03
+ + up->bpf[7] * 3.156599e-03
+ + up->bpf[8] * 6.176213e-03;
+
+ up->monitor = signal / 4.; /* note monitor after filter */
+
+ /*
+ * Soft limiter/discriminator. The 11-sample discriminator lag
+ * interval corresponds to three cycles of 2125 Hz, which
+ * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
+ * Hz. The discriminator output varies +-0.5 interval for input
+ * frequency 2025-2225 Hz. However, we don't get to sample at
+ * this frequency, so the discriminator output is biased. Life
+ * at 8000 Hz sucks.
+ */
+ limit = signal;
+ if (limit > LIMIT)
+ limit = LIMIT;
+ else if (limit < -LIMIT)
+ limit = -LIMIT;
+ disc = up->disc[up->discptr] * -limit;
+ up->disc[up->discptr] = limit;
+ up->discptr = (up->discptr + 1 ) % LAG;
+ if (disc >= 0)
+ disc = SQRT(disc);
+ else
+ disc = -SQRT(-disc);
+
+ /*
+ * Lowpass filter. Raised cosine, Ts = 1 / 300, beta = 0.1.
+ */
+ lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
+ lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
+ lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
+ lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
+ lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
+ lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
+ lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
+ lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
+ lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
+ lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
+ lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
+ lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
+ lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
+ lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
+ lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
+ lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
+ lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
+ lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
+ lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
+ lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
+ lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
+ lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
+ lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
+ lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
+ lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
+ lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
+ lpf += up->lpf[0] = disc * 2.538771e-02;
+
+ /*
+ * Maximum likelihood decoder. The UART updates each of the
+ * eight survivors and determines the span, slice level and
+ * tentative decoded character. Valid 11-bit characters are
+ * framed so that bit 1 and bit 11 (stop bits) are mark and bit
+ * 2 (start bit) is space. When a valid character is found, the
+ * survivor with maximum distance determines the final decoded
+ * character.
+ */
+ up->baud += 1. / SECOND;
+ if (up->baud > 1. / (BAUD * 8.)) {
+ up->baud -= 1. / (BAUD * 8.);
+ sp = &up->surv[up->decptr];
+ span = sp->es_max - sp->es_min;
+ up->maxsignal += (span - up->maxsignal) / 80.;
+ if (up->dbrk > 0) {
+ up->dbrk--;
+ } else if ((sp->uart & 0x403) == 0x401 && span > 1000.)
+ {
+ dist = 0;
+ j = 0;
+ for (i = 0; i < 8; i++) {
+ if (up->surv[i].dist > dist) {
+ dist = up->surv[i].dist;
+ j = i;
+ }
+ }
+ chu_decode(peer, (up->surv[j].uart >> 2) &
+ 0xff);
+ up->dbrk = 80;
+ }
+ up->decptr = (up->decptr + 1) % 8;
+ chu_uart(sp, -lpf * AGAIN);
+ }
+}
+
+
+/*
+ * chu_uart - maximum likelihood UART
+ *
+ * This routine updates a shift register holding the last 11 envelope
+ * samples. It then computes the slice level and span over these samples
+ * and determines the tentative data bits and distance. The calling
+ * program selects over the last eight survivors the one with maximum
+ * distance to determine the decoded character.
+ */
+static void
+chu_uart(
+ struct surv *sp, /* survivor structure pointer */
+ double sample /* baseband signal */
+ )
+{
+ double es_max, es_min; /* max/min envelope */
+ double slice; /* slice level */
+ double dist; /* distance */
+ double dtemp;
+ int i;
+
+ /*
+ * Save the sample and shift right. At the same time, measure
+ * the maximum and minimum over all eleven samples.
+ */
+ es_max = -1e6;
+ es_min = 1e6;
+ sp->shift[0] = sample;
+ for (i = 11; i > 0; i--) {
+ sp->shift[i] = sp->shift[i - 1];
+ if (sp->shift[i] > es_max)
+ es_max = sp->shift[i];
+ if (sp->shift[i] < es_min)
+ es_min = sp->shift[i];
+ }
+
+ /*
+ * Determine the slice level midway beteen the maximum and
+ * minimum and the span as the maximum less the minimum. Compute
+ * the distance on the assumption the first and last bits must
+ * be mark, the second space and the rest either mark or space.
+ */
+ slice = (es_max + es_min) / 2.;
+ dist = 0;
+ sp->uart = 0;
+ for (i = 1; i < 12; i++) {
+ sp->uart <<= 1;
+ dtemp = sp->shift[i];
+ if (dtemp > slice)
+ sp->uart |= 0x1;
+ if (i == 1 || i == 11) {
+ dist += dtemp - es_min;
+ } else if (i == 10) {
+ dist += es_max - dtemp;
+ } else {
+ if (dtemp > slice)
+ dist += dtemp - es_min;
+ else
+ dist += es_max - dtemp;
+ }
+ }
+ sp->es_max = es_max;
+ sp->es_min = es_min;
+ sp->dist = dist / (11 * (es_max - es_min));
+}
+#endif /* HAVE_AUDIO */
+
+
+/*
+ * chu_serial_receive - receive data from the serial device
+ */
+static void
+chu_serial_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ u_char *dpt; /* receive buffer pointer */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ up->timestamp = rbufp->recv_time;
+ dpt = (u_char *)&rbufp->recv_space;
+ chu_decode(peer, *dpt);
+}
+
+
+/*
+ * chu_decode - decode the character data
+ */
+static void
+chu_decode(
+ struct peer *peer, /* peer structure pointer */
+ int hexhex /* data character */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ l_fp tstmp; /* timestamp temp */
+ double dtemp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * If the interval since the last character is greater than the
+ * longest burst, process the last burst and start a new one. If
+ * the interval is less than this but greater than two
+ * characters, consider this a noise burst and reject it.
+ */
+ tstmp = up->timestamp;
+ if (L_ISZERO(&up->laststamp))
+ up->laststamp = up->timestamp;
+ L_SUB(&tstmp, &up->laststamp);
+ up->laststamp = up->timestamp;
+ LFPTOD(&tstmp, dtemp);
+ if (dtemp > BURST * CHAR) {
+ chu_burst(peer);
+ up->ndx = 0;
+ } else if (dtemp > 2.5 * CHAR) {
+ up->ndx = 0;
+ }
+
+ /*
+ * Append the character to the current burst and append the
+ * timestamp to the timestamp list.
+ */
+ if (up->ndx < BURST) {
+ up->cbuf[up->ndx] = hexhex & 0xff;
+ up->cstamp[up->ndx] = up->timestamp;
+ up->ndx++;
+
+ }
+}
+
+
+/*
+ * chu_burst - search for valid burst format
+ */
+static void
+chu_burst(
+ struct peer *peer
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ int i;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Correlate a block of five characters with the next block of
+ * five characters. The burst distance is defined as the number
+ * of bits that match in the two blocks for format A and that
+ * match the inverse for format B.
+ */
+ if (up->ndx < MINCHAR) {
+ up->status |= RUNT;
+ return;
+ }
+ up->burdist = 0;
+ for (i = 0; i < 5 && i < up->ndx - 5; i++)
+ up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
+
+ /*
+ * If the burst distance is at least MINDIST, this must be a
+ * format A burst; if the value is not greater than -MINDIST, it
+ * must be a format B burst. If the B burst is perfect, we
+ * believe it; otherwise, it is a noise burst and of no use to
+ * anybody.
+ */
+ if (up->burdist >= MINDIST) {
+ chu_a(peer, up->ndx);
+ } else if (up->burdist <= -MINDIST) {
+ chu_b(peer, up->ndx);
+ } else {
+ up->status |= NOISE;
+ return;
+ }
+
+ /*
+ * If this is a valid burst, wait a guard time of ten seconds to
+ * allow for more bursts, then arm the poll update routine to
+ * process the minute. Don't do this if this is called from the
+ * timer interrupt routine.
+ */
+ if (peer->outdate != current_time)
+ peer->nextdate = current_time + 10;
+}
+
+
+/*
+ * chu_b - decode format B burst
+ */
+static void
+chu_b(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ char tbuf[80]; /* trace buffer */
+ l_fp offset; /* timestamp offset */
+ int i;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * In a format B burst, a character is considered valid only if
+ * the first occurrence matches the last occurrence. The burst
+ * is considered valid only if all characters are valid; that
+ * is, only if the distance is 40. Note that once a valid frame
+ * has been found errors are ignored.
+ */
+ sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar,
+ -up->burdist);
+ for (i = 0; i < nchar; i++)
+ sprintf(&tbuf[strlen(tbuf)], "%02x", up->cbuf[i]);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->burdist > -40) {
+ up->status |= BFRAME;
+ return;
+ }
+ up->status |= BVALID;
+
+ /*
+ * Convert the burst data to internal format. If this succeeds,
+ * save the timestamps for later.
+ */
+ for (i = 0; i < 5; i++) {
+ code[2 * i] = hexchar[up->cbuf[i] & 0xf];
+ code[2 * i + 1] = hexchar[(up->cbuf[i] >>
+ 4) & 0xf];
+ }
+ if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
+ &pp->year, &up->tai, &up->dst) != 5) {
+ up->status |= BFORMAT;
+ return;
+ }
+ if (up->leap & 0x8)
+ up->dut = -up->dut;
+ offset.l_ui = 31;
+ offset.l_f = 0;
+ for (i = 0; i < nchar && i < 10; i++) {
+ up->tstamp[up->ntstamp] = up->cstamp[i];
+ L_SUB(&up->tstamp[up->ntstamp], &offset);
+ L_ADD(&offset, &up->charstamp);
+ if (up->ntstamp < MAXSTAGE - 1)
+ up->ntstamp++;
+ }
+}
+
+
+/*
+ * chu_a - decode format A burst
+ */
+static void
+chu_a(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ char tbuf[80]; /* trace buffer */
+ l_fp offset; /* timestamp offset */
+ int val; /* distance */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Determine correct burst phase. There are three cases
+ * corresponding to in-phase, one character early or one
+ * character late. These cases are distinguished by the position
+ * of the framing digits x6 at positions 0 and 5 and x3 at
+ * positions 4 and 9. The correct phase is when the distance
+ * relative to the framing digits is maximum. The burst is valid
+ * only if the maximum distance is at least MINSYNC.
+ */
+ up->syndist = k = 0;
+ val = -16;
+ for (i = -1; i < 2; i++) {
+ temp = up->cbuf[i + 4] & 0xf;
+ if (i >= 0)
+ temp |= (up->cbuf[i] & 0xf) << 4;
+ val = chu_dist(temp, 0x63);
+ temp = (up->cbuf[i + 5] & 0xf) << 4;
+ if (i + 9 < nchar)
+ temp |= up->cbuf[i + 9] & 0xf;
+ val += chu_dist(temp, 0x63);
+ if (val > up->syndist) {
+ up->syndist = val;
+ k = i;
+ }
+ }
+ temp = (up->cbuf[k + 4] >> 4) & 0xf;
+ if (temp > 9 || k + 9 >= nchar || temp != ((up->cbuf[k + 9] >>
+ 4) & 0xf))
+ temp = 0;
+#ifdef HAVE_AUDIO
+ if (up->fd_audio)
+ sprintf(tbuf, "chuA %04x %4.0f %2d %2d %2d %2d %1d ",
+ up->status, up->maxsignal, nchar, up->burdist, k,
+ up->syndist, temp);
+ else
+ sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ",
+ up->status, nchar, up->burdist, k, up->syndist,
+ temp);
+
+#else
+ sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ", up->status,
+ nchar, up->burdist, k, up->syndist, temp);
+#endif /* HAVE_AUDIO */
+ for (i = 0; i < nchar; i++)
+ sprintf(&tbuf[strlen(tbuf)], "%02x",
+ up->cbuf[i]);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->syndist < MINSYNC) {
+ up->status |= AFRAME;
+ return;
+ }
+
+ /*
+ * A valid burst requires the first seconds number to match the
+ * last seconds number. If so, the burst timestamps are
+ * corrected to the current minute and saved for later
+ * processing. In addition, the seconds decode is advanced from
+ * the previous burst to the current one.
+ */
+ if (temp != 0) {
+ pp->second = 30 + temp;
+ offset.l_ui = 30 + temp;
+ offset.l_f = 0;
+ i = 0;
+ if (k < 0)
+ offset = up->charstamp;
+ else if (k > 0)
+ i = 1;
+ for (; i < nchar && i < k + 10; i++) {
+ up->tstamp[up->ntstamp] = up->cstamp[i];
+ L_SUB(&up->tstamp[up->ntstamp], &offset);
+ L_ADD(&offset, &up->charstamp);
+ if (up->ntstamp < MAXSTAGE - 1)
+ up->ntstamp++;
+ }
+ while (temp > up->prevsec) {
+ for (j = 15; j > 0; j--) {
+ up->decode[9][j] = up->decode[9][j - 1];
+ up->decode[19][j] =
+ up->decode[19][j - 1];
+ }
+ up->decode[9][j] = up->decode[19][j] = 0;
+ up->prevsec++;
+ }
+ }
+ i = -(2 * k);
+ for (j = 0; j < nchar; j++) {
+ if (i < 0 || i > 18) {
+ i += 2;
+ continue;
+ }
+ up->decode[i][up->cbuf[j] & 0xf]++;
+ i++;
+ up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
+ i++;
+ }
+ up->status |= AVALID;
+ up->burstcnt++;
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(
+ int unit,
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ l_fp offset;
+ char synchar, qual, leapchar;
+ int minset, i;
+ double dtemp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (pp->coderecv == pp->codeproc)
+ up->errflg = CEVNT_TIMEOUT;
+ else
+ pp->polls++;
+
+ /*
+ * If once in sync and the radio has not been heard for awhile
+ * (30 m), it is no longer reachable. If not heard in a long
+ * while (one day), turn out the lights and start from scratch.
+ */
+ minset = ((current_time - peer->update) + 30) / 60;
+ if (up->status & INSYNC) {
+ if (minset > PANIC)
+ up->status = 0;
+ else if (minset <= HOLD)
+ peer->reach |= 1;
+ }
+
+ /*
+ * Process the last burst, if still in the burst buffer.
+ * Don't mess with anything if nothing has been heard. If the
+ * minute contains a valid A frame and valid B frame, assume
+ * synchronized; however, believe the time only if within metric
+ * threshold. Note the quality indicator is only for
+ * diagnostics; the data are used only if in sync and above
+ * metric threshold.
+ */
+ chu_burst(peer);
+ if (up->burstcnt == 0) {
+#ifdef ICOM
+ chu_newchan(peer, 0);
+#endif /* ICOM */
+ return;
+ }
+ dtemp = chu_major(peer);
+ qual = 0;
+ if (up->status & (BFRAME | AFRAME))
+ qual |= SYNERR;
+ if (up->status & (BFORMAT | AFORMAT))
+ qual |= FMTERR;
+ if (up->status & DECODE)
+ qual |= DECERR;
+ if (up->status & STAMP)
+ qual |= TSPERR;
+ if (up->status & AVALID && up->status & BVALID)
+ up->status |= INSYNC;
+ synchar = leapchar = ' ';
+ if (!(up->status & INSYNC)) {
+ pp->leap = LEAP_NOTINSYNC;
+ synchar = '?';
+ } else if (up->leap & 0x2) {
+ pp->leap = LEAP_ADDSECOND;
+ leapchar = 'L';
+ } else if (up->leap & 0x4) {
+ pp->leap = LEAP_DELSECOND;
+ leapchar = 'l';
+ } else {
+ pp->leap = LEAP_NOWARNING;
+ }
+#ifdef HAVE_AUDIO
+ if (up->fd_audio)
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour,
+ pp->minute, pp->second, leapchar, up->dst, up->dut,
+ minset, up->gain, up->ident, dtemp, up->ntstamp);
+ else
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour,
+ pp->minute, pp->second, leapchar, up->dst, up->dut,
+ minset, up->ident, dtemp, up->ntstamp);
+#else
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, leapchar, up->dst, up->dut, minset, up->ident,
+ dtemp, up->ntstamp);
+#endif /* HAVE_AUDIO */
+ pp->lencode = strlen(pp->a_lastcode);
+
+ /*
+ * If in sync and the signal metric is above threshold, the
+ * timecode is ipso fatso valid and can be selected to
+ * discipline the clock. Be sure not to leave stray timestamps
+ * around if signals are too weak or the clock time is invalid.
+ */
+ if (up->status & INSYNC && dtemp > METRIC) {
+ if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
+ up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ offset.l_uf = 0;
+ for (i = 0; i < up->ntstamp; i++)
+ refclock_process_offset(pp, offset,
+ up->tstamp[i], FUDGE +
+ pp->fudgetime1);
+ pp->lastref = up->timestamp;
+ refclock_receive(peer);
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ } else if (pp->sloppyclockflag & CLK_FLAG4) {
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("chu: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+#ifdef ICOM
+ chu_newchan(peer, dtemp);
+#endif /* ICOM */
+ chu_clear(peer);
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+}
+
+
+/*
+ * chu_major - majority decoder
+ */
+static double
+chu_major(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ int mindist; /* minimum distance */
+ int val1, val2; /* maximum distance */
+ int synchar; /* stray cat */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Majority decoder. Each burst encodes two replications at each
+ * digit position in the timecode. Each row of the decoding
+ * matrix encodes the number of occurrences of each digit found
+ * at the corresponding position. The maximum over all
+ * occurrences at each position is the distance for this
+ * position and the corresponding digit is the maximum
+ * likelihood candidate. If the distance is zero, assume a miss
+ * '_'; if the distance is not more than half the total number
+ * of occurrences, assume a soft error '*'; if two different
+ * digits with the same distance are found, assume a hard error
+ * '='. These will later cause a format error when the timecode
+ * is interpreted. The decoding distance is defined as the
+ * minimum distance over the first nine digits. The tenth digit
+ * varies over the seconds, so we don't count it.
+ */
+ mindist = 16;
+ for (i = 0; i < 9; i++) {
+ val1 = val2 = 0;
+ k = 0;
+ for (j = 0; j < 16; j++) {
+ temp = up->decode[i][j] + up->decode[i + 10][j];
+ if (temp > val1) {
+ val2 = val1;
+ val1 = temp;
+ k = j;
+ }
+ }
+ if (val1 == 0)
+ code[i] = HEX_MISS;
+ else if (val1 == val2)
+ code[i] = HEX_HARD;
+ else if (val1 <= up->burstcnt)
+ code[i] = HEX_SOFT;
+ else
+ code[i] = k;
+ if (val1 < mindist)
+ mindist = val1;
+ code[i] = hexchar[code[i]];
+ }
+ code[i] = 0;
+
+ /*
+ * A valid timecode requires a minimum distance at least half
+ * the total number of occurrences. A valid timecode also
+ * requires at least 20 valid timestamps.
+ */
+ if (up->burstcnt < MINBURST || mindist < up->burstcnt)
+ up->status |= DECODE;
+ if (up->ntstamp < MINSTAMP)
+ up->status |= STAMP;
+
+ /*
+ * Compute the timecode timestamp from the days, hours and
+ * minutes of the timecode. Use clocktime() for the aggregate
+ * minutes and the minute offset computed from the burst
+ * seconds. Note that this code relies on the filesystem time
+ * for the years and does not use the years of the timecode.
+ */
+ if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
+ &pp->hour, &pp->minute) != 4) {
+ up->status |= AFORMAT;
+ return (0);
+ }
+ if (up->status & (DECODE | STAMP)) {
+ up->errflg = CEVNT_BADREPLY;
+ return (0);
+ }
+ return (mindist * 100. / (2. * up->burstcnt));
+}
+
+
+/*
+ * chu_clear - clear decoding matrix
+ */
+static void
+chu_clear(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Clear stuff for the minute.
+ */
+ up->ndx = up->prevsec = 0;
+ up->burstcnt = up->ntstamp = 0;
+ up->status &= INSYNC;
+ for (i = 0; i < 20; i++) {
+ for (j = 0; j < 16; j++)
+ up->decode[i][j] = 0;
+ }
+}
+
+#ifdef ICOM
+/*
+ * chu_newchan - called once per minute to find the best channel;
+ * returns zero on success, nonzero if ICOM error.
+ */
+static int
+chu_newchan(
+ struct peer *peer,
+ double met
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct xmtr *sp;
+ char tbuf[80]; /* trace buffer */
+ int rval;
+ double metric;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * The radio can be tuned to three channels: 0 (3330 kHz), 1
+ * (7335 kHz) and 2 (14670 kHz). There are five one-minute
+ * dwells in each cycle. During the first dwell the radio is
+ * tuned to one of three probe channels; during the remaining
+ * four dwells the radio is tuned to the data channel. The probe
+ * channel is selects as the least recently used. At the end of
+ * each dwell the channel metrics are measured and the highest
+ * one is selected as the data channel.
+ */
+ if (up->fd_icom <= 0)
+ return (0);
+
+ sp = &up->xmtr[up->achan];
+ sp->metric -= sp->integ[sp->iptr];
+ sp->integ[sp->iptr] = met;
+ sp->metric += sp->integ[sp->iptr];
+ sp->iptr = (sp->iptr + 1) % ISTAGE;
+ metric = 0;
+ j = 0;
+ for (i = 0; i < NCHAN; i++) {
+ up->xmtr[i].probe++;
+ if (i == up->achan)
+ up->xmtr[i].probe = 0;
+ if (up->xmtr[i].metric < metric)
+ continue;
+ metric = up->xmtr[i].metric;
+ j = i;
+ }
+ if (j != up->chan && metric > 0) {
+ up->chan = j;
+ sprintf(tbuf, "chu: QSY to %.3f MHz metric %.0f",
+ qsy[up->chan], metric);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ }
+
+ /*
+ * Start the next dwell. We speed up the initial sync a little.
+ * If not in sync and no bursts were heard the previous dwell,
+ * restart the probe.
+ */
+ rval = 0;
+ if (up->burstcnt == 0 && !(up->status & INSYNC))
+ up->dwell = 0;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "chu: at %ld dwell %d achan %d metric %.0f chan %d\n",
+ current_time, up->dwell, up->achan, sp->metric,
+ up->chan);
+#endif
+ if (up->dwell == 0) {
+ rval = 0;
+ for (i = 0; i < NCHAN; i++) {
+ if (up->xmtr[i].probe < rval)
+ continue;
+ rval = up->xmtr[i].probe;
+ up->achan = i;
+ }
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[up->achan] + TUNE);
+#ifdef DEBUG
+ if (debug)
+ printf("chu: at %ld probe channel %d\n",
+ current_time, up->achan);
+#endif
+ } else {
+ if (up->achan != up->chan) {
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[up->chan] + TUNE);
+ up->achan = up->chan;
+ }
+ }
+ sprintf(up->ident, "CHU%d", up->achan);
+ memcpy(&peer->refid, up->ident, 4);
+ up->dwell = (up->dwell + 1) % DWELL;
+ return (rval);
+}
+#endif /* ICOM */
+
+/*
+ * chu_dist - determine the distance of two octet arguments
+ */
+static int
+chu_dist(
+ int x, /* an octet of bits */
+ int y /* another octet of bits */
+ )
+{
+ int val; /* bit count */
+ int temp;
+ int i;
+
+ /*
+ * The distance is determined as the weight of the exclusive OR
+ * of the two arguments. The weight is determined by the number
+ * of one bits in the result. Each one bit increases the weight,
+ * while each zero bit decreases it.
+ */
+ temp = x ^ y;
+ val = 0;
+ for (i = 0; i < 8; i++) {
+ if ((temp & 0x1) == 0)
+ val++;
+ else
+ val--;
+ temp >>= 1;
+ }
+ return (val);
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_gain - adjust codec gain
+ *
+ * This routine is called once each second. If the signal envelope
+ * amplitude is too low, the codec gain is bumped up by four units; if
+ * too high, it is bumped down. The decoder is relatively insensitive to
+ * amplitude, so this crudity works just fine. The input port is set and
+ * the error flag is cleared, mostly to be ornery.
+ */
+static void
+chu_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+}
+#endif /* HAVE_AUDIO */
+
+
+#else
+int refclock_chu_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_conf.c b/contrib/ntp/ntpd/refclock_conf.c
new file mode 100644
index 0000000..dc29d1e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_conf.c
@@ -0,0 +1,319 @@
+/*
+ * refclock_conf.c - reference clock configuration
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+static struct refclock refclock_none = {
+ noentry, noentry, noentry, noentry, noentry, noentry, NOFLAGS
+};
+
+#ifdef CLOCK_LOCAL
+extern struct refclock refclock_local;
+#else
+#define refclock_local refclock_none
+#endif
+
+#if 0 && defined(CLOCK_TRAK) && defined(PPS)
+extern struct refclock refclock_trak;
+#else
+#define refclock_trak refclock_none
+#endif
+
+#ifdef CLOCK_PST
+extern struct refclock refclock_pst;
+#else
+#define refclock_pst refclock_none
+#endif
+
+#ifdef CLOCK_CHU
+extern struct refclock refclock_chu;
+#else
+#define refclock_chu refclock_none
+#endif
+
+#ifdef CLOCK_WWV
+extern struct refclock refclock_wwv;
+#else
+#define refclock_wwv refclock_none
+#endif
+
+#ifdef CLOCK_SPECTRACOM
+extern struct refclock refclock_wwvb;
+#else
+#define refclock_wwvb refclock_none
+#endif
+
+#ifdef CLOCK_PARSE
+extern struct refclock refclock_parse;
+#else
+#define refclock_parse refclock_none
+#endif
+
+#if defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_mx4200;
+#else
+#define refclock_mx4200 refclock_none
+#endif
+
+#ifdef CLOCK_AS2201
+extern struct refclock refclock_as2201;
+#else
+#define refclock_as2201 refclock_none
+#endif
+
+#ifdef CLOCK_ARBITER
+extern struct refclock refclock_arbiter;
+#else
+#define refclock_arbiter refclock_none
+#endif
+
+#ifdef CLOCK_TPRO
+extern struct refclock refclock_tpro;
+#else
+#define refclock_tpro refclock_none
+#endif
+
+#ifdef CLOCK_LEITCH
+extern struct refclock refclock_leitch;
+#else
+#define refclock_leitch refclock_none
+#endif
+
+#ifdef CLOCK_IRIG
+extern struct refclock refclock_irig;
+#else
+#define refclock_irig refclock_none
+#endif
+
+#if 0 && defined(CLOCK_MSFEES) && defined(PPS)
+extern struct refclock refclock_msfees;
+#else
+#define refclock_msfees refclock_none
+#endif
+
+#ifdef CLOCK_BANC
+extern struct refclock refclock_bancomm;
+#else
+#define refclock_bancomm refclock_none
+#endif
+
+#ifdef CLOCK_TRUETIME
+extern struct refclock refclock_true;
+#else
+#define refclock_true refclock_none
+#endif
+
+#ifdef CLOCK_DATUM
+extern struct refclock refclock_datum;
+#else
+#define refclock_datum refclock_none
+#endif
+
+#ifdef CLOCK_ACTS
+extern struct refclock refclock_acts;
+#else
+#define refclock_acts refclock_none
+#endif
+
+#ifdef CLOCK_HEATH
+extern struct refclock refclock_heath;
+#else
+#define refclock_heath refclock_none
+#endif
+
+#ifdef CLOCK_NMEA
+extern struct refclock refclock_nmea;
+#else
+#define refclock_nmea refclock_none
+#endif
+
+#ifdef CLOCK_ATOM
+extern struct refclock refclock_atom;
+#else
+#define refclock_atom refclock_none
+#endif
+
+#ifdef CLOCK_HPGPS
+extern struct refclock refclock_hpgps;
+#else
+#define refclock_hpgps refclock_none
+#endif
+
+#ifdef CLOCK_GPSVME
+extern struct refclock refclock_gpsvme;
+#else
+#define refclock_gpsvme refclock_none
+#endif
+
+#ifdef CLOCK_ARCRON_MSF
+extern struct refclock refclock_arc;
+#else
+#define refclock_arc refclock_none
+#endif
+
+#ifdef CLOCK_SHM
+extern struct refclock refclock_shm;
+#else
+#define refclock_shm refclock_none
+#endif
+
+#ifdef CLOCK_PALISADE
+extern struct refclock refclock_palisade;
+#else
+#define refclock_palisade refclock_none
+#endif
+
+#if defined(CLOCK_ONCORE)
+extern struct refclock refclock_oncore;
+#else
+#define refclock_oncore refclock_none
+#endif
+
+#if defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_jupiter;
+#else
+#define refclock_jupiter refclock_none
+#endif
+
+#if defined(CLOCK_CHRONOLOG)
+extern struct refclock refclock_chronolog;
+#else
+#define refclock_chronolog refclock_none
+#endif
+
+#if defined(CLOCK_DUMBCLOCK)
+extern struct refclock refclock_dumbclock;
+#else
+#define refclock_dumbclock refclock_none
+#endif
+
+#ifdef CLOCK_ULINK
+extern struct refclock refclock_ulink;
+#else
+#define refclock_ulink refclock_none
+#endif
+
+#ifdef CLOCK_PCF
+extern struct refclock refclock_pcf;
+#else
+#define refclock_pcf refclock_none
+#endif
+
+#ifdef CLOCK_FG
+extern struct refclock refclock_fg;
+#else
+#define refclock_fg refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_SERIAL
+extern struct refclock refclock_hopfser;
+#else
+#define refclock_hopfser refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_PCI
+extern struct refclock refclock_hopfpci;
+#else
+#define refclock_hopfpci refclock_none
+#endif
+
+#ifdef CLOCK_JJY
+extern struct refclock refclock_jjy;
+#else
+#define refclock_jjy refclock_none
+#endif
+
+#ifdef CLOCK_TT560
+extern struct refclock refclock_tt560;
+#else
+#define refclock_tt560 refclock_none
+#endif
+
+#ifdef CLOCK_ZYFER
+extern struct refclock refclock_zyfer;
+#else
+#define refclock_zyfer refclock_none
+#endif
+
+#ifdef CLOCK_RIPENCC
+extern struct refclock refclock_ripencc;
+#else
+#define refclock_ripencc refclock_none
+#endif
+
+#ifdef CLOCK_NEOCLOCK4X
+extern struct refclock refclock_neoclock4x;
+#else
+#define refclock_neoclock4x refclock_none
+#endif
+
+/*
+ * Order is clock_start(), clock_shutdown(), clock_poll(),
+ * clock_control(), clock_init(), clock_buginfo, clock_flags;
+ *
+ * Types are defined in ntp.h. The index must match this.
+ */
+struct refclock *refclock_conf[] = {
+ &refclock_none, /* 0 REFCLK_NONE */
+ &refclock_local, /* 1 REFCLK_LOCAL */
+ &refclock_none, /* 2 deprecated: REFCLK_GPS_TRAK */
+ &refclock_pst, /* 3 REFCLK_WWV_PST */
+ &refclock_wwvb, /* 4 REFCLK_SPECTRACOM */
+ &refclock_true, /* 5 REFCLK_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU_AUDIO */
+ &refclock_parse, /* 8 REFCLK_PARSE */
+ &refclock_mx4200, /* 9 REFCLK_GPS_MX4200 */
+ &refclock_as2201, /* 10 REFCLK_GPS_AS2201 */
+ &refclock_arbiter, /* 11 REFCLK_GPS_ARBITER */
+ &refclock_tpro, /* 12 REFCLK_IRIG_TPRO */
+ &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */
+ &refclock_none, /* 14 deprecated: REFCLK_MSF_EES */
+ &refclock_none, /* 15 not used */
+ &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */
+ &refclock_datum, /* 17 REFCLK_GPS_DATUM */
+ &refclock_acts, /* 18 REFCLK_ACTS */
+ &refclock_heath, /* 19 REFCLK_WWV_HEATH */
+ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */
+ &refclock_gpsvme, /* 21 REFCLK_GPS_VME */
+ &refclock_atom, /* 22 REFCLK_ATOM_PPS */
+ &refclock_none, /* 23 not used */
+ &refclock_none, /* 24 not used */
+ &refclock_none, /* 25 not used */
+ &refclock_hpgps, /* 26 REFCLK_GPS_HP */
+ &refclock_arc, /* 27 REFCLK_ARCRON_MSF */
+ &refclock_shm, /* 28 REFCLK_SHM */
+ &refclock_palisade, /* 29 REFCLK_PALISADE */
+ &refclock_oncore, /* 30 REFCLK_ONCORE */
+ &refclock_jupiter, /* 31 REFCLK_GPS_JUPITER */
+ &refclock_chronolog, /* 32 REFCLK_CHRONOLOG */
+ &refclock_dumbclock, /* 33 REFCLK_DUMBCLOCK */
+ &refclock_ulink, /* 34 REFCLOCK_ULINK */
+ &refclock_pcf, /* 35 REFCLOCK_PCF */
+ &refclock_wwv, /* 36 REFCLOCK_WWV_AUDIO */
+ &refclock_fg, /* 37 REFCLOCK_FG */
+ &refclock_hopfser, /* 38 REFCLK_HOPF_SERIAL */
+ &refclock_hopfpci, /* 39 REFCLK_HOPF_PCI */
+ &refclock_jjy, /* 40 REFCLK_JJY */
+ &refclock_tt560, /* 41 REFCLK_TT560 */
+ &refclock_zyfer, /* 42 REFCLK_ZYFER */
+ &refclock_ripencc, /* 43 REFCLK_RIPENCC */
+ &refclock_neoclock4x /* 44 REFCLK_NEOCLOCK4X */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#else
+int refclock_conf_bs;
+#endif
diff --git a/contrib/ntp/ntpd/refclock_datum.c b/contrib/ntp/ntpd/refclock_datum.c
new file mode 100644
index 0000000..b26fe73
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_datum.c
@@ -0,0 +1,877 @@
+/*
+** refclock_datum - clock driver for the Datum Programmable Time Server
+**
+** Important note: This driver assumes that you have termios. If you have
+** a system that does not have termios, you will have to modify this driver.
+**
+** Sorry, I have only tested this driver on SUN and HP platforms.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_DATUM)
+
+/*
+** Include Files
+*/
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(WWVBCLK)
+#include <sys/clkdefs.h>
+#endif /* WWVBCLK */
+#endif /* STREAM */
+
+#include "ntp_stdlib.h"
+
+/*
+** This driver supports the Datum Programmable Time System (PTS) clock.
+** The clock works in very straight forward manner. When it receives a
+** time code request (e.g., the ascii string "//k/mn"), it responds with
+** a seven byte BCD time code. This clock only responds with a
+** time code after it first receives the "//k/mn" message. It does not
+** periodically send time codes back at some rate once it is started.
+** the returned time code can be broken down into the following fields.
+**
+** _______________________________
+** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+** ===============================
+** byte 0: | - - - - | H D |
+** ===============================
+** byte 1: | T D | U D |
+** ===============================
+** byte 2: | - - | T H | U H |
+** ===============================
+** byte 3: | - | T M | U M |
+** ===============================
+** byte 4: | - | T S | U S |
+** ===============================
+** byte 5: | t S | h S |
+** ===============================
+** byte 6: | m S | - - - - |
+** ===============================
+**
+** In the table above:
+**
+** "-" means don't care
+** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days
+** "T H", and "UH" means Tens and Units of Hours
+** "T M", and "U M" means Tens and Units of Minutes
+** "T S", and "U S" means Tens and Units of Seconds
+** "t S", "h S", and "m S" means tenths, hundredths, and thousandths
+** of seconds
+**
+** The Datum PTS communicates throught the RS232 port on your machine.
+** Right now, it assumes that you have termios. This driver has been tested
+** on SUN and HP workstations. The Datum PTS supports various IRIG and
+** NASA input codes. This driver assumes that the name of the device is
+** /dev/datum. You will need to make a soft link to your RS232 device or
+** create a new driver to use this refclock.
+*/
+
+/*
+** Datum PTS defines
+*/
+
+/*
+** Note that if GMT is defined, then the Datum PTS must use Greenwich
+** time. Otherwise, this driver allows the Datum PTS to use the current
+** wall clock for its time. It determines the time zone offset by minimizing
+** the error after trying several time zone offsets. If the Datum PTS
+** time is Greenwich time and GMT is not defined, everything should still
+** work since the time zone will be found to be 0. What this really means
+** is that your system time (at least to start with) must be within the
+** correct time by less than +- 30 minutes. The default is for GMT to not
+** defined. If you really want to force GMT without the funny +- 30 minute
+** stuff then you must define (uncomment) GMT below.
+*/
+
+/*
+#define GMT
+#define DEBUG_DATUM_PTC
+#define LOG_TIME_ERRORS
+*/
+
+
+#define PRECISION (-10) /* precision assumed 1/1024 ms */
+#define REFID "DATM" /* reference id */
+#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */
+#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */
+#define DATUM_DEV "/dev/datum" /* device name */
+
+#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR)
+
+/*
+** The Datum PTS structure
+*/
+
+/*
+** I don't use a fixed array of MAXUNITS like everyone else just because
+** I don't like to program that way. Sorry if this bothers anyone. I assume
+** that you can use any id for your unit and I will search for it in a
+** dynamic array of units until I find it. I was worried that users might
+** enter a bad id in their configuration file (larger than MAXUNITS) and
+** besides, it is just cleaner not to have to assume that you have a fixed
+** number of anything in a program.
+*/
+
+struct datum_pts_unit {
+ struct peer *peer; /* peer used by ntp */
+ struct refclockio io; /* io structure used by ntp */
+ int PTS_fd; /* file descriptor for PTS */
+ u_int unit; /* id for unit */
+ u_long timestarted; /* time started */
+ l_fp lastrec; /* time tag for the receive time (system) */
+ l_fp lastref; /* reference time (Datum time) */
+ u_long yearstart; /* the year that this clock started */
+ int coderecv; /* number of time codes received */
+ int day; /* day */
+ int hour; /* hour */
+ int minute; /* minutes */
+ int second; /* seconds */
+ int msec; /* miliseconds */
+ int usec; /* miliseconds */
+ u_char leap; /* funny leap character code */
+ char retbuf[8]; /* returned time from the datum pts */
+ char nbytes; /* number of bytes received from datum pts */
+ double sigma2; /* average squared error (roughly) */
+ int tzoff; /* time zone offest from GMT */
+};
+
+/*
+** PTS static constant variables for internal use
+*/
+
+static char TIME_REQUEST[6]; /* request message sent to datum for time */
+static int nunits; /* number of active units */
+static struct datum_pts_unit
+**datum_pts_unit; /* dynamic array of datum PTS structures */
+
+/*
+** Callback function prototypes that ntpd needs to know about.
+*/
+
+static int datum_pts_start P((int, struct peer *));
+static void datum_pts_shutdown P((int, struct peer *));
+static void datum_pts_poll P((int, struct peer *));
+static void datum_pts_control P((int, struct refclockstat *,
+ struct refclockstat *, struct peer *));
+static void datum_pts_init P((void));
+static void datum_pts_buginfo P((int, struct refclockbug *, struct peer *));
+
+/*
+** This is the call back function structure that ntpd actually uses for
+** this refclock.
+*/
+
+struct refclock refclock_datum = {
+ datum_pts_start, /* start up a new Datum refclock */
+ datum_pts_shutdown, /* shutdown a Datum refclock */
+ datum_pts_poll, /* sends out the time request */
+ datum_pts_control, /* not used */
+ datum_pts_init, /* initialization (called first) */
+ datum_pts_buginfo, /* not used */
+ NOFLAGS /* we are not setting any special flags */
+};
+
+/*
+** The datum_pts_receive callback function is handled differently from the
+** rest. It is passed to the ntpd io data structure. Basically, every
+** 64 seconds, the datum_pts_poll() routine is called. It sends out the time
+** request message to the Datum Programmable Time System. Then, ntpd
+** waits on a select() call to receive data back. The datum_pts_receive()
+** function is called as data comes back. We expect a seven byte time
+** code to be returned but the datum_pts_receive() function may only get
+** a few bytes passed to it at a time. In other words, this routine may
+** get called by the io stuff in ntpd a few times before we get all seven
+** bytes. Once the last byte is received, we process it and then pass the
+** new time measurement to ntpd for updating the system time. For now,
+** there is no 3 state filtering done on the time measurements. The
+** jitter may be a little high but at least for its current use, it is not
+** a problem. We have tried to keep things as simple as possible. This
+** clock should not jitter more than 1 or 2 mseconds at the most once
+** things settle down. It is important to get the right drift calibrated
+** in the ntpd.drift file as well as getting the right tick set up right
+** using tickadj for SUNs. Tickadj is not used for the HP but you need to
+** remember to bring up the adjtime daemon because HP does not support
+** the adjtime() call.
+*/
+
+static void datum_pts_receive P((struct recvbuf *));
+
+/*......................................................................*/
+/* datum_pts_start - start up the datum PTS. This means open the */
+/* RS232 device and set up the data structure for my unit. */
+/*......................................................................*/
+
+static int
+datum_pts_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct datum_pts_unit **temp_datum_pts_unit;
+ struct datum_pts_unit *datum_pts;
+ int fd;
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Starting Datum PTS unit %d\n", unit);
+#endif
+
+ /*
+ ** Open the Datum PTS device
+ */
+ fd = open(DATUM_DEV, O_RDWR);
+
+ if (fd < 0) {
+ msyslog(LOG_ERR, "Datum_PTS: open(\"%s\", O_RDWR) failed: %m", DATUM_DEV);
+ return 0;
+ }
+
+ /*
+ ** Create the memory for the new unit
+ */
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits+1)*sizeof(struct datum_pts_unit *));
+ if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ nunits*sizeof(struct datum_pts_unit *));
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+ datum_pts_unit[nunits] = (struct datum_pts_unit *)
+ malloc(sizeof(struct datum_pts_unit));
+ datum_pts = datum_pts_unit[nunits];
+
+ datum_pts->unit = unit; /* set my unit id */
+ datum_pts->yearstart = 0; /* initialize the yearstart to 0 */
+ datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */
+
+ datum_pts->PTS_fd = fd;
+
+ fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Opening RS232 port with file descriptor %d\n",
+ datum_pts->PTS_fd);
+#endif
+
+ /*
+ ** Set up the RS232 terminal device information. Note that we assume that
+ ** we have termios. This code has only been tested on SUNs and HPs. If your
+ ** machine does not have termios this driver cannot be initialized. You can change this
+ ** if you want by editing this source. Please give the changes back to the
+ ** ntp folks so that it can become part of their regular distribution.
+ */
+
+#ifdef HAVE_TERMIOS
+
+ arg.c_iflag = IGNBRK;
+ arg.c_oflag = 0;
+ arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */
+ arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */
+
+ tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg);
+
+#else
+
+ msyslog(LOG_ERR, "Datum_PTS: Termios not supported in this driver");
+ (void)close(datum_pts->PTS_fd);
+
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ return 0;
+
+#endif
+
+ /*
+ ** Initialize the ntpd IO structure
+ */
+
+ datum_pts->peer = peer;
+ datum_pts->io.clock_recv = datum_pts_receive;
+ datum_pts->io.srcclock = (caddr_t)datum_pts;
+ datum_pts->io.datalen = 0;
+ datum_pts->io.fd = datum_pts->PTS_fd;
+
+ if (!io_addclock(&(datum_pts->io))) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Problem adding clock\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Problem adding clock");
+ (void)close(datum_pts->PTS_fd);
+
+ return 0;
+ }
+
+ /*
+ ** Now add one to the number of units and return a successful code
+ */
+
+ nunits++;
+ return 1;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_shutdown - this routine shuts doen the device and */
+/* removes the memory for the unit. */
+/*......................................................................*/
+
+static void
+datum_pts_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ int i,j;
+ struct datum_pts_unit **temp_datum_pts_unit;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Shutdown Datum PTS\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS");
+
+ /*
+ ** First we have to find the right unit (i.e., the one with the same id).
+ ** We do this by looping through the dynamic array of units intil we find
+ ** it. Note, that I don't simply use an array with a maximimum number of
+ ** Datum PTS units. Everything is completely dynamic.
+ */
+
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+
+ /*
+ ** We found the unit so close the file descriptor and free up the memory used
+ ** by the structure.
+ */
+
+ io_closeclock(&datum_pts_unit[i]->io);
+ close(datum_pts_unit[i]->PTS_fd);
+ free(datum_pts_unit[i]);
+
+ /*
+ ** Now clean up the datum_pts_unit dynamic array so that there are no holes.
+ ** This may mean moving pointers around, etc., to keep things compact.
+ */
+
+ if (nunits > 1) {
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits-1)*sizeof(struct datum_pts_unit *));
+ if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ i*sizeof(struct datum_pts_unit *));
+
+ for (j=i+1; j<nunits; j++) {
+ temp_datum_pts_unit[j-1] = datum_pts_unit[j];
+ }
+
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+
+ }else{
+
+ free(datum_pts_unit);
+ datum_pts_unit = NULL;
+
+ }
+
+ return;
+
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Error, could not shut down unit %d\n",unit);
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit);
+
+}
+
+/*......................................................................*/
+/* datum_pts_poll - this routine sends out the time request to the */
+/* Datum PTS device. The time will be passed back in the */
+/* datum_pts_receive() routine. */
+/*......................................................................*/
+
+static void
+datum_pts_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ int i;
+ int unit_index;
+ int error_code;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Poll Datum PTS\n");
+#endif
+
+ /*
+ ** Find the right unit and send out a time request once it is found.
+ */
+
+ unit_index = -1;
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+ unit_index = i;
+ datum_pts = datum_pts_unit[i];
+ error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6);
+ if (error_code != 6) perror("TIME_REQUEST");
+ datum_pts->nbytes = 0;
+ break;
+ }
+ }
+
+ /*
+ ** Print out an error message if we could not find the right unit.
+ */
+
+ if (unit_index == -1) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Error, could not poll unit %d\n",unit);
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit);
+ return;
+
+ }
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_control - not used */
+/*......................................................................*/
+
+static void
+datum_pts_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Control Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_init - initializes things for all possible Datum */
+/* time code generators that might be used. In practice, this is */
+/* only called once at the beginning before anything else is */
+/* called. */
+/*......................................................................*/
+
+static void
+datum_pts_init(void)
+{
+
+ /* */
+ /*...... open up the log file if we are debugging ......................*/
+ /* */
+
+ /*
+ ** Open up the log file if we are debugging. For now, send data out to the
+ ** screen (stdout).
+ */
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Init Datum PTS\n");
+#endif
+
+ /*
+ ** Initialize the time request command string. This is the only message
+ ** that we ever have to send to the Datum PTS (although others are defined).
+ */
+
+ memcpy(TIME_REQUEST, "//k/mn",6);
+
+ /*
+ ** Initialize the number of units to 0 and set the dynamic array of units to
+ ** NULL since there are no units defined yet.
+ */
+
+ datum_pts_unit = NULL;
+ nunits = 0;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_buginfo - not used */
+/*......................................................................*/
+
+static void
+datum_pts_buginfo(
+ int unit,
+ register struct refclockbug *bug,
+ register struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Buginfo Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_receive - receive the time buffer that was read in */
+/* by the ntpd io handling routines. When 7 bytes have been */
+/* received (it may take several tries before all 7 bytes are */
+/* received), then the time code must be unpacked and sent to */
+/* the ntpd clock_receive() routine which causes the systems */
+/* clock to be updated (several layers down). */
+/*......................................................................*/
+
+static void
+datum_pts_receive(
+ struct recvbuf *rbufp
+ )
+{
+ int i;
+ l_fp tstmp;
+ struct datum_pts_unit *datum_pts;
+ char *dpt;
+ int dpend;
+ int tzoff;
+ int timerr;
+ double ftimerr, abserr;
+#ifdef DEBUG_DATUM_PTC
+ double dispersion;
+#endif
+ int goodtime;
+ /*double doffset;*/
+
+ /*
+ ** Get the time code (maybe partial) message out of the rbufp buffer.
+ */
+
+ datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock;
+ dpt = (char *)&rbufp->recv_space;
+ dpend = rbufp->recv_length;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Receive Datum PTS: %d bytes\n", dpend);
+#endif
+
+ /* */
+ /*...... save the ntp system time when the first byte is received ......*/
+ /* */
+
+ /*
+ ** Save the ntp system time when the first byte is received. Note that
+ ** because it may take several calls to this routine before all seven
+ ** bytes of our return message are finally received by the io handlers in
+ ** ntpd, we really do want to use the time tag when the first byte is
+ ** received to reduce the jitter.
+ */
+
+ if (datum_pts->nbytes == 0) {
+ datum_pts->lastrec = rbufp->recv_time;
+ }
+
+ /*
+ ** Increment our count to the number of bytes received so far. Return if we
+ ** haven't gotten all seven bytes yet.
+ */
+
+ for (i=0; i<dpend; i++) {
+ datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i];
+ }
+
+ datum_pts->nbytes += dpend;
+
+ if (datum_pts->nbytes != 7) {
+ return;
+ }
+
+ /*
+ ** Convert the seven bytes received in our time buffer to day, hour, minute,
+ ** second, and msecond values. The usec value is not used for anything
+ ** currently. It is just the fractional part of the time stored in units
+ ** of microseconds.
+ */
+
+ datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) +
+ 10*((datum_pts->retbuf[1] & 0xf0)>>4) +
+ (datum_pts->retbuf[1] & 0x0f);
+
+ datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) +
+ (datum_pts->retbuf[2] & 0x0f);
+
+ datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) +
+ (datum_pts->retbuf[3] & 0x0f);
+
+ datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) +
+ (datum_pts->retbuf[4] & 0x0f);
+
+ datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) +
+ 10*(datum_pts->retbuf[5] & 0x0f) +
+ ((datum_pts->retbuf[6] & 0xf0)>>4);
+
+ datum_pts->usec = 1000*datum_pts->msec;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("day %d, hour %d, minute %d, second %d, msec %d\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec);
+#endif
+
+ /*
+ ** Get the GMT time zone offset. Note that GMT should be zero if the Datum
+ ** reference time is using GMT as its time base. Otherwise we have to
+ ** determine the offset if the Datum PTS is using time of day as its time
+ ** base.
+ */
+
+ goodtime = 0; /* We are not sure about the time and offset yet */
+
+#ifdef GMT
+
+ /*
+ ** This is the case where the Datum PTS is using GMT so there is no time
+ ** zone offset.
+ */
+
+ tzoff = 0; /* set time zone offset to 0 */
+
+#else
+
+ /*
+ ** This is the case where the Datum PTS is using regular time of day for its
+ ** time so we must compute the time zone offset. The way we do it is kind of
+ ** funny but it works. We loop through different time zones (0 to 24) and
+ ** pick the one that gives the smallest error (+- one half hour). The time
+ ** zone offset is stored in the datum_pts structure for future use. Normally,
+ ** the clocktime() routine is only called once (unless the time zone offset
+ ** changes due to daylight savings) since the goodtime flag is set when a
+ ** good time is found (with a good offset). Note that even if the Datum
+ ** PTS is using GMT, this mechanism will still work since it should come up
+ ** with a value for tzoff = 0 (assuming that your system clock is within
+ ** a half hour of the Datum time (even with time zone differences).
+ */
+
+ for (tzoff=0; tzoff<24; tzoff++) {
+ if (clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ (tzoff + datum_pts->tzoff) % 24,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+ datum_pts->lastref.l_uf = 0;
+ error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error);
+#endif
+
+ if ((error < 1799) && (error > -1799)) {
+ tzoff = (tzoff + datum_pts->tzoff) % 24;
+ datum_pts->tzoff = tzoff;
+ goodtime = 1;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone found (clocktime method) = %d\n",tzoff);
+#endif
+
+ break;
+ }
+
+ }
+ }
+
+#endif
+
+ /*
+ ** Make sure that we have a good time from the Datum PTS. Clocktime() also
+ ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e.,
+ ** the fraction of a second) stuff later.
+ */
+
+ if (!goodtime) {
+
+ if (!clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ {
+ printf("Error: bad clocktime\n");
+ printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n",
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ datum_pts->yearstart,
+ datum_pts->lastref.l_ui);
+ }
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Bad clocktime");
+
+ return;
+
+ }else{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Good clocktime\n");
+#endif
+
+ }
+
+ }
+
+ /*
+ ** We have datum_pts->lastref.l_ui set (which is the integer part of the
+ ** time. Now set the microseconds field.
+ */
+
+ TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf);
+
+ /*
+ ** Compute the time correction as the difference between the reference
+ ** time (i.e., the Datum time) minus the receive time (system time).
+ */
+
+ tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */
+ L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */
+ datum_pts->coderecv++; /* increment a counter */
+
+#ifdef DEBUG_DATUM_PTC
+ dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */
+ ftimerr = dispersion;
+ ftimerr /= (1024.0 * 64.0);
+ if (debug)
+ printf("dispersion = %d, %f\n", dispersion, ftimerr);
+#endif
+
+ /*
+ ** Pass the new time to ntpd through the refclock_receive function. Note
+ ** that we are not trying to make any corrections due to the time it takes
+ ** for the Datum PTS to send the message back. I am (erroneously) assuming
+ ** that the time for the Datum PTS to send the time back to us is negligable.
+ ** I suspect that this time delay may be as much as 15 ms or so (but probably
+ ** less). For our needs at JPL, this kind of error is ok so it is not
+ ** necessary to use fudge factors in the ntp.conf file. Maybe later we will.
+ */
+ /*LFPTOD(&tstmp, doffset);*/
+ datum_pts->lastref = datum_pts->lastrec;
+ refclock_receive(datum_pts->peer);
+
+ /*
+ ** Compute sigma squared (not used currently). Maybe later, this could be
+ ** used for the dispersion estimate. The problem is that ntpd does not link
+ ** in the math library so sqrt() is not available. Anyway, this is useful
+ ** for debugging. Maybe later I will just use absolute values for the time
+ ** error to come up with my dispersion estimate. Anyway, for now my dispersion
+ ** is set to 0.
+ */
+
+ timerr = tstmp.l_ui<<20;
+ timerr |= (tstmp.l_uf>>12) & 0x000fffff;
+ ftimerr = timerr;
+ ftimerr /= 1024*1024;
+ abserr = ftimerr;
+ if (ftimerr < 0.0) abserr = -ftimerr;
+
+ if (datum_pts->sigma2 == 0.0) {
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = abserr*abserr;
+ }else{
+ datum_pts->sigma2 = DATUM_MAX_ERROR2;
+ }
+ }else{
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr;
+ }else{
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2;
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Time error = %f seconds\n", ftimerr);
+#endif
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+ if (debug)
+ printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec,
+ ftimerr);
+#endif
+
+}
+#else
+int refclock_datum_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_dumbclock.c b/contrib/ntp/ntpd/refclock_dumbclock.c
new file mode 100644
index 0000000..2788649
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_dumbclock.c
@@ -0,0 +1,385 @@
+/*
+ * refclock_dumbclock - clock driver for a unknown time distribution system
+ * that only provides hh:mm:ss (in local time, yet!).
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports a generic dumb clock that only outputs hh:mm:ss,
+ * in local time, no less.
+ *
+ * Input format:
+ *
+ * hh:mm:ss <cr>
+ *
+ * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only
+ * way it could get stupider.) We take time on the <cr>.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/dumbclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "dumbclock" /* reference ID */
+#define DESCRIPTION "Dumb clock" /* WRU */
+
+
+/*
+ * Insanity check. Since the time is local, we need to make sure that during midnight
+ * transitions, we can convert back to Unix time. If the conversion results in some number
+ * worse than this number of seconds away, assume the next day and retry.
+ */
+#define INSANE_SECONDS 3600
+
+/*
+ * Dumb clock control structure
+ */
+struct dumbclock_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+ struct tm ymd; /* struct tm for y/m/d only */
+};
+
+/*
+ * Function prototypes
+ */
+static int dumbclock_start P((int, struct peer *));
+static void dumbclock_shutdown P((int, struct peer *));
+static void dumbclock_receive P((struct recvbuf *));
+#if 0
+static void dumbclock_poll P((int, struct peer *));
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_dumbclock = {
+ dumbclock_start, /* start up driver */
+ dumbclock_shutdown, /* shut down driver */
+ noentry, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * dumbclock_start - open the devices and initialize data for processing
+ */
+static int
+dumbclock_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ struct tm *tm_time_p;
+ time_t now;
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Dumbclock with device %s\n",device);
+#endif
+ fd = refclock_open(device, SPEED232, 0);
+ if (fd < 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct dumbclock_unit *)emalloc(sizeof(struct dumbclock_unit));
+ if (up == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct dumbclock_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = dumbclock_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+
+ time(&now);
+#ifdef GET_LOCALTIME
+ tm_time_p = localtime(&now);
+#else
+ tm_time_p = gmtime(&now);
+#endif
+ if (tm_time_p)
+ {
+ up->ymd = *tm_time_p;
+ }
+ else
+ {
+ return 0;
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * dumbclock_shutdown - shut down the clock
+ */
+static void
+dumbclock_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * dumbclock_receive - receive data from the serial interface
+ */
+static void
+dumbclock_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = (u_short)temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("dumbclock: timecode %d %s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format...
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+ struct tm *gmtp;
+ struct tm *lt_p;
+ time_t asserted_time; /* the SPM time based on the composite time+date */
+ struct tm asserted_tm; /* the struct tm of the same */
+ int adjyear;
+ int adjmon;
+ int reality_delta;
+ time_t now;
+
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to figure out what day it is. Easier said
+ * than done...
+ */
+
+ asserted_tm.tm_year = up->ymd.tm_year;
+ asserted_tm.tm_mon = up->ymd.tm_mon;
+ asserted_tm.tm_mday = up->ymd.tm_mday;
+ asserted_tm.tm_hour = hours;
+ asserted_tm.tm_min = minutes;
+ asserted_tm.tm_sec = seconds;
+ asserted_tm.tm_isdst = -1;
+
+#ifdef GET_LOCALTIME
+ asserted_time = mktime (&asserted_tm);
+ time(&now);
+#else
+#include "GMT unsupported for dumbclock!"
+#endif
+ reality_delta = asserted_time - now;
+
+ /*
+ * We assume that if the time is grossly wrong, it's because we got the
+ * year/month/day wrong.
+ */
+ if (reality_delta > INSANE_SECONDS)
+ {
+ asserted_time -= SECSPERDAY; /* local clock behind real time */
+ }
+ else if (-reality_delta > INSANE_SECONDS)
+ {
+ asserted_time += SECSPERDAY; /* local clock ahead of real time */
+ }
+ lt_p = localtime(&asserted_time);
+ if (lt_p)
+ {
+ up->ymd = *lt_p;
+ }
+ else
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+
+ if ((gmtp = gmtime (&asserted_time)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+ got_good=1;
+ }
+
+ if (!got_good)
+ {
+ if (up->linect > 0)
+ up->linect--;
+ else
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = (u_char)pp->hour;
+}
+
+#if 0
+/*
+ * dumbclock_poll - called by the transmit procedure
+ */
+static void
+dumbclock_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ if (peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+#endif
+
+#else
+int refclock_dumbclock_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_fg.c b/contrib/ntp/ntpd/refclock_fg.c
new file mode 100644
index 0000000..ce5085a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_fg.c
@@ -0,0 +1,345 @@
+/*
+ * refclock_fg - clock driver for the Forum Graphic GPS datating station
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_FG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Forum Graphic GPS dating station.
+ * More information about FG GPS is available on http://www.forumgraphic.com
+ * Contact das@amt.ru for any question about this driver.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/fgclock%d"
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS"
+#define DESCRIPTION "Forum Graphic GPS dating station"
+#define LENFG 26 /* timecode length */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * Function prototypes
+ */
+static int fg_init P((int));
+static int fg_start P((int, struct peer *));
+static void fg_shutdown P((int, struct peer *));
+static void fg_poll P((int, struct peer *));
+static void fg_receive P((struct recvbuf *));
+
+/*
+ * Forum Graphic unit control structure
+ */
+
+struct fgunit {
+ int pollnum; /* Use peer.poll instead? */
+ int status; /* Hug to check status information on GPS */
+ int y2kwarn; /* Y2K bug */
+};
+
+/*
+ * Queries definition
+ */
+static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_fg = {
+ fg_start, /* start up driver */
+ fg_shutdown, /* shut down driver */
+ fg_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * fg_init - Initialization of FG GPS.
+ */
+
+static int
+fg_init(
+ int fd
+ )
+{
+ if (write(fd, fginit, LENFG) != LENFG)
+ return 0;
+
+ return (1);
+
+}
+
+/*
+ * fg_start - open the device and initialize data for processing
+ */
+static int
+fg_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ int fd;
+ char device[20];
+
+
+ /*
+ * Open device file for reading.
+ */
+ (void)sprintf(device, DEVICE, unit);
+
+#ifdef DEBUG
+ if (debug)
+ printf ("starting FG with device %s\n",device);
+#endif
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+
+ if (!(up = (struct fgunit *)
+ emalloc(sizeof(struct fgunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct fgunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = fg_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ return (0);
+ }
+
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 3);
+ up->pollnum = 0;
+
+ /*
+ * Setup dating station to use GPS receiver.
+ * GPS receiver should work before this operation.
+ */
+ if(!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * fg_shutdown - shut down the clock
+ */
+static void
+fg_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+
+ pp = peer->procptr;
+ up = (struct fgunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * fg_poll - called by the transmit procedure
+ */
+static void
+fg_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Time to poll the clock. The FG clock responds to a
+ * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+
+ if (write(pp->io.fd, fgdate, LENFG) != LENFG)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+
+ if (peer->burst > 0)
+ return;
+ /*
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ */
+ peer->burst = NSTAGE;
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+
+ return;
+
+}
+
+/*
+ * fg_receive - receive data from the serial interface
+ */
+static void
+fg_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ struct peer *peer;
+ char *bpt;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ * We can't use gtlin function because we need bynary data in buf */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct fgunit *)pp->unitptr;
+
+ /*
+ * Below hug to implement receiving of status information
+ */
+ if(!up->pollnum)
+ {
+ up->pollnum++;
+ return;
+ }
+
+
+ if (rbufp->recv_length < (LENFG-2))
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return; /* The reply is invalid discard it. */
+ }
+
+ /* Below I trying to find a correct reply in buffer.
+ * Sometime GPS reply located in the beginnig of buffer,
+ * sometime you can find it with some offset.
+ */
+
+ bpt = (char *)rbufp->recv_space.X_recv_buffer;
+ while(*bpt != '')
+ bpt++;
+
+#define BP2(x) ( bpt[x] & 15 )
+#define BP1(x) (( bpt[x] & 240 ) >> 4)
+
+ pp->year = BP1(2)*10 + BP2(2);
+
+ if(pp->year == 94)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ if(!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ /* GPS is just powered up. The date is invalid -
+ discarding it. Initilize GPS one more time */
+ /* Sorry - this driver will broken in 2094 ;) */
+ }
+
+ if (pp->year < 99)
+ pp->year += 100;
+
+ pp->year += 1900;
+ pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
+
+/*
+ After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
+ benahour. It doubles day number for an hours in replys after 10:10:10 UTC
+ and doubles min every hour at HH:10:ss for a minute.
+ Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
+ Below small code to avoid such situation.
+*/
+ if(up->y2kwarn > 10)
+ pp->hour = BP1(6)*10 + BP2(6);
+ else
+ pp->hour = BP1(5)*10 + BP2(5);
+
+ if((up->y2kwarn > 10) && (pp->hour == 10))
+ {
+ pp->minute = BP1(7)*10 + BP2(7);
+ pp->second = BP1(8)*10 + BP2(8);
+ pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
+ pp->nsec += BP1(10) * 1000;
+ } else {
+ pp->hour = BP1(5)*10 + BP2(5);
+ pp->minute = BP1(6)*10 + BP2(6);
+ pp->second = BP1(7)*10 + BP2(7);
+ pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
+ pp->nsec += BP1(9) * 1000;
+ }
+
+ if((pp->hour == 10) && (pp->minute == 10))
+ {
+ up->y2kwarn++;
+ }
+
+ sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second);
+ pp->lencode = strlen(pp->a_lastcode);
+ /*get_systime(&pp->lastrec);*/
+
+#ifdef DEBUG
+ if (debug)
+ printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second);
+#endif
+ pp->disp = (10e-6);
+ pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
+ /* pp->leap = LEAP_NOWARNING; */
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ return;
+}
+
+
+#else
+int refclock_fg_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_gpsvme.c b/contrib/ntp/ntpd/refclock_gpsvme.c
new file mode 100644
index 0000000..9f9e85c
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_gpsvme.c
@@ -0,0 +1,254 @@
+/* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef __hpux
+#include <sys/rtprio.h> /* may already be included above */
+#include <sys/lock.h> /* NEEDED for PROCLOCK */
+#endif /* __hpux */
+
+#ifdef __linux__
+#include <sys/ioctl.h> /* for _IOR, ioctl */
+#endif /* __linux__ */
+
+enum { /* constants */
+ BUFSIZE = 32,
+ PSC_SYNC_OK = 0x40, /* Sync status bit */
+ DP_LEAPSEC_DAY10DAY1 = 0x82, /* DP RAM address */
+ DP_LEAPSEC_DAY1000DAY100 = 0x83,
+ DELAY = 1,
+ NUNIT = 2 /* max UNITS */
+};
+
+/* clock card registers */
+struct psc_regs {
+ uint32_t low_time; /* card base + 0x00 */
+ uint32_t high_time; /* card base + 0x04 */
+ uint32_t ext_low_time; /* card base + 0x08 */
+ uint32_t ext_high_time; /* card base + 0x0C */
+ uint8_t device_status; /* card base + 0x10 */
+ uint8_t device_control; /* card base + 0x11 */
+ uint8_t reserved0; /* card base + 0x12 */
+ uint8_t ext_100ns; /* card base + 0x13 */
+ uint8_t match_usec; /* card base + 0x14 */
+ uint8_t match_msec; /* card base + 0x15 */
+ uint8_t reserved1; /* card base + 0x16 */
+ uint8_t reserved2; /* card base + 0x17 */
+ uint8_t reserved3; /* card base + 0x18 */
+ uint8_t reserved4; /* card base + 0x19 */
+ uint8_t dp_ram_addr; /* card base + 0x1A */
+ uint8_t reserved5; /* card base + 0x1B */
+ uint8_t reserved6; /* card base + 0x1C */
+ uint8_t reserved7; /* card base + 0x1D */
+ uint8_t dp_ram_data; /* card base + 0x1E */
+ uint8_t reserved8; /* card base + 0x1F */
+} *volatile regp[NUNIT];
+
+#define PSC_REGS _IOR('K', 0, long) /* ioctl argument */
+
+/* Macros to swap byte order and convert BCD to binary */
+#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
+(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
+#define BCD2INT2(val) ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
+#define BCD2INT3(val) ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
+((val) & 0x0f) )
+
+/* PSC interface definitions */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "USNO" /* reference ID */
+#define DESCRIPTION "Brandywine PCI-SyncClock32"
+#define DEVICE "/dev/refclock%1d" /* device file */
+
+/* clock unit control structure */
+struct psc_unit {
+ short unit; /* NTP refclock unit number */
+ short last_hour; /* last hour (monitor leap sec) */
+ int msg_flag[2]; /* count error messages */
+};
+int fd[NUNIT]; /* file descriptor */
+
+/* Local function prototypes */
+static int psc_start(int, struct peer *);
+static void psc_shutdown(int, struct peer *);
+static void psc_poll(int, struct peer *);
+static void check_leap_sec(struct refclockproc *, int);
+
+/* Transfer vector */
+struct refclock refclock_gpsvme = {
+ psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
+};
+
+/* psc_start: open device and initialize data for processing */
+static int
+psc_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ char buf[BUFSIZE];
+ struct refclockproc *pp;
+ struct psc_unit *up = emalloc(sizeof *up);
+
+ if (unit < 0 || unit > 1) { /* support units 0 and 1 */
+ msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
+ return 0;
+ }
+
+ if (!up) {
+ msyslog(LOG_ERR, "psc_start: unit: %d, emalloc: %m", unit);
+ return 0;
+ }
+ memset(up, '\0', sizeof *up);
+
+ sprintf(buf, DEVICE, unit); /* dev file name */
+ fd[unit] = open(buf, O_RDONLY); /* open device file */
+ if (fd[unit] < 0) {
+ msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit);
+ return 0;
+ }
+
+ /* get the address of the mapped regs */
+ if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
+ msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit);
+ return 0;
+ }
+
+ /* initialize peer variables */
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t) peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+ pp->unitptr = (caddr_t) up;
+ get_systime(&pp->lastrec);
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ up->unit = unit;
+#ifdef __hpux
+ rtprio(0,120); /* set real time priority */
+ plock(PROCLOCK); /* lock process in memory */
+#endif /* __hpux */
+ return 1;
+}
+
+/* psc_shutdown: shut down the clock */
+static void
+psc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ free(peer->procptr->unitptr);
+ close(fd[unit]);
+}
+
+/* psc_poll: read, decode, and record device time */
+static void
+psc_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ struct psc_unit *up;
+ unsigned tlo, thi;
+ unsigned char status;
+
+ up = (struct psc_unit *) pp->unitptr;
+ tlo = regp[unit]->low_time; /* latch and read first 4 bytes */
+ thi = regp[unit]->high_time; /* read 4 higher order bytes */
+ status = regp[unit]->device_status; /* read device status byte */
+
+ if (!(status & PSC_SYNC_OK)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ if (!up->msg_flag[unit]) { /* write once to system log */
+ msyslog(LOG_WARNING,
+ "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
+ status, unit);
+ up->msg_flag[unit] = 1;
+ }
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ tlo = SWAP(tlo); /* little to big endian swap on */
+ thi = SWAP(thi); /* copy of data */
+ /* convert the BCD time to broken down time used by refclockproc */
+ pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16);
+ pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8);
+ pp->minute = BCD2INT2(thi & 0x000000FF);
+ pp->second = BCD2INT2(tlo >> 24);
+ /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
+ second in microseconds if usec is nonzero. */
+ pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
+ BCD2INT3(tlo & 0x00000FFF);
+
+ sprintf(pp->a_lastcode, "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x",
+ pp->day, pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
+ tlo);
+ pp->lencode = strlen(pp->a_lastcode);
+
+ /* compute the timecode timestamp */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ /* simulate the NTP receive and packet procedures */
+ refclock_receive(peer);
+ /* write clock statistics to file */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /* With the first timecode beginning the day, check for a GPS
+ leap second notification. */
+ if (pp->hour < up->last_hour) {
+ check_leap_sec(pp, unit);
+ up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */
+ }
+ up->last_hour = pp->hour;
+}
+
+/* check_leap_sec: read the Dual Port RAM leap second day registers. The
+ onboard GPS receiver should write the hundreds digit of day of year in
+ DP_LeapSec_Day1000Day100 and the tens and ones digits in
+ DP_LeapSec_Day10Day1. If these values are nonzero and today, we have
+ a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
+ If the BCD data are zero or a date other than today, set pp->leap to
+ LEAP_NOWARNING. */
+static void
+check_leap_sec(struct refclockproc *pp, int unit)
+{
+ unsigned char dhi, dlo;
+ int leap_day;
+
+ regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
+ usleep(DELAY);
+ dlo = regp[unit]->dp_ram_data;
+ regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
+ usleep(DELAY);
+ dhi = regp[unit]->dp_ram_data;
+ leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
+
+ pp->leap = LEAP_NOWARNING; /* default */
+ if (leap_day && leap_day == pp->day) {
+ pp->leap = LEAP_ADDSECOND; /* leap second today */
+ msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
+ leap_day, dhi, dlo);
+ }
+}
+
+#else
+int refclock_gpsvme_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_heath.c b/contrib/ntp/ntpd/refclock_heath.c
new file mode 100644
index 0000000..a56b491
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_heath.c
@@ -0,0 +1,451 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000
+ * (but no longer the GC-1001 Model II, which apparently never worked)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HEATH)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* not HAVE_SYS_IOCTL_H */
+
+/*
+ * This driver supports the Heath GC-1000 Most Accurate Clock, with
+ * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ * robust than other supported receivers. Its claimed accuracy is 100 ms
+ * when actually synchronized to the broadcast signal, but this doesn't
+ * happen even most of the time, due to propagation conditions, ambient
+ * noise sources, etc. When not synchronized, the accuracy is at the
+ * whim of the internal clock oscillator, which can wander into the
+ * sunset without warning. Since the indicated precision is 100 ms,
+ * expect a host synchronized only to this thing to wander to and fro,
+ * occasionally being rudely stepped when the offset exceeds the default
+ * clock_max of 128 ms.
+ *
+ * There were two GC-1000 versions supported by this driver. The original
+ * GC-1000 with RS-232 output first appeared in 1983, but dissapeared
+ * from the market a few years later. The GC-1001 II with RS-232 output
+ * first appeared circa 1990, but apparently is no longer manufactured.
+ * The two models differ considerably, both in interface and commands.
+ * The GC-1000 has a pseudo-bipolar timecode output triggered by a RTS
+ * transition. The timecode includes both the day of year and time of
+ * day. The GC-1001 II has a true bipolar output and a complement of
+ * single character commands. The timecode includes only the time of
+ * day.
+ *
+ * The GC-1001 II was apparently never tested and, based on a Coverity
+ * scan, apparently never worked [Bug 689]. Related code has been disabled.
+ *
+ * GC-1000
+ *
+ * The internal DIPswitches should be set to operate in MANUAL mode. The
+ * external DIPswitches should be set to GMT and 24-hour format.
+ *
+ * In MANUAL mode the clock responds to a rising edge of the request to
+ * send (RTS) modem control line by sending the timecode. Therefore, it
+ * is necessary that the operating system implement the TIOCMBIC and
+ * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
+ * restrictions require the use of a POSIX-compatible programming
+ * interface, although other interfaces may work as well.
+ *
+ * A simple hardware modification to the clock can be made which
+ * prevents the clock hearing the request to send (RTS) if the HI SPEC
+ * lamp is out. Route the HISPEC signal to the tone decoder board pin
+ * 19, from the display, pin 19. Isolate pin 19 of the decoder board
+ * first, but maintain connection with pin 10. Also isolate pin 38 of
+ * the CPU on the tone board, and use half an added 7400 to gate the
+ * original signal to pin 38 with that from pin 19.
+ *
+ * The clock message consists of 23 ASCII printing characters in the
+ * following format:
+ *
+ * hh:mm:ss.f AM dd/mm/yr<cr>
+ *
+ * hh:mm:ss.f = hours, minutes, seconds
+ * f = deciseconds ('?' when out of spec)
+ * AM/PM/bb = blank in 24-hour mode
+ * dd/mm/yr = day, month, year
+ *
+ * The alarm condition is indicated by '?', rather than a digit, at f.
+ * Note that 0?:??:??.? is displayed before synchronization is first
+ * established and hh:mm:ss.? once synchronization is established and
+ * then lost again for about a day.
+ *
+ * GC-1001 II
+ *
+ * Commands consist of a single letter and are case sensitive. When
+ * enterred in lower case, a description of the action performed is
+ * displayed. When enterred in upper case the action is performed.
+ * Following is a summary of descriptions as displayed by the clock:
+ *
+ * The clock responds with a command The 'A' command returns an ASCII
+ * local time string: HH:MM:SS.T xx<CR>, where
+ *
+ * HH = hours
+ * MM = minutes
+ * SS = seconds
+ * T = tenths-of-seconds
+ * xx = 'AM', 'PM', or ' '
+ * <CR> = carriage return
+ *
+ * The 'D' command returns 24 pairs of bytes containing the variable
+ * divisor value at the end of each of the previous 24 hours. This
+ * allows the timebase trimming process to be observed. UTC hour 00 is
+ * always returned first. The first byte of each pair is the high byte
+ * of (variable divisor * 16); the second byte is the low byte of
+ * (variable divisor * 16). For example, the byte pair 3C 10 would be
+ * returned for a divisor of 03C1 hex (961 decimal).
+ *
+ * The 'I' command returns: | TH | TL | ER | DH | DL | U1 | I1 | I2 | ,
+ * where
+ *
+ * TH = minutes since timebase last trimmed (high byte)
+ * TL = minutes since timebase last trimmed (low byte)
+ * ER = last accumulated error in 1.25 ms increments
+ * DH = high byte of (current variable divisor * 16)
+ * DL = low byte of (current variable divisor * 16)
+ * U1 = UT1 offset (/.1 s): | + | 4 | 2 | 1 | 0 | 0 | 0 | 0 |
+ * I1 = information byte 1: | W | C | D | I | U | T | Z | 1 | ,
+ * where
+ *
+ * W = set by WWV(H)
+ * C = CAPTURE LED on
+ * D = TRIM DN LED on
+ * I = HI SPEC LED on
+ * U = TRIM UP LED on
+ * T = DST switch on
+ * Z = UTC switch on
+ * 1 = UT1 switch on
+ *
+ * I2 = information byte 2: | 8 | 8 | 4 | 2 | 1 | D | d | S | ,
+ * where
+ *
+ * 8, 8, 4, 2, 1 = TIME ZONE switch settings
+ * D = DST bit (#55) in last-received frame
+ * d = DST bit (#2) in last-received frame
+ * S = clock is in simulation mode
+ *
+ * The 'P' command returns 24 bytes containing the number of frames
+ * received without error during UTC hours 00 through 23, providing an
+ * indication of hourly propagation. These bytes are updated each hour
+ * to reflect the previous 24 hour period. UTC hour 00 is always
+ * returned first.
+ *
+ * The 'T' command returns the UTC time: | HH | MM | SS | T0 | , where
+ * HH = tens-of-hours and hours (packed BCD)
+ * MM = tens-of-minutes and minutes (packed BCD)
+ * SS = tens-of-seconds and seconds (packed BCD)
+ * T = tenths-of-seconds (BCD)
+ *
+ * Fudge Factors
+ *
+ * A fudge time1 value of .04 s appears to center the clock offset
+ * residuals. The fudge time2 parameter is the local time offset east of
+ * Greenwich, which depends on DST. Sorry about that, but the clock
+ * gives no hint on what the DIPswitches say.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/heath%d" /* device name and unit */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define LENHEATH1 23 /* min timecode length */
+#if 0 /* BUG 689 */
+#define LENHEATH2 13 /* min timecode length */
+#endif
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Baud rate table. The GC-1000 supports 1200, 2400 and 4800; the
+ * GC-1001 II supports only 9600.
+ */
+static int speed[] = {B1200, B2400, B4800, B9600};
+
+/*
+ * Function prototypes
+ */
+static int heath_start P((int, struct peer *));
+static void heath_shutdown P((int, struct peer *));
+static void heath_receive P((struct recvbuf *));
+static void heath_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_heath = {
+ heath_start, /* start up driver */
+ heath_shutdown, /* shut down driver */
+ heath_poll, /* transmit poll message */
+ noentry, /* not used (old heath_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old heath_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, speed[peer->ttl & 0x3],
+ LDISC_REMOTE)))
+ return (0);
+ pp = peer->procptr;
+ pp->io.clock_recv = heath_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ io_closeclock(&pp->io);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char dsec, a[5];
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &trtmp);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ switch (pp->lencode) {
+
+ /*
+ * GC-1000 timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ * GC-1001 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH1:
+ if (sscanf(pp->a_lastcode,
+ "%2d:%2d:%2d.%c%5c%2d/%2d/%2d", &pp->hour,
+ &pp->minute, &pp->second, &dsec, a, &month, &day,
+ &pp->year) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+#if 0 /* BUG 689 */
+ /*
+ * GC-1001 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH2:
+ if (sscanf(pp->a_lastcode, "%2d:%2d:%2d.%c", &pp->hour,
+ &pp->minute, &pp->second, &dsec) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } else {
+ struct tm *tm_time_p;
+ time_t now;
+
+ time(&now); /* we should grab 'now' earlier */
+ tm_time_p = gmtime(&now);
+ /*
+ * There is a window of time around midnight
+ * where this will Do The Wrong Thing.
+ */
+ if (tm_time_p) {
+ month = tm_time_p->tm_mon + 1;
+ day = tm_time_p->tm_mday;
+ } else {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ break;
+#endif
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * We determine the day of the year from the DIPswitches. This
+ * should be fixed, since somebody might forget to set them.
+ * Someday this hazard will be fixed by a fiendish scheme that
+ * looks at the timecode and year the radio shows, then computes
+ * the residue of the seconds mod the seconds in a leap cycle.
+ * If in the third year of that cycle and the third and later
+ * months of that year, add one to the day. Then, correct the
+ * timecode accordingly. Icky pooh. This bit of nonsense could
+ * be avoided if the engineers had been required to write a
+ * device driver before finalizing the timecode format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ /*
+ * Determine synchronization and last update
+ */
+ if (!isdigit((int)dsec))
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->nsec = (dsec - '0') * 100000000;
+ pp->leap = LEAP_NOWARNING;
+ }
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * heath_poll - called by the transmit procedure
+ */
+static void
+heath_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int bits = TIOCM_RTS;
+
+ /*
+ * At each poll we check for timeout and toggle the RTS modem
+ * control line, then take a timestamp. Presumably, this is the
+ * event the radio captures to generate the timecode.
+ * Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp = peer->procptr;
+
+ /*
+ * We toggle the RTS modem control lead (GC-1000) and sent a T
+ * (GC-1001 II) to kick a timecode loose from the radio. This
+ * code works only for POSIX and SYSV interfaces. With bsd you
+ * are on your own. We take a timestamp between the up and down
+ * edges to lengthen the pulse, which should be about 50 usec on
+ * a Sun IPC. With hotshot CPUs, the pulse might get too short.
+ * Later.
+ *
+ * Bug 689: Even though we no longer support the GC-1001 II,
+ * I'm leaving the 'T' write in for timing purposes.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+ get_systime(&pp->lastrec);
+ if (write(pp->io.fd, "T", 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("heath: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ peer->burst = MAXSTAGE;
+ pp->polls++;
+}
+
+#else
+int refclock_heath_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_hopfpci.c b/contrib/ntp/ntpd/refclock_hopfpci.c
new file mode 100644
index 0000000..81c186d
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_hopfpci.c
@@ -0,0 +1,265 @@
+/*
+ * refclock_hopfpci.c
+ *
+ * - clock driver for hopf 6039 PCI board (GPS or DCF77)
+ * Bernd Altmeier altmeier@atlsoft.de
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ * In order to run this driver you have to install and test
+ * the PCI-board driver for your system first.
+ *
+ * On Linux/UNIX
+ *
+ * The driver attempts to open the device /dev/hopf6039 .
+ * The device entry will be made by the installation process of
+ * the kernel module for the PCI-bus board. The driver sources
+ * belongs to the delivery equipment of the PCI-board.
+ *
+ * On Windows NT/2000
+ *
+ * The driver attempts to open the device by calling the function
+ * "OpenHopfDevice()". This function will be installed by the
+ * Device Driver for the PCI-bus board. The driver belongs to the
+ * delivery equipment of the PCI-board.
+ *
+ *
+ * Start 21.03.2000 Revision: 01.20
+ * changes 22.12.2000 Revision: 01.40 flag1 = 1 sync even if Quarz
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HOPF_PCI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/ioctl.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+# include "hopf6039.h"
+#else
+# include "hopf_PCI_io.h"
+#endif
+
+/*
+ * hopfpci interface definitions
+ */
+#define PRECISION (-10) /* precision assumed (1 ms) */
+#define REFID "hopf" /* reference ID */
+#define DESCRIPTION "hopf Elektronik PCI radio board"
+
+#define NSAMPLES 3 /* stages of median filter */
+#ifndef SYS_WINNT
+# define DEVICE "/dev/hopf6039" /* device name inode*/
+#else
+# define DEVICE "hopf6039" /* device name WinNT */
+#endif
+
+#define LEWAPWAR 0x20 /* leap second warning bit */
+
+#define HOPF_OPMODE 0xC0 /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x40 /* internal clock */
+#define HOPF_RADIO 0x80 /* radio clock */
+#define HOPF_RADIOHP 0xC0 /* high precision radio clock */
+
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ short unit; /* NTP refclock unit number */
+ char leap_status; /* leap second flag */
+};
+int fd; /* file descr. */
+
+/*
+ * Function prototypes
+ */
+static int hopfpci_start (int, struct peer *);
+static void hopfpci_shutdown (int, struct peer *);
+static void hopfpci_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfpci = {
+ hopfpci_start, /* start up driver */
+ hopfpci_shutdown, /* shut down driver */
+ hopfpci_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfpci_start - attach to hopf PCI board 6039
+ */
+static int
+hopfpci_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct hopfclock_unit *up;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct hopfclock_unit *) emalloc(sizeof(struct hopfclock_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "hopfPCIClock(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("hopfPCIClock(%d) emalloc\n",unit);
+#endif
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct hopfclock_unit));
+
+#ifndef SYS_WINNT
+
+ fd = open(DEVICE,O_RDWR); /* try to open hopf clock device */
+
+#else
+ if (!OpenHopfDevice()){
+ msyslog(LOG_ERR,"Start: %s unit: %d failed!",DEVICE,unit);
+ return (0);
+ }
+#endif
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = INVALID_SOCKET;
+ pp->unitptr = (caddr_t)up;
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ if (pp->unitptr!=0) {
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ return (1);
+ }
+ else {
+ return 0;
+ }
+}
+
+
+/*
+ * hopfpci_shutdown - shut down the clock
+ */
+static void
+hopfpci_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+
+#ifndef SYS_WINNT
+ close(fd);
+#else
+ CloseHopfDevice();
+#endif
+}
+
+
+/*
+ * hopfpci_poll - called by the transmit procedure
+ */
+static void
+hopfpci_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ HOPFTIME m_time;
+
+ pp = peer->procptr;
+
+#ifndef SYS_WINNT
+ ioctl(fd,HOPF_CLOCK_GET_UTC,&m_time);
+#else
+ GetHopfSystemTime(&m_time);
+#endif
+ pp->polls++;
+
+ pp->day = ymd2yd(m_time.wYear,m_time.wMonth,m_time.wDay);
+ pp->hour = m_time.wHour;
+ pp->minute = m_time.wMinute;
+ pp->second = m_time.wSecond;
+ pp->nsec = m_time.wMilliseconds * 1000000;
+ if (m_time.wStatus & LEWAPWAR)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ sprintf(pp->a_lastcode,"ST: %02X T: %02d:%02d:%02d.%03ld D: %02d.%02d.%04d",
+ m_time.wStatus, pp->hour, pp->minute, pp->second,
+ pp->nsec / 1000000, m_time.wDay, m_time.wMonth, m_time.wYear);
+ pp->lencode = (u_short)strlen(pp->a_lastcode);
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INVALID) { /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ return;
+}
+
+#else
+int refclock_hopfpci_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_hopfser.c b/contrib/ntp/ntpd/refclock_hopfser.c
new file mode 100644
index 0000000..94b660b
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_hopfser.c
@@ -0,0 +1,380 @@
+/*
+ *
+ * refclock_hopfser.c
+ * - clock driver for hopf serial boards (GPS or DCF77)
+ *
+ * Date: 30.03.2000 Revision: 01.10
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# ifndef __QNXNTO__
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * clock definitions
+ */
+#define DESCRIPTION "hopf Elektronik serial clock" /* Long name */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "hopf\0" /* reference ID */
+/*
+ * I/O definitions
+ */
+#define DEVICE "/dev/hopfclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+
+#define STX 0x02
+#define ETX 0x03
+#define CR 0x0c
+#define LF 0x0a
+
+/* parse states */
+#define REC_QUEUE_EMPTY 0
+#define REC_QUEUE_FULL 1
+
+#define HOPF_OPMODE 0x0C /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x04 /* internal clock */
+#define HOPF_RADIO 0x08 /* radio clock */
+#define HOPF_RADIOHP 0x0C /* high precision radio clock */
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int rpt_next;
+};
+
+/*
+ * Function prototypes
+ */
+
+static int hopfserial_start P((int, struct peer *));
+static void hopfserial_shutdown P((int, struct peer *));
+static void hopfserial_receive P((struct recvbuf *));
+static void hopfserial_poll P((int, struct peer *));
+/* static void hopfserial_io P((struct recvbuf *)); */
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfser = {
+ hopfserial_start, /* start up driver */
+ hopfserial_shutdown, /* shut down driver */
+ hopfserial_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfserial_start - open the devices and initialize data for processing
+ */
+static int
+hopfserial_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+#ifdef SYS_WINNT
+ (void) sprintf(gpsdev, "COM%d:", unit);
+#else
+ (void) sprintf(gpsdev, DEVICE, unit);
+#endif
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct hopfclock_unit *) emalloc(sizeof(struct hopfclock_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "hopfSerialClock(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) emalloc\n",unit);
+#endif
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct hopfclock_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = hopfserial_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) io_addclock\n",unit);
+#endif
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->clockdesc = DESCRIPTION;
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+
+ return (1);
+}
+
+
+/*
+ * hopfserial_shutdown - shut down the clock
+ */
+static void
+hopfserial_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct hopfclock_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+
+/*
+ * hopfserial_receive - receive data from the serial interface
+ */
+
+static void
+hopfserial_receive (
+ struct recvbuf *rbufp
+ )
+{
+ struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int synch; /* synchhronization indicator */
+ int DoW; /* Dow */
+
+ int day, month; /* ddd conversion */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct hopfclock_unit *)pp->unitptr;
+
+ if (up->rpt_next == 0 )
+ return;
+
+
+ up->rpt_next = 0; /* wait until next poll interval occur */
+
+ pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+
+ if (pp->lencode == 0)
+ return;
+
+ sscanf(pp->a_lastcode,
+#if 1
+ "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */
+#else
+ "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
+#endif
+ &synch,
+ &DoW,
+ &pp->hour,
+ &pp->minute,
+ &pp->second,
+ &day,
+ &month,
+ &pp->year);
+
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year,month,day);
+ pp->leap=0;
+
+ /* Year-2000 check! */
+ /* wrap 2-digit date into 4-digit */
+
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */
+ pp->year += 1900;
+
+ /* preparation for timecode ntpq rl command ! */
+
+#if 0
+ wsprintf(pp->a_lastcode,
+ "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d",
+ synch,
+ DoW,
+ day,
+ month,
+ pp->year,
+ pp->hour,
+ pp->minute,
+ pp->second);
+
+ pp->lencode = strlen(pp->a_lastcode);
+ if ((synch && 0xc) == 0 ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#endif
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+#if 0
+ msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second);
+#endif
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ return;
+}
+
+
+/*
+ * hopfserial_poll - called by the transmit procedure
+ *
+ */
+static void
+hopfserial_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ up = (struct hopfclock_unit *)pp->unitptr;
+
+ pp->polls++;
+ up->rpt_next = 1;
+
+#if 0
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#endif
+
+ return;
+}
+
+#else
+int refclock_hopfser_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_hpgps.c b/contrib/ntp/ntpd/refclock_hpgps.c
new file mode 100644
index 0000000..5efd19e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_hpgps.c
@@ -0,0 +1,619 @@
+/*
+ * refclock_hpgps - clock driver for HP 58503A GPS receiver
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HPGPS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* Version 0.1 April 1, 1995
+ * 0.2 April 25, 1995
+ * tolerant of missing timecode response prompt and sends
+ * clear status if prompt indicates error;
+ * can use either local time or UTC from receiver;
+ * can get receiver status screen via flag4
+ *
+ * WARNING!: This driver is UNDER CONSTRUCTION
+ * Everything in here should be treated with suspicion.
+ * If it looks wrong, it probably is.
+ *
+ * Comments and/or questions to: Dave Vitanye
+ * Hewlett Packard Company
+ * dave@scd.hp.com
+ * (408) 553-2856
+ *
+ * Thanks to the author of the PST driver, which was the starting point for
+ * this one.
+ *
+ * This driver supports the HP 58503A Time and Frequency Reference Receiver.
+ * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver.
+ * The receiver accuracy when locked to GPS in normal operation is better
+ * than 1 usec. The accuracy when operating in holdover is typically better
+ * than 10 usec. per day.
+ *
+ * The same driver also handles the HP Z3801A which is available surplus
+ * from the cell phone industry. It's popular with hams.
+ * It needs a different line setup: 19200 baud, 7 data bits, odd parity
+ * That is selected by adding "mode 1" to the server line in ntp.conf
+ * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005
+ *
+ *
+ * The receiver should be operated with factory default settings.
+ * Initial driver operation: expects the receiver to be already locked
+ * to GPS, configured and able to output timecode format 2 messages.
+ *
+ * The driver uses the poll sequence :PTIME:TCODE? to get a response from
+ * the receiver. The receiver responds with a timecode string of ASCII
+ * printing characters, followed by a <cr><lf>, followed by a prompt string
+ * issued by the receiver, in the following format:
+ * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi >
+ *
+ * The driver processes the response at the <cr> and <lf>, so what the
+ * driver sees is the prompt from the previous poll, followed by this
+ * timecode. The prompt from the current poll is (usually) left unread until
+ * the next poll. So (except on the very first poll) the driver sees this:
+ *
+ * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ * The T is the on-time character, at 980 msec. before the next 1PPS edge.
+ * The # is the timecode format type. We look for format 2.
+ * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp
+ * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps,
+ * so the first approximation for fudge time1 is nominally -0.955 seconds.
+ * This number probably needs adjusting for each machine / OS type, so far:
+ * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05
+ * -0.953175 on an HP 9000 Model 370 HP-UX 9.10
+ *
+ * This receiver also provides a 1PPS signal, but I haven't figured out
+ * how to deal with any of the CLK or PPS stuff yet. Stay tuned.
+ *
+ */
+
+/*
+ * Fudge Factors
+ *
+ * Fudge time1 is used to accomodate the timecode serial interface adjustment.
+ * Fudge flag4 can be set to request a receiver status screen summary, which
+ * is recorded in the clockstats file.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/hpgps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define SPEED232Z B19200 /* uart speed (19200 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "HP 58503A GPS Time and Frequency Reference Receiver"
+
+#define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */
+
+#define MTZONE 2 /* number of fields in timezone reply */
+#define MTCODET2 12 /* number of fields in timecode format T2 */
+#define NTCODET2 21 /* number of chars to checksum in format T2 */
+
+/*
+ * Tables to compute the day of year from yyyymmdd timecode.
+ * Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct hpgpsunit {
+ int pollcnt; /* poll message counter */
+ int tzhour; /* timezone offset, hours */
+ int tzminute; /* timezone offset, minutes */
+ int linecnt; /* set for expected multiple line responses */
+ char *lastptr; /* pointer to receiver response data */
+ char statscrn[SMAX]; /* receiver status screen buffer */
+};
+
+/*
+ * Function prototypes
+ */
+static int hpgps_start P((int, struct peer *));
+static void hpgps_shutdown P((int, struct peer *));
+static void hpgps_receive P((struct recvbuf *));
+static void hpgps_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hpgps = {
+ hpgps_start, /* start up driver */
+ hpgps_shutdown, /* shut down driver */
+ hpgps_poll, /* transmit poll message */
+ noentry, /* not used (old hpgps_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old hpgps_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * hpgps_start - open the devices and initialize data for processing
+ */
+static int
+hpgps_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ * Default is HP 58503A, mode arg selects HP Z3801A
+ */
+ (void)sprintf(device, DEVICE, unit);
+ /* mode parameter to server config line shares ttl slot */
+ if ((peer->ttl == 1)) {
+ if (!(fd = refclock_open(device, SPEED232Z,
+ LDISC_CLK | LDISC_7O1)))
+ return (0);
+ } else {
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+ }
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct hpgpsunit *)
+ emalloc(sizeof(struct hpgpsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct hpgpsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = hpgps_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tzhour = 0;
+ up->tzminute = 0;
+
+ *up->statscrn = '\0';
+ up->lastptr = up->statscrn;
+ up->pollcnt = 2;
+
+ /*
+ * Get the identifier string, which is logged but otherwise ignored,
+ * and get the local timezone information
+ */
+ up->linecnt = 1;
+ if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20)
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * hpgps_shutdown - shut down the clock
+ */
+static void
+hpgps_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * hpgps_receive - receive data from the serial interface
+ */
+static void
+hpgps_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char tcodechar1; /* identifies timecode format */
+ char tcodechar2; /* identifies timecode format */
+ char timequal; /* time figure of merit: 0-9 */
+ char freqqual; /* frequency figure of merit: 0-3 */
+ char leapchar; /* leapsecond: + or 0 or - */
+ char servchar; /* request for service: 0 = no, 1 = yes */
+ char syncchar; /* time info is invalid: 0 = no, 1 = yes */
+ short expectedsm; /* expected timecode byte checksum */
+ short tcodechksm; /* computed timecode byte checksum */
+ int i,m,n;
+ int month, day, lastday;
+ char *tcp; /* timecode pointer (skips over the prompt) */
+ char prompt[BMAX]; /* prompt in response from receiver */
+
+ /*
+ * Initialize pointers and read the receiver response
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ *pp->a_lastcode = '\0';
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: lencode: %d timecode:%s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * If there's no characters in the reply, we can quit now
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linecnt is greater than zero, we are getting information only,
+ * such as the receiver identification string or the receiver status
+ * screen, so put the receiver response at the end of the status
+ * screen buffer. When we have the last line, write the buffer to
+ * the clockstats file and return without further processing.
+ *
+ * If linecnt is zero, we are expecting either the timezone
+ * or a timecode. At this point, also write the response
+ * to the clockstats file, and go on to process the prompt (if any),
+ * timezone, or timecode and timestamp.
+ */
+
+
+ if (up->linecnt-- > 0) {
+ if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) {
+ *up->lastptr++ = '\n';
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ }
+ if (up->linecnt == 0)
+ record_clock_stats(&peer->srcadr, up->statscrn);
+
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ pp->lastrec = trtmp;
+
+ up->lastptr = up->statscrn;
+ *up->lastptr = '\0';
+ up->pollcnt = 2;
+
+ /*
+ * We get down to business: get a prompt if one is there, issue
+ * a clear status command if it contains an error indication.
+ * Next, check for either the timezone reply or the timecode reply
+ * and decode it. If we don't recognize the reply, or don't get the
+ * proper number of decoded fields, or get an out of range timezone,
+ * or if the timecode checksum is bad, then we declare bad format
+ * and exit.
+ *
+ * Timezone format (including nominal prompt):
+ * scpi > -H,-M<cr><lf>
+ *
+ * Timecode format (including nominal prompt):
+ * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ */
+
+ (void)strcpy(prompt,pp->a_lastcode);
+ tcp = strrchr(pp->a_lastcode,'>');
+ if (tcp == NULL)
+ tcp = pp->a_lastcode;
+ else
+ tcp++;
+ prompt[tcp - pp->a_lastcode] = '\0';
+ while ((*tcp == ' ') || (*tcp == '\t')) tcp++;
+
+ /*
+ * deal with an error indication in the prompt here
+ */
+ if (strrchr(prompt,'E') > strrchr(prompt,'s')){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: error indicated in prompt: %s\n", prompt);
+#endif
+ if (write(pp->io.fd, "*CLS\r\r", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+
+ /*
+ * make sure we got a timezone or timecode format and
+ * then process accordingly
+ */
+ m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2);
+
+ if (m != 2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: no format indicator\n");
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ switch (tcodechar1) {
+
+ case '+':
+ case '-':
+ m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute);
+ if (m != MTZONE) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timezone\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if ((up->tzhour < -12) || (up->tzhour > 13) ||
+ (up->tzminute < -59) || (up->tzminute > 59)){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: timezone %d, %d out of range\n",
+ up->tzhour, up->tzminute);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ return;
+
+ case 'T':
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized reply format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar1 switch */
+
+
+ switch (tcodechar2) {
+
+ case '2':
+ m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
+ &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second,
+ &timequal, &freqqual, &leapchar, &servchar, &syncchar,
+ &expectedsm);
+ n = NTCODET2;
+
+ if (m != MTCODET2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timecode\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized timecode format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar2 format switch */
+
+ /*
+ * Compute and verify the checksum.
+ * Characters are summed starting at tcodechar1, ending at just
+ * before the expected checksum. Bail out if incorrect.
+ */
+ tcodechksm = 0;
+ while (n-- > 0) tcodechksm += *tcp++;
+ tcodechksm &= 0x00ff;
+
+ if (tcodechksm != expectedsm) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: checksum %2hX doesn't match %2hX expected\n",
+ tcodechksm, expectedsm);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Compute the day of year from the yyyymmdd format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ if ( ! isleap_4(pp->year) ) { /* Y2KFixes */
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day1tab[i];
+ lastday = 365;
+ } else {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day2tab[i];
+ lastday = 366;
+ }
+
+ /*
+ * Deal with the timezone offset here. The receiver timecode is in
+ * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values.
+ * For example, Pacific Standard Time is -8 hours , 0 minutes.
+ * Deal with the underflows and overflows.
+ */
+ pp->minute -= up->tzminute;
+ pp->hour -= up->tzhour;
+
+ if (pp->minute < 0) {
+ pp->minute += 60;
+ pp->hour--;
+ }
+ if (pp->minute > 59) {
+ pp->minute -= 60;
+ pp->hour++;
+ }
+ if (pp->hour < 0) {
+ pp->hour += 24;
+ day--;
+ if (day < 1) {
+ pp->year--;
+ if ( isleap_4(pp->year) ) /* Y2KFixes */
+ day = 366;
+ else
+ day = 365;
+ }
+ }
+
+ if (pp->hour > 23) {
+ pp->hour -= 24;
+ day++;
+ if (day > lastday) {
+ pp->year++;
+ day = 1;
+ }
+ }
+
+ pp->day = day;
+
+ /*
+ * Decode the MFLRV indicators.
+ * NEED TO FIGURE OUT how to deal with the request for service,
+ * time quality, and frequency quality indicators some day.
+ */
+ if (syncchar != '0') {
+ pp->leap = LEAP_NOTINSYNC;
+ }
+ else {
+ switch (leapchar) {
+
+ case '+':
+ pp->leap = LEAP_ADDSECOND;
+ break;
+
+ case '0':
+ pp->leap = LEAP_NOWARNING;
+ break;
+
+ case '-':
+ pp->leap = LEAP_DELSECOND;
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized leap indicator: %c\n",
+ leapchar);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } /* end of leapchar switch */
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If CLK_FLAG4 is set, ask for the status screen response.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4){
+ up->linecnt = 22;
+ if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+
+/*
+ * hpgps_poll - called by the transmit procedure
+ */
+static void
+hpgps_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The HP 58503A responds to a
+ * ":PTIME:TCODE?" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ else
+ pp->polls++;
+}
+
+#else
+int refclock_hpgps_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_irig.c b/contrib/ntp/ntpd/refclock_irig.c
new file mode 100644
index 0000000..6be09d9
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_irig.c
@@ -0,0 +1,1048 @@
+/*
+ * refclock_irig - audio IRIG-B/E demodulator/decoder
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_IRIG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "audio.h"
+
+/*
+ * Audio IRIG-B/E demodulator/decoder
+ *
+ * This driver receives, demodulates and decodes IRIG-B/E signals when
+ * connected to the audio codec /dev/audio. The IRIG signal format is an
+ * amplitude-modulated carrier with pulse-width modulated data bits. For
+ * IRIG-B, the carrier frequency is 1000 Hz and bit rate 100 b/s; for
+ * IRIG-E, the carrier frequenchy is 100 Hz and bit rate 10 b/s. The
+ * driver automatically recognizes which format is in use.
+ *
+ * The program processes 8000-Hz mu-law companded samples using separate
+ * signal filters for IRIG-B and IRIG-E, a comb filter, envelope
+ * detector and automatic threshold corrector. Cycle crossings relative
+ * to the corrected slice level determine the width of each pulse and
+ * its value - zero, one or position identifier. The data encode 20 BCD
+ * digits which determine the second, minute, hour and day of the year
+ * and sometimes the year and synchronization condition. The comb filter
+ * exponentially averages the corresponding samples of successive baud
+ * intervals in order to reliably identify the reference carrier cycle.
+ * A type-II phase-lock loop (PLL) performs additional integration and
+ * interpolation to accurately determine the zero crossing of that
+ * cycle, which determines the reference timestamp. A pulse-width
+ * discriminator demodulates the data pulses, which are then encoded as
+ * the BCD digits of the timecode.
+ *
+ * The timecode and reference timestamp are updated once each second
+ * with IRIG-B (ten seconds with IRIG-E) and local clock offset samples
+ * saved for later processing. At poll intervals of 64 s, the saved
+ * samples are processed by a trimmed-mean filter and used to update the
+ * system clock.
+ *
+ * An automatic gain control feature provides protection against
+ * overdriven or underdriven input signal amplitudes. It is designed to
+ * maintain adequate demodulator signal amplitude while avoiding
+ * occasional noise spikes. In order to assure reliable capture, the
+ * decompanded input signal amplitude must be greater than 100 units and
+ * the codec sample frequency error less than 250 PPM (.025 percent).
+ *
+ * The program performs a number of error checks to protect against
+ * overdriven or underdriven input signal levels, incorrect signal
+ * format or improper hardware configuration. Specifically, if any of
+ * the following errors occur for a time measurement, the data are
+ * rejected.
+ *
+ * o The peak carrier amplitude is less than DRPOUT (100). This usually
+ * means dead IRIG signal source, broken cable or wrong input port.
+ *
+ * o The frequency error is greater than MAXFREQ +-250 PPM (.025%). This
+ * usually means broken codec hardware or wrong codec configuration.
+ *
+ * o The modulation index is less than MODMIN (0.5). This usually means
+ * overdriven IRIG signal or wrong IRIG format.
+ *
+ * o A frame synchronization error has occurred. This usually means
+ * wrong IRIG signal format or the IRIG signal source has lost
+ * synchronization (signature control).
+ *
+ * o A data decoding error has occurred. This usually means wrong IRIG
+ * signal format.
+ *
+ * o The current second of the day is not exactly one greater than the
+ * previous one. This usually means a very noisy IRIG signal or
+ * insufficient CPU resources.
+ *
+ * o An audio codec error (overrun) occurred. This usually means
+ * insufficient CPU resources, as sometimes happens with Sun SPARC
+ * IPCs when doing something useful.
+ *
+ * Note that additional checks are done elsewhere in the reference clock
+ * interface routines.
+ *
+ * Debugging aids
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the IRIG signal and codec
+ * connections. With debugging enabled (-d on the ntpd command line),
+ * the driver produces one line for each timecode in the following
+ * format:
+ *
+ * 00 1 98 23 19:26:52 721 143 0.694 20 0.1 66.5 3094572411.00027
+ *
+ * The most recent line is also written to the clockstats file at 64-s
+ * intervals.
+ *
+ * The first field contains the error flags in hex, where the hex bits
+ * are interpreted as below. This is followed by the IRIG status
+ * indicator, year of century, day of year and time of day. The status
+ * indicator and year are not produced by some IRIG devices. Following
+ * these fields are the signal amplitude (0-8100), codec gain (0-255),
+ * modulation index (0-1), time constant (2-20), carrier phase error
+ * (us) and carrier frequency error (PPM). The last field is the on-time
+ * timestamp in NTP format.
+ *
+ * The fraction part of the on-time timestamp is a good indicator of how
+ * well the driver is doing. Once upon a time, an UltrSPARC 30 and
+ * Solaris 2.7 kept the clock within a few tens of microseconds relative
+ * to the IRIG-B signal. Accuracy with IRIG-E was about ten times worse.
+ * Unfortunately, Sun broke the 2.7 audio driver in 2.8, which has a 10-
+ * ms sawtooth modulation. The driver attempts to remove the modulation
+ * by some clever estimation techniques which mostly work. With the
+ * "mixerctl -o" command before starting the daemon, the jitter is down
+ * to about 100 microseconds. Your experience may vary.
+ *
+ * Unlike other drivers, which can have multiple instantiations, this
+ * one supports only one. It does not seem likely that more than one
+ * audio codec would be useful in a single machine. More than one would
+ * probably chew up too much CPU time anyway.
+ *
+ * Fudge factors
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. Fudge flag2 selects the audio input
+ * port, where 0 is the mike port (default) and 1 is the line-in port.
+ * It does not seem useful to select the compact disc player port. Fudge
+ * flag3 enables audio monitoring of the input signal. For this purpose,
+ * the monitor gain is set to a default value. Fudgetime2 is used as a
+ * frequency vernier for broken codec sample frequency.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define PRECISION (-17) /* precision assumed (about 10 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "Generic IRIG Audio Driver" /* WRU */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (40 ms) */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 80 /* samples per baud interval */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define CYCLE 8 /* samples per carrier cycle */
+#define SUBFLD 10 /* bits per subfield */
+#define FIELD 10 /* subfields per field */
+#define MINTC 2 /* min PLL time constant */
+#define MAXTC 20 /* max PLL time constant max */
+#define MAXAMP 6000. /* maximum signal level */
+#define MAXCLP 100 /* max clips above reference per s */
+#define DRPOUT 100. /* dropout signal level */
+#define MODMIN 0.5 /* minimum modulation index */
+#define MAXFREQ (250e-6 * SECOND) /* freq tolerance (.025%) */
+#define PI 3.1415926535 /* the real thing */
+#ifdef IRIG_SUCKS
+#define WIGGLE 11 /* wiggle filter length */
+#endif /* IRIG_SUCKS */
+
+/*
+ * Experimentally determined filter delays
+ */
+#define IRIG_B .0019 /* IRIG-B filter delay */
+#define IRIG_E .0019 /* IRIG-E filter delay */
+
+/*
+ * Data bit definitions
+ */
+#define BIT0 0 /* zero */
+#define BIT1 1 /* one */
+#define BITP 2 /* position identifier */
+
+/*
+ * Error flags (up->errflg)
+ */
+#define IRIG_ERR_AMP 0x01 /* low carrier amplitude */
+#define IRIG_ERR_FREQ 0x02 /* frequency tolerance exceeded */
+#define IRIG_ERR_MOD 0x04 /* low modulation index */
+#define IRIG_ERR_SYNCH 0x08 /* frame synch error */
+#define IRIG_ERR_DECODE 0x10 /* frame decoding error */
+#define IRIG_ERR_CHECK 0x20 /* second numbering discrepancy */
+#define IRIG_ERR_ERROR 0x40 /* codec error (overrun) */
+#define IRIG_ERR_SIGERR 0x80 /* IRIG status error (Spectracom) */
+
+/*
+ * IRIG unit control structure
+ */
+struct irigunit {
+ u_char timecode[21]; /* timecode string */
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ double integ[BAUD]; /* baud integrator */
+ double phase, freq; /* logical clock phase and frequency */
+ double zxing; /* phase detector integrator */
+ double yxing; /* cycle phase */
+ double exing; /* envelope phase */
+ double modndx; /* modulation index */
+ double irig_b; /* IRIG-B signal amplitude */
+ double irig_e; /* IRIG-E signal amplitude */
+ int errflg; /* error flags */
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clipped count */
+ int seccnt; /* second interval counter */
+
+ /*
+ * RF variables
+ */
+ double hpf[5]; /* IRIG-B filter shift register */
+ double lpf[5]; /* IRIG-E filter shift register */
+ double intmin, intmax; /* integrated envelope min and max */
+ double envmax; /* peak amplitude */
+ double envmin; /* noise amplitude */
+ double maxsignal; /* integrated peak amplitude */
+ double noise; /* integrated noise amplitude */
+ double lastenv[CYCLE]; /* last cycle amplitudes */
+ double lastint[CYCLE]; /* last integrated cycle amplitudes */
+ double lastsig; /* last carrier sample */
+ double fdelay; /* filter delay */
+ int decim; /* sample decimation factor */
+ int envphase; /* envelope phase */
+ int envptr; /* envelope phase pointer */
+ int carphase; /* carrier phase */
+ int envsw; /* envelope state */
+ int envxing; /* envelope slice crossing */
+ int tc; /* time constant */
+ int tcount; /* time constant counter */
+ int badcnt; /* decimation interval counter */
+
+ /*
+ * Decoder variables
+ */
+ int pulse; /* cycle counter */
+ int cycles; /* carrier cycles */
+ int dcycles; /* data cycles */
+ int xptr; /* translate table pointer */
+ int lastbit; /* last code element length */
+ int second; /* previous second */
+ int fieldcnt; /* subfield count in field */
+ int bits; /* demodulated bits */
+ int bitcnt; /* bit count in subfield */
+#ifdef IRIG_SUCKS
+ l_fp wigwag; /* wiggle accumulator */
+ int wp; /* wiggle filter pointer */
+ l_fp wiggle[WIGGLE]; /* wiggle filter */
+ l_fp wigbot[WIGGLE]; /* wiggle bottom fisher*/
+#endif /* IRIG_SUCKS */
+ l_fp wuggle;
+};
+
+/*
+ * Function prototypes
+ */
+static int irig_start P((int, struct peer *));
+static void irig_shutdown P((int, struct peer *));
+static void irig_receive P((struct recvbuf *));
+static void irig_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void irig_base P((struct peer *, double));
+static void irig_rf P((struct peer *, double));
+static void irig_decode P((struct peer *, int));
+static void irig_gain P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Global variables
+ */
+static char hexchar[] = { /* really quick decoding table */
+ '0', '8', '4', 'c', /* 0000 0001 0010 0011 */
+ '2', 'a', '6', 'e', /* 0100 0101 0110 0111 */
+ '1', '9', '5', 'd', /* 1000 1001 1010 1011 */
+ '3', 'b', '7', 'f' /* 1100 1101 1110 1111 */
+};
+
+
+/*
+ * irig_start - open the devices and initialize data for processing
+ */
+static int
+irig_start(
+ int unit, /* instance number (used for PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct irigunit *)
+ emalloc(sizeof(struct irigunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct irigunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = irig_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void)close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tc = MINTC;
+ up->decim = 1;
+ up->fdelay = IRIG_B;
+ up->gain = 127;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+ return (1);
+}
+
+
+/*
+ * irig_shutdown - shut down the clock
+ */
+static void
+irig_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * irig_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the irig clock by dropping or duplicating codec samples.
+ */
+static void
+irig_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXAMP. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXAMP) {
+ sample = MAXAMP;
+ up->clipcnt++;
+ } else if (sample < -MAXAMP) {
+ sample = -MAXAMP;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += up->freq / SECOND;
+ up->phase += pp->fudgetime2 / 1e6;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ irig_rf(peer, sample);
+ irig_rf(peer, sample);
+ } else {
+ irig_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+
+ /*
+ * Once each second, determine the IRIG format and gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ if (up->irig_b > up->irig_e) {
+ up->decim = 1;
+ up->fdelay = IRIG_B;
+ } else {
+ up->decim = 10;
+ up->fdelay = IRIG_E;
+ }
+ irig_gain(peer);
+ up->irig_b = up->irig_e = 0;
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+/*
+ * irig_rf - RF processing
+ *
+ * This routine filters the RF signal using a highpass filter for IRIG-B
+ * and a lowpass filter for IRIG-E. In case of IRIG-E, the samples are
+ * decimated by a factor of ten. The lowpass filter functions also as a
+ * decimation filter in this case. Note that the codec filters function
+ * as roofing filters to attenuate both the high and low ends of the
+ * passband. IIR filter coefficients were determined using Matlab Signal
+ * Processing Toolkit.
+ */
+static void
+irig_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double irig_b, irig_e; /* irig filter outputs */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * IRIG-B filter. 4th-order elliptic, 800-Hz highpass, 0.3 dB
+ * passband ripple, -50 dB stopband ripple, phase delay .0022
+ * s)
+ */
+ irig_b = (up->hpf[4] = up->hpf[3]) * 2.322484e-01;
+ irig_b += (up->hpf[3] = up->hpf[2]) * -1.103929e+00;
+ irig_b += (up->hpf[2] = up->hpf[1]) * 2.351081e+00;
+ irig_b += (up->hpf[1] = up->hpf[0]) * -2.335036e+00;
+ up->hpf[0] = sample - irig_b;
+ irig_b = up->hpf[0] * 4.335855e-01
+ + up->hpf[1] * -1.695859e+00
+ + up->hpf[2] * 2.525004e+00
+ + up->hpf[3] * -1.695859e+00
+ + up->hpf[4] * 4.335855e-01;
+ up->irig_b += irig_b * irig_b;
+
+ /*
+ * IRIG-E filter. 4th-order elliptic, 130-Hz lowpass, 0.3 dB
+ * passband ripple, -50 dB stopband ripple, phase delay .0219 s.
+ */
+ irig_e = (up->lpf[4] = up->lpf[3]) * 8.694604e-01;
+ irig_e += (up->lpf[3] = up->lpf[2]) * -3.589893e+00;
+ irig_e += (up->lpf[2] = up->lpf[1]) * 5.570154e+00;
+ irig_e += (up->lpf[1] = up->lpf[0]) * -3.849667e+00;
+ up->lpf[0] = sample - irig_e;
+ irig_e = up->lpf[0] * 3.215696e-03
+ + up->lpf[1] * -1.174951e-02
+ + up->lpf[2] * 1.712074e-02
+ + up->lpf[3] * -1.174951e-02
+ + up->lpf[4] * 3.215696e-03;
+ up->irig_e += irig_e * irig_e;
+
+ /*
+ * Decimate by a factor of either 1 (IRIG-B) or 10 (IRIG-E).
+ */
+ up->badcnt = (up->badcnt + 1) % up->decim;
+ if (up->badcnt == 0) {
+ if (up->decim == 1)
+ irig_base(peer, irig_b);
+ else
+ irig_base(peer, irig_e);
+ }
+}
+
+/*
+ * irig_base - baseband processing
+ *
+ * This routine processes the baseband signal and demodulates the AM
+ * carrier using a synchronous detector. It then synchronizes to the
+ * data frame at the baud rate and decodes the data pulses.
+ */
+static void
+irig_base(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double xxing; /* phase detector interpolated output */
+ double lope; /* integrator output */
+ double env; /* envelope detector output */
+ double dtemp; /* double temp */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Synchronous baud integrator. Corresponding samples of current
+ * and past baud intervals are integrated to refine the envelope
+ * amplitude and phase estimate. We keep one cycle of both the
+ * raw and integrated data for later use.
+ */
+ up->envphase = (up->envphase + 1) % BAUD;
+ up->carphase = (up->carphase + 1) % CYCLE;
+ up->integ[up->envphase] += (sample - up->integ[up->envphase]) /
+ (5 * up->tc);
+ lope = up->integ[up->envphase];
+ up->lastenv[up->carphase] = sample;
+ up->lastint[up->carphase] = lope;
+
+ /*
+ * Phase detector. Sample amplitudes are integrated over the
+ * baud interval. Cycle phase is determined from these
+ * amplitudes using an eight-sample cyclic buffer. A phase
+ * change of 360 degrees produces an output change of one unit.
+ */
+ if (up->lastsig > 0 && lope <= 0) {
+ xxing = lope / (up->lastsig - lope);
+ up->zxing += (up->carphase - 4 + xxing) / CYCLE;
+ }
+ up->lastsig = lope;
+
+ /*
+ * Update signal/noise estimates and PLL phase/frequency.
+ */
+ if (up->envphase == 0) {
+
+ /*
+ * Update envelope signal and noise estimates and mess
+ * with error bits.
+ */
+ up->maxsignal = up->intmax;
+ up->noise = up->intmin;
+ if (up->maxsignal < DRPOUT)
+ up->errflg |= IRIG_ERR_AMP;
+ if (up->maxsignal > 0)
+ up->modndx = (up->intmax - up->intmin) /
+ up->intmax;
+ else
+ up->modndx = 0;
+ if (up->modndx < MODMIN)
+ up->errflg |= IRIG_ERR_MOD;
+ up->intmin = 1e6; up->intmax = 0;
+ if (up->errflg & (IRIG_ERR_AMP | IRIG_ERR_FREQ |
+ IRIG_ERR_MOD | IRIG_ERR_SYNCH)) {
+ up->tc = MINTC;
+ up->tcount = 0;
+ }
+
+ /*
+ * Update PLL phase and frequency. The PLL time constant
+ * is set initially to stabilize the frequency within a
+ * minute or two, then increases to the maximum. The
+ * frequency is clamped so that the PLL capture range
+ * cannot be exceeded.
+ */
+ dtemp = up->zxing * up->decim / BAUD;
+ up->yxing = dtemp;
+ up->zxing = 0.;
+ up->phase += dtemp / up->tc;
+ up->freq += dtemp / (4. * up->tc * up->tc);
+ if (up->freq > MAXFREQ) {
+ up->freq = MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ } else if (up->freq < -MAXFREQ) {
+ up->freq = -MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ }
+ }
+
+ /*
+ * Synchronous demodulator. There are eight samples in the cycle
+ * and ten cycles in the baud interval. The amplitude of each
+ * cycle is determined at the last sample in the cycle. The
+ * beginning of the data pulse is determined from the integrated
+ * samples, while the end of the pulse is determined from the
+ * raw samples. The raw data bits are demodulated relative to
+ * the slice level and left-shifted in the decoding register.
+ */
+ if (up->carphase != 7)
+ return;
+
+ env = (up->lastenv[2] - up->lastenv[6]) / 2.;
+ lope = (up->lastint[2] - up->lastint[6]) / 2.;
+ if (lope > up->intmax)
+ up->intmax = lope;
+ if (lope < up->intmin)
+ up->intmin = lope;
+
+ /*
+ * Pulse code demodulator and reference timestamp. The decoder
+ * looks for a sequence of ten bits; the first two bits must be
+ * one, the last two bits must be zero. Frame synch is asserted
+ * when three correct frames have been found.
+ */
+ up->pulse = (up->pulse + 1) % 10;
+ if (up->pulse == 1)
+ up->envmax = env;
+ else if (up->pulse == 9)
+ up->envmin = env;
+ up->dcycles <<= 1;
+ if (env >= (up->envmax + up->envmin) / 2.)
+ up->dcycles |= 1;
+ up->cycles <<= 1;
+ if (lope >= (up->maxsignal + up->noise) / 2.)
+ up->cycles |= 1;
+ if ((up->cycles & 0x303c0f03) == 0x300c0300) {
+ l_fp ltemp;
+ int bitz;
+
+ /*
+ * The PLL time constant starts out small, in order to
+ * sustain a frequency tolerance of 250 PPM. It
+ * gradually increases as the loop settles down. Note
+ * that small wiggles are not believed, unless they
+ * persist for lots of samples.
+ */
+ if (up->pulse != 9)
+ up->errflg |= IRIG_ERR_SYNCH;
+ up->pulse = 9;
+ up->exing = -up->yxing;
+ if (fabs(up->envxing - up->envphase) <= 1) {
+ up->tcount++;
+ if (up->tcount > 50 * up->tc) {
+ up->tc++;
+ if (up->tc > MAXTC)
+ up->tc = MAXTC;
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ } else {
+ up->exing -= up->envxing - up->envphase;
+ }
+ } else {
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ }
+
+ /*
+ * Determine a reference timestamp, accounting for the
+ * codec delay and filter delay. Note the timestamp is
+ * for the previous frame, so we have to backtrack for
+ * this plus the delay since the last carrier positive
+ * zero crossing.
+ */
+ dtemp = up->decim * ((up->exing + BAUD) / SECOND + 1.) +
+ up->fdelay;
+ DTOLFP(dtemp, &ltemp);
+ pp->lastrec = up->timestamp;
+ L_SUB(&pp->lastrec, &ltemp);
+
+ /*
+ * The data bits are collected in ten-bit frames. The
+ * first two and last two bits are determined by frame
+ * sync and ignored here; the resulting patterns
+ * represent zero (0-1 bits), one (2-4 bits) and
+ * position identifier (5-6 bits). The remaining
+ * patterns represent errors and are treated as zeros.
+ */
+ bitz = up->dcycles & 0xfc;
+ switch(bitz) {
+
+ case 0x00:
+ case 0x80:
+ irig_decode(peer, BIT0);
+ break;
+
+ case 0xc0:
+ case 0xe0:
+ case 0xf0:
+ irig_decode(peer, BIT1);
+ break;
+
+ case 0xf8:
+ case 0xfc:
+ irig_decode(peer, BITP);
+ break;
+
+ default:
+ irig_decode(peer, 0);
+ up->errflg |= IRIG_ERR_DECODE;
+ }
+ }
+}
+
+
+/*
+ * irig_decode - decode the data
+ *
+ * This routine assembles bits into digits, digits into subfields and
+ * subfields into the timecode field. Bits can have values of zero, one
+ * or position identifier. There are four bits per digit, two digits per
+ * subfield and ten subfields per field. The last bit in every subfield
+ * and the first bit in the first subfield are position identifiers.
+ */
+static void
+irig_decode(
+ struct peer *peer, /* peer structure pointer */
+ int bit /* data bit (0, 1 or 2) */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+#ifdef IRIG_SUCKS
+ int i;
+#endif /* IRIG_SUCKS */
+
+ /*
+ * Local variables
+ */
+ char syncchar; /* sync character (Spectracom) */
+ char sbs[6]; /* binary seconds since 0h */
+ char spare[2]; /* mulligan digits */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Assemble subfield bits.
+ */
+ up->bits <<= 1;
+ if (bit == BIT1) {
+ up->bits |= 1;
+ } else if (bit == BITP && up->lastbit == BITP) {
+
+ /*
+ * Frame sync - two adjacent position identifiers.
+ * Monitor the reference timestamp and wiggle the
+ * clock, but only if no errors have occurred.
+ */
+ up->bitcnt = 1;
+ up->fieldcnt = 0;
+ up->lastbit = 0;
+ if (up->errflg == 0) {
+#ifdef IRIG_SUCKS
+ l_fp ltemp;
+
+ /*
+ * You really don't wanna know what comes down
+ * here. Leave it to say Solaris 2.8 broke the
+ * nice clean audio stream, apparently affected
+ * by a 5-ms sawtooth jitter. Sundown on
+ * Solaris. This leaves a little twilight.
+ *
+ * The scheme involves differentiation, forward
+ * learning and integration. The sawtooth has a
+ * period of 11 seconds. The timestamp
+ * differences are integrated and subtracted
+ * from the signal.
+ */
+ ltemp = pp->lastrec;
+ L_SUB(&ltemp, &pp->lastref);
+ if (ltemp.l_f < 0)
+ ltemp.l_i = -1;
+ else
+ ltemp.l_i = 0;
+ pp->lastref = pp->lastrec;
+ if (!L_ISNEG(&ltemp))
+ L_CLR(&up->wigwag);
+ else
+ L_ADD(&up->wigwag, &ltemp);
+ L_SUB(&pp->lastrec, &up->wigwag);
+ up->wiggle[up->wp] = ltemp;
+
+ /*
+ * Bottom fisher. To understand this, you have
+ * to know about velocity microphones and AM
+ * transmitters. No further explanation is
+ * offered, as this is truly a black art.
+ */
+ up->wigbot[up->wp] = pp->lastrec;
+ for (i = 0; i < WIGGLE; i++) {
+ if (i != up->wp)
+ up->wigbot[i].l_ui++;
+ L_SUB(&pp->lastrec, &up->wigbot[i]);
+ if (L_ISNEG(&pp->lastrec))
+ L_ADD(&pp->lastrec,
+ &up->wigbot[i]);
+ else
+ pp->lastrec = up->wigbot[i];
+ }
+ up->wp++;
+ up->wp %= WIGGLE;
+ up->wuggle = pp->lastrec;
+ refclock_process(pp);
+#else /* IRIG_SUCKS */
+ pp->lastref = pp->lastrec;
+ up->wuggle = pp->lastrec;
+ refclock_process(pp);
+#endif /* IRIG_SUCKS */
+ }
+ up->errflg = 0;
+ }
+ up->bitcnt = (up->bitcnt + 1) % SUBFLD;
+ if (up->bitcnt == 0) {
+
+ /*
+ * End of subfield. Encode two hexadecimal digits in
+ * little-endian timecode field.
+ */
+ if (up->fieldcnt == 0)
+ up->bits <<= 1;
+ if (up->xptr < 2)
+ up->xptr = 2 * FIELD;
+ up->timecode[--up->xptr] = hexchar[(up->bits >> 5) &
+ 0xf];
+ up->timecode[--up->xptr] = hexchar[up->bits & 0xf];
+ up->fieldcnt = (up->fieldcnt + 1) % FIELD;
+ if (up->fieldcnt == 0) {
+
+ /*
+ * End of field. Decode the timecode and wind
+ * the clock. Not all IRIG generators have the
+ * year; if so, it is nonzero after year 2000.
+ * Not all have the hardware status bit; if so,
+ * it is lit when the source is okay and dim
+ * when bad. We watch this only if the year is
+ * nonzero. Not all are configured for signature
+ * control. If so, all BCD digits are set to
+ * zero if the source is bad. In this case the
+ * refclock_process() will reject the timecode
+ * as invalid.
+ */
+ up->xptr = 2 * FIELD;
+ if (sscanf((char *)up->timecode,
+ "%6s%2d%c%2s%3d%2d%2d%2d", sbs, &pp->year,
+ &syncchar, spare, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 8)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ up->second = (up->second + up->decim) % 60;
+ if (pp->year > 0)
+ pp->year += 2000;
+ if (pp->second != up->second)
+ up->errflg |= IRIG_ERR_CHECK;
+ up->second = pp->second;
+ sprintf(pp->a_lastcode,
+ "%02x %c %2d %3d %02d:%02d:%02d %4.0f %3d %6.3f %2d %6.1f %6.1f %s",
+ up->errflg, syncchar, pp->year, pp->day,
+ pp->hour, pp->minute, pp->second,
+ up->maxsignal, up->gain, up->modndx,
+ up->tc, up->exing * 1e6 / SECOND, up->freq *
+ 1e6 / SECOND, ulfptoa(&up->wuggle, 6));
+ pp->lencode = strlen(pp->a_lastcode);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig: %s\n",
+ pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ }
+ }
+ up->lastbit = bit;
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ *
+ * This routine sweeps up the timecode updates since the last poll. For
+ * IRIG-B there should be at least 60 updates; for IRIG-E there should
+ * be at least 6. If nothing is heard, a timeout event is declared and
+ * any orphaned timecode updates are sent to foster care.
+ */
+static void
+irig_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+
+ } else {
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig: %s\n", pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ pp->polls++;
+
+}
+
+
+/*
+ * irig_gain - adjust codec gain
+ *
+ * This routine is called once each second. If the signal envelope
+ * amplitude is too low, the codec gain is bumped up by four units; if
+ * too high, it is bumped down. The decoder is relatively insensitive to
+ * amplitude, so this crudity works just fine. The input port is set and
+ * the error flag is cleared, mostly to be ornery.
+ */
+static void
+irig_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+}
+
+#else
+int refclock_irig_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_jjy.c b/contrib/ntp/ntpd/refclock_jjy.c
new file mode 100644
index 0000000..9d1419a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_jjy.c
@@ -0,0 +1,1011 @@
+/*
+ * refclock_jjy - clock driver for JJY receivers
+ */
+
+/**********************************************************************/
+/* */
+/* Copyright (C) 2001-2004, Takao Abe. All rights reserved. */
+/* */
+/* Permission to use, copy, modify, and distribute this software */
+/* and its documentation for any purpose is hereby granted */
+/* without fee, provided that the following conditions are met: */
+/* */
+/* One retains the entire copyright notice properly, and both the */
+/* copyright notice and this license. in the documentation and/or */
+/* other materials provided with the distribution. */
+/* */
+/* This software and the name of the author must not be used to */
+/* endorse or promote products derived from this software without */
+/* prior written permission. */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED */
+/* WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE */
+/* IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A */
+/* PARTICULAR PURPOSE. */
+/* IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT, */
+/* INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
+/* ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */
+/* GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS */
+/* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
+/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING */
+/* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF */
+/* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+/* */
+/* This driver is developed in my private time, and is opened as */
+/* voluntary contributions for the NTP. */
+/* The manufacturer of the JJY receiver has not participated in */
+/* a development of this driver. */
+/* The manufacturer does not warrant anything about this driver, */
+/* and is not liable for anything about this driver. */
+/* */
+/**********************************************************************/
+/* */
+/* Author Takao Abe */
+/* Email abetakao@bea.hi-ho.ne.jp */
+/* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */
+/* */
+/**********************************************************************/
+/* */
+/* History */
+/* */
+/* 2001/07/15 */
+/* [New] Support the Tristate Ltd. JJY receiver */
+/* */
+/* 2001/08/04 */
+/* [Change] Log to clockstats even if bad reply */
+/* [Fix] PRECISION = (-3) (about 100 ms) */
+/* [Add] Support the C-DEX Co.Ltd. JJY receiver */
+/* */
+/* 2001/12/04 */
+/* [Fix] C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp ) */
+/* */
+/* 2002/07/12 */
+/* [Fix] Portability for FreeBSD ( patched by the user ) */
+/* */
+/* 2004/10/31 */
+/* [Change] Command send timing for the Tristate Ltd. JJY receiver */
+/* JJY-01 ( Firmware version 2.01 ) */
+/* Thanks to Andy Taki for testing under FreeBSD */
+/* */
+/* 2004/11/28 */
+/* [Add] Support the Echo Keisokuki LT-2000 receiver */
+/* */
+/* 2006/11/04 */
+/* [Fix] C-DEX JST2000 */
+/* Thanks to Hideo Kuramatsu for the patch */
+/* */
+/**********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JJY)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/**********************************************************************/
+/* */
+/* The Tristate Ltd. JJY receiver JJY01 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* date<CR><LF> YYYY/MM/DD XXX<CR><LF> */
+/* time<CR><LF> HH:MM:SS<CR><LF> */
+/* stim<CR><LF> HH:MM:SS<CR><LF> Reply at just second */
+/* */
+/* During synchronization after a receiver is turned on, */
+/* It replies the past time from 2000/01/01 00:00:00. */
+/* The function "refclock_process" checks the time and tells */
+/* as an insanity time. */
+/* */
+/**********************************************************************/
+/* */
+/* The C-DEX Co. Ltd. JJY receiver JST2000 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* <ENQ>1J<ETX> <STX>JYYMMDD HHMMSSS<ETX> */
+/* */
+/**********************************************************************/
+/* */
+/* The Echo Keisokuki Co. Ltd. JJY receiver LT2000 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* # Mode 1 (Request&Send) */
+/* T YYMMDDWHHMMSS<BCC1><BCC2><CR> */
+/* C Mode 2 (Continuous) */
+/* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
+/* <SUB> Second signal */
+/* */
+/**********************************************************************/
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/jjy%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define REFID "JJY" /* reference ID */
+#define DESCRIPTION "JJY Receiver"
+#define PRECISION (-3) /* precision assumed (about 100 ms) */
+
+/*
+ * JJY unit control structure
+ */
+struct jjyunit {
+ char unittype ; /* UNITTYPE_XXXXXXXXXX */
+ short operationmode ; /* Echo Keisokuki LT-2000 : 1 or 2 */
+ short version ;
+ short linediscipline ; /* LDISC_CLK or LDISC_RAW */
+ int linecount ;
+ int lineerror ;
+ int year, month, day, hour, minute, second, msecond ;
+/* LDISC_RAW only */
+#define MAX_LINECOUNT 8
+#define MAX_RAWBUF 64
+ int lineexpect ;
+ int charexpect [ MAX_LINECOUNT ] ;
+ int charcount ;
+ char rawbuf [ MAX_RAWBUF ] ;
+};
+
+#define UNITTYPE_TRISTATE_JJY01 1
+#define UNITTYPE_CDEX_JST2000 2
+#define UNITTYPE_ECHOKEISOKUKI_LT2000 3
+
+/*
+ * Function prototypes
+ */
+static int jjy_start P((int, struct peer *));
+static void jjy_shutdown P((int, struct peer *));
+static void jjy_poll P((int, struct peer *));
+static void jjy_poll_tristate_jjy01 P((int, struct peer *));
+static void jjy_poll_cdex_jst2000 P((int, struct peer *));
+static void jjy_poll_echokeisokuki_lt2000 P((int, struct peer *));
+static void jjy_receive P((struct recvbuf *));
+static int jjy_receive_tristate_jjy01 P((struct recvbuf *));
+static int jjy_receive_cdex_jst2000 P((struct recvbuf *));
+static int jjy_receive_echokeisokuki_lt2000 P((struct recvbuf *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jjy = {
+ jjy_start, /* start up driver */
+ jjy_shutdown, /* shutdown driver */
+ jjy_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Start up driver return code
+ */
+#define RC_START_SUCCESS 1
+#define RC_START_ERROR 0
+
+/*
+ * Local constants definition
+ */
+
+#define MAX_LOGTEXT 64
+
+
+/**************************************************************************************************/
+/* jjy_start - open the devices and initialize data for processing */
+/**************************************************************************************************/
+static int
+jjy_start ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ int fd ;
+ char *pDeviceName ;
+ short iDiscipline ;
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_start (refclock_jjy.c) : %s mode=%d ", ntoa(&peer->srcadr), peer->ttl ) ;
+ printf ( DEVICE, unit ) ;
+ printf ( "\n" ) ;
+ }
+#endif
+ /*
+ * Open serial port
+ */
+ if ( ! ( pDeviceName = (char*) emalloc ( strlen(DEVICE) + 10 ) ) ) {
+ return RC_START_ERROR ;
+ }
+ sprintf ( pDeviceName, DEVICE, unit ) ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ case 1 : iDiscipline = LDISC_CLK ; break ;
+ case 2 : iDiscipline = LDISC_RAW ; break ;
+ case 3 : iDiscipline = LDISC_CLK ; break ;
+ default :
+ msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
+ ntoa(&peer->srcadr), peer->ttl ) ;
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+
+ if ( ! ( fd = refclock_open ( pDeviceName, SPEED232, iDiscipline ) ) ) {
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+ free ( (void*) pDeviceName ) ;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if ( ! ( up = (struct jjyunit *) emalloc (sizeof(struct jjyunit)) ) ) {
+ close ( fd ) ;
+ return RC_START_ERROR ;
+ }
+
+ memset ( (char*)up, 0, sizeof(struct jjyunit) ) ;
+ up->linediscipline = iDiscipline ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ /*
+ * The mode 0 is a default clock type at this time.
+ * But this will be change to auto-detect mode in the future.
+ */
+ case 1 :
+ up->unittype = UNITTYPE_TRISTATE_JJY01 ;
+ up->version = 100 ;
+ up->lineexpect = 2 ;
+ up->charexpect[0] = 14 ; /* YYYY/MM/DD WWW<CR><LF> */
+ up->charexpect[1] = 8 ; /* HH:MM:SS<CR><LF> */
+ break ;
+ case 2 :
+ up->unittype = UNITTYPE_CDEX_JST2000 ;
+ up->lineexpect = 1 ;
+ up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */
+ break ;
+ case 3 :
+ up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
+ up->operationmode = 2 ; /* Mode 2 : Continuous mode */
+ up->lineexpect = 1 ;
+ switch ( up->operationmode ) {
+ case 1 :
+ up->charexpect[0] = 15 ; /* YYMMDDWHHMMSS<BCC1><BCC2><CR> */
+ break ;
+ case 2 :
+ up->charexpect[0] = 17 ; /* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
+ break ;
+ }
+ break ;
+ default :
+ msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
+ ntoa(&peer->srcadr), peer->ttl ) ;
+ close ( fd ) ;
+ free ( (void*) up ) ;
+ return RC_START_ERROR ;
+ }
+
+ pp = peer->procptr ;
+ pp->unitptr = (caddr_t) up ;
+ pp->io.clock_recv = jjy_receive ;
+ pp->io.srcclock = (caddr_t) peer ;
+ pp->io.datalen = 0 ;
+ pp->io.fd = fd ;
+ if ( ! io_addclock(&pp->io) ) {
+ close ( fd ) ;
+ free ( (void*) up ) ;
+ return RC_START_ERROR ;
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION ;
+ peer->burst = 1 ;
+ pp->clockdesc = DESCRIPTION ;
+ memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ;
+
+ return RC_START_SUCCESS ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_shutdown - shutdown the clock */
+/**************************************************************************************************/
+static void
+jjy_shutdown ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+ io_closeclock ( &pp->io ) ;
+ free ( (void*) up ) ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_receive - receive data from the serial interface */
+/**************************************************************************************************/
+static void
+jjy_receive ( struct recvbuf *rbufp )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ l_fp tRecvTimestamp; /* arrival timestamp */
+ int rc ;
+ char sLogText [ MAX_LOGTEXT ] ;
+ int i, bCntrlChar ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ /*
+ * Get next input line
+ */
+ pp->lencode = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ /*
+ * The reply with <STX> and <ETX> may give a blank line
+ */
+ if ( pp->lencode == 0 && up->charcount == 0 ) return ;
+ /*
+ * Copy received charaters to temporary buffer
+ */
+ for ( i = 0 ; i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ; i ++ , up->charcount ++ ) {
+ up->rawbuf[up->charcount] = pp->a_lastcode[i] ;
+ }
+ while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) {
+ for ( i = 0 ; i < up->charcount - 1 ; i ++ ) up->rawbuf[i] = up->rawbuf[i+1] ;
+ up->charcount -- ;
+ }
+ bCntrlChar = 0 ;
+ for ( i = 0 ; i < up->charcount ; i ++ ) {
+ if ( up->rawbuf[i] < ' ' ) {
+ bCntrlChar = 1 ;
+ break ;
+ }
+ }
+ if ( pp->lencode > 0 && up->linecount < up->lineexpect ) {
+ if ( bCntrlChar == 0 && up->charcount < up->charexpect[up->linecount] ) return ;
+ }
+ up->rawbuf[up->charcount] = 0 ;
+ } else {
+ /*
+ * The reply with <CR><LF> gives a blank line
+ */
+ if ( pp->lencode == 0 ) return ;
+ }
+ /*
+ * We get down to business
+ */
+
+ pp->lastrec = tRecvTimestamp ;
+
+ up->linecount ++ ;
+
+ if ( up->lineerror != 0 ) return ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ rc = jjy_receive_tristate_jjy01 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_ECHOKEISOKUKI_LT2000 :
+ rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ;
+ break ;
+
+ default :
+ rc = 0 ;
+ break ;
+
+ }
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ if ( up->linecount <= up->lineexpect && up->charcount > up->charexpect[up->linecount-1] ) {
+ for ( i = 0 ; i < up->charcount - up->charexpect[up->linecount-1] ; i ++ ) {
+ up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ;
+ }
+ up->charcount -= up->charexpect[up->linecount-1] ;
+ } else {
+ up->charcount = 0 ;
+ }
+ }
+
+ if ( rc == 0 ) return ;
+
+ if ( up->lineerror != 0 ) {
+ refclock_report ( peer, CEVNT_BADREPLY ) ;
+ strcpy ( sLogText, "BAD REPLY [" ) ;
+ if ( up->linediscipline == LDISC_RAW ) {
+ strncat ( sLogText, up->rawbuf, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ;
+ } else {
+ strncat ( sLogText, pp->a_lastcode, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ;
+ }
+ sLogText[MAX_LOGTEXT-1] = 0 ;
+ if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 ) strcat ( sLogText, "]" ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+ return ;
+ }
+
+ pp->year = up->year ;
+ pp->day = ymd2yd ( up->year, up->month, up->day ) ;
+ pp->hour = up->hour ;
+ pp->minute = up->minute ;
+ pp->second = up->second ;
+ pp->nsec = up->msecond * 1000000;
+
+ /*
+ * JST to UTC
+ */
+ pp->hour -= 9 ;
+ if ( pp->hour < 0 ) {
+ pp->hour += 24 ;
+ pp->day -- ;
+ if ( pp->day < 1 ) {
+ pp->year -- ;
+ pp->day = ymd2yd ( pp->year, 12, 31 ) ;
+ }
+ }
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d.%1d JST ",
+ up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond/100 ) ;
+ printf ( "( %04d/%03d %02d:%02d:%02d.%1d UTC )\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second, (int)(pp->nsec/100000000) ) ;
+ }
+#endif
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+
+ sprintf ( sLogText, "%04d/%02d/%02d %02d:%02d:%02d.%1d JST",
+ up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond/100 ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+
+ if ( ! refclock_process ( pp ) ) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return ;
+ }
+
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
+{
+
+ static char *sFunctionName = "jjy_receive_tristate_jjy01" ;
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* YYYY/MM/DD WWW */
+
+ if ( iLen != 14 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( up->linecount=%d iLen=%d )\n", sFunctionName, up->linecount, iLen ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;
+ if ( rc != 3 || up->year < 2000 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Date error ( up->linecount=%d )\n", sFunctionName, up->linecount ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ /*** Start of modification on 2004/10/31 */
+ /*
+ * Following codes are moved from the function jjy_poll_tristate_jjy01 in this source.
+ * The Tristate JJY-01 ( Firmware version 1.01 ) accepts "time" and "stim" commands without any delay.
+ * But the JJY-01 ( Firmware version 2.01 ) does not accept these commands continuously,
+ * so this driver issues the second command "stim" after the reply of the first command "date".
+ */
+
+ /*
+ * Send "stim<CR><LF>" or "time<CR><LF>" command
+ */
+
+
+ if ( up->version >= 100 ) {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send 'stim<CR><LF>'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "stim\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ } else {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send 'time<CR><LF>'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "time\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ }
+ /*** End of modification ***/
+
+ return 0 ;
+
+ case 2 : /* HH:MM:SS */
+
+ if ( iLen != 8 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( up->linecount=%d iLen=%d )\n", sFunctionName, up->linecount, iLen ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ;
+ if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error ( up->linecount=%d )\n", sFunctionName, up->linecount ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ up->msecond = 0 ;
+ if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
+ /*
+ * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver continuously.
+ * But the JJY receiver replies a date and time separately.
+ * Just after midnight transitions, we ignore this time.
+ */
+ return 0 ;
+ }
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
+{
+
+ static char *sFunctionName = "jjy_receive_cdex_jst2000" ;
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* JYYMMDD HHMMSSS */
+
+ if ( iLen != 15 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n", sFunctionName, iLen ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
+ &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second, &up->msecond ) ;
+ if ( rc != 7 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
+ || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d.%1d ]\n", sFunctionName,
+ rc, up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+ up->year += 2000 ;
+ up->msecond *= 100 ;
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
+{
+
+ static char *sFunctionName = "jjy_receive_echokeisokuki_lt2000" ;
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+ int i, ibcc, ibcc1, ibcc2 ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */
+
+ if ( ( up->operationmode == 1 && iLen != 15 ) || ( up->operationmode == 2 && iLen != 17 ) ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n", sFunctionName, iLen ) ;
+ }
+#endif
+ if ( up->operationmode == 1 ) {
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ }
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ if ( up->operationmode == 1 ) {
+
+ for ( i = ibcc = 0 ; i < 13 ; i ++ ) ibcc ^= pBuf[i] ;
+ ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ;
+ ibcc2 = 0x30 | ( ( ibcc ) & 0xF ) ;
+ if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : BCC error ( Recv=%02X,%02X / Calc=%02X,%02X)\n", sFunctionName, pBuf[13]&0xFF, pBuf[14]&0xFF, ibcc1, ibcc2 ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ }
+
+ rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d",
+ &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second ) ;
+ if ( rc != 6 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
+ || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+#ifdef DEBUG
+ if ( debug >= 2 ) {
+ printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d ]\n", sFunctionName,
+ rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ;
+ }
+#endif
+ up->lineerror = 1 ;
+ break ;
+ }
+
+ up->year += 2000 ;
+
+ if ( up->operationmode == 2 ) {
+
+ /* A time stamp comes on every 0.5 seccond in the mode 2 of the LT-2000. */
+ up->msecond = 500 ;
+ pp->second -- ;
+ if ( pp->second < 0 ) {
+ pp->second = 59 ;
+ pp->minute -- ;
+ if ( pp->minute < 0 ) {
+ pp->minute = 59 ;
+ pp->hour -- ;
+ if ( pp->hour < 0 ) {
+ pp->hour = 23 ;
+ pp->day -- ;
+ if ( pp->day < 1 ) {
+ pp->year -- ;
+ pp->day = ymd2yd ( pp->year, 12, 31 ) ;
+ }
+ }
+ }
+ }
+
+ /* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ }
+
+ break ;
+
+ default : /* Unexpected reply */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ;
+ }
+#endif
+ if ( write ( pp->io.fd, "#",1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+/* jjy_poll - called by the transmit procedure */
+/**************************************************************************************************/
+static void
+jjy_poll ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( pp->polls > 0 && up->linecount == 0 ) {
+ /*
+ * No reply for last command
+ */
+ refclock_report ( peer, CEVNT_TIMEOUT ) ;
+ }
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ;
+ }
+#endif
+
+ pp->polls ++ ;
+
+ up->linecount = 0 ;
+ up->lineerror = 0 ;
+ up->charcount = 0 ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ jjy_poll_tristate_jjy01 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ jjy_poll_cdex_jst2000 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_ECHOKEISOKUKI_LT2000 :
+ jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ;
+ break ;
+
+ default :
+ break ;
+
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_tristate_jjy01 ( int unit, struct peer *peer )
+{
+
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Send "date<CR><LF>" command
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll_tristate_jjy01 (refclock_jjy.c) : send 'date<CR><LF>'\n" ) ;
+ }
+#endif
+
+ if ( write ( pp->io.fd, "date\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
+{
+
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Send "<ENQ>1J<ETX>" command
+ */
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll_cdex_jst2000 (refclock_jjy.c) : send '<ENQ>1J<ETX>'\n" ) ;
+ }
+#endif
+
+ if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ char sCmd[2] ;
+
+ pp = peer->procptr;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ /*
+ * Send "T" or "C" command
+ */
+
+ switch ( up->operationmode ) {
+ case 1 : sCmd[0] = 'T' ; break ;
+ case 2 : sCmd[0] = 'C' ; break ;
+ }
+ sCmd[1] = 0 ;
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll_echokeisokuki_lt2000 (refclock_jjy.c) : send '%s'\n", sCmd ) ;
+ }
+#endif
+
+ if ( write ( pp->io.fd, sCmd, 1 ) != 1 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+#else
+int refclock_jjy_bs ;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_jupiter.c b/contrib/ntp/ntpd/refclock_jupiter.c
new file mode 100644
index 0000000..c10d9fa
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_jupiter.c
@@ -0,0 +1,1137 @@
+/*
+ * Copyright (c) 1997, 1998, 2003
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "jupiter.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+#ifdef XNTP_BIG_ENDIAN
+#define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#else
+#define getshort(s) (s)
+#define putshort(s) (s)
+#endif
+
+/* XXX */
+#ifdef sun
+char *strerror(int);
+#endif
+
+/*
+ * This driver supports the Rockwell Jupiter GPS Receiver board
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It may work (with minor modifications) with other Rockwell GPS
+ * receivers such as the CityTracker.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+/* Unix timestamp for the GPS epoch: January 6, 1980 */
+#define GPS_EPOCH 315964800
+
+/* Double short to unsigned int */
+#define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* Double short to signed int */
+#define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* One week's worth of seconds */
+#define WEEKSECS (7 * 24 * 60 * 60)
+
+/*
+ * Jupiter unit control structure.
+ */
+struct instance {
+ struct peer *peer; /* peer */
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+#ifdef HAVE_PPSAPI
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t pps_handle; /* pps handle */
+ u_int assert; /* pps edge to use */
+ u_int hardpps; /* enable kernel mode */
+ struct timespec ts; /* last timestamp */
+#endif
+ l_fp limit;
+ u_int gpos_gweek; /* Current GPOS GPS week number */
+ u_int gpos_sweek; /* Current GPOS GPS seconds into week */
+ u_int gweek; /* current GPS week number */
+ u_int32 lastsweek; /* last seconds into GPS week */
+ time_t timecode; /* current ntp timecode */
+ u_int32 stime; /* used to detect firmware bug */
+ int wantid; /* don't reconfig on channel id msg */
+ u_int moving; /* mobile platform? */
+ u_char sloppyclockflag; /* fudge flags */
+ u_short sbuf[512]; /* local input buffer */
+ int ssize; /* space used in sbuf */
+};
+
+/*
+ * Function prototypes
+ */
+static void jupiter_canmsg P((struct instance *, u_int));
+static u_short jupiter_cksum P((u_short *, u_int));
+static int jupiter_config P((struct instance *));
+static void jupiter_debug P((struct peer *, char *, char *, ...))
+ __attribute__ ((format (printf, 3, 4)));
+static char * jupiter_parse_t P((struct instance *, u_short *));
+static char * jupiter_parse_gpos P((struct instance *, u_short *));
+static void jupiter_platform P((struct instance *, u_int));
+static void jupiter_poll P((int, struct peer *));
+static void jupiter_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+#ifdef HAVE_PPSAPI
+static int jupiter_ppsapi P((struct instance *));
+static int jupiter_pps P((struct instance *));
+#endif /* HAVE_PPSAPI */
+static int jupiter_recv P((struct instance *));
+static void jupiter_receive P((struct recvbuf *rbufp));
+static void jupiter_reqmsg P((struct instance *, u_int, u_int));
+static void jupiter_reqonemsg P((struct instance *, u_int));
+static char * jupiter_send P((struct instance *, struct jheader *));
+static void jupiter_shutdown P((int, struct peer *));
+static int jupiter_start P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jupiter = {
+ jupiter_start, /* start up driver */
+ jupiter_shutdown, /* shut down driver */
+ jupiter_poll, /* transmit poll message */
+ jupiter_control, /* (clock control) */
+ noentry, /* (clock init) */
+ noentry, /* (clock buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * jupiter_start - open the devices and initialize data for processing
+ */
+static int
+jupiter_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ int fd = -1;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+ if (fd == 0) {
+ jupiter_debug(peer, "jupiter_start", "open %s: %s",
+ gpsdev, strerror(errno));
+ return (0);
+ }
+
+ /* Allocate unit structure */
+ if ((instance = (struct instance *)
+ emalloc(sizeof(struct instance))) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)instance, 0, sizeof(struct instance));
+ instance->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = jupiter_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(instance);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)instance;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+#ifdef HAVE_PPSAPI
+ instance->assert = 1;
+ instance->hardpps = 0;
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &instance->pps_handle) < 0) {
+ instance->pps_handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_create failed: %m");
+ }
+ else if (!jupiter_ppsapi(instance))
+ goto clean_up;
+#endif /* HAVE_PPSAPI */
+
+ /* Ensure the receiver is properly configured */
+ if (!jupiter_config(instance))
+ goto clean_up;
+
+ return (1);
+
+clean_up:
+ jupiter_shutdown(unit, peer);
+ pp->unitptr = 0;
+ return (0);
+}
+
+/*
+ * jupiter_shutdown - shut down the clock
+ */
+static void
+jupiter_shutdown(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+ if (!instance)
+ return;
+
+#ifdef HAVE_PPSAPI
+ if (instance->pps_handle) {
+ time_pps_destroy(instance->pps_handle);
+ instance->pps_handle = 0;
+ }
+#endif /* HAVE_PPSAPI */
+
+ io_closeclock(&pp->io);
+ free(instance);
+}
+
+/*
+ * jupiter_config - Configure the receiver
+ */
+static int
+jupiter_config(struct instance *instance)
+{
+ jupiter_debug(instance->peer, "jupiter_config", "init receiver");
+
+ /*
+ * Initialize the unit variables
+ */
+ instance->sloppyclockflag = instance->peer->procptr->sloppyclockflag;
+ instance->moving = !!(instance->sloppyclockflag & CLK_FLAG2);
+ if (instance->moving)
+ jupiter_debug(instance->peer, "jupiter_config",
+ "mobile platform");
+
+ instance->pollcnt = 2;
+ instance->polled = 0;
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ instance->gweek = 0;
+ instance->lastsweek = 2 * WEEKSECS;
+ instance->timecode = 0;
+ instance->stime = 0;
+ instance->ssize = 0;
+
+ /* Stop outputting all messages */
+ jupiter_canmsg(instance, JUPITER_ALL);
+
+ /* Request the receiver id so we can syslog the firmware version */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+
+ /* Flag that this the id was requested (so we don't get called again) */
+ instance->wantid = 1;
+
+ /* Request perodic time mark pulse messages */
+ jupiter_reqmsg(instance, JUPITER_O_PULSE, 1);
+
+ /* Request perodic geodetic position status */
+ jupiter_reqmsg(instance, JUPITER_O_GPOS, 1);
+
+ /* Set application platform type */
+ if (instance->moving)
+ jupiter_platform(instance, JUPITER_I_PLAT_MED);
+ else
+ jupiter_platform(instance, JUPITER_I_PLAT_LOW);
+
+ return (1);
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * Initialize PPSAPI
+ */
+int
+jupiter_ppsapi(
+ struct instance *instance /* unit structure pointer */
+ )
+{
+ int capability;
+
+ if (time_pps_getcap(instance->pps_handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&instance->pps_params, 0, sizeof(pps_params_t));
+ if (!instance->assert)
+ instance->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ instance->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!(instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: invalid capture edge %d",
+ instance->assert);
+ return (0);
+ }
+ instance->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(instance->pps_handle, &instance->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (instance->hardpps) {
+ if (time_pps_kcbind(instance->pps_handle, PPS_KC_HARDPPS,
+ instance->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+/* instance->peer->precision = PPS_PRECISION; */
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(instance->pps_handle, &instance->pps_params);
+ jupiter_debug(instance->peer, "refclock_jupiter",
+ "pps capability 0x%x version %d mode 0x%x kern %d",
+ capability, instance->pps_params.api_version,
+ instance->pps_params.mode, instance->hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+jupiter_pps(struct instance *instance)
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (instance->pps_handle == 0)
+ return 1;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &instance->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(instance->pps_handle, PPS_TSFMT_TSPEC, &instance->pps_info,
+ &timeout) < 0)
+ return 1;
+ if (instance->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ instance->pps_info.assert_sequence)
+ return 1;
+ ts = instance->pps_info.assert_timestamp;
+ } else if (instance->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ instance->pps_info.clear_sequence)
+ return 1;
+ ts = instance->pps_info.clear_timestamp;
+ } else {
+ return 1;
+ }
+ if ((instance->ts.tv_sec == ts.tv_sec) && (instance->ts.tv_nsec == ts.tv_nsec))
+ return 1;
+ instance->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ instance->peer->procptr->lastrec = tstmp;
+ return 0;
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * jupiter_poll - jupiter watchdog routine
+ */
+static void
+jupiter_poll(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (instance->pollcnt > 0) {
+ instance->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /* Request the receiver id to trigger a reconfig */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+ instance->wantid = 0;
+ }
+
+ /*
+ * polled every 64 seconds. Ask jupiter_receive to hand in
+ * a timestamp.
+ */
+ instance->polled = 1;
+ pp->polls++;
+}
+
+/*
+ * jupiter_control - fudge control
+ */
+static void
+jupiter_control(
+ int unit, /* unit (not used) */
+ struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ u_char sloppyclockflag;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ DTOLFP(pp->fudgetime2, &instance->limit);
+ /* Force positive value. */
+ if (L_ISNEG(&instance->limit))
+ L_NEG(&instance->limit);
+
+#ifdef HAVE_PPSAPI
+ instance->assert = !(pp->sloppyclockflag & CLK_FLAG3);
+ jupiter_ppsapi(instance);
+#endif /* HAVE_PPSAPI */
+
+ sloppyclockflag = instance->sloppyclockflag;
+ instance->sloppyclockflag = pp->sloppyclockflag;
+ if ((instance->sloppyclockflag & CLK_FLAG2) !=
+ (sloppyclockflag & CLK_FLAG2)) {
+ jupiter_debug(peer,
+ "jupiter_control",
+ "mode switch: reset receiver");
+ jupiter_config(instance);
+ return;
+ }
+}
+
+/*
+ * jupiter_receive - receive gps data
+ * Gag me!
+ */
+static void
+jupiter_receive(struct recvbuf *rbufp)
+{
+ int bpcnt, cc, size, ppsret;
+ time_t last_timecode;
+ u_int32 laststime;
+ char *cp;
+ u_char *bp;
+ u_short *sp;
+ struct jid *ip;
+ struct jheader *hp;
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct instance *instance;
+ l_fp tstamp;
+
+ /* Initialize pointers and read the timecode and timestamp */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ bp = (u_char *)rbufp->recv_buffer;
+ bpcnt = rbufp->recv_length;
+
+ /* This shouldn't happen */
+ if (bpcnt > sizeof(instance->sbuf) - instance->ssize)
+ bpcnt = sizeof(instance->sbuf) - instance->ssize;
+
+ /* Append to input buffer */
+ memcpy((u_char *)instance->sbuf + instance->ssize, bp, bpcnt);
+ instance->ssize += bpcnt;
+
+ /* While there's at least a header and we parse an intact message */
+ while (instance->ssize > sizeof(*hp) && (cc = jupiter_recv(instance)) > 0) {
+ instance->pollcnt = 2;
+
+ tstamp = rbufp->recv_time;
+ hp = (struct jheader *)instance->sbuf;
+ sp = (u_short *)(hp + 1);
+ size = cc - sizeof(*hp);
+ switch (getshort(hp->id)) {
+
+ case JUPITER_O_PULSE:
+ if (size != sizeof(struct jpulse)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: len %d != %u",
+ size, (int)sizeof(struct jpulse));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ /*
+ * There appears to be a firmware bug related
+ * to the pulse message; in addition to the one
+ * per second messages, we get an extra pulse
+ * message once an hour (on the anniversary of
+ * the cold start). It seems to come 200 ms
+ * after the one requested. So if we've seen a
+ * pulse message in the last 210 ms, we skip
+ * this one.
+ */
+ laststime = instance->stime;
+ instance->stime = DS2UI(((struct jpulse *)sp)->stime);
+ if (laststime != 0 && instance->stime - laststime <= 21) {
+ jupiter_debug(peer, "jupiter_receive",
+ "avoided firmware bug (stime %.2f, laststime %.2f)",
+ (double)instance->stime * 0.01, (double)laststime * 0.01);
+ break;
+ }
+
+ /* Retrieve pps timestamp */
+ ppsret = jupiter_pps(instance);
+
+ /*
+ * Add one second if msg received early
+ * (i.e. before limit, a.k.a. fudgetime2) in
+ * the second.
+ */
+ L_SUB(&tstamp, &pp->lastrec);
+ if (!L_ISGEQ(&tstamp, &instance->limit))
+ ++pp->lastrec.l_ui;
+
+ /* Parse timecode (even when there's no pps) */
+ last_timecode = instance->timecode;
+ if ((cp = jupiter_parse_t(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: %s", cp);
+ break;
+ }
+
+ /* Bail if we didn't get a pps timestamp */
+ if (ppsret)
+ break;
+
+ /* Bail if we don't have the last timecode yet */
+ if (last_timecode == 0)
+ break;
+
+ /* Add the new sample to a median filter */
+ tstamp.l_ui = JAN_1970 + last_timecode;
+ tstamp.l_uf = 0;
+
+ refclock_process_offset(pp, tstamp, pp->lastrec, pp->fudgetime1);
+
+ /*
+ * The clock will blurt a timecode every second
+ * but we only want one when polled. If we
+ * havn't been polled, bail out.
+ */
+ if (!instance->polled)
+ break;
+ instance->polled = 0;
+
+ /*
+ * It's a live one! Remember this time.
+ */
+
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If we get here - what we got from the clock is
+ * OK, so say so
+ */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ instance->polled = 0;
+ break;
+
+ case JUPITER_O_GPOS:
+ if (size != sizeof(struct jgpos)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: len %d != %u",
+ size, (int)sizeof(struct jgpos));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ if ((cp = jupiter_parse_gpos(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: %s", cp);
+ break;
+ }
+ break;
+
+ case JUPITER_O_ID:
+ if (size != sizeof(struct jid)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "id: len %d != %u",
+ size, (int)sizeof(struct jid));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If we got this message because the Jupiter
+ * just powered instance, it needs to be reconfigured.
+ */
+ ip = (struct jid *)sp;
+ jupiter_debug(peer,
+ "jupiter_receive", "%s chan ver %s, %s (%s)",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ msyslog(LOG_DEBUG,
+ "jupiter_receive: %s chan ver %s, %s (%s)",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ if (instance->wantid)
+ instance->wantid = 0;
+ else {
+ jupiter_debug(peer,
+ "jupiter_receive", "reset receiver");
+ jupiter_config(instance);
+ /*
+ * Restore since jupiter_config() just
+ * zeroed it
+ */
+ instance->ssize = cc;
+ }
+ break;
+
+ default:
+ jupiter_debug(peer,
+ "jupiter_receive", "unknown message id %d",
+ getshort(hp->id));
+ break;
+ }
+ instance->ssize -= cc;
+ if (instance->ssize < 0) {
+ fprintf(stderr, "jupiter_recv: negative ssize!\n");
+ abort();
+ } else if (instance->ssize > 0)
+ memcpy(instance->sbuf, (u_char *)instance->sbuf + cc, instance->ssize);
+ }
+}
+
+static char *
+jupiter_parse_t(struct instance *instance, u_short *sp)
+{
+ struct tm *tm;
+ char *cp;
+ struct jpulse *jp;
+ u_int32 sweek;
+ time_t last_timecode;
+ u_short flags;
+
+ jp = (struct jpulse *)sp;
+
+ /* The timecode is presented as seconds into the current GPS week */
+ sweek = DS2UI(jp->sweek) % WEEKSECS;
+
+ /*
+ * If we don't know the current GPS week, calculate it from the
+ * current time. (It's too bad they didn't include this
+ * important value in the pulse message). We'd like to pick it
+ * up from one of the other messages like gpos or chan but they
+ * don't appear to be synchronous with time keeping and changes
+ * too soon (something like 10 seconds before the new GPS
+ * week).
+ *
+ * If we already know the current GPS week, increment it when
+ * we wrap into a new week.
+ */
+ if (instance->gweek == 0) {
+ if (!instance->gpos_gweek) {
+ return ("jupiter_parse_t: Unknown gweek");
+ }
+
+ instance->gweek = instance->gpos_gweek;
+
+ /*
+ * Fix warps. GPOS has GPS time and PULSE has UTC.
+ * Plus, GPOS need not be completely in synch with
+ * the PPS signal.
+ */
+ if (instance->gpos_sweek >= sweek) {
+ if ((instance->gpos_sweek - sweek) > WEEKSECS / 2)
+ ++instance->gweek;
+ }
+ else {
+ if ((sweek - instance->gpos_sweek) > WEEKSECS / 2)
+ --instance->gweek;
+ }
+ }
+ else if (sweek == 0 && instance->lastsweek == WEEKSECS - 1) {
+ ++instance->gweek;
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "NEW gps week %u", instance->gweek);
+ }
+
+ /*
+ * See if the sweek stayed the same (this happens when there is
+ * no pps pulse).
+ *
+ * Otherwise, look for time warps:
+ *
+ * - we have stored at least one lastsweek and
+ * - the sweek didn't increase by one and
+ * - we didn't wrap to a new GPS week
+ *
+ * Then we warped.
+ */
+ if (instance->lastsweek == sweek)
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek not incrementing (%d)",
+ sweek);
+ else if (instance->lastsweek != 2 * WEEKSECS &&
+ instance->lastsweek + 1 != sweek &&
+ !(sweek == 0 && instance->lastsweek == WEEKSECS - 1))
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek jumped (was %d, now %d)",
+ instance->lastsweek, sweek);
+ instance->lastsweek = sweek;
+
+ /* This timecode describes next pulse */
+ last_timecode = instance->timecode;
+ instance->timecode =
+ GPS_EPOCH + (instance->gweek * WEEKSECS) + sweek;
+
+ if (last_timecode == 0)
+ /* XXX debugging */
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC <none> (gweek/sweek %u/%u)",
+ instance->gweek, sweek);
+ else {
+ /* XXX debugging */
+ tm = gmtime(&last_timecode);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC %.24s (gweek/sweek %u/%u)",
+ cp, instance->gweek, sweek);
+
+ /* Billboard last_timecode (which is now the current time) */
+ instance->peer->procptr->year = tm->tm_year + 1900;
+ instance->peer->procptr->day = tm->tm_yday + 1;
+ instance->peer->procptr->hour = tm->tm_hour;
+ instance->peer->procptr->minute = tm->tm_min;
+ instance->peer->procptr->second = tm->tm_sec;
+ }
+
+ flags = getshort(jp->flags);
+
+ /* Toss if not designated "valid" by the gps */
+ if ((flags & JUPITER_O_PULSE_VALID) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not valid");
+ }
+
+ /* We better be sync'ed to UTC... */
+ if ((flags & JUPITER_O_PULSE_UTC) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not sync'ed to UTC");
+ }
+
+ return (NULL);
+}
+
+static char *
+jupiter_parse_gpos(struct instance *instance, u_short *sp)
+{
+ struct jgpos *jg;
+ time_t t;
+ struct tm *tm;
+ char *cp;
+
+ jg = (struct jgpos *)sp;
+
+ if (jg->navval != 0) {
+ /*
+ * Solution not valid. Use caution and refuse
+ * to determine GPS week from this message.
+ */
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ return ("Navigation solution not valid");
+ }
+
+ instance->gpos_gweek = jg->gweek;
+ instance->gpos_sweek = DS2UI(jg->sweek);
+ while(instance->gpos_sweek >= WEEKSECS) {
+ instance->gpos_sweek -= WEEKSECS;
+ ++instance->gpos_gweek;
+ }
+ instance->gweek = 0;
+
+ t = GPS_EPOCH + (instance->gpos_gweek * WEEKSECS) + instance->gpos_sweek;
+ tm = gmtime(&t);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_g", "GPS %.24s (gweek/sweek %u/%u)",
+ cp, instance->gpos_gweek, instance->gpos_sweek);
+ return (NULL);
+}
+
+/*
+ * jupiter_debug - print debug messages
+ */
+#if defined(__STDC__) || defined(SYS_WINNT)
+static void
+jupiter_debug(struct peer *peer, char *function, char *fmt, ...)
+#else
+static void
+jupiter_debug(peer, function, fmt, va_alist)
+ struct peer *peer;
+ char *function;
+ char *fmt;
+#endif /* __STDC__ */
+{
+ char buffer[200];
+ va_list ap;
+
+#if defined(__STDC__) || defined(SYS_WINNT)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ record_clock_stats(&(peer->srcadr), buffer);
+#ifdef DEBUG
+ if (debug) {
+ fprintf(stdout, "%s: ", function);
+ fprintf(stdout, buffer);
+ fprintf(stdout, "\n");
+ fflush(stdout);
+ }
+#endif
+
+ va_end(ap);
+}
+
+/* Checksum and transmit a message to the Jupiter */
+static char *
+jupiter_send(struct instance *instance, struct jheader *hp)
+{
+ u_int len, size;
+ int cc;
+ u_short *sp;
+ static char errstr[132];
+
+ size = sizeof(*hp);
+ hp->hsum = putshort(jupiter_cksum((u_short *)hp,
+ (size / sizeof(u_short)) - 1));
+ len = getshort(hp->len);
+ if (len > 0) {
+ sp = (u_short *)(hp + 1);
+ sp[len] = putshort(jupiter_cksum(sp, len));
+ size += (len + 1) * sizeof(u_short);
+ }
+
+ if ((cc = write(instance->peer->procptr->io.fd, (char *)hp, size)) < 0) {
+ (void)sprintf(errstr, "write: %s", strerror(errno));
+ return (errstr);
+ } else if (cc != size) {
+ (void)sprintf(errstr, "short write (%d != %d)", cc, size);
+ return (errstr);
+ }
+ return (NULL);
+}
+
+/* Request periodic message output */
+static struct {
+ struct jheader jheader;
+ struct jrequest jrequest;
+} reqmsg = {
+ { putshort(JUPITER_SYNC), 0,
+ putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1),
+ 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK |
+ JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 },
+ { 0, 0, 0, 0 }
+};
+
+/* An interval of zero means to output on trigger */
+static void
+jupiter_reqmsg(struct instance *instance, u_int id,
+ u_int interval)
+{
+ struct jheader *hp;
+ struct jrequest *rp;
+ char *cp;
+
+ hp = &reqmsg.jheader;
+ hp->id = putshort(id);
+ rp = &reqmsg.jrequest;
+ rp->trigger = putshort(interval == 0);
+ rp->interval = putshort(interval);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqmsg", "%u: %s", id, cp);
+}
+
+/* Cancel periodic message output */
+static struct jheader canmsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC,
+ 0
+};
+
+static void
+jupiter_canmsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &canmsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_canmsg", "%u: %s", id, cp);
+}
+
+/* Request a single message output */
+static struct jheader reqonemsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY,
+ 0
+};
+
+static void
+jupiter_reqonemsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &reqonemsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqonemsg", "%u: %s", id, cp);
+}
+
+/* Set the platform dynamics */
+static struct {
+ struct jheader jheader;
+ struct jplat jplat;
+} platmsg = {
+ { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT),
+ putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0,
+ JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 },
+ { 0, 0, 0 }
+};
+
+static void
+jupiter_platform(struct instance *instance, u_int platform)
+{
+ struct jheader *hp;
+ struct jplat *pp;
+ char *cp;
+
+ hp = &platmsg.jheader;
+ pp = &platmsg.jplat;
+ pp->platform = putshort(platform);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_platform", "%u: %s", platform, cp);
+}
+
+/* Checksum "len" shorts */
+static u_short
+jupiter_cksum(u_short *sp, u_int len)
+{
+ u_short sum, x;
+
+ sum = 0;
+ while (len-- > 0) {
+ x = *sp++;
+ sum += getshort(x);
+ }
+ return (~sum + 1);
+}
+
+/* Return the size of the next message (or zero if we don't have it all yet) */
+static int
+jupiter_recv(struct instance *instance)
+{
+ int n, len, size, cc;
+ struct jheader *hp;
+ u_char *bp;
+ u_short *sp;
+
+ /* Must have at least a header's worth */
+ cc = sizeof(*hp);
+ size = instance->ssize;
+ if (size < cc)
+ return (0);
+
+ /* Search for the sync short if missing */
+ sp = instance->sbuf;
+ hp = (struct jheader *)sp;
+ if (getshort(hp->sync) != JUPITER_SYNC) {
+ /* Wasn't at the front, sync up */
+ jupiter_debug(instance->peer, "jupiter_recv", "syncing");
+ bp = (u_char *)sp;
+ n = size;
+ while (n >= 2) {
+ if (bp[0] != (JUPITER_SYNC & 0xff)) {
+ /*
+ jupiter_debug(instance->peer, "{0x%x}", bp[0]);
+ */
+ ++bp;
+ --n;
+ continue;
+ }
+ if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff))
+ break;
+ /*
+ jupiter_debug(instance->peer, "{0x%x 0x%x}", bp[0], bp[1]);
+ */
+ bp += 2;
+ n -= 2;
+ }
+ /*
+ jupiter_debug(instance->peer, "\n");
+ */
+ /* Shuffle data to front of input buffer */
+ if (n > 0)
+ memcpy(sp, bp, n);
+ size = n;
+ instance->ssize = size;
+ if (size < cc || hp->sync != JUPITER_SYNC)
+ return (0);
+ }
+
+ if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) !=
+ getshort(hp->hsum)) {
+ jupiter_debug(instance->peer, "jupiter_recv", "bad header checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+
+ /* Check for a payload */
+ len = getshort(hp->len);
+ if (len > 0) {
+ n = (len + 1) * sizeof(u_short);
+ /* Not enough data yet */
+ if (size < cc + n)
+ return (0);
+
+ /* Check payload checksum */
+ sp = (u_short *)(hp + 1);
+ if (jupiter_cksum(sp, len) != getshort(sp[len])) {
+ jupiter_debug(instance->peer,
+ "jupiter_recv", "bad payload checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+ cc += n;
+ }
+ return (cc);
+}
+
+#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
+int refclock_jupiter_bs;
+#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
diff --git a/contrib/ntp/ntpd/refclock_leitch.c b/contrib/ntp/ntpd/refclock_leitch.c
new file mode 100644
index 0000000..e1ba0c4
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_leitch.c
@@ -0,0 +1,626 @@
+/*
+ * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_LEITCH)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef STREAM
+#include <stropts.h>
+#if defined(LEITCHCLK)
+#include <sys/clkdefs.h>
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+
+#include "ntp_stdlib.h"
+
+
+/*
+ * Driver for Leitch CSD-5300 Master Clock System
+ *
+ * COMMANDS:
+ * DATE: D <CR>
+ * TIME: T <CR>
+ * STATUS: S <CR>
+ * LOOP: L <CR>
+ *
+ * FORMAT:
+ * DATE: YYMMDD<CR>
+ * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
+ * second bondaried on the stop bit of the <CR>
+ * second boundaries at '/' above.
+ * STATUS: G (good), D (diag fail), T (time not provided) or
+ * P (last phone update failed)
+ */
+#define MAXUNITS 1 /* max number of LEITCH units */
+#define LEITCHREFID "ATOM" /* reference id */
+#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
+#define LEITCH232 "/dev/leitch%d" /* name of radio device */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#ifdef DEBUG
+#define leitch_send(A,M) \
+if (debug) fprintf(stderr,"write leitch %s\n",M); \
+if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ if (debug) \
+ fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
+ else \
+ msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+#else
+#define leitch_send(A,M) \
+if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+#endif
+
+#define STATE_IDLE 0
+#define STATE_DATE 1
+#define STATE_TIME1 2
+#define STATE_TIME2 3
+#define STATE_TIME3 4
+
+/*
+ * LEITCH unit control structure
+ */
+struct leitchunit {
+ struct peer *peer;
+ struct refclockio leitchio;
+ u_char unit;
+ short year;
+ short yearday;
+ short month;
+ short day;
+ short hour;
+ short second;
+ short minute;
+ short state;
+ u_short fudge1;
+ l_fp reftime1;
+ l_fp reftime2;
+ l_fp reftime3;
+ l_fp codetime1;
+ l_fp codetime2;
+ l_fp codetime3;
+ u_long yearstart;
+};
+
+/*
+ * Function prototypes
+ */
+static void leitch_init P((void));
+static int leitch_start P((int, struct peer *));
+static void leitch_shutdown P((int, struct peer *));
+static void leitch_poll P((int, struct peer *));
+static void leitch_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+#define leitch_buginfo noentry
+static void leitch_receive P((struct recvbuf *));
+static void leitch_process P((struct leitchunit *));
+#if 0
+static void leitch_timeout P((struct peer *));
+#endif
+static int leitch_get_date P((struct recvbuf *, struct leitchunit *));
+static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int));
+static int days_per_year P((int));
+
+static struct leitchunit leitchunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_int32 refid[MAXUNITS];
+
+static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_leitch = {
+ leitch_start, leitch_shutdown, leitch_poll,
+ leitch_control, leitch_init, leitch_buginfo, NOFLAGS
+};
+
+/*
+ * leitch_init - initialize internal leitch driver data
+ */
+static void
+leitch_init(void)
+{
+ int i;
+
+ memset((char*)leitchunits, 0, sizeof(leitchunits));
+ memset((char*)unitinuse, 0, sizeof(unitinuse));
+ for (i = 0; i < MAXUNITS; i++)
+ memcpy((char *)&refid[i], LEITCHREFID, 4);
+}
+
+/*
+ * leitch_shutdown - shut down a LEITCH clock
+ */
+static void
+leitch_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_shutdown()\n");
+#endif
+}
+
+/*
+ * leitch_poll - called by the transmit procedure
+ */
+static void
+leitch_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+
+ /* start the state machine rolling */
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_poll()\n");
+#endif
+ if (unit >= MAXUNITS) {
+ /* XXXX syslog it */
+ return;
+ }
+
+ leitch = &leitchunits[unit];
+
+ if (leitch->state != STATE_IDLE) {
+ /* reset and wait for next poll */
+ /* XXXX syslog it */
+ leitch->state = STATE_IDLE;
+ } else {
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ }
+}
+
+static void
+leitch_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *passed_peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "leitch_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in) {
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = (&leitchunits[unit])->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_ATOM_LEITCH;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgeval1 = (int32)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->p_lastcode = "";
+ out->clockdesc = LEITCH_DESCRIPTION;
+ }
+}
+
+/*
+ * leitch_start - open the LEITCH devices and initialize data for processing
+ */
+static int
+leitch_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+ int fd232;
+ char leitchdev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port.
+ */
+ (void) sprintf(leitchdev, LEITCH232, unit);
+ fd232 = open(leitchdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR,
+ "leitch_start: open of %s: %m", leitchdev);
+ return (0);
+ }
+
+ leitch = &leitchunits[unit];
+ memset((char*)leitch, 0, sizeof(*leitch));
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcgetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcsetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcflush(%s): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(LEITCHCLK)
+ int ldisc = CLKLDISC;
+#endif /* LEITCHCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(LEITCHCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* LEITCHCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
+ goto screwed;
+ }
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
+ goto screwed;
+ }
+#endif /* LEITCHCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Set up the structures
+ */
+ leitch->peer = peer;
+ leitch->unit = unit;
+ leitch->state = STATE_IDLE;
+ leitch->fudge1 = 15; /* 15ms */
+
+ leitch->leitchio.clock_recv = leitch_receive;
+ leitch->leitchio.srcclock = (caddr_t) leitch;
+ leitch->leitchio.datalen = 0;
+ leitch->leitchio.fd = fd232;
+ if (!io_addclock(&leitch->leitchio)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return(1);
+
+ /*
+ * Something broke; abandon ship.
+ */
+ screwed:
+ close(fd232);
+ return(0);
+}
+
+/*
+ * leitch_receive - receive data from the serial interface on a leitch
+ * clock
+ */
+static void
+leitch_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_recieve(%*.*s)\n",
+ rbufp->recv_length, rbufp->recv_length,
+ rbufp->recv_buffer);
+#endif
+ if (rbufp->recv_length != 7)
+ return; /* The date is return with a trailing newline,
+ discard it. */
+
+ switch (leitch->state) {
+ case STATE_IDLE: /* unexpected, discard and resync */
+ return;
+ case STATE_DATE:
+ if (!leitch_get_date(rbufp,leitch)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch_send(leitch,"T\r");
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n",leitch->yearday);
+#endif
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ if (!leitch_get_time(rbufp,leitch,1)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime1.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch->reftime1.l_uf = 0;
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
+ leitch->codetime1 = rbufp->recv_time;
+ leitch->state = STATE_TIME2;
+ break;
+ case STATE_TIME2:
+ if (!leitch_get_time(rbufp,leitch,2)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime2.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
+ leitch->codetime2 = rbufp->recv_time;
+ leitch->state = STATE_TIME3;
+ break;
+ case STATE_TIME3:
+ if (!leitch_get_time(rbufp,leitch,3)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, GMT, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime3.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
+ leitch->codetime3 = rbufp->recv_time;
+ leitch_process(leitch);
+ leitch->state = STATE_IDLE;
+ break;
+ default:
+ msyslog(LOG_ERR,
+ "leitech_receive: invalid state %d unit %d",
+ leitch->state, leitch->unit);
+ }
+}
+
+/*
+ * leitch_process - process a pile of samples from the clock
+ *
+ * This routine uses a three-stage median filter to calculate offset and
+ * dispersion. reduce jitter. The dispersion is calculated as the span
+ * of the filter (max - min), unless the quality character (format 2) is
+ * non-blank, in which case the dispersion is calculated on the basis of
+ * the inherent tolerance of the internal radio oscillator, which is
+ * +-2e-5 according to the radio specifications.
+ */
+static void
+leitch_process(
+ struct leitchunit *leitch
+ )
+{
+ l_fp off;
+ l_fp tmp_fp;
+ /*double doffset;*/
+
+ off = leitch->reftime1;
+ L_SUB(&off,&leitch->codetime1);
+ tmp_fp = leitch->reftime2;
+ L_SUB(&tmp_fp,&leitch->codetime2);
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ tmp_fp = leitch->reftime3;
+ L_SUB(&tmp_fp,&leitch->codetime3);
+
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ /*LFPTOD(&off, doffset);*/
+ refclock_receive(leitch->peer);
+}
+
+/*
+ * days_per_year
+ */
+static int
+days_per_year(
+ int year
+ )
+{
+ if (year%4) { /* not a potential leap year */
+ return (365);
+ } else {
+ if (year % 100) { /* is a leap year */
+ return (366);
+ } else {
+ if (year % 400) {
+ return (365);
+ } else {
+ return (366);
+ }
+ }
+ }
+}
+
+static int
+leitch_get_date(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch
+ )
+{
+ int i;
+
+ if (rbufp->recv_length < 6)
+ return(0);
+#undef BAD /* confict: defined as (-1) in AIX sys/param.h */
+#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
+ leitch->year = ATOB(0)*10 + ATOB(1);
+ leitch->month = ATOB(2)*10 + ATOB(3);
+ leitch->day = ATOB(4)*10 + ATOB(5);
+
+ /* sanity checks */
+ if (leitch->month > 12)
+ return(0);
+ if (leitch->day > days_in_month[leitch->month-1])
+ return(0);
+
+ /* calculate yearday */
+ i = 0;
+ leitch->yearday = leitch->day;
+
+ while ( i < (leitch->month-1) )
+ leitch->yearday += days_in_month[i++];
+
+ if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
+ leitch->month > 2)
+ leitch->yearday--;
+
+ return(1);
+}
+
+/*
+ * leitch_get_time
+ */
+static int
+leitch_get_time(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch,
+ int which
+ )
+{
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+ leitch->hour = ATOB(0)*10 +ATOB(1);
+ leitch->minute = ATOB(2)*10 +ATOB(3);
+ leitch->second = ATOB(4)*10 +ATOB(5);
+
+ if ((leitch->hour > 23) || (leitch->minute > 60) ||
+ (leitch->second > 60))
+ return(0);
+ return(1);
+}
+
+#else
+int refclock_leitch_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_local.c b/contrib/ntp/ntpd/refclock_local.c
new file mode 100644
index 0000000..dc6f1ae
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_local.c
@@ -0,0 +1,258 @@
+
+/*
+ * refclock_local - local pseudo-clock driver
+ *
+ * wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef REFCLOCK
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif
+
+/*
+ * This is a hack to allow a machine to use its own system clock as a
+ * reference clock, i.e., to free-run using no outside clock discipline
+ * source. This is useful if you want to use NTP in an isolated
+ * environment with no radio clock or NIST modem available. Pick a
+ * machine that you figure has a good clock oscillator and configure it
+ * with this driver. Set the clock using the best means available, like
+ * eyeball-and-wristwatch. Then, point all the other machines at this
+ * one or use broadcast (not multicast) mode to distribute time.
+ *
+ * Another application for this driver is if you want to use a
+ * particular server's clock as the clock of last resort when all other
+ * normal synchronization sources have gone away. This is especially
+ * useful if that server has an ovenized oscillator. For this you would
+ * configure this driver at a higher stratum (say 5) to prevent the
+ * server's stratum from falling below that.
+ *
+ * A third application for this driver is when an external discipline
+ * source is available, such as the NIST "lockclock" program, which
+ * synchronizes the local clock via a telephone modem and the NIST
+ * Automated Computer Time Service (ACTS), or the Digital Time
+ * Synchronization Service (DTSS), which runs on DCE machines. In this
+ * case the stratum should be set at zero, indicating a bona fide
+ * stratum-1 source. Exercise some caution with this, since there is no
+ * easy way to telegraph via NTP that something might be wrong in the
+ * discipline source itself. In the case of DTSS, the local clock can
+ * have a rather large jitter, depending on the interval between
+ * corrections and the intrinsic frequency error of the clock
+ * oscillator. In extreme cases, this can cause clients to exceed the
+ * 128-ms slew window and drop off the NTP subnet.
+ *
+ * THis driver includes provisions to telegraph synchronization state
+ * and related variables by means of kernel variables with specially
+ * modified kernels. This is done using the ntp_adjtime() syscall.
+ * In the cases where another protocol or device synchronizes the local
+ * host, the data given to the kernel can be slurped up by this driver
+ * and distributed to clients by ordinary NTP messaging.
+ *
+ * In the default mode the behavior of the clock selection algorithm is
+ * modified when this driver is in use. The algorithm is designed so
+ * that this driver will never be selected unless no other discipline
+ * source is available. This can be overriden with the prefer keyword of
+ * the server configuration command, in which case only this driver will
+ * be selected for synchronization and all other discipline sources will
+ * be ignored. This behavior is intended for use when an external
+ * discipline source controls the system clock.
+ *
+ * Fudge Factors
+ *
+ * The stratum for this driver set at 5 by default, but it can be
+ * changed by the fudge command and/or the ntpdc utility. The reference
+ * ID is 127.0.0.1 by default, but can be changed using the same mechanism.
+ * *NEVER* configure this driver to operate at a stratum which might
+ * possibly disrupt a client with access to a bona fide primary server,
+ * unless the local clock oscillator is reliably disciplined by another
+ * source. *NEVER NEVER* configure a server which might devolve to an
+ * undisciplined local clock to use multicast mode. Always remember that
+ * an improperly configured local clock driver let loose in the Internet
+ * can cause very serious disruption. This is why most of us who care
+ * about good time use cryptographic authentication.
+ *
+ * This driver provides a mechanism to trim the local clock in both time
+ * and frequency, as well as a way to manipulate the leap bits. The
+ * fudge time1 parameter adjusts the time, in seconds, and the fudge
+ * time2 parameter adjusts the frequency, in ppm. The fudge time1
+ * parameter is additive; that is, it adds an increment to the current
+ * time. The fudge time2 parameter directly sets the frequency.
+ */
+/*
+ * Local interface definitions
+ */
+#define PRECISION (-7) /* about 10 ms precision */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+#define STRATUM 5 /* default stratum */
+#define DISPERSION .01 /* default dispersion (10 ms) */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_proto
+ */
+extern s_char sys_precision;
+
+#ifdef KERNEL_PLL
+/*
+ * Imported from ntp_loopfilter
+ */
+extern int pll_control; /* kernel pll control */
+extern int kern_enable; /* kernel pll enabled */
+extern int ext_enable; /* external clock enable */
+#endif /* KERNEL_PLL */
+
+/*
+ * Function prototypes
+ */
+static int local_start P((int, struct peer *));
+static void local_poll P((int, struct peer *));
+
+/*
+ * Local variables
+ */
+static u_long poll_time; /* last time polled */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_local = {
+ local_start, /* start up driver */
+ noentry, /* shut down driver (not used) */
+ local_poll, /* transmit poll message */
+ noentry, /* not used (old lcl_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old lcl_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * local_start - start up the clock
+ */
+static int
+local_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = sys_precision;
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM;
+ pp->stratum = STRATUM;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, "LOCL", 4);
+ poll_time = current_time;
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ *
+ * LOCKCLOCK: If the kernel supports the nanokernel or microkernel
+ * system calls, the leap bits are extracted from the kernel. If there
+ * is a kernel error or the kernel leap bits are set to 11, the NTP leap
+ * bits are set to 11 and the stratum is set to infinity. Otherwise, the
+ * NTP leap bits are set to the kernel leap bits and the stratum is set
+ * as fudged. This behavior does not faithfully follow the
+ * specification, but is probably more appropriate in a multiple-server
+ * national laboratory network.
+ */
+static void
+local_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ struct timex ntv;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ struct refclockproc *pp;
+
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ if (unit == VMS_LOCALUNIT) {
+ extern void vms_local_poll(struct peer *);
+
+ vms_local_poll(peer);
+ return;
+ }
+#endif /* VMS && VMS_LOCALUNIT */
+ pp = peer->procptr;
+ pp->polls++;
+
+ /*
+ * Ramble through the usual filtering and grooming code, which
+ * is essentially a no-op and included mostly for pretty
+ * billboards. We allow a one-time time adjustment using fudge
+ * time1 (s) and a continuous frequency adjustment using fudge
+ * time 2 (ppm).
+ */
+ get_systime(&pp->lastrec);
+ pp->fudgetime1 += pp->fudgetime2 * 1e-6 * (current_time -
+ poll_time);
+ poll_time = current_time;
+ refclock_process_offset(pp, pp->lastrec, pp->lastrec,
+ pp->fudgetime1);
+
+ /*
+ * If another process is disciplining the system clock, we set
+ * the leap bits and quality indicators from the kernel.
+ */
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ memset(&ntv, 0, sizeof ntv);
+ switch (ntp_adjtime(&ntv)) {
+ case TIME_OK:
+ pp->leap = LEAP_NOWARNING;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_INS:
+ pp->leap = LEAP_ADDSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_DEL:
+ pp->leap = LEAP_DELSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ }
+ pp->disp = 0;
+ pp->jitter = 0;
+#else /* KERNEL_PLL LOCKCLOCK */
+ pp->leap = LEAP_NOWARNING;
+ pp->disp = DISPERSION;
+ pp->jitter = 0;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ pp->fudgetime1 = 0;
+}
+#else
+int refclock_local_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_msfees.c b/contrib/ntp/ntpd/refclock_msfees.c
new file mode 100644
index 0000000..98034b5
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_msfees.c
@@ -0,0 +1,1462 @@
+/* refclock_ees - clock driver for the EES M201 receiver */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
+
+/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
+ * were removed as the code was overly hairy, they weren't in use
+ * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
+ */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include <ctype.h>
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#include "ntp_stdlib.h"
+
+int dbg = 0;
+/*
+ fudgefactor = fudgetime1;
+ os_delay = fudgetime2;
+ offset_fudge = os_delay + fudgefactor + inherent_delay;
+ stratumtouse = fudgeval1 & 0xf
+ dbg = fudgeval2;
+ sloppyclockflag = flags & CLK_FLAG1;
+ 1 log smoothing summary when processing sample
+ 4 dump the buffer from the clock
+ 8 EIOGETKD the last n uS time stamps
+ if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
+ ees->dump_vals = flags & CLK_FLAG3;
+ ees->usealldata = flags & CLK_FLAG4;
+
+
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10]= ees->yearstart, 0;
+ */
+
+/* This should support the use of an EES M201 receiver with RS232
+ * output (modified to transmit time once per second).
+ *
+ * For the format of the message sent by the clock, see the EESM_
+ * definitions below.
+ *
+ * It appears to run free for an integral number of minutes, until the error
+ * reaches 4mS, at which point it steps at second = 01.
+ * It appears that sometimes it steps 4mS (say at 7 min interval),
+ * then the next minute it decides that it was an error, so steps back.
+ * On the next minute it steps forward again :-(
+ * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
+ * or 9.5uS/S then 3990.5uS at a 7min re-sync,
+ * at which point it may lose the "00" second time stamp.
+ * I assume that the most accurate time is just AFTER the re-sync.
+ * Hence remember the last cycle interval,
+ *
+ * Can run in any one of:
+ *
+ * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
+ * (sun) *in the interupt code*, so as to avoid problems with
+ * the STREAMS scheduling.
+ *
+ * It appears that it goes 16.5 uS slow each second, then every 4 mins it
+ * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
+ */
+
+/* Definitions */
+#ifndef MAXUNITS
+#define MAXUNITS 4 /* maximum number of EES units permitted */
+#endif
+
+#ifndef EES232
+#define EES232 "/dev/ees%d" /* Device to open to read the data */
+#endif
+
+/* Other constant stuff */
+#ifndef EESPRECISION
+#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
+#endif
+#ifndef EESREFID
+#define EESREFID "MSF\0" /* String to identify the clock */
+#endif
+#ifndef EESHSREFID
+#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
+#endif
+
+/* Description of clock */
+#define EESDESCRIPTION "EES M201 MSF Receiver"
+
+/* Speed we run the clock port at. If this is changed the UARTDELAY
+ * value should be recomputed to suit.
+ */
+#ifndef SPEED232
+#define SPEED232 B9600 /* 9600 baud */
+#endif
+
+/* What is the inherent delay for this mode of working, i.e. when is the
+ * data time stamped.
+ */
+#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
+#define BITS_TO_L_FP(bits, baud) \
+(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
+#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
+#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
+
+#ifndef STREAM_PP1
+#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
+#endif
+#ifndef STREAM_PP2
+#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
+#endif
+
+ /* Offsets of the bytes of the serial line code. The clock gives
+ * local time with a GMT/BST indication. The EESM_ definitions
+ * give offsets into ees->lastcode.
+ */
+#define EESM_CSEC 0 /* centiseconds - always zero in our clock */
+#define EESM_SEC 1 /* seconds in BCD */
+#define EESM_MIN 2 /* minutes in BCD */
+#define EESM_HOUR 3 /* hours in BCD */
+#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
+#define EESM_DAY 5 /* day of month in BCD */
+#define EESM_MON 6 /* month in BCD */
+#define EESM_YEAR 7 /* year MOD 100 in BCD */
+#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
+#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
+#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
+ /* followed by a frame alignment byte (0xff) /
+ / which is not put into the lastcode buffer*/
+
+/* Length of the serial time code, in characters. The first length
+ * is less the frame alignment byte.
+ */
+#define LENEESPRT (EESM_MSFOK+1)
+#define LENEESCODE (LENEESPRT+1)
+
+ /* Code state. */
+#define EESCS_WAIT 0 /* waiting for start of timecode */
+#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
+
+ /* Default fudge factor and character to receive */
+#define DEFFUDGETIME 0 /* Default user supplied fudge factor */
+#ifndef DEFOSTIME
+#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
+#endif
+#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
+
+ /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
+ * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
+ * as the estimated offset, otherwise average the remainder.
+ */
+#define FULLSHIFT 6 /* NCODES root 2 */
+#define NCODES (1<< FULLSHIFT) /* 64 */
+#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
+
+ /* Towards the high ( Why ?) end of half */
+#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
+
+ /* Leap hold time. After a leap second the clock will no longer be
+ * reliable until it resynchronizes. Hope 40 minutes is enough. */
+#define EESLEAPHOLD (40 * 60)
+
+#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
+#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
+#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
+#define EES_STEP_NOTES 50 /* Only do a limited number */
+#define MAX_STEP 16 /* Max number of steps to remember */
+
+ /* debug is a bit mask of debugging that is wanted */
+#define DB_SYSLOG_SMPLI 0x0001
+#define DB_SYSLOG_SMPLE 0x0002
+#define DB_SYSLOG_SMTHI 0x0004
+#define DB_SYSLOG_NSMTHE 0x0008
+#define DB_SYSLOG_NSMTHI 0x0010
+#define DB_SYSLOG_SMTHE 0x0020
+#define DB_PRINT_EV 0x0040
+#define DB_PRINT_CDT 0x0080
+#define DB_PRINT_CDTC 0x0100
+#define DB_SYSLOG_KEEPD 0x0800
+#define DB_SYSLOG_KEEPE 0x1000
+#define DB_LOG_DELTAS 0x2000
+#define DB_PRINT_DELTAS 0x4000
+#define DB_LOG_AWAITMORE 0x8000
+#define DB_LOG_SAMPLES 0x10000
+#define DB_NO_PPS 0x20000
+#define DB_INC_PPS 0x40000
+#define DB_DUMP_DELTAS 0x80000
+
+ struct eesunit { /* EES unit control structure. */
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp reftime; /* reference time */
+ l_fp lastsampletime; /* time as in txt from last EES msg */
+ l_fp arrvtime; /* Time at which pkt arrived */
+ l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */
+ l_fp offset; /* chosen offset (for clkbug) */
+ l_fp lowoffset; /* lowest sample offset (for clkbug) */
+ l_fp highoffset; /* highest " " (for clkbug) */
+ char lastcode[LENEESCODE+6]; /* last time code we received */
+ u_long lasttime; /* last time clock heard from */
+ u_long clocklastgood; /* last time good radio seen */
+ u_char lencode; /* length of code in buffer */
+ u_char nsamples; /* number of samples we've collected */
+ u_char codestate; /* state of 232 code reception */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ char tz; /* timezone from clock */
+ u_char ttytype; /* method used */
+ u_char dump_vals; /* Should clock values be dumped */
+ u_char usealldata; /* Use ALL samples */
+ u_short day; /* day of year from last code */
+ u_long yearstart; /* start of current year */
+ u_long leaphold; /* time of leap hold expiry */
+ u_long badformat; /* number of bad format codes */
+ u_long baddata; /* number of invalid time codes */
+ u_long timestarted; /* time we started this */
+ long last_pps_no; /* The serial # of the last PPS */
+ char fix_pending; /* Is a "sync to time" pending ? */
+ /* Fine tuning - compensate for 4 mS ramping .... */
+ l_fp last_l; /* last time stamp */
+ u_char last_steps[MAX_STEP]; /* Most recent n steps */
+ int best_av_step; /* Best guess at average step */
+ char best_av_step_count; /* # of steps over used above */
+ char this_step; /* Current pos in buffer */
+ int last_step_late; /* How late the last step was (0-59) */
+ long jump_fsecs; /* # of fractions of a sec last jump */
+ u_long last_step; /* time of last step */
+ int last_step_secs; /* Number of seconds in last step */
+ int using_ramp; /* 1 -> noemal, -1 -> over stepped */
+ };
+#define last_sec last_l.l_ui
+#define last_sfsec last_l.l_f
+#define this_uisec ((ees->arrvtime).l_ui)
+#define this_sfsec ((ees->arrvtime).l_f)
+#define msec(x) ((x) / (1<<22))
+#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
+#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
+
+/* Bitmask for what methods to try to use -- currently only PPS enabled */
+#define T_CBREAK 1
+#define T_PPS 8
+/* macros to test above */
+#define is_cbreak(x) ((x)->ttytype & T_CBREAK)
+#define is_pps(x) ((x)->ttytype & T_PPS)
+#define is_any(x) ((x)->ttytype)
+
+#define CODEREASON 20 /* reason codes */
+
+/* Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back. */
+static struct eesunit *eesunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/* Keep the fudge factors separately so they can be set even
+ * when no clock is configured. */
+static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */
+static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */
+static l_fp os_delay[MAXUNITS]; /* fudgetime2 */
+static l_fp offset_fudge[MAXUNITS]; /* Sum of above */
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+static int deltas[60];
+
+static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
+static l_fp onesec; /* = { 1, 0 }; */
+
+#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
+#define DUMP_BUF_SIZE 10112
+#endif
+
+/* ees_reset - reset the count back to zero */
+#define ees_reset(ees) (ees)->nsamples = 0; \
+(ees)->codestate = EESCS_WAIT
+
+/* ees_event - record and report an event */
+#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
+ees_report_event((ees), (evcode))
+
+ /* Find the precision of the system clock by reading it */
+#define USECS 1000000
+#define MINSTEP 5 /* some systems increment uS on each call */
+#define MAXLOOPS (USECS/9)
+
+/*
+ * Function prototypes
+ */
+
+static int msfees_start P((int unit, struct peer *peer));
+static void msfees_shutdown P((int unit, struct peer *peer));
+static void msfees_poll P((int unit, struct peer *peer));
+static void msfees_init P((void));
+static void dump_buf P((l_fp *coffs, int from, int to, char *text));
+static void ees_report_event P((struct eesunit *ees, int code));
+static void ees_receive P((struct recvbuf *rbufp));
+static void ees_process P((struct eesunit *ees));
+#ifdef QSORT_USES_VOID_P
+static int offcompare P((const void *va, const void *vb));
+#else
+static int offcompare P((const l_fp *a, const l_fp *b));
+#endif /* QSORT_USES_VOID_P */
+
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_msfees = {
+ msfees_start, /* start up driver */
+ msfees_shutdown, /* shut down driver */
+ msfees_poll, /* transmit poll message */
+ noentry, /* not used */
+ msfees_init, /* initialize driver */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+static void
+dump_buf(
+ l_fp *coffs,
+ int from,
+ int to,
+ char *text
+ )
+{
+ char buff[DUMP_BUF_SIZE + 80];
+ int i;
+ register char *ptr = buff;
+
+ sprintf(ptr, text);
+ for (i=from; i<to; i++)
+ { while (*ptr) ptr++;
+ if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff);
+ sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
+ }
+ msyslog(LOG_DEBUG, "D: %s", buff);
+}
+
+/* msfees_init - initialize internal ees driver data */
+static void
+msfees_init(void)
+{
+ register int i;
+ /* Just zero the data arrays */
+ memset((char *)eesunits, 0, sizeof eesunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ acceptable_slop.l_ui = 0;
+ acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
+
+ onesec.l_ui = 1;
+ onesec.l_uf = 0;
+
+ /* Initialize fudge factors to default. */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ os_delay[i].l_ui = 0;
+ os_delay[i].l_uf = DEFOSTIME;
+ inherent_delay[i].l_ui = 0;
+ inherent_delay[i].l_uf = DEFINHTIME;
+ offset_fudge[i] = os_delay[i];
+ L_ADD(&offset_fudge[i], &fudgefactor[i]);
+ L_ADD(&offset_fudge[i], &inherent_delay[i]);
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+
+/* msfees_start - open the EES devices and initialize data for processing */
+static int
+msfees_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+ register int i;
+ int fd232 = -1;
+ char eesdev[20];
+ struct termios ttyb, *ttyp;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
+ return 0;
+ }
+
+ /* Unit okay, attempt to open the devices. We do them both at
+ * once to make sure we can */
+ (void) sprintf(eesdev, EES232, unit);
+
+ fd232 = open(eesdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
+ return 0;
+ }
+
+#ifdef TIOCEXCL
+ /* Set for exclusive use */
+ if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
+ msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
+ goto screwed;
+ }
+#endif
+
+ /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ /* Set port characteristics. If we don't have a STREAMS module or
+ * a clock line discipline, cooked mode is just usable, even though it
+ * strips the top bit. The only EES byte which uses the top
+ * bit is the year, and we don't use that anyway. If we do
+ * have the line discipline, we choose raw mode, and the
+ * line discipline code will block up the messages.
+ */
+
+ /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_oflag = 0;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ inherent_delay[unit].l_uf = INH_DELAY_PPS;
+
+ /* offset fudge (how *late* the timestamp is) = fudge + os delays */
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+
+ /* Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc() one.
+ */
+ if (eesunits[unit] != 0) /* The one we want is okay */
+ ees = eesunits[unit];
+ else {
+ /* Look for an unused, but allocated struct */
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && eesunits[i] != 0)
+ break;
+ }
+
+ if (i < MAXUNITS) { /* Reclaim this one */
+ ees = eesunits[i];
+ eesunits[i] = 0;
+ } /* no spare -- make a new one */
+ else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
+ }
+ memset((char *)ees, 0, sizeof(struct eesunit));
+ eesunits[unit] = ees;
+
+ /* Set up the structures */
+ ees->peer = peer;
+ ees->unit = (u_char)unit;
+ ees->timestarted= current_time;
+ ees->ttytype = 0;
+ ees->io.clock_recv= ees_receive;
+ ees->io.srcclock= (caddr_t)ees;
+ ees->io.datalen = 0;
+ ees->io.fd = fd232;
+
+ /* Okay. Push one of the two (linked into the kernel, or dynamically
+ * loaded) STREAMS module, and give it to the I/O code to start
+ * receiving stuff.
+ */
+
+#ifdef STREAM
+ {
+ int rc1;
+ /* Pop any existing onews first ... */
+ while (ioctl(fd232, I_POP, 0 ) >= 0) ;
+
+ /* Now try pushing either of the possible modules */
+ if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
+ ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
+ msyslog(LOG_ERR,
+ "ees clock: Push of `%s' and `%s' to %s failed %m",
+ STREAM_PP1, STREAM_PP2, eesdev);
+ goto screwed;
+ }
+ else {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
+ (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
+ ees->ttytype |= T_PPS;
+ }
+ }
+#endif /* STREAM */
+
+ /* Add the clock */
+ if (!io_addclock(&ees->io)) {
+ /* Oh shit. Just close and return. */
+ msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
+ goto screwed;
+ }
+
+
+ /* All done. Initialize a few random peer variables, then
+ * return success. */
+ peer->precision = sys_precision;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1) {
+ memcpy((char *)&pp->refid, EESREFID, 4);
+ if (unit > 0 && unit < 10)
+ ((char *)&pp->refid)[3] = '0' + unit;
+ } else {
+ peer->refid = htonl(EESHSREFID);
+ }
+ unitinuse[unit] = 1;
+ pp->unitptr = (caddr_t) &eesunits[unit];
+ pp->clockdesc = EESDESCRIPTION;
+ msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
+ return (1);
+
+ screwed:
+ if (fd232 != -1)
+ (void) close(fd232);
+ return (0);
+}
+
+
+/* msfees_shutdown - shut down a EES clock */
+static void
+msfees_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
+ unit, MAXUNITS);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
+ return;
+ }
+
+ /* Tell the I/O module to turn us off. We're history. */
+ ees = eesunits[unit];
+ io_closeclock(&ees->io);
+ unitinuse[unit] = 0;
+}
+
+
+/* ees_report_event - note the occurance of an event */
+static void
+ees_report_event(
+ struct eesunit *ees,
+ int code
+ )
+{
+ if (ees->status != (u_char)code) {
+ ees->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ ees->lastevent = (u_char)code;
+ /* Should report event to trap handler in here.
+ * Soon...
+ */
+ }
+}
+
+
+/* ees_receive - receive data from the serial interface on an EES clock */
+static void
+ees_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register int n_sample;
+ register int day;
+ register struct eesunit *ees;
+ register u_char *dpt; /* Data PoinTeR: move along ... */
+ register u_char *dpend; /* Points just *after* last data char */
+ register char *cp;
+ l_fp tmp;
+ int call_pps_sample = 0;
+ l_fp pps_arrvstamp;
+ int sincelast;
+ int pps_step = 0;
+ int suspect_4ms_step = 0;
+ struct ppsclockev ppsclockev;
+ long *ptr = (long *) &ppsclockev;
+ int rc;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+
+ /* Get the clock this applies to and a pointer to the data */
+ ees = (struct eesunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
+ printf("[%d] ", rbufp->recv_length);
+
+ /* Check out our state and process appropriately */
+ switch (ees->codestate) {
+ case EESCS_WAIT:
+ /* Set an initial guess at the timestamp as the recv time.
+ * If just running in CBREAK mode, we can't improve this.
+ * If we have the CLOCK Line Discipline, PPSCD, or sime such,
+ * then we will do better later ....
+ */
+ ees->arrvtime = rbufp->recv_time;
+ ees->codestate = EESCS_GOTSOME;
+ ees->lencode = 0;
+ /*FALLSTHROUGH*/
+
+ case EESCS_GOTSOME:
+ cp = &(ees->lastcode[ees->lencode]);
+
+ /* Gobble the bytes until the final (possibly stripped) 0xff */
+ while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
+ *cp++ = (char)*dpt++;
+ ees->lencode++;
+ /* Oh dear -- too many bytes .. */
+ if (ees->lencode > LENEESPRT) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,
+ "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
+ ees->lencode, dpend - dpt, LENEESPRT,
+#define D(x) (ees->lastcode[x])
+ D(0), D(1), D(2), D(3), D(4), D(5), D(6),
+ D(7), D(8), D(9), D(10), D(11), D(12));
+#undef D
+ ees->badformat++;
+ ees->reason = CODEREASON + 1;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+ }
+ /* Gave up because it was end of the buffer, rather than ff */
+ if (dpt == dpend) {
+ /* Incomplete. Wait for more. */
+ if (dbg & DB_LOG_AWAITMORE)
+ msyslog(LOG_INFO,
+ "I: ees clock %d: %p == %p: await more",
+ ees->unit, dpt, dpend);
+ return;
+ }
+
+ /* This shouldn't happen ... ! */
+ if ((*dpt & 0x7f) != 0x7f) {
+ msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
+ ees->badformat++;
+ ees->reason = CODEREASON + 2;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Skip the 0xff */
+ dpt++;
+
+ /* Finally, got a complete buffer. Mainline code will
+ * continue on. */
+ cp = ees->lastcode;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
+ ees->unit, ees->codestate);
+ ees->reason = CODEREASON + 5;
+ ees_event(ees, CEVNT_FAULT);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Boy! After all that crap, the lastcode buffer now contains
+ * something we hope will be a valid time code. Do length
+ * checks and sanity checks on constant data.
+ */
+ ees->codestate = EESCS_WAIT;
+ ees->lasttime = current_time;
+ if (ees->lencode != LENEESPRT) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 6;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ cp = ees->lastcode;
+
+ /* Check that centisecond is zero */
+ if (cp[EESM_CSEC] != 0) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 7;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Check flag formats */
+ if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 8;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 9;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 10;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* So far, so good. Compute day, hours, minutes, seconds,
+ * time zone. Do range checks on these.
+ */
+
+#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
+#define istrue(x) ((x)?1:0)
+
+ ees->second = bcdunpack(cp[EESM_SEC]); /* second */
+ ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
+ ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
+
+ day = bcdunpack(cp[EESM_DAY]); /* day of month */
+
+ switch (bcdunpack(cp[EESM_MON])) { /* month */
+
+ /* Add in lengths of all previous months. Add one more
+ if it is a leap year and after February.
+ */
+ case 12: day += NOV; /*FALLSTHROUGH*/
+ case 11: day += OCT; /*FALLSTHROUGH*/
+ case 10: day += SEP; /*FALLSTHROUGH*/
+ case 9: day += AUG; /*FALLSTHROUGH*/
+ case 8: day += JUL; /*FALLSTHROUGH*/
+ case 7: day += JUN; /*FALLSTHROUGH*/
+ case 6: day += MAY; /*FALLSTHROUGH*/
+ case 5: day += APR; /*FALLSTHROUGH*/
+ case 4: day += MAR; /*FALLSTHROUGH*/
+ case 3: day += FEB;
+ if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
+ case 2: day += JAN; /*FALLSTHROUGH*/
+ case 1: break;
+ default: ees->baddata++;
+ ees->reason = CODEREASON + 11;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ ees->day = day;
+
+ /* Get timezone. The clocktime routine wants the number
+ * of hours to add to the delivered time to get UT.
+ * Currently -1 if BST flag set, 0 otherwise. This
+ * is the place to tweak things if double summer time
+ * ever happens.
+ */
+ ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
+
+ if (ees->day > 366 || ees->day < 1 ||
+ ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 12;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ n_sample = ees->nsamples;
+
+ /* Now, compute the reference time value: text -> tmp.l_ui */
+ if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
+ ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
+ &tmp.l_ui)) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 13;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+ tmp.l_uf = 0;
+
+ /* DON'T use ees->arrvtime -- it may be < reftime */
+ ees->lastsampletime = tmp;
+
+ /* If we are synchronised to the radio, update the reference time.
+ * Also keep a note of when clock was last good.
+ */
+ if (istrue(cp[EESM_MSFOK])) {
+ ees->reftime = tmp;
+ ees->clocklastgood = current_time;
+ }
+
+
+ /* Compute the offset. For the fractional part of the
+ * offset we use the expected delay for the message.
+ */
+ ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
+ ees->codeoffsets[n_sample].l_uf = 0;
+
+ /* Number of seconds since the last step */
+ sincelast = this_uisec - ees->last_step;
+
+ memset((char *) &ppsclockev, 0, sizeof ppsclockev);
+
+ rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
+ if (dbg & DB_PRINT_EV) fprintf(stderr,
+ "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
+ DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
+ rc, errno, ptr[0], ptr[1], ptr[2]);
+
+ /* If we managed to get the time of arrival, process the info */
+ if (rc >= 0) {
+ int conv = -1;
+ pps_step = ppsclockev.serial - ees->last_pps_no;
+
+ /* Possible that PPS triggered, but text message didn't */
+ if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
+ if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
+ if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
+
+ /* allow for single loss of PPS only */
+ if (pps_step != 1 && pps_step != 2)
+ fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
+ ppsclockev.serial, ees->last_pps_no, pps_step);
+ else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
+ fprintf(stderr, "buftvtots failed\n");
+ else { /* if ((ABS(time difference) - 0.25) < 0)
+ * then believe it ...
+ */
+ l_fp diff;
+ diff = pps_arrvstamp;
+ conv = 0;
+ L_SUB(&diff, &ees->arrvtime);
+ if (dbg & DB_PRINT_CDT)
+ printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
+ DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
+ (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
+ (long)diff.l_ui, (long)diff.l_uf,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ ees->arrvtime = pps_arrvstamp;
+ conv++;
+ call_pps_sample++;
+ }
+ /* Some loss of some signals around sec = 1 */
+ else if (ees->second == 1) {
+ diff = pps_arrvstamp;
+ L_ADD(&diff, &onesec);
+ L_SUB(&diff, &ees->arrvtime);
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
+ pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
+ pps_arrvstamp.l_uf,
+ ees->arrvtime.l_uf,
+ diff.l_ui, diff.l_uf,
+ (int)ppsclockev.tv.tv_usec,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ suspect_4ms_step |= 2;
+ ees->arrvtime = pps_arrvstamp;
+ L_ADD(&ees->arrvtime, &onesec);
+ conv++;
+ call_pps_sample++;
+ }
+ }
+ }
+ ees->last_pps_no = ppsclockev.serial;
+ if (dbg & DB_PRINT_CDTC)
+ printf(
+ "[%x] %08lx %08lx %d u%d (%d %d)\n",
+ DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
+ (long)pps_arrvstamp.l_uf, conv, ees->unit,
+ call_pps_sample, pps_step);
+ }
+
+ /* See if there has been a 4ms jump at a minute boundry */
+ { l_fp delta;
+#define delta_isec delta.l_ui
+#define delta_ssec delta.l_i
+#define delta_sfsec delta.l_f
+ long delta_f_abs;
+
+ delta.l_i = ees->arrvtime.l_i;
+ delta.l_f = ees->arrvtime.l_f;
+
+ L_SUB(&delta, &ees->last_l);
+ delta_f_abs = delta_sfsec;
+ if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
+
+ /* Dump the deltas each minute */
+ if (dbg & DB_DUMP_DELTAS)
+ { if (/*0 <= ees->second && */
+ ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
+ /* Dump on second 1, as second 0 sometimes missed */
+ if (ees->second == 1) {
+ char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
+ char *cptr=text;
+ int i;
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
+ sprintf(cptr, " %d.%04d",
+ msec(deltas[i]), subms(deltas[i]));
+ while (*cptr) cptr++;
+ }
+ msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
+ msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
+ text+1);
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
+ }
+ }
+
+ /* Lets see if we have a 4 mS step at a minute boundaary */
+ if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
+ (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
+ (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
+ (sincelast < 0 || sincelast > 122)
+ ) { /* 4ms jump at min boundry */
+ int old_sincelast;
+ int count=0;
+ int sum = 0;
+ /* Yes -- so compute the ramp time */
+ if (ees->last_step == 0) sincelast = 0;
+ old_sincelast = sincelast;
+
+ /* First time in, just set "ees->last_step" */
+ if(ees->last_step) {
+ int other_step = 0;
+ int third_step = 0;
+ int this_step = (sincelast + (60 /2)) / 60;
+ int p_step = ees->this_step;
+ int p;
+ ees->last_steps[p_step] = this_step;
+ p= p_step;
+ p_step++;
+ if (p_step >= LAST_STEPS) p_step = 0;
+ ees->this_step = p_step;
+ /* Find the "average" interval */
+ while (p != p_step) {
+ int this = ees->last_steps[p];
+ if (this == 0) break;
+ if (this != this_step) {
+ if (other_step == 0 && (
+ this== (this_step +2) ||
+ this== (this_step -2) ||
+ this== (this_step +1) ||
+ this== (this_step -1)))
+ other_step = this;
+ if (other_step != this) {
+ int idelta = (this_step - other_step);
+ if (idelta < 0) idelta = - idelta;
+ if (third_step == 0 && (
+ (idelta == 1) ? (
+ this == (other_step +1) ||
+ this == (other_step -1) ||
+ this == (this_step +1) ||
+ this == (this_step -1))
+ :
+ (
+ this == (this_step + other_step)/2
+ )
+ )) third_step = this;
+ if (third_step != this) break;
+ }
+ }
+ sum += this;
+ p--;
+ if (p < 0) p += LAST_STEPS;
+ count++;
+ }
+ msyslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
+ if (count != 0) sum = ((sum * 60) + (count /2)) / count;
+#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
+ msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+ printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+#undef SV
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ if (sincelast > 170)
+ ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
+ else ees->last_step_late = 30;
+ if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
+ if (ees->last_step_late < 0) ees->last_step_late = 0;
+ if (ees->last_step_late >= 60) ees->last_step_late = 59;
+ sincelast = 0;
+ }
+ else { /* First time in -- just save info */
+ ees->last_step_late = 30;
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ sum = 4 * 60;
+ }
+ ees->last_step = this_uisec;
+ printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, old_sincelast, ees->last_step_late, count, sum,
+ ees->last_step_secs);
+ msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
+ old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+ if (sum) ees->last_step_secs = sum;
+ }
+ /* OK, so not a 4ms step at a minute boundry */
+ else {
+ if (suspect_4ms_step) msyslog(LOG_ERR,
+ "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
+ ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
+ msec(EES_STEP_F - EES_STEP_F_GRACE),
+ subms(EES_STEP_F - EES_STEP_F_GRACE),
+ (int)msec(delta_f_abs),
+ (int)subms(delta_f_abs),
+ msec(EES_STEP_F + EES_STEP_F_GRACE),
+ subms(EES_STEP_F + EES_STEP_F_GRACE),
+ ees->second,
+ sincelast);
+ if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
+ static int ees_step_notes = EES_STEP_NOTES;
+ if (ees_step_notes > 0) {
+ ees_step_notes--;
+ printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
+ msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
+ }
+ }
+ }
+ }
+ ees->last_l = ees->arrvtime;
+
+ /* IF we have found that it's ramping
+ * && it's within twice the expected ramp period
+ * && there is a non zero step size (avoid /0 !)
+ * THEN we twiddle things
+ */
+ if (ees->using_ramp &&
+ sincelast < (ees->last_step_secs)*2 &&
+ ees->last_step_secs)
+ { long sec_of_ramp = sincelast + ees->last_step_late;
+ long fsecs;
+ l_fp inc;
+
+ /* Ramp time may vary, so may ramp for longer than last time */
+ if (sec_of_ramp > (ees->last_step_secs + 120))
+ sec_of_ramp = ees->last_step_secs;
+
+ /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
+ fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
+
+ if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
+ DB_LOG_DELTAS,
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+ if (dbg & DB_PRINT_DELTAS) printf(
+ "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+
+ /* Must sign extend the result */
+ inc.l_i = (fsecs < 0) ? -1 : 0;
+ inc.l_f = fsecs;
+ if (dbg & DB_INC_PPS)
+ { L_SUB(&pps_arrvstamp, &inc);
+ L_SUB(&ees->arrvtime, &inc);
+ }
+ else
+ { L_ADD(&pps_arrvstamp, &inc);
+ L_ADD(&ees->arrvtime, &inc);
+ }
+ }
+ else {
+ if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ if (dbg & DB_PRINT_DELTAS) printf(
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ }
+
+ L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
+ L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
+
+ if (call_pps_sample && !(dbg & DB_NO_PPS)) {
+ /* Sigh -- it expects its args negated */
+ L_NEG(&pps_arrvstamp);
+ /*
+ * I had to disable this here, since it appears there is no pointer to the
+ * peer structure.
+ *
+ (void) pps_sample(peer, &pps_arrvstamp);
+ */
+ }
+
+ /* Subtract off the local clock time stamp */
+ L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
+ if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR,
+ "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
+ ees->unit, DB_LOG_DELTAS, n_sample,
+ ees->codeoffsets[n_sample].l_f,
+ ees->codeoffsets[n_sample].l_f / 4295,
+ pps_arrvstamp.l_f,
+ pps_arrvstamp.l_f /4295,
+ (dbg & DB_NO_PPS) ? " [no PPS]" : "");
+
+ if (ees->nsamples++ == NCODES-1) ees_process(ees);
+
+ /* Done! */
+}
+
+
+/* offcompare - auxiliary comparison routine for offset sort */
+
+#ifdef QSORT_USES_VOID_P
+static int
+offcompare(
+ const void *va,
+ const void *vb
+ )
+{
+ const l_fp *a = (const l_fp *)va;
+ const l_fp *b = (const l_fp *)vb;
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+#else
+static int
+offcompare(
+ const l_fp *a,
+ const l_fp *b
+ )
+{
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+#endif /* QSORT_USES_VOID_P */
+
+
+/* ees_process - process a pile of samples from the clock */
+static void
+ees_process(
+ struct eesunit *ees
+ )
+{
+ static int last_samples = -1;
+ register int i, j;
+ register int noff;
+ register l_fp *coffs = ees->codeoffsets;
+ l_fp offset, tmp;
+ double dispersion; /* ++++ */
+ int lostsync, isinsync;
+ int samples = ees->nsamples;
+ int samplelog = 0; /* keep "gcc -Wall" happy ! */
+ int samplereduce = (samples + 1) / 2;
+ double doffset;
+
+ /* Reset things to zero so we don't have to worry later */
+ ees_reset(ees);
+
+ if (sloppyclockflag[ees->unit]) {
+ samplelog = (samples < 2) ? 0 :
+ (samples < 5) ? 1 :
+ (samples < 9) ? 2 :
+ (samples < 17) ? 3 :
+ (samples < 33) ? 4 : 5;
+ samplereduce = (1 << samplelog);
+ }
+
+ if (samples != last_samples &&
+ ((samples != (last_samples-1)) || samples < 3)) {
+ msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
+ samples, last_samples, samplereduce);
+ last_samples = samples;
+ }
+ if (samples < 1) return;
+
+ /* If requested, dump the raw data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:");
+
+ /* Sort the offsets, trim off the extremes, then choose one. */
+ qsort(
+#ifdef QSORT_USES_VOID_P
+ (void *)
+#else
+ (char *)
+#endif
+ coffs, (size_t)samples, sizeof(l_fp), offcompare);
+
+ noff = samples;
+ i = 0;
+ while ((noff - i) > samplereduce) {
+ /* Trim off the sample which is further away
+ * from the median. We work this out by doubling
+ * the median, subtracting off the end samples, and
+ * looking at the sign of the answer, using the
+ * identity (c-b)-(b-a) == 2*b-a-c
+ */
+ tmp = coffs[(noff + i)/2];
+ L_ADD(&tmp, &tmp);
+ L_SUB(&tmp, &coffs[i]);
+ L_SUB(&tmp, &coffs[noff-1]);
+ if (L_ISNEG(&tmp)) noff--; else i++;
+ }
+
+ /* If requested, dump the reduce data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
+
+ /* What we do next depends on the setting of the sloppy clock flag.
+ * If it is on, average the remainder to derive our estimate.
+ * Otherwise, just pick a representative value from the remaining stuff
+ */
+ if (sloppyclockflag[ees->unit]) {
+ offset.l_ui = offset.l_uf = 0;
+ for (j = i; j < noff; j++)
+ L_ADD(&offset, &coffs[j]);
+ for (j = samplelog; j > 0; j--)
+ L_RSHIFTU(&offset);
+ }
+ else offset = coffs[i+BESTSAMPLE];
+
+ /* Compute the dispersion as the difference between the
+ * lowest and highest offsets that remain in the
+ * consideration list.
+ *
+ * It looks like MOST clocks have MOD (max error), so halve it !
+ */
+ tmp = coffs[noff-1];
+ L_SUB(&tmp, &coffs[i]);
+#define FRACT_SEC(n) ((1 << 30) / (n/2))
+ dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
+ if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
+ (dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
+ dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
+ offset.l_f / 4295, offset.l_f,
+ (dispersion * 1526) / 100,
+ (sloppyclockflag[ees->unit]) ? " by averaging" : "",
+ FRACT_SEC(10) / 4295,
+ (coffs[0].l_f) / 4295,
+ i,
+ (coffs[i].l_f) / 4295,
+ (coffs[samples/2].l_f) / 4295,
+ (coffs[i+BESTSAMPLE].l_f) / 4295,
+ noff-1,
+ (coffs[noff-1].l_f) / 4295,
+ (coffs[samples-1].l_f) / 4295);
+
+ /* Are we playing silly wotsits ?
+ * If we are using all data, see if there is a "small" delta,
+ * and if so, blurr this with 3/4 of the delta from the last value
+ */
+ if (ees->usealldata && ees->offset.l_uf) {
+ long diff = (long) (ees->offset.l_uf - offset.l_uf);
+
+ /* is the delta small enough ? */
+ if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
+ int samd = (64 * 4) / samples;
+ long new;
+ if (samd < 2) samd = 2;
+ new = offset.l_uf + ((diff * (samd -1)) / samd);
+
+ /* Sign change -> need to fix up int part */
+ if ((new & 0x80000000) !=
+ (((long) offset.l_uf) & 0x80000000))
+ { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
+ new & 0x80000000,
+ ((long) offset.l_uf) & 0x80000000,
+ new, (long) offset.l_uf,
+ (new < 0) ? -1 : 1);
+ offset.l_ui += (new < 0) ? -1 : 1;
+ }
+ dispersion /= 4;
+ if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
+ (dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
+ dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
+ ((long) offset.l_uf) / 4295, new / 4295,
+ (dispersion * 1526) / 100);
+ offset.l_uf = (unsigned long) new;
+ }
+ else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "[%x] No smooth as delta not %d < %ld < %d",
+ dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ - FRACT_SEC(100), diff, FRACT_SEC(100));
+ }
+ else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
+ dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
+ offset.l_f, ees->offset.l_f - offset.l_f);
+
+ /* Collect offset info for debugging info */
+ ees->offset = offset;
+ ees->lowoffset = coffs[i];
+ ees->highoffset = coffs[noff-1];
+
+ /* Determine synchronization status. Can be unsync'd either
+ * by a report from the clock or by a leap hold.
+ *
+ * Loss of the radio signal for a short time does not cause
+ * us to go unsynchronised, since the receiver keeps quite
+ * good time on its own. The spec says 20ms in 4 hours; the
+ * observed drift in our clock (Cambridge) is about a second
+ * a day, but even that keeps us within the inherent tolerance
+ * of the clock for about 15 minutes. Observation shows that
+ * the typical "short" outage is 3 minutes, so to allow us
+ * to ride out those, we will give it 5 minutes.
+ */
+ lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
+ isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
+
+ /* Done. Use time of last good, synchronised code as the
+ * reference time, and lastsampletime as the receive time.
+ */
+ if (ees->fix_pending) {
+ msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
+ ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
+ ees->fix_pending = 0;
+ }
+ LFPTOD(&offset, doffset);
+ refclock_receive(ees->peer);
+ ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
+}
+
+/* msfees_poll - called by the transmit procedure */
+static void
+msfees_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
+ unit);
+ return;
+ }
+
+ ees_process(eesunits[unit]);
+
+ if ((current_time - eesunits[unit]->lasttime) > 150)
+ ees_event(eesunits[unit], CEVNT_FAULT);
+}
+
+
+#else
+int refclock_msfees_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_mx4200.c b/contrib/ntp/ntpd/refclock_mx4200.c
new file mode 100644
index 0000000..68150b7
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_mx4200.c
@@ -0,0 +1,1652 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
+ *
+ * 1. Added support for alternate PPS schemes, with code mostly
+ * copied from the Oncore driver (Thanks, Poul-Henning Kamp).
+ * This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "mx4200.h"
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#include "ntp_sprintf.h"
+
+#ifndef HAVE_STRUCT_PPSCLOCKEV
+struct ppsclockev {
+# ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* ! HAVE_STRUCT_PPSCLOCKEV */
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver supports the Magnavox Model MX 4200 GPS Receiver
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It's likely that other compatible Magnavox receivers such as the
+ * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code.
+ */
+
+/*
+ * Check this every time you edit the code!
+ */
+#define YEAR_LAST_MODIFIED 2000
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B4800 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+#define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
+
+/*
+ * Position Averaging.
+ */
+#define INTERVAL 1 /* Interval between position measurements (s) */
+#define AVGING_TIME 24 /* Number of hours to average */
+#define NOT_INITIALIZED -9999. /* initial pivot longitude */
+
+/*
+ * MX4200 unit control structure.
+ */
+struct mx4200unit {
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+ u_int lastserial; /* last pps serial number */
+ struct ppsclockev ppsev; /* PPS control structure */
+ double avg_lat; /* average latitude */
+ double avg_lon; /* average longitude */
+ double avg_alt; /* average height */
+ double central_meridian; /* central meridian */
+ double N_fixes; /* Number of position measurements */
+ int last_leap; /* leap second warning */
+ u_int moving; /* mobile platform? */
+ u_long sloppyclockflag; /* fudge flags */
+ u_int known; /* position known yet? */
+ u_long clamp_time; /* when to stop postion averaging */
+ u_long log_time; /* when to print receiver status */
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+ pps_info_t pps_i;
+};
+
+static char pmvxg[] = "PMVXG";
+
+/* XXX should be somewhere else */
+#ifdef __GNUC__
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */
+#else
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ */
+/* XXX end */
+
+/*
+ * Function prototypes
+ */
+static int mx4200_start P((int, struct peer *));
+static void mx4200_shutdown P((int, struct peer *));
+static void mx4200_receive P((struct recvbuf *));
+static void mx4200_poll P((int, struct peer *));
+
+static char * mx4200_parse_t P((struct peer *));
+static char * mx4200_parse_p P((struct peer *));
+static char * mx4200_parse_s P((struct peer *));
+#ifdef QSORT_USES_VOID_P
+int mx4200_cmpl_fp P((const void *, const void *));
+#else
+int mx4200_cmpl_fp P((const l_fp *, const l_fp *));
+#endif /* not QSORT_USES_VOID_P */
+static int mx4200_config P((struct peer *));
+static void mx4200_ref P((struct peer *));
+static void mx4200_send P((struct peer *, char *, ...))
+ __attribute__ ((format (printf, 2, 3)));
+static u_char mx4200_cksum P((char *, int));
+static int mx4200_jday P((int, int, int));
+static void mx4200_debug P((struct peer *, char *, ...))
+ __attribute__ ((format (printf, 2, 3)));
+static int mx4200_pps P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_mx4200 = {
+ mx4200_start, /* start up driver */
+ mx4200_shutdown, /* shut down driver */
+ mx4200_poll, /* transmit poll message */
+ noentry, /* not used (old mx4200_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old mx4200_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+
+/*
+ * mx4200_start - open the devices and initialize data for processing
+ */
+static int
+mx4200_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
+ return (0);
+ }
+
+ /*
+ * Allocate unit structure
+ */
+ if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
+ perror("emalloc");
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct mx4200unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = mx4200_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ /* Ensure the receiver is properly configured */
+ return mx4200_config(peer);
+}
+
+
+/*
+ * mx4200_shutdown - shut down the clock
+ */
+static void
+mx4200_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * mx4200_config - Configure the receiver
+ */
+static int
+mx4200_config(
+ struct peer *peer
+ )
+{
+ char tr_mode;
+ int add_mode;
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int mode;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * Initialize the unit variables
+ *
+ * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
+ * at the time mx4200_start is called. These are set later,
+ * and so the code must be prepared to handle changing flags.
+ */
+ up->sloppyclockflag = pp->sloppyclockflag;
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ up->moving = 1; /* Receiver on mobile platform */
+ msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
+ } else {
+ up->moving = 0; /* Static Installation */
+ }
+ up->pollcnt = 2;
+ up->polled = 0;
+ up->known = 0;
+ up->avg_lat = 0.0;
+ up->avg_lon = 0.0;
+ up->avg_alt = 0.0;
+ up->central_meridian = NOT_INITIALIZED;
+ up->N_fixes = 0.0;
+ up->last_leap = 0; /* LEAP_NOWARNING */
+ up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
+ up->log_time = current_time + SLEEPTIME;
+
+ if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
+ perror("time_pps_create");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_create failed: %m");
+ return (0);
+ }
+ if (time_pps_getcap(up->pps_h, &mode) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
+ up->pps_p.mode &= mode; /* only set what is legal */
+
+ if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
+ perror("time_pps_setparams");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_setparams failed: %m");
+ exit(1);
+ }
+
+ if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
+ PPS_TSFMT_TSPEC) < 0) {
+ perror("time_pps_kcbind");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_kcbind failed: %m");
+ exit(1);
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Zero the output list (do it twice to flush possible junk)
+ */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ /* control port output block Label */
+ 1); /* clear current output control list (1=yes) */
+ /* add/delete sentences from list */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF, 1);
+
+ /*
+ * Request software configuration so we can syslog the firmware version
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
+
+ /*
+ * "001" Initialization/Mode Control, Part A
+ * Where ARE we?
+ */
+ mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
+ PMVXG_S_INITMODEA);
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ /* latitude DDMM.MMMM */
+ /* north/south */
+ /* longitude DDDMM.MMMM */
+ /* east/west */
+ /* height */
+ /* Altitude Reference 1=MSL */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Start off in 2d/3d coast mode, holding altitude to last known
+ * value if only 3 satellites available.
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 3, /* 2d/3d coast */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver.
+ * (Set field 1 'D' == dynamic if we are on a moving platform).
+ * (Set field 1 'S' == static if we are not moving).
+ * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
+ */
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->moving = 1; /* Receiver on mobile platform */
+ else
+ up->moving = 0; /* Static Installation */
+
+ up->pollcnt = 2;
+ if (up->moving) {
+ /* dynamic: solve for pos, alt, time, while moving */
+ tr_mode = 'D';
+ } else {
+ /* static: solve for pos, alt, time, while stationary */
+ tr_mode = 'S';
+ }
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ tr_mode, /* time recovery mode (see above ) */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * Output position information (to calculate fixed installation
+ * location) only if we are not moving
+ */
+ if (up->moving) {
+ add_mode = 2; /* delete from list */
+ } else {
+ add_mode = 1; /* add to list */
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Output "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ add_mode, /* add/delete sentences from list (1=add, 2=del) */
+ /* must be null */
+ INTERVAL); /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ return (1);
+}
+
+/*
+ * mx4200_ref - Reconfigure unit as a reference station at a known position.
+ */
+static void
+mx4200_ref(
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ double minute, lat, lon, alt;
+ char lats[16], lons[16];
+ char nsc, ewc;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return;
+
+ /*
+ * Set up to output status information in the near future
+ */
+ up->log_time = current_time + SLEEPTIME;
+
+ /*
+ * "007" Control Port Configuration
+ * Stop outputting "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ 2); /* add/delete sentences from list (2=delete) */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Put receiver in fully-constrained 2d nav mode
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 2, /* 2d nav */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver. Solve for time only.
+ * This should improve the time resolution dramatically.
+ */
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ 'K', /* known position: solve for time only */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * "000" Initialization/Mode Control - Part A
+ * Fix to our averaged position.
+ */
+ if (up->central_meridian != NOT_INITIALIZED) {
+ up->avg_lon += up->central_meridian;
+ if (up->avg_lon < -180.0) up->avg_lon += 360.0;
+ if (up->avg_lon > 180.0) up->avg_lon -= 360.0;
+ }
+
+ if (up->avg_lat >= 0.0) {
+ lat = up->avg_lat;
+ nsc = 'N';
+ } else {
+ lat = up->avg_lat * (-1.0);
+ nsc = 'S';
+ }
+ if (up->avg_lon >= 0.0) {
+ lon = up->avg_lon;
+ ewc = 'E';
+ } else {
+ lon = up->avg_lon * (-1.0);
+ ewc = 'W';
+ }
+ alt = up->avg_alt;
+ minute = (lat - (double)(int)lat) * 60.0;
+ sprintf(lats,"%02d%02.4f", (int)lat, minute);
+ minute = (lon - (double)(int)lon) * 60.0;
+ sprintf(lons,"%03d%02.4f", (int)lon, minute);
+
+ mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
+ PMVXG_S_INITMODEA,
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ lats, /* latitude DDMM.MMMM */
+ nsc, /* north/south */
+ lons, /* longitude DDDMM.MMMM */
+ ewc, /* east/west */
+ alt, /* Altitude */
+ 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
+
+ msyslog(LOG_DEBUG,
+ "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
+ lats, nsc, lons, ewc, alt );
+
+}
+
+/*
+ * mx4200_poll - mx4200 watchdog routine
+ */
+static void
+mx4200_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (up->pollcnt > 0) {
+ up->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /*
+ * Request a "000" status message which should trigger a
+ * reconfig
+ */
+ mx4200_send(peer, "%s,%03d",
+ "CDGPQ", /* query from CDU to GPS */
+ PMVXG_D_STATUS); /* label of desired sentence */
+ }
+
+ /*
+ * polled every 64 seconds. Ask mx4200_receive to hand in
+ * a timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+
+ /*
+ * Output receiver status information.
+ */
+ if ((up->log_time > 0) && (current_time > up->log_time)) {
+ up->log_time = 0;
+ /*
+ * Output the following messages once, for debugging.
+ * "004" Mode Data
+ * "523" Time Recovery Parameters
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
+ }
+}
+
+static char char2hex[] = "0123456789ABCDEF";
+
+/*
+ * mx4200_receive - receive gps data
+ */
+static void
+mx4200_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char *cp;
+ int sentence_type;
+ u_char ck;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * If operating mode has been changed, then reinitialize the receiver
+ * before doing anything else.
+ */
+ if ((pp->sloppyclockflag & CLK_FLAG2) !=
+ (up->sloppyclockflag & CLK_FLAG2)) {
+ up->sloppyclockflag = pp->sloppyclockflag;
+ mx4200_debug(peer,
+ "mx4200_receive: mode switch: reset receiver\n");
+ mx4200_config(peer);
+ return;
+ }
+ up->sloppyclockflag = pp->sloppyclockflag;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (pp->lencode == 0)
+ return;
+
+ up->pollcnt = 2;
+ pp->a_lastcode[pp->lencode] = '\0';
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ mx4200_debug(peer, "mx4200_receive: %d %s\n",
+ pp->lencode, pp->a_lastcode);
+
+ /*
+ * The structure of the control port sentences is based on the
+ * NMEA-0183 Standard for interfacing Marine Electronics
+ * Navigation Devices (Version 1.5)
+ *
+ * $PMVXG,XXX, ....................*CK<cr><lf>
+ *
+ * $ Sentence Start Identifier (reserved char)
+ * (Start-of-Sentence Identifier)
+ * P Special ID (Proprietary)
+ * MVX Originator ID (Magnavox)
+ * G Interface ID (GPS)
+ * , Field Delimiters (reserved char)
+ * XXX Sentence Type
+ * ...... Data
+ * * Checksum Field Delimiter (reserved char)
+ * CK Checksum
+ * <cr><lf> Carriage-Return/Line Feed (reserved chars)
+ * (End-of-Sentence Identifier)
+ *
+ * Reject if any important landmarks are missing.
+ */
+ cp = pp->a_lastcode + pp->lencode - 3;
+ if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
+ mx4200_debug(peer, "mx4200_receive: bad format\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Check and discard the checksum
+ */
+ ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
+ if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
+ mx4200_debug(peer, "mx4200_receive: bad checksum\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ *cp = '\0';
+
+ /*
+ * Get the sentence type.
+ */
+ sentence_type = 0;
+ if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
+ mx4200_debug(peer, "mx4200_receive: no sentence\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ cp++;
+ sentence_type = strtol(cp, &cp, 10);
+
+ /*
+ * Process the sentence according to its type.
+ */
+ switch (sentence_type) {
+
+ /*
+ * "000" Status message
+ */
+ case PMVXG_D_STATUS:
+ /*
+ * XXX
+ * Since we configure the receiver to not give us status
+ * messages and since the receiver outputs status messages by
+ * default after being reset to factory defaults when sent the
+ * "$PMVXG,018,C\r\n" message, any status message we get
+ * indicates the reciever needs to be initialized; thus, it is
+ * not necessary to decode the status message.
+ */
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: status: %s\n", cp);
+ }
+ mx4200_debug(peer, "mx4200_receive: reset receiver\n");
+ mx4200_config(peer);
+ break;
+
+ /*
+ * "021" Position, Height, Velocity message,
+ * if we are still averaging our position
+ */
+ case PMVXG_D_PHV:
+ if (!up->known) {
+ /*
+ * Parse the message, calculating our averaged position.
+ */
+ if ((cp = mx4200_parse_p(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
+ return;
+ }
+ mx4200_debug(peer,
+ "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
+ up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
+ /*
+ * Reinitialize as a reference station
+ * if position is well known.
+ */
+ if (current_time > up->clamp_time) {
+ up->known++;
+ mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
+ mx4200_ref(peer);
+ }
+ }
+ break;
+
+ /*
+ * Print to the syslog:
+ * "004" Mode Data
+ * "030" Software Configuration
+ * "523" Time Recovery Parameters Currently in Use
+ */
+ case PMVXG_D_MODEDATA:
+ case PMVXG_D_SOFTCONF:
+ case PMVXG_D_TRECOVUSEAGE:
+
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: multi-record: %s\n", cp);
+ }
+ break;
+
+ /*
+ * "830" Time Recovery Results message
+ */
+ case PMVXG_D_TRECOVOUT:
+
+ /*
+ * Capture the last PPS signal.
+ * Precision timestamp is returned in pp->lastrec
+ */
+ if (mx4200_pps(peer) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: pps failure\n");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+
+ /*
+ * Parse the time recovery message, and keep the info
+ * to print the pretty billboards.
+ */
+ if ((cp = mx4200_parse_t(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Add the new sample to a median filter.
+ */
+ if (!refclock_process(pp)) {
+ mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
+ pp->offset);
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /*
+ * Return offset and dispersion to control module. We use
+ * lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data.
+ */
+ mx4200_debug(peer, "mx4200_receive: process time: ");
+ mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second,
+ prettydate(&pp->lastrec), pp->offset);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+ break;
+
+ /*
+ * Ignore all other sentence types
+ */
+ default:
+ break;
+
+ } /* switch (sentence_type) */
+
+ return;
+}
+
+
+/*
+ * Parse a mx4200 time recovery message. Returns a string if error.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 830=Time Recovery Results
+ * This sentence is output approximately 1 second
+ * preceding the 1PPS output. It indicates the
+ * exact time of the next pulse, whether or not the
+ * time mark will be valid (based on operator-specified
+ * error tolerance), the time to which the pulse is
+ * synchronized, the receiver operating mode,
+ * and the time error of the *last* 1PPS output.
+ * 1 char Time Mark Valid: T=Valid, F=Not Valid
+ * 2 int Year: 1993-
+ * 3 int Month of Year: 1-12
+ * 4 int Day of Month: 1-31
+ * 5 int Time of Day: HH:MM:SS
+ * 6 char Time Synchronization: U=UTC, G=GPS
+ * 7 char Time Recovery Mode: D=Dynamic, S=Static,
+ * K=Known Position, N=No Time Recovery
+ * 8 int Oscillator Offset: The filter's estimate of the oscillator
+ * frequency error, in parts per billion (ppb).
+ * 9 int Time Mark Error: The computed error of the *last* pulse
+ * output, in nanoseconds.
+ * 10 int User Time Bias: Operator specified bias, in nanoseconds
+ * 11 int Leap Second Flag: Indicates that a leap second will
+ * occur. This value is usually zero, except during
+ * the week prior to the leap second occurrence, when
+ * this value will be set to +1 or -1. A value of
+ * +1 indicates that GPS time will be 1 second
+ * further ahead of UTC time.
+ *
+ */
+static char *
+mx4200_parse_t(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ char time_mark_valid, time_sync, op_mode;
+ int sentence_type, valid;
+ int year, day_of_year, month, day_of_month;
+ int hour, minute, second, leapsec;
+ int oscillator_offset, time_mark_error, time_bias;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ leapsec = 0; /* Not all receivers output leap second warnings (!) */
+ sscanf(pp->a_lastcode,
+ "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
+ &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
+ &hour, &minute, &second, &time_sync, &op_mode,
+ &oscillator_offset, &time_mark_error, &time_bias, &leapsec);
+
+ if (sentence_type != PMVXG_D_TRECOVOUT)
+ return ("wrong rec-type");
+
+ switch (time_mark_valid) {
+ case 'T':
+ valid = 1;
+ break;
+ case 'F':
+ valid = 0;
+ break;
+ default:
+ return ("bad pulse-valid");
+ }
+
+ switch (time_sync) {
+ case 'G':
+ return ("synchronized to GPS; should be UTC");
+ case 'U':
+ break; /* UTC -> ok */
+ default:
+ return ("not synchronized to UTC");
+ }
+
+ /*
+ * Check for insane time (allow for possible leap seconds)
+ */
+ if (second > 60 || minute > 59 || hour > 23 ||
+ second < 0 || minute < 0 || hour < 0) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad time %02d:%02d:%02d",
+ hour, minute, second);
+ if (leapsec != 0)
+ mx4200_debug(peer, " (leap %+d\n)", leapsec);
+ mx4200_debug(peer, "\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("bad time");
+ }
+ if ( second == 60 ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second! %02d:%02d:%02d",
+ hour, minute, second);
+ }
+
+ /*
+ * Check for insane date
+ * (Certainly can't be any year before this code was last altered!)
+ */
+ if (day_of_month > 31 || month > 12 ||
+ day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
+ year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return ("bad date");
+ }
+
+ /*
+ * Silly Hack for MX4200:
+ * ASCII message is for *next* 1PPS signal, but we have the
+ * timestamp for the *last* 1PPS signal. So we have to subtract
+ * a second. Discard if we are on a month boundary to avoid
+ * possible leap seconds and leap days.
+ */
+ second--;
+ if (second < 0) {
+ second = 59;
+ minute--;
+ if (minute < 0) {
+ minute = 59;
+ hour--;
+ if (hour < 0) {
+ hour = 23;
+ day_of_month--;
+ if (day_of_month < 1) {
+ return ("sorry, month boundary");
+ }
+ }
+ }
+ }
+
+ /*
+ * Calculate Julian date
+ */
+ if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
+ day_of_year, year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return("invalid julian date");
+ }
+
+ /*
+ * Setup leap second indicator
+ */
+ switch (leapsec) {
+ case 0:
+ pp->leap = LEAP_NOWARNING;
+ break;
+ case 1:
+ pp->leap = LEAP_ADDSECOND;
+ break;
+ case -1:
+ pp->leap = LEAP_DELSECOND;
+ break;
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ }
+
+ /*
+ * Any change to the leap second warning status?
+ */
+ if (leapsec != up->last_leap ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second warning: %d to %d (%d)",
+ up->last_leap, leapsec, pp->leap);
+ }
+ up->last_leap = leapsec;
+
+ /*
+ * Copy time data for billboard monitoring.
+ */
+
+ pp->year = year;
+ pp->day = day_of_year;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp->second = second;
+
+ /*
+ * Toss if sentence is marked invalid
+ */
+ if (!valid || pp->leap == LEAP_NOTINSYNC) {
+ mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("pulse invalid");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Calculate the checksum
+ */
+static u_char
+mx4200_cksum(
+ register char *cp,
+ register int n
+ )
+{
+ register u_char ck;
+
+ for (ck = 0; n-- > 0; cp++)
+ ck ^= *cp;
+ return (ck);
+}
+
+/*
+ * Tables to compute the day of year. Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Calculate the the Julian Day
+ */
+static int
+mx4200_jday(
+ int year,
+ int month,
+ int day_of_month
+ )
+{
+ register int day, i;
+ int leap_year;
+
+ /*
+ * Is this a leap year ?
+ */
+ if (year % 4) {
+ leap_year = 0; /* FALSE */
+ } else {
+ if (year % 100) {
+ leap_year = 1; /* TRUE */
+ } else {
+ if (year % 400) {
+ leap_year = 0; /* FALSE */
+ } else {
+ leap_year = 1; /* TRUE */
+ }
+ }
+ }
+
+ /*
+ * Calculate the Julian Date
+ */
+ day = day_of_month;
+
+ if (leap_year) {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ } else {
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ }
+ return (day);
+}
+
+/*
+ * Parse a mx4200 position/height/velocity sentence.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 021=Position, Height Velocity Data
+ * This sentence gives the receiver position, height,
+ * navigation mode, and velocity north/east.
+ * *This sentence is intended for post-analysis
+ * applications.*
+ * 1 float UTC measurement time (seconds into week)
+ * 2 float WGS-84 Lattitude (degrees, minutes)
+ * 3 char N=North, S=South
+ * 4 float WGS-84 Longitude (degrees, minutes)
+ * 5 char E=East, W=West
+ * 6 float Altitude (meters above mean sea level)
+ * 7 float Geoidal height (meters)
+ * 8 float East velocity (m/sec)
+ * 9 float West Velocity (m/sec)
+ * 10 int Navigation Mode
+ * Mode if navigating:
+ * 1 = Position from remote device
+ * 2 = 2-D position
+ * 3 = 3-D position
+ * 4 = 2-D differential position
+ * 5 = 3-D differential position
+ * 6 = Static
+ * 8 = Position known -- reference station
+ * 9 = Position known -- Navigator
+ * Mode if not navigating:
+ * 51 = Too few satellites
+ * 52 = DOPs too large
+ * 53 = Position STD too large
+ * 54 = Velocity STD too large
+ * 55 = Too many iterations for velocity
+ * 56 = Too many iterations for position
+ * 57 = 3 sat startup failed
+ * 58 = Command abort
+ */
+static char *
+mx4200_parse_p(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type, mode;
+ double mtime, lat, lon, alt, geoid, vele, veln;
+ char north_south, east_west;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return ("mobile platform - no pos!");
+
+ sscanf ( pp->a_lastcode,
+ "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
+ &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
+ &alt, &geoid, &vele, &veln, &mode);
+
+ /* Sentence type */
+ if (sentence_type != PMVXG_D_PHV)
+ return ("wrong rec-type");
+
+ /*
+ * return if not navigating
+ */
+ if (mode > 10)
+ return ("not navigating");
+ if (mode != 3 && mode != 5)
+ return ("not navigating in 3D");
+
+ /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
+ if (lat < 0.0) return ("negative latitude");
+ if (lat > 9000.0) lat = 9000.0;
+ lat *= 0.01;
+ lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
+
+ /* North/South */
+ switch (north_south) {
+ case 'N':
+ break;
+ case 'S':
+ lat *= -1.0;
+ break;
+ default:
+ return ("invalid north/south indicator");
+ }
+
+ /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
+ if (lon < 0.0) return ("negative longitude");
+ if (lon > 180.0) lon = 180.0;
+ lon *= 0.01;
+ lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
+
+ /* East/West */
+ switch (east_west) {
+ case 'E':
+ break;
+ case 'W':
+ lon *= -1.0;
+ break;
+ default:
+ return ("invalid east/west indicator");
+ }
+
+ /*
+ * Normalize longitude to near 0 degrees.
+ * Assume all data are clustered around first reading.
+ */
+ if (up->central_meridian == NOT_INITIALIZED) {
+ up->central_meridian = lon;
+ mx4200_debug(peer,
+ "mx4200_receive: central meridian = %.9f \n",
+ up->central_meridian);
+ }
+ lon -= up->central_meridian;
+ if (lon < -180.0) lon += 360.0;
+ if (lon > 180.0) lon -= 360.0;
+
+ /*
+ * Calculate running averages
+ */
+
+ up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
+ up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
+ up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
+
+ up->N_fixes += 1.0;
+
+ up->avg_lon /= up->N_fixes;
+ up->avg_lat /= up->N_fixes;
+ up->avg_alt /= up->N_fixes;
+
+ mx4200_debug(peer,
+ "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
+ up->N_fixes, lat, lon, alt, up->central_meridian);
+
+ return (NULL);
+}
+
+/*
+ * Parse a mx4200 Status sentence
+ * Parse a mx4200 Mode Data sentence
+ * Parse a mx4200 Software Configuration sentence
+ * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
+ * (used only for logging raw strings)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,000,XXX,XX,X,HHMM,X
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 000=Status.
+ * Returns status of the receiver to the controller.
+ * 1 Current Receiver Status:
+ * ACQ = Satellite re-acquisition
+ * ALT = Constellation selection
+ * COR = Providing corrections (for reference stations only)
+ * IAC = Initial acquisition
+ * IDL = Idle, no satellites
+ * NAV = Navigation
+ * STS = Search the Sky (no almanac available)
+ * TRK = Tracking
+ * 2 Number of satellites that should be visible
+ * 3 Number of satellites being tracked
+ * 4 Time since last navigation status if not currently navigating
+ * (hours, minutes)
+ * 5 Initialization status:
+ * 0 = Waiting for initialization parameters
+ * 1 = Initialization completed
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 004=Software Configuration.
+ * Defines the navigation mode and criteria for
+ * acceptable navigation for the receiver.
+ * 1 Constrain Altitude Mode:
+ * 0 = Auto. Constrain altitude (2-D solution) and use
+ * manual altitude input when 3 sats avalable. Do
+ * not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 1 = Always constrain altitude (2-D solution).
+ * 2 = Never constrain altitude (3-D solution).
+ * 3 = Coast. Constrain altitude (2-D solution) and use
+ * last GPS altitude calculation when 3 sats avalable.
+ * Do not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 2 Altitude Reference: (always 0 for MX4200)
+ * 0 = Ellipsoid
+ * 1 = Geoid (MSL)
+ * 3 Differential Navigation Control:
+ * 0 = Disabled
+ * 1 = Enabled
+ * 4 Horizontal Acceleration Constant (m/sec**2)
+ * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
+ * 6 Tracking Elevation Limit (degrees)
+ * 7 HDOP Limit
+ * 8 VDOP Limit
+ * 9 Time Output Mode:
+ * U = UTC
+ * L = Local time
+ * 10 Local Time Offset (minutes) (absent on MX4200)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,030,NNNN,FFF
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 030=Software Configuration.
+ * This sentence contains the navigation processor
+ * and baseband firmware version numbers.
+ * 1 Nav Processor Version Number
+ * 2 Baseband Firmware Version Number
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 523=Time Recovery Parameters Currently in Use.
+ * This sentence contains the configuration of the
+ * time recovery feature of the receiver.
+ * 1 Time Recovery Mode:
+ * D = Dynamic; solve for position and time while moving
+ * S = Static; solve for position and time while stationary
+ * K = Known position input, solve for time only
+ * N = No time recovery
+ * 2 Time Synchronization:
+ * U = UTC time
+ * G = GPS time
+ * 3 Time Mark Mode:
+ * A = Always output a time pulse
+ * V = Only output time pulse if time is valid (as determined
+ * by Maximum Time Error)
+ * 4 Maximum Time Error - the maximum error (in nanoseconds) for
+ * which a time mark will be considered valid.
+ * 5 User Time Bias - external bias in nanoseconds
+ * 6 Time Message Control:
+ * 0 = Do not output the time recovery message
+ * 1 = Output the time recovery message (record 830) to
+ * Control port
+ * 2 = Output the time recovery message (record 830) to
+ * Equipment port
+ * 7 Reserved
+ * 8 Position Known PRN (absent on MX 4200)
+ *
+ */
+static char *
+mx4200_parse_s(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
+
+ /* Sentence type */
+ switch (sentence_type) {
+
+ case PMVXG_D_STATUS:
+ msyslog(LOG_DEBUG,
+ "mx4200: status: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_MODEDATA:
+ msyslog(LOG_DEBUG,
+ "mx4200: mode data: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_SOFTCONF:
+ msyslog(LOG_DEBUG,
+ "mx4200: firmware configuration: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_TRECOVUSEAGE:
+ msyslog(LOG_DEBUG,
+ "mx4200: time recovery parms: %s", pp->a_lastcode);
+ break;
+ default:
+ return ("wrong rec-type");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Process a PPS signal, placing a timestamp in pp->lastrec.
+ */
+static int
+mx4200_pps(
+ struct peer *peer
+ )
+{
+ int temp_serial;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ struct timespec timeout;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * Grab the timestamp of the PPS signal.
+ */
+ temp_serial = up->pps_i.assert_sequence;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
+ &timeout) < 0) {
+ mx4200_debug(peer,
+ "mx4200_pps: time_pps_fetch: serial=%ul, %s\n",
+ (unsigned long)up->pps_i.assert_sequence, strerror(errno));
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ if (temp_serial == up->pps_i.assert_sequence) {
+ mx4200_debug(peer,
+ "mx4200_pps: assert_sequence serial not incrementing: %ul\n",
+ (unsigned long)up->pps_i.assert_sequence);
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ /*
+ * Check pps serial number against last one
+ */
+ if (up->lastserial + 1 != up->pps_i.assert_sequence &&
+ up->lastserial != 0) {
+ if (up->pps_i.assert_sequence == up->lastserial) {
+ mx4200_debug(peer, "mx4200_pps: no new pps event\n");
+ } else {
+ mx4200_debug(peer, "mx4200_pps: missed %ul pps events\n",
+ up->pps_i.assert_sequence - up->lastserial - 1UL);
+ }
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ up->lastserial = up->pps_i.assert_sequence;
+
+ /*
+ * Return the timestamp in pp->lastrec
+ */
+
+ pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
+ (u_int32) JAN_1970;
+ pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
+ 4.2949672960) + 0.5;
+
+ return(0);
+}
+
+/*
+ * mx4200_debug - print debug messages
+ */
+#if defined(__STDC__)
+static void
+mx4200_debug(struct peer *peer, char *fmt, ...)
+#else
+static void
+mx4200_debug(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+#endif /* __STDC__ */
+{
+#ifdef DEBUG
+ va_list ap;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ if (debug) {
+
+#if defined(__STDC__)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ vprintf(fmt, ap);
+
+ va_end(ap);
+ }
+#endif
+}
+
+/*
+ * Send a character string to the receiver. Checksum is appended here.
+ */
+#if defined(__STDC__)
+static void
+mx4200_send(struct peer *peer, char *fmt, ...)
+#else
+static void
+mx4200_send(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ register char *cp;
+ register int n, m;
+ va_list ap;
+ char buf[1024];
+ u_char ck;
+
+#if defined(__STDC__)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ cp = buf;
+ *cp++ = '$';
+ n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
+ ck = mx4200_cksum(cp, n);
+ cp += n;
+ ++n;
+ n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
+
+ m = write(pp->io.fd, buf, (unsigned)n);
+ if (m < 0)
+ msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
+ mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
+ va_end(ap);
+}
+
+#else
+int refclock_mx4200_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_neoclock4x.c b/contrib/ntp/ntpd/refclock_neoclock4x.c
new file mode 100644
index 0000000..374c81a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_neoclock4x.c
@@ -0,0 +1,1113 @@
+/*
+ *
+ * Refclock_neoclock4x.c
+ * - NeoClock4X driver for DCF77 or FIA Timecode
+ *
+ * Date: 2006-01-11 v1.15
+ *
+ * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
+ * for details about the NeoClock4X device
+ *
+ * Copyright (C) 2002-2004 by Linum Software GmbH <neoclock4x@linum.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# ifndef __QNXNTO__
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * NTP version 4.20 change the pp->msec field to pp->nsec.
+ * To allow to support older ntp versions with this sourcefile
+ * you can define NTP_PRE_420 to allow this driver to compile
+ * with ntp version back to 4.1.2.
+ *
+ */
+#if 0
+#define NTP_PRE_420
+#endif
+
+/*
+ * If you want the driver for whatever reason to not use
+ * the TX line to send anything to your NeoClock4X
+ * device you must tell the NTP refclock driver which
+ * firmware you NeoClock4X device uses.
+ *
+ * If you want to enable this feature change the "#if 0"
+ * line to "#if 1" and make sure that the defined firmware
+ * matches the firmware off your NeoClock4X receiver!
+ *
+ */
+
+#if 0
+#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
+#endif
+
+/* at this time only firmware version A is known */
+#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
+
+#define NEOCLOCK4X_TIMECODELEN 37
+
+#define NEOCLOCK4X_OFFSET_SERIAL 3
+#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
+#define NEOCLOCK4X_OFFSET_DAY 12
+#define NEOCLOCK4X_OFFSET_MONTH 14
+#define NEOCLOCK4X_OFFSET_YEAR 16
+#define NEOCLOCK4X_OFFSET_HOUR 18
+#define NEOCLOCK4X_OFFSET_MINUTE 20
+#define NEOCLOCK4X_OFFSET_SECOND 22
+#define NEOCLOCK4X_OFFSET_HSEC 24
+#define NEOCLOCK4X_OFFSET_DOW 26
+#define NEOCLOCK4X_OFFSET_TIMESOURCE 28
+#define NEOCLOCK4X_OFFSET_DSTSTATUS 29
+#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
+#define NEOCLOCK4X_OFFSET_ANTENNA1 31
+#define NEOCLOCK4X_OFFSET_ANTENNA2 33
+#define NEOCLOCK4X_OFFSET_CRC 35
+
+#define NEOCLOCK4X_DRIVER_VERSION "1.15 (2006-01-11)"
+
+#define NSEC_TO_MILLI 1000000
+
+struct neoclock4x_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int recvnow;
+
+ char firmware[80];
+ char firmwaretag;
+ char serial[7];
+ char radiosignal[4];
+ char timesource;
+ char dststatus;
+ char quarzstatus;
+ int antenna1;
+ int antenna2;
+ int utc_year;
+ int utc_month;
+ int utc_day;
+ int utc_hour;
+ int utc_minute;
+ int utc_second;
+ int utc_msec;
+};
+
+static int neoclock4x_start P((int, struct peer *));
+static void neoclock4x_shutdown P((int, struct peer *));
+static void neoclock4x_receive P((struct recvbuf *));
+static void neoclock4x_poll P((int, struct peer *));
+static void neoclock4x_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+
+static int neol_atoi_len P((const char str[], int *, int));
+static int neol_hexatoi_len P((const char str[], int *, int));
+static void neol_jdn_to_ymd P((unsigned long, int *, int *, int *));
+static void neol_localtime P((unsigned long, int* , int*, int*, int*, int*, int*));
+static unsigned long neol_mktime P((int, int, int, int, int, int));
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int neol_query_firmware P((int, int, char *, int));
+static int neol_check_firmware P((int, const char*, char *));
+#endif
+
+struct refclock refclock_neoclock4x = {
+ neoclock4x_start, /* start up driver */
+ neoclock4x_shutdown, /* shut down driver */
+ neoclock4x_poll, /* transmit poll message */
+ neoclock4x_control,
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+static int
+neoclock4x_start(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char dev[20];
+ int sl232;
+#if defined(HAVE_TERMIOS)
+ struct termios termsettings;
+#endif
+#if !defined(NEOCLOCK4X_FIRMWARE)
+ int tries;
+#endif
+
+ (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
+
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(dev, B2400, LDISC_STD);
+ if(fd <= 0)
+ {
+ return (0);
+ }
+
+#if defined(HAVE_TERMIOS)
+
+#if 1
+ if(tcgetattr(fd, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
+ termsettings.c_oflag = 0;
+ termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
+ (void)cfsetispeed(&termsettings, (u_int)B2400);
+ (void)cfsetospeed(&termsettings, (u_int)B2400);
+
+ if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+#else
+ if(tcgetattr(fd, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#endif
+
+#elif defined(HAVE_SYSV_TTYS)
+ if(ioctl(fd, TCGETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(ioctl(fd, TCSETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#ifdef TIOCM_RTS
+ sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#else
+ sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#endif
+ if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
+ unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+ up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
+ if(!(up))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct neoclock4x_unit));
+ pp = peer->procptr;
+ pp->clockdesc = "NeoClock4X";
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = neoclock4x_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ /*
+ * no fudge time is given by user!
+ * use 169.583333 ms to compensate the serial line delay
+ * formula is:
+ * 2400 Baud / 11 bit = 218.18 charaters per second
+ * (NeoClock4X timecode len)
+ */
+ pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = -10;
+ peer->burst = NSTAGE;
+ memcpy((char *)&pp->refid, "neol", 4);
+
+ up->leap_status = 0;
+ up->unit = unit;
+ strcpy(up->firmware, "?");
+ up->firmwaretag = '?';
+ strcpy(up->serial, "?");
+ strcpy(up->radiosignal, "?");
+ up->timesource = '?';
+ up->dststatus = '?';
+ up->quarzstatus = '?';
+ up->antenna1 = -1;
+ up->antenna2 = -1;
+ up->utc_year = 0;
+ up->utc_month = 0;
+ up->utc_day = 0;
+ up->utc_hour = 0;
+ up->utc_minute = 0;
+ up->utc_second = 0;
+ up->utc_msec = 0;
+
+#if defined(NEOCLOCK4X_FIRMWARE)
+#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
+ strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
+ up->firmwaretag = 'A';
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
+ unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+#endif
+#else
+ for(tries=0; tries < 5; tries++)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
+ /* wait 3 seconds for receiver to power up */
+ sleep(3);
+ if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
+ {
+ break;
+ }
+ }
+
+ /* can I handle this firmware version? */
+ if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
+ {
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+#endif
+
+ if(!io_addclock(&pp->io))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
+
+ return (1);
+}
+
+static void
+neoclock4x_shutdown(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int sl232;
+
+ if(NULL != peer)
+ {
+ pp = peer->procptr;
+ if(pp != NULL)
+ {
+ up = (struct neoclock4x_unit *)pp->unitptr;
+ if(up != NULL)
+ {
+ if(-1 != pp->io.fd)
+ {
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
+ unit);
+ }
+#ifdef TIOCM_RTS
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
+#else
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
+#endif
+ if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
+ unit);
+ }
+#endif
+ io_closeclock(&pp->io);
+ }
+ free(up);
+ pp->unitptr = NULL;
+ }
+ }
+ }
+
+ msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
+}
+
+static void
+neoclock4x_receive(struct recvbuf *rbufp)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ unsigned long calc_utc;
+ int day;
+ int month; /* ddd conversion */
+ int c;
+ int dsec;
+ unsigned char calc_chksum;
+ int recv_chksum;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct neoclock4x_unit *)pp->unitptr;
+
+ /* wait till poll interval is reached */
+ if(0 == up->recvnow)
+ return;
+
+ /* reset poll interval flag */
+ up->recvnow = 0;
+
+ /* read last received timecode */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+ pp->leap = LEAP_NOWARNING;
+
+ if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
+ up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
+
+ /* calculate checksum */
+ calc_chksum = 0;
+ for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
+ {
+ calc_chksum += pp->a_lastcode[c];
+ }
+ if(recv_chksum != calc_chksum)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* Allow synchronization even is quartz clock is
+ * never initialized.
+ * WARNING: This is dangerous!
+ */
+ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
+ up->unit, pp->a_lastcode);
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+ }
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /*
+ * If NeoClock4X is not synchronized to a radio clock
+ * check if we're allowed to synchronize with the quartz
+ * clock.
+ */
+ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('A' != up->timesource)
+ {
+ /* not allowed to sync with quartz clock */
+ if(0==(pp->sloppyclockflag & CLK_FLAG1))
+ {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+ }
+
+ /* this should only used when first install is done */
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /* 123456789012345678901234567890123456789012345 */
+ /* S/N123456DCF1004021010001202ASX1213CR\r\n */
+
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
+#if defined(NTP_PRE_420)
+ pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
+#else
+ pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
+#endif
+
+ memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
+ up->radiosignal[3] = 0;
+ memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
+ up->serial[6] = 0;
+ up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+
+ /* Year-2000 check not needed anymore. Same problem
+ * will arise at 2099 but what should we do...?
+ *
+ * wrap 2-digit date into 4-digit
+ *
+ * if(pp->year < YEAR_PIVOT)
+ * {
+ * pp->year += 100;
+ * }
+ */
+ pp->year += 2000;
+
+ /* adjust NeoClock4X local time to UTC */
+ calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
+ calc_utc -= 3600;
+ /* adjust NeoClock4X daylight saving time if needed */
+ if('S' == up->dststatus)
+ calc_utc -= 3600;
+ neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
+
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->leap = 0;
+
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
+ up->unit,
+ pp->year, month, day,
+ pp->hour, pp->minute, pp->second,
+#if defined(NTP_PRE_420)
+ pp->msec
+#else
+ pp->nsec/NSEC_TO_MILLI
+#endif
+ );
+ }
+
+ up->utc_year = pp->year;
+ up->utc_month = month;
+ up->utc_day = day;
+ up->utc_hour = pp->hour;
+ up->utc_minute = pp->minute;
+ up->utc_second = pp->second;
+#if defined(NTP_PRE_420)
+ up->utc_msec = pp->msec;
+#else
+ up->utc_msec = pp->nsec/NSEC_TO_MILLI;
+#endif
+
+ if(!refclock_process(pp))
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ refclock_receive(peer);
+
+ /* report good status */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+static void
+neoclock4x_poll(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct neoclock4x_unit *)pp->unitptr;
+
+ pp->polls++;
+ up->recvnow = 1;
+}
+
+static void
+neoclock4x_control(int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ if(NULL == peer)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ pp = peer->procptr;
+ if(NULL == pp)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ up = (struct neoclock4x_unit *)pp->unitptr;
+ if(NULL == up)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ if(NULL != in)
+ {
+ /* check to see if a user supplied time offset is given */
+ if(in->haveflags & CLK_HAVETIME1)
+ {
+ pp->fudgetime1 = in->fudgetime1;
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
+ unit, pp->fudgetime1);
+ }
+
+ /* notify */
+ if(pp->sloppyclockflag & CLK_FLAG1)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
+ }
+ }
+
+ if(NULL != out)
+ {
+ char *tt;
+ char tmpbuf[80];
+
+ out->kv_list = (struct ctl_var *)0;
+ out->type = REFCLK_NEOCLOCK4X;
+
+ snprintf(tmpbuf, sizeof(tmpbuf)-1,
+ "%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ up->utc_year, up->utc_month, up->utc_day,
+ up->utc_hour, up->utc_minute, up->utc_second,
+ up->utc_msec);
+ tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
+ snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
+
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('A' == up->timesource)
+ snprintf(tt, 39, "timesource=\"radio\"");
+ else if('C' == up->timesource)
+ snprintf(tt, 39, "timesource=\"quartz\"");
+ else
+ snprintf(tt, 39, "timesource=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('I' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"synchronized\"");
+ else if('X' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
+ else
+ snprintf(tt, 39, "quartzstatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('S' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"summer\"");
+ else if('W' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"winter\"");
+ else
+ snprintf(tt, 39, "dststatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
+ }
+}
+
+static int
+neol_hexatoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int hexdigit;
+ int i;
+ int n = 0;
+
+ for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
+ {
+ hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
+ n = 16 * n + hexdigit;
+ }
+ *result = n;
+ return (n);
+}
+
+static int
+neol_atoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int digit;
+ int i;
+ int n = 0;
+
+ for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
+ {
+ digit = str[i] - '0';
+ n = 10 * n + digit;
+ }
+ *result = n;
+ return (n);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static unsigned long
+neol_mktime(int year,
+ int mon,
+ int day,
+ int hour,
+ int min,
+ int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+static void
+neol_localtime(unsigned long utc,
+ int* year,
+ int* month,
+ int* day,
+ int* hour,
+ int* min,
+ int* sec)
+{
+ *sec = utc % 60;
+ utc /= 60;
+ *min = utc % 60;
+ utc /= 60;
+ *hour = utc % 24;
+ utc /= 24;
+
+ /* JDN Date 1/1/1970 */
+ neol_jdn_to_ymd(utc + 2440588L, year, month, day);
+}
+
+static void
+neol_jdn_to_ymd(unsigned long jdn,
+ int *yy,
+ int *mm,
+ int *dd)
+{
+ unsigned long x, z, m, d, y;
+ unsigned long daysPer400Years = 146097UL;
+ unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
+
+ x = jdn + 68569UL;
+ z = 4UL * x / daysPer400Years;
+ x = x - (daysPer400Years * z + 3UL) / 4UL;
+ y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
+ x = x - 1461UL * y / 4UL + 31UL;
+ m = 80UL * x / 2447UL;
+ d = x - 2447UL * m / 80UL;
+ x = m / 11UL;
+ m = m + 2UL - 12UL * x;
+ y = 100UL * (z - 49UL) + y + x;
+
+ *yy = (int)y;
+ *mm = (int)m;
+ *dd = (int)d;
+}
+
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int
+neol_query_firmware(int fd,
+ int unit,
+ char *firmware,
+ int maxlen)
+{
+ char tmpbuf[256];
+ int len;
+ int lastsearch;
+ unsigned char c;
+ int last_c_was_crlf;
+ int last_crlf_conv_len;
+ int init;
+ int read_errors;
+ int flag = 0;
+ int chars_read;
+
+ /* wait a little bit */
+ sleep(1);
+ if(-1 != write(fd, "V", 1))
+ {
+ /* wait a little bit */
+ sleep(1);
+ memset(tmpbuf, 0x00, sizeof(tmpbuf));
+
+ len = 0;
+ lastsearch = 0;
+ last_c_was_crlf = 0;
+ last_crlf_conv_len = 0;
+ init = 1;
+ read_errors = 0;
+ chars_read = 0;
+ for(;;)
+ {
+ if(read_errors > 5)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
+ strcpy(tmpbuf, "unknown due to timeout");
+ break;
+ }
+ if(chars_read > 500)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
+ strcpy(tmpbuf, "unknown due to garbage input");
+ break;
+ }
+ if(-1 == read(fd, &c, 1))
+ {
+ if(EAGAIN != errno)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
+ read_errors++;
+ }
+ else
+ {
+ sleep(1);
+ }
+ continue;
+ }
+ else
+ {
+ chars_read++;
+ }
+
+ if(init)
+ {
+ if(0xA9 != c) /* wait for (c) char in input stream */
+ continue;
+
+ strcpy(tmpbuf, "(c)");
+ len = 3;
+ init = 0;
+ continue;
+ }
+
+#if 0
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
+#endif
+
+ if(0x0A == c || 0x0D == c)
+ {
+ if(last_c_was_crlf)
+ {
+ char *ptr;
+ ptr = strstr(&tmpbuf[lastsearch], "S/N");
+ if(NULL != ptr)
+ {
+ tmpbuf[last_crlf_conv_len] = 0;
+ flag = 1;
+ break;
+ }
+ /* convert \n to / */
+ last_crlf_conv_len = len;
+ tmpbuf[len++] = ' ';
+ tmpbuf[len++] = '/';
+ tmpbuf[len++] = ' ';
+ lastsearch = len;
+ }
+ last_c_was_crlf = 1;
+ }
+ else
+ {
+ last_c_was_crlf = 0;
+ if(0x00 != c)
+ tmpbuf[len++] = (char) c;
+ }
+ tmpbuf[len] = '\0';
+ if(len > sizeof(tmpbuf)-5)
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
+ strcpy(tmpbuf, "unknown error");
+ }
+ strncpy(firmware, tmpbuf, maxlen);
+ firmware[maxlen] = '\0';
+
+ if(flag)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
+ }
+
+ return (flag);
+}
+
+static int
+neol_check_firmware(int unit,
+ const char *firmware,
+ char *firmwaretag)
+{
+ char *ptr;
+
+ *firmwaretag = '?';
+ ptr = strstr(firmware, "NDF:");
+ if(NULL != ptr)
+ {
+ if((strlen(firmware) - strlen(ptr)) >= 7)
+ {
+ if(':' == *(ptr+5) && '*' == *(ptr+6))
+ *firmwaretag = *(ptr+4);
+ }
+ }
+
+ if('A' != *firmwaretag)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
+ return (0);
+ }
+
+ return (1);
+}
+#endif
+
+#else
+int refclock_neoclock4x_bs;
+#endif /* REFCLOCK */
+
+/*
+ * History:
+ * refclock_neoclock4x.c
+ *
+ * 2002/04/27 cjh
+ * Revision 1.0 first release
+ *
+ * 2002/07/15 cjh
+ * preparing for bitkeeper reposity
+ *
+ * 2002/09/09 cjh
+ * Revision 1.1
+ * - don't assume sprintf returns an int anymore
+ * - change the way the firmware version is read
+ * - some customers would like to put a device called
+ * data diode to the NeoClock4X device to disable
+ * the write line. We need to now the firmware
+ * version even in this case. We made a compile time
+ * definition in this case. The code was previously
+ * only available on request.
+ *
+ * 2003/01/08 cjh
+ * Revision 1.11
+ * - changing xprinf to xnprinf to avoid buffer overflows
+ * - change some logic
+ * - fixed memory leaks if drivers can't initialize
+ *
+ * 2003/01/10 cjh
+ * Revision 1.12
+ * - replaced ldiv
+ * - add code to support FreeBSD
+ *
+ * 2003/07/07 cjh
+ * Revision 1.13
+ * - fix reporting of clock status
+ * changes. previously a bad clock
+ * status was never reset.
+ *
+ * 2004/04/07 cjh
+ * Revision 1.14
+ * - open serial port in a way
+ * AIX and some other OS can
+ * handle much better
+ *
+ * 2006/01/11 cjh
+ * Revision 1.15
+ * - remove some unsued #ifdefs
+ * - fix nsec calculation, closes #499
+ *
+ */
diff --git a/contrib/ntp/ntpd/refclock_nmea.c b/contrib/ntp/ntpd/refclock_nmea.c
new file mode 100644
index 0000000..5b124cb
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_nmea.c
@@ -0,0 +1,762 @@
+/*
+ * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
+ * Michael Petry Jun 20, 1994
+ * based on refclock_heathn.c
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_NMEA)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver supports the NMEA GPS Receiver with
+ *
+ * Protype was refclock_trak.c, Thanks a lot.
+ *
+ * The receiver used spits out the NMEA sentences for boat navigation.
+ * And you thought it was an information superhighway. Try a raging river
+ * filled with rapids and whirlpools that rip away your data and warp time.
+ *
+ * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
+ * On startup if initialization of the PPSAPI fails, it will fall back
+ * to the "normal" timestamps.
+ *
+ * The PPSAPI part of the driver understands fudge flag2 and flag3. If
+ * flag2 is set, it will use the clear edge of the pulse. If flag3 is
+ * set, kernel hardpps is enabled.
+ *
+ * GPS sentences other than RMC (the default) may be enabled by setting
+ * the relevent bits of 'mode' in the server configuration line
+ * server 127.127.20.x mode X
+ *
+ * bit 0 - enables RMC (1)
+ * bit 1 - enables GGA (2)
+ * bit 2 - enables GLL (4)
+ * multiple sentences may be selected
+ */
+
+/*
+ * Definitions
+ */
+#ifdef SYS_WINNT
+# define DEVICE "COM%d:" /* COM 1 - 3 supported */
+#else
+# define DEVICE "/dev/gps%d" /* name of radio device */
+#endif
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+#define NANOSECOND 1000000000 /* one second (ns) */
+#define RANGEGATE 500000 /* range gate (ns) */
+
+#define LENNMEA 75 /* min timecode length */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct nmeaunit {
+ int pollcnt; /* poll message counter */
+ int polled; /* Hand in a sample? */
+ l_fp tstamp; /* timestamp of last poll */
+#ifdef HAVE_PPSAPI
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+#endif /* HAVE_PPSAPI */
+};
+
+/*
+ * Function prototypes
+ */
+static int nmea_start P((int, struct peer *));
+static void nmea_shutdown P((int, struct peer *));
+#ifdef HAVE_PPSAPI
+static void nmea_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int nmea_ppsapi P((struct peer *, int, int));
+static int nmea_pps P((struct nmeaunit *, l_fp *));
+#endif /* HAVE_PPSAPI */
+static void nmea_receive P((struct recvbuf *));
+static void nmea_poll P((int, struct peer *));
+static void gps_send P((int, const char *, struct peer *));
+static char *field_parse P((char *, int));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_nmea = {
+ nmea_start, /* start up driver */
+ nmea_shutdown, /* shut down driver */
+ nmea_poll, /* transmit poll message */
+#ifdef HAVE_PPSAPI
+ nmea_control, /* fudge control */
+#else
+ noentry, /* fudge control */
+#endif /* HAVE_PPSAPI */
+ noentry, /* initialize driver */
+ noentry, /* buginfo */
+ NOFLAGS /* not used */
+};
+
+/*
+ * nmea_start - open the GPS devices and initialize data for processing
+ */
+static int
+nmea_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd <= 0) {
+#ifdef HAVE_READLINK
+ /* nmead support added by Jon Miner (cp_n18@yahoo.com)
+ *
+ * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
+ * for information about nmead
+ *
+ * To use this, you need to create a link from /dev/gpsX to
+ * the server:port where nmead is running. Something like this:
+ *
+ * ln -s server:port /dev/gps1
+ */
+ char buffer[80];
+ char *nmea_host;
+ int nmea_port;
+ int len;
+ struct hostent *he;
+ struct protoent *p;
+ struct sockaddr_in so_addr;
+
+ if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
+ return(0);
+ buffer[len] = 0;
+
+ if ((nmea_host = strtok(buffer,":")) == NULL)
+ return(0);
+
+ nmea_port = atoi(strtok(NULL,":"));
+
+ if ((he = gethostbyname(nmea_host)) == NULL)
+ return(0);
+ if ((p = getprotobyname("ip")) == NULL)
+ return(0);
+ so_addr.sin_family = AF_INET;
+ so_addr.sin_port = htons(nmea_port);
+ so_addr.sin_addr = *((struct in_addr *) he->h_addr);
+
+ if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1)
+ return(0);
+ if (connect(fd,(struct sockaddr *)&so_addr,SOCKLEN(&so_addr)) == -1) {
+ close(fd);
+ return (0);
+ }
+#else
+ return (0);
+#endif
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct nmeaunit *)emalloc(sizeof(struct nmeaunit));
+ if (up == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct nmeaunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = nmea_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
+
+#ifdef HAVE_PPSAPI
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_create failed: %m");
+ return (1);
+ }
+ return(nmea_ppsapi(peer, 0, 0));
+#else
+ return (1);
+#endif /* HAVE_PPSAPI */
+}
+
+/*
+ * nmea_shutdown - shut down a GPS clock
+ */
+static void
+nmea_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+#ifdef HAVE_PPSAPI
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+#endif /* HAVE_PPSAPI */
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * nmea_control - fudge control
+ */
+static void
+nmea_control(
+ int unit, /* unit (not used */
+ struct refclockstat *in, /* input parameters (not uded) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+nmea_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct nmeaunit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+nmea_pps(
+ struct nmeaunit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ *tsptr = tstmp;
+ return (1);
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * nmea_receive - receive data from the serial interface
+ */
+static void
+nmea_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int month, day;
+ int i;
+ char *cp, *dp;
+ int cmdtype;
+ /* Use these variables to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ rd_lencode = (u_short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+
+ /*
+ * There is a case that a <CR><LF> gives back a "blank" line
+ */
+ if (rd_lencode == 0)
+ return;
+
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: gpsread %d %s\n", rd_lencode,
+ rd_lastcode);
+#endif
+
+ /*
+ * We check the timecode format and decode its contents. The
+ * we only care about a few of them. The most important being
+ * the $GPRMC format
+ * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
+ * For Magellan (ColorTrak) GLL probably datum (order of sentences)
+ * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
+ * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
+ * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
+ * $GPRMB,...
+ * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
+ * $GPAPB,...
+ * $GPGSA,...
+ * $GPGSV,...
+ * $GPGSV,...
+ */
+#define GPXXX 0
+#define GPRMC 1
+#define GPGGA 2
+#define GPGLL 4
+ cp = rd_lastcode;
+ cmdtype=0;
+ if(strncmp(cp,"$GPRMC",6)==0) {
+ cmdtype=GPRMC;
+ }
+ else if(strncmp(cp,"$GPGGA",6)==0) {
+ cmdtype=GPGGA;
+ }
+ else if(strncmp(cp,"$GPGLL",6)==0) {
+ cmdtype=GPGLL;
+ }
+ else if(strncmp(cp,"$GPXXX",6)==0) {
+ cmdtype=GPXXX;
+ }
+ else
+ return;
+
+
+ /* See if I want to process this message type */
+ if ( ((peer->ttl == 0) && (cmdtype != GPRMC))
+ || ((peer->ttl != 0) && !(cmdtype & peer->ttl)) )
+ return;
+
+ pp->lencode = rd_lencode;
+ strcpy(pp->a_lastcode,rd_lastcode);
+ cp = pp->a_lastcode;
+
+ pp->lastrec = up->tstamp = rd_tmp;
+ up->pollcnt = 2;
+
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+
+ /* Grab field depending on clock string type */
+ switch( cmdtype ) {
+ case GPRMC:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,2);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,1);
+ break;
+
+
+ case GPGGA:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,6);
+ if( dp[0] == '0')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,1);
+ break;
+
+
+ case GPGLL:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,6);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,5);
+ break;
+
+
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ /*
+ * Check time code format of NMEA
+ */
+
+ if( !isdigit((int)dp[0]) ||
+ !isdigit((int)dp[1]) ||
+ !isdigit((int)dp[2]) ||
+ !isdigit((int)dp[3]) ||
+ !isdigit((int)dp[4]) ||
+ !isdigit((int)dp[5])
+ ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ /*
+ * Convert time and check values.
+ */
+ pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
+ pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
+ pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
+ /* Default to 0 milliseconds, if decimal convert milliseconds in
+ one, two or three digits
+ */
+ pp->nsec = 0;
+ if (dp[6] == '.') {
+ if (isdigit((int)dp[7])) {
+ pp->nsec = (dp[7] - '0') * 100000000;
+ if (isdigit((int)dp[8])) {
+ pp->nsec += (dp[8] - '0') * 10000000;
+ if (isdigit((int)dp[9])) {
+ pp->nsec += (dp[9] - '0') * 1000000;
+ }
+ }
+ }
+ }
+
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59
+ || pp->nsec > 1000000000) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+
+ /*
+ * Convert date and check values.
+ */
+ if (cmdtype==GPRMC) {
+ dp = field_parse(cp,9);
+ day = dp[0] - '0';
+ day = (day * 10) + dp[1] - '0';
+ month = dp[2] - '0';
+ month = (month * 10) + dp[3] - '0';
+ pp->year = dp[4] - '0';
+ pp->year = (pp->year * 10) + dp[5] - '0';
+ }
+ else {
+ /* only time */
+ time_t tt = time(NULL);
+ struct tm * t = gmtime(&tt);
+ day = t->tm_mday;
+ month = t->tm_mon + 1;
+ pp->year= t->tm_year;
+ }
+
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
+ /* good thing that 2000 is a leap year */
+ /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+
+#ifdef HAVE_PPSAPI
+ /*
+ * If the PPSAPI is working, rather use its timestamps.
+ * assume that the PPS occurs on the second so blow any msec
+ */
+ if (nmea_pps(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->nsec = 0;
+ }
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+
+
+ /*
+ * Only go on if we had been polled.
+ */
+ if (!up->polled)
+ return;
+ up->polled = 0;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /* If we get here - what we got from the clock is OK, so say so */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+}
+
+/*
+ * nmea_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+nmea_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+ up->polled = 1;
+
+ /*
+ * usually nmea_receive can get a timestamp every second
+ */
+
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
+}
+
+/*
+ *
+ * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
+ * as gps_send(fd,"rqts,u\r", peer);
+ *
+ * We don't currently send any data, but would like to send
+ * RTCM SC104 messages for differential positioning. It should
+ * also give us better time. Without a PPS output, we're
+ * Just fooling ourselves because of the serial code paths
+ *
+ */
+static void
+gps_send(
+ int fd,
+ const char *cmd,
+ struct peer *peer
+ )
+{
+
+ if (write(fd, cmd, strlen(cmd)) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+static char *
+field_parse(
+ char *cp,
+ int fn
+ )
+{
+ char *tp;
+ int i = fn;
+
+ for (tp = cp; *tp != '\0'; tp++) {
+ if (*tp == ',')
+ i--;
+ if (i == 0)
+ break;
+ }
+ return (++tp);
+}
+#else
+int refclock_nmea_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_oncore.c b/contrib/ntp/ntpd/refclock_oncore.c
new file mode 100644
index 0000000..e1d23a9
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_oncore.c
@@ -0,0 +1,3732 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * refclock_oncore.c
+ *
+ * Driver for some of the various the Motorola Oncore GPS receivers.
+ * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
+ * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
+ * than the others.
+ * The receivers without position hold (GT, GT+) will be less accurate.
+ *
+ * Tested with:
+ *
+ * (UT) (VP)
+ * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC.
+ * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P
+ * SOFTWARE VER # 2 SOFTWARE VER # 8
+ * SOFTWARE REV # 2 SOFTWARE REV # 8
+ * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996
+ * MODEL # R1121N1114 MODEL # B4121P1155
+ * HWDR P/N # 1 HDWR P/N # _
+ * SERIAL # R0010A SERIAL # SSG0226478
+ * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02
+ * OPTIONS LIST IB
+ *
+ * (Basic) (M12)
+ * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC.
+ * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A
+ * SOFTWARE VER # 5 SOFTWARE VER # 1
+ * SOFTWARE REV # 0 SOFTWARE REV # 3
+ * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000
+ * MODEL # A11121P116 MODEL # P143T12NR1
+ * HDWR P/N # _ HWDR P/N # 1
+ * SERIAL # SSG0049809 SERIAL # P003UD
+ * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27
+ * OPTIONS LIST AB
+ *
+ * (M12+T) (M12+T later version)
+ * COPYRIGHT 1991-2002 MOTOROLA INC. COPYRIGHT 1991-2003 MOTOROLA INC.
+ * SFTW P/N # 61-G10268A SFTW P/N # 61-G10268A
+ * SOFTWARE VER # 2 SOFTWARE VER # 2
+ * SOFTWARE REV # 0 SOFTWARE REV # 1
+ * SOFTWARE DATE AUG 14 2002 SOFTWARE DATE APR 16 2003
+ * MODEL # P283T12T11 MODEL # P273T12T12
+ * HWDR P/N # 2 HWDR P/N # 2
+ * SERIAL # P04DC2 SERIAL # P05Z7Z
+ * MANUFACTUR DATE 2J17 MANUFACTUR DATE 3G15
+ *
+ * --------------------------------------------------------------------------
+ * Reg Clemens (Feb 2006)
+ * Fix some gcc4 compiler complaints
+ * Fix possible segfault in oncore_init_shmem
+ * change all (possible) fprintf(stderr, to record_clock_stats
+ * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct
+ * immediately after new Almanac Read.
+ * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it>
+ * now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or
+ * the new one. Compiles depending on timepps.h seen.
+ * --------------------------------------------------------------------------
+ * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds
+ * (the oncore driver was setting the wrong ntpd variable)
+ * --------------------------------------------------------------------------
+ * Reg.Clemens (Mar 2004)
+ * Support for interfaces other than PPSAPI removed, for Solaris, SunOS,
+ * SCO, you now need to use one of the timepps.h files in the root dir.
+ * this driver will 'grab' it for you if you dont have one in /usr/include
+ * --------------------------------------------------------------------------
+ * This code uses the two devices
+ * /dev/oncore.serial.n
+ * /dev/oncore.pps.n
+ * which may be linked to the same device.
+ * and can read initialization data from the file
+ * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
+ * n or N are the unit number, viz 127.127.30.N.
+ * --------------------------------------------------------------------------
+ * Reg.Clemens <reg@dwf.com> Sep98.
+ * Original code written for FreeBSD.
+ * With these mods it works on FreeBSD, SunOS, Solaris and Linux
+ * (SunOS 4.1.3 + ppsclock)
+ * (Solaris7 + MU4)
+ * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
+ *
+ * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
+ * state machine state) are printed to CLOCKSTATS if that file is enabled
+ * in /etc/ntp.conf.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
+ * doing an average of 10000 valid 2D and 3D fixes is what the automatic
+ * site survey mode does. Looking at the output from the receiver
+ * it seems like it is only using 3D fixes.
+ * When we do it ourselves, take 10000 3D fixes.
+ */
+
+#define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */
+
+/*
+ * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
+ * "STATUS" line in the oncore config file, which contains the most recent
+ * copy of all types of messages we recognize. This file can be mmap(2)'ed
+ * by monitoring and statistics programs.
+ *
+ * See separate HTML documentation for this option.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#ifdef ONCORE_SHMEM_STATUS
+# ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+# define MAP_FAILED ((u_char *) -1)
+# endif /* MAP_FAILED */
+# endif /* HAVE_SYS_MMAN_H */
+#endif /* ONCORE_SHMEM_STATUS */
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+#ifdef HAVE_SYS_SIO_H
+# include <sys/sio.h>
+#endif
+
+enum receive_state {
+ ONCORE_NO_IDEA,
+ ONCORE_CHECK_ID,
+ ONCORE_CHECK_CHAN,
+ ONCORE_HAVE_CHAN,
+ ONCORE_RESET_SENT,
+ ONCORE_TEST_SENT,
+ ONCORE_INIT,
+ ONCORE_ALMANAC,
+ ONCORE_RUN
+};
+
+enum site_survey_state {
+ ONCORE_SS_UNKNOWN,
+ ONCORE_SS_TESTING,
+ ONCORE_SS_HW,
+ ONCORE_SS_SW,
+ ONCORE_SS_DONE
+};
+
+enum antenna_state {
+ ONCORE_ANTENNA_UNKNOWN = -1,
+ ONCORE_ANTENNA_OK = 0,
+ ONCORE_ANTENNA_OC = 1,
+ ONCORE_ANTENNA_UC = 2,
+ ONCORE_ANTENNA_NV = 3
+};
+
+/* Model Name, derived from the @@Cj message.
+ * Used to initialize some variables.
+ */
+
+enum oncore_model {
+ ONCORE_BASIC,
+ ONCORE_PVT6,
+ ONCORE_VP,
+ ONCORE_UT,
+ ONCORE_UTPLUS,
+ ONCORE_GT,
+ ONCORE_GTPLUS,
+ ONCORE_SL,
+ ONCORE_M12,
+ ONCORE_UNKNOWN
+};
+
+/* the bits that describe these properties are in the same place
+ * on the VP/UT, but have moved on the M12. As such we extract
+ * them, and use them from this struct.
+ *
+ */
+
+struct RSM {
+ u_char posn0D;
+ u_char posn2D;
+ u_char posn3D;
+ u_char bad_almanac;
+ u_char bad_fix;
+};
+
+/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
+ * see what mode it is in. The bits on the M12 are multiplexed with
+ * other messages, so we have to 'keep' the last known mode here.
+ */
+
+enum posn_mode {
+ MODE_UNKNOWN,
+ MODE_0D,
+ MODE_2D,
+ MODE_3D
+};
+
+struct instance {
+ int unit; /* 127.127.30.unit */
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int ttyfd; /* TTY file descriptor */
+ int ppsfd; /* PPS file descriptor */
+ int shmemfd; /* Status shm descriptor */
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+ enum receive_state o_state; /* Receive state */
+ enum posn_mode mode; /* 0D, 2D, 3D */
+ enum site_survey_state site_survey; /* Site Survey state */
+ enum antenna_state ant_state; /* antenna state */
+
+ int Bj_day;
+
+ u_long delay; /* ns */
+ long offset; /* ns */
+
+ u_char *shmem;
+ char *shmem_fname;
+ u_int shmem_Cb;
+ u_int shmem_Ba;
+ u_int shmem_Ea;
+ u_int shmem_Ha;
+ u_char shmem_reset;
+ u_char shmem_Posn;
+ u_char shmem_bad_Ea;
+ u_char almanac_from_shmem;
+
+ double ss_lat;
+ double ss_long;
+ double ss_ht;
+ double dH;
+ int ss_count;
+ u_char posn_set;
+
+ enum oncore_model model;
+ u_int version;
+ u_int revision;
+
+ u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
+ s_char traim; /* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */
+ /* the following 7 are all timing counters */
+ u_char traim_delay; /* seconds counter, waiting for reply */
+ u_char count; /* cycles thru Ea before starting */
+ u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */
+ u_char count2; /* cycles thru Ea after count, to check for @@Ea */
+ u_char count3; /* cycles thru Ea checking for # channels */
+ u_char count4; /* cycles thru leap after Gj to issue Bj */
+ u_char count5; /* cycles thru get_timestamp waiting for valid UTC correction */
+ u_char count5_set; /* only set count5 once */
+ u_char pollcnt;
+ u_char timeout; /* count to retry Cj after Fa self-test */
+
+ struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */
+ u_char printed;
+ u_char polled;
+ u_long ev_serial;
+ int Rcvptr;
+ u_char Rcvbuf[500];
+ u_char BEHa[160]; /* Ba, Ea or Ha */
+ u_char BEHn[80]; /* Bn , En , or Hn */
+ u_char Cj[300];
+ u_char Ag; /* Satellite mask angle */
+ u_char saw_At;
+ u_char saw_Ay;
+ u_char saw_Az;
+ s_char saw_Gj;
+ u_char have_dH;
+ u_char init_type;
+ s_char saw_tooth;
+ s_char chan_in; /* chan number from INPUT, will always use it */
+ u_char chan_id; /* chan number determined from part number */
+ u_char chan_ck; /* chan number determined by sending commands to hardware */
+ s_char traim_in; /* TRAIM from INPUT, will always use ON/OFF specified */
+ s_char traim_id; /* TRAIM determined from part number */
+ u_char traim_ck; /* TRAIM determined by sending commands to hardware */
+ u_char once; /* one pass code at top of BaEaHa */
+ s_char assert;
+ u_char hardpps;
+};
+
+#define rcvbuf instance->Rcvbuf
+#define rcvptr instance->Rcvptr
+
+static int oncore_start P((int, struct peer *));
+static void oncore_poll P((int, struct peer *));
+static void oncore_shutdown P((int, struct peer *));
+static void oncore_consume P((struct instance *));
+static void oncore_read_config P((struct instance *));
+static void oncore_receive P((struct recvbuf *));
+static int oncore_ppsapi P((struct instance *));
+static void oncore_get_timestamp P((struct instance *, long, long));
+static void oncore_init_shmem P((struct instance *));
+
+static void oncore_antenna_report P((struct instance *, enum antenna_state));
+static void oncore_chan_test P((struct instance *));
+static void oncore_check_almanac P((struct instance *));
+static void oncore_check_antenna P((struct instance *));
+static void oncore_check_leap_sec P((struct instance *));
+static int oncore_checksum_ok P((u_char *, int));
+static void oncore_compute_dH P((struct instance *));
+static void oncore_load_almanac P((struct instance *));
+static void oncore_print_Cb P((struct instance *, u_char *));
+/* static void oncore_print_array P((u_char *, int)); */
+static void oncore_print_posn P((struct instance *));
+static void oncore_sendmsg P((int, u_char *, size_t));
+static void oncore_set_posn P((struct instance *));
+static void oncore_set_traim P((struct instance *));
+static void oncore_shmem_get_3D P((struct instance *));
+static void oncore_ss P((struct instance *));
+static int oncore_wait_almanac P((struct instance *));
+
+static void oncore_msg_any P((struct instance *, u_char *, size_t, int));
+static void oncore_msg_Adef P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ag P((struct instance *, u_char *, size_t));
+static void oncore_msg_As P((struct instance *, u_char *, size_t));
+static void oncore_msg_At P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ay P((struct instance *, u_char *, size_t));
+static void oncore_msg_Az P((struct instance *, u_char *, size_t));
+static void oncore_msg_BaEaHa P((struct instance *, u_char *, size_t));
+static void oncore_msg_Bd P((struct instance *, u_char *, size_t));
+static void oncore_msg_Bj P((struct instance *, u_char *, size_t));
+static void oncore_msg_BnEnHn P((struct instance *, u_char *, size_t));
+static void oncore_msg_CaFaIa P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cb P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cf P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj_id P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj_init P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ga P((struct instance *, u_char *, size_t));
+static void oncore_msg_Gb P((struct instance *, u_char *, size_t));
+static void oncore_msg_Gj P((struct instance *, u_char *, size_t));
+static void oncore_msg_Sz P((struct instance *, u_char *, size_t));
+
+struct refclock refclock_oncore = {
+ oncore_start, /* start up driver */
+ oncore_shutdown, /* shut down driver */
+ oncore_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Understanding the next bit here is not easy unless you have a manual
+ * for the the various Oncore Models.
+ */
+
+static struct msg_desc {
+ const char flag[3];
+ const int len;
+ void (*handler) P((struct instance *, u_char *, size_t));
+ const char *fmt;
+ int shmem;
+} oncore_messages[] = {
+ /* Ea and En first since they're most common */
+ { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
+ { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
+ { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
+ { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
+ { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
+ { "Hn", 78, oncore_msg_BnEnHn, "" },
+ { "Ab", 10, 0, "" },
+ { "Ac", 11, 0, "" },
+ { "Ad", 11, oncore_msg_Adef, "" },
+ { "Ae", 11, oncore_msg_Adef, "" },
+ { "Af", 15, oncore_msg_Adef, "" },
+ { "Ag", 8, oncore_msg_Ag, "" }, /* Satellite mask angle */
+ { "As", 20, oncore_msg_As, "" },
+ { "At", 8, oncore_msg_At, "" },
+ { "Au", 12, 0, "" },
+ { "Av", 8, 0, "" },
+ { "Aw", 8, 0, "" },
+ { "Ay", 11, oncore_msg_Ay, "" },
+ { "Az", 11, oncore_msg_Az, "" },
+ { "AB", 8, 0, "" },
+ { "Bb", 92, 0, "" },
+ { "Bd", 23, oncore_msg_Bd, "" },
+ { "Bj", 8, oncore_msg_Bj, "" },
+ { "Ca", 9, oncore_msg_CaFaIa, "" },
+ { "Cb", 33, oncore_msg_Cb, "" },
+ { "Cf", 7, oncore_msg_Cf, "" },
+ { "Cg", 8, 0, "" },
+ { "Ch", 9, 0, "" },
+ { "Cj", 294, oncore_msg_Cj, "" },
+ { "Ek", 71, 0, "" },
+ { "Fa", 9, oncore_msg_CaFaIa, "" },
+ { "Ga", 20, oncore_msg_Ga, "" },
+ { "Gb", 17, oncore_msg_Gb, "" },
+ { "Gc", 8, 0, "" },
+ { "Gd", 8, 0, "" },
+ { "Ge", 8, 0, "" },
+ { "Gj", 21, oncore_msg_Gj, "" },
+ { "Ia", 10, oncore_msg_CaFaIa, "" },
+ { "Sz", 8, oncore_msg_Sz, "" },
+ { {0}, 7, 0, "" }
+};
+
+
+static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */
+static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */
+static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */
+static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */
+static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */
+static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */
+static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */
+static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */
+static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */
+static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */
+static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */
+ 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */
+ 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */
+static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */
+static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */
+static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */
+static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */
+static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */
+static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */
+static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */
+static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */
+static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */
+static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */
+static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */
+static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */
+static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */
+static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */
+static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */
+static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */
+static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */
+static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */
+static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on, traim on */
+static u_char oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim off */
+static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */
+static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */
+static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */
+static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */
+static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */
+static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */
+static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */
+static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on, traim on */
+static u_char oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim off */
+static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */
+static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */
+static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */
+ 0xff, 0xff, 0xff, 0xff, /* */
+ 0xff, 0xff, 0xff, 0xff, 0xff }; /* */
+static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */
+static u_char oncore_cmd_Gc[] = { 'G', 'c', 1 }; /* 12 PPS Control: On Cont */
+static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */
+static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */
+static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */
+static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */
+static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */
+static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */
+static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */
+static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */
+static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */
+static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */
+static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */
+static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */
+
+/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
+ * the GT had Au,Av, but not As,At
+ * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
+ * Bj in UT at v1.3
+ * dont see Bd in UT/GT thru 1999
+ * Gj in UT as of 3.0, 1999 , Bj as of 1.3
+ */
+
+static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly",
+ "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
+#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
+
+#define SPEED B9600 /* Oncore Binary speed (9600 bps) */
+
+/*
+ * Assemble and disassemble 32bit signed quantities from a buffer.
+ *
+ */
+
+ /* to buffer, int w, u_char *buf */
+#define w32_buf(buf,w) { u_int i_tmp; \
+ i_tmp = (w<0) ? (~(-w)+1) : (w); \
+ (buf)[0] = (i_tmp >> 24) & 0xff; \
+ (buf)[1] = (i_tmp >> 16) & 0xff; \
+ (buf)[2] = (i_tmp >> 8) & 0xff; \
+ (buf)[3] = (i_tmp ) & 0xff; \
+ }
+
+#define w32(buf) (((buf)[0]&0xff) << 24 | \
+ ((buf)[1]&0xff) << 16 | \
+ ((buf)[2]&0xff) << 8 | \
+ ((buf)[3]&0xff) )
+
+ /* from buffer, char *buf, result to an int */
+#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
+
+
+/*
+ * oncore_start - initialize data for processing
+ */
+
+static int
+oncore_start(
+ int unit,
+ struct peer *peer
+ )
+{
+#define STRING_LEN 32
+ register struct instance *instance;
+ struct refclockproc *pp;
+ int fd1, fd2, num;
+ char device1[STRING_LEN], device2[STRING_LEN], Msg[160];
+ const char *cp;
+ struct stat stat1, stat2;
+
+ /* create instance structure for this unit */
+
+ if (!(instance = (struct instance *) malloc(sizeof *instance))) {
+ perror("malloc");
+ return (0);
+ }
+ memset((char *) instance, 0, sizeof *instance);
+
+ /* initialize miscellaneous variables */
+
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t) instance;
+ instance->pp = pp;
+ instance->unit = unit;
+ instance->peer = peer;
+ instance->assert = 1;
+ instance->once = 1;
+
+ instance->Bj_day = -1;
+ instance->traim = -1;
+ instance->traim_in = -1;
+ instance->chan_in = -1;
+ instance->model = ONCORE_UNKNOWN;
+ instance->mode = MODE_UNKNOWN;
+ instance->site_survey = ONCORE_SS_UNKNOWN;
+ instance->Ag = 0xff; /* Satellite mask angle, unset by user */
+ instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
+
+ peer->precision = -26;
+ peer->minpoll = 4;
+ peer->maxpoll = 4;
+ pp->clockdesc = "Motorola Oncore GPS Receiver";
+ memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
+
+ cp = "ONCORE DRIVER -- CONFIGURING";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->o_state = ONCORE_NO_IDEA;
+ cp = "state = ONCORE_NO_IDEA";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ /* Now open files.
+ * This is a bit complicated, a we dont want to open the same file twice
+ * (its a problem on some OS), and device2 may not exist for the new PPS
+ */
+
+ (void)sprintf(device1, DEVICE1, unit);
+ (void)sprintf(device2, DEVICE2, unit);
+
+ /* OPEN DEVICES */
+ /* opening different devices for fd1 and fd2 presents no problems */
+ /* opening the SAME device twice, seems to be OS dependent.
+ (a) on Linux (no streams) no problem
+ (b) on SunOS (and possibly Solaris, untested), (streams)
+ never see the line discipline.
+ Since things ALWAYS work if we only open the device once, we check
+ to see if the two devices are in fact the same, then proceed to
+ do one open or two.
+ */
+
+ if (stat(device1, &stat1)) {
+ sprintf(Msg, "Can't stat fd1 (%s)\n", device1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ exit(1);
+ }
+
+ if (stat(device2, &stat2)) {
+ sprintf(Msg, "Can't stat fd2 (%s)\n", device2);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ exit(1);
+ }
+
+ if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW))) {
+ sprintf(Msg, "Can't open fd1 (%s)\n", device1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ exit(1);
+ }
+
+ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) /* same device here */
+ fd2 = fd1;
+ else { /* different devices here */
+ if ((fd2=open(device2, O_RDWR)) < 0) {
+ sprintf(Msg, "Can't open fd2 (%s)\n", device2);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ exit(1);
+ }
+ }
+ num = fd2;
+
+ /* open ppsapi soure */
+
+ if (time_pps_create(num, &instance->pps_h) < 0) {
+ record_clock_stats(&(instance->peer->srcadr), "PPSAPI not found in kernel");
+ return(0);
+ }
+
+ /* continue initialization */
+
+ instance->ttyfd = fd1;
+ instance->ppsfd = fd2;
+
+ /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
+
+ oncore_read_config(instance);
+
+ if (!oncore_ppsapi(instance))
+ return(0);
+
+ pp->io.clock_recv = oncore_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd1;
+ if (!io_addclock(&pp->io)) {
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: io_addclock");
+ (void) close(fd1);
+ free(instance);
+ return (0);
+ }
+
+#ifdef ONCORE_SHMEM_STATUS
+ /*
+ * Before starting ONCORE, lets setup SHMEM
+ * This will include merging an old SHMEM into the new one if
+ * an old one is found.
+ */
+
+ oncore_init_shmem(instance);
+#endif
+
+ /*
+ * This will return the Model of the Oncore receiver.
+ * and start the Initialization loop in oncore_msg_Cj.
+ */
+
+ instance->o_state = ONCORE_CHECK_ID;
+ cp = "state = ONCORE_CHECK_ID";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+
+ instance->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * oncore_shutdown - shut down the clock
+ */
+
+static void
+oncore_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *) pp->unitptr;
+
+ io_closeclock(&pp->io);
+
+ time_pps_destroy (instance->pps_h);
+
+ close(instance->ttyfd);
+
+ if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
+ close(instance->ppsfd);
+
+ if (instance->shmemfd)
+ close(instance->shmemfd);
+
+ free(instance);
+}
+
+
+
+/*
+ * oncore_poll - called by the transmit procedure
+ */
+
+static void
+oncore_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct instance *instance;
+
+ instance = (struct instance *) peer->procptr->unitptr;
+ if (instance->timeout) {
+ char *cp;
+
+ instance->timeout--;
+ if (instance->timeout == 0) {
+ cp = "Oncore: No response from @@Cj, shutting down driver";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_shutdown(unit, peer);
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ cp = "Oncore: Resend @@Cj";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ return;
+ }
+
+ if (!instance->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ instance->pollcnt--;
+ peer->procptr->polls++;
+ instance->polled = 1;
+}
+
+
+
+/*
+ * Initialize PPSAPI
+ */
+
+static int
+oncore_ppsapi(
+ struct instance *instance
+ )
+{
+ int cap, mode, mode1;
+ char *cp, Msg[160];
+
+ if (time_pps_getcap(instance->pps_h, &cap) < 0) {
+ msyslog(LOG_ERR, "time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
+ msyslog(LOG_ERR, "time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ if (instance->assert) {
+ cp = "Assert.";
+ mode = PPS_CAPTUREASSERT;
+ mode1 = PPS_OFFSETASSERT;
+ } else {
+ cp = "Clear.";
+ mode = PPS_CAPTURECLEAR;
+ mode1 = PPS_OFFSETCLEAR;
+ }
+ sprintf(Msg, "Initializing timeing to %s.", cp);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ if (!(mode & cap)) {
+ sprintf(Msg, "Can't set timeing to %s, exiting...", cp);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ return(0);
+ }
+
+ if (!(mode1 & cap)) {
+ sprintf(Msg, "Can't set PPS_%sCLEAR, this will increase jitter.", cp);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ mode1 = 0;
+ }
+
+ /* only set what is legal */
+
+ instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
+
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: time_pps_setparams fails");
+ exit(1);
+ }
+
+ /* If HARDPPS is on, we tell kernel */
+
+ if (instance->hardpps) {
+ int i;
+
+ record_clock_stats(&(instance->peer->srcadr), "HARDPPS Set.");
+
+ if (instance->assert)
+ i = PPS_CAPTUREASSERT;
+ else
+ i = PPS_CAPTURECLEAR;
+
+ /* we know that 'i' is legal from above */
+
+ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR, "time_pps_kcbind failed: %m");
+ record_clock_stats(&(instance->peer->srcadr), "HARDPPS failed, abort...");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ return(1);
+}
+
+
+
+#ifdef ONCORE_SHMEM_STATUS
+static void
+oncore_init_shmem(
+ struct instance *instance
+ )
+{
+ int i, l, n, fd, shmem_old_size, n1;
+ char Msg[160];
+ u_char *cp, *cp1, *buf, *shmem_old;
+ struct msg_desc *mp;
+ struct stat sbuf;
+ size_t shmem_length;
+
+ /*
+ * The first thing we do is see if there is an instance->shmem_fname file (still)
+ * out there from a previous run. If so, we copy it in and use it to initialize
+ * shmem (so we won't lose our almanac if we need it).
+ */
+
+ shmem_old = 0;
+ shmem_old_size = 0;
+ if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't open SHMEM file");
+ else {
+ fstat(fd, &sbuf);
+ shmem_old_size = sbuf.st_size;
+ if (shmem_old_size != 0) {
+ shmem_old = (u_char *) malloc((unsigned) sbuf.st_size);
+ if (shmem_old == NULL)
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't malloc buffer for shmem_old");
+ else
+ read(fd, shmem_old, shmem_old_size);
+ }
+ close(fd);
+ }
+
+ /* OK, we now create the NEW SHMEM. */
+
+ if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't open shmem");
+ if (shmem_old)
+ free(shmem_old);
+
+ return;
+ }
+
+ /* see how big it needs to be */
+
+ n = 1;
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ mp->shmem = n;
+ /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
+ if (!strcmp(mp->flag, "Cb")) {
+ instance->shmem_Cb = n;
+ n += (mp->len + 3) * 34;
+ }
+ if (!strcmp(mp->flag, "Ba")) {
+ instance->shmem_Ba = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ea")) {
+ instance->shmem_Ea = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ha")) {
+ instance->shmem_Ha = n;
+ n += (mp->len + 3) * 3;
+ }
+ n += (mp->len + 3);
+ }
+ shmem_length = n + 2;
+
+ buf = malloc(shmem_length);
+ if (buf == NULL) {
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't malloc buffer for shmem");
+ close(instance->shmemfd);
+ if (shmem_old)
+ free(shmem_old);
+
+ return;
+ }
+
+ memset(buf, 0, shmem_length);
+
+ /* next build the new SHMEM buffer in memory */
+
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ l = mp->shmem;
+ buf[l + 0] = mp->len >> 8;
+ buf[l + 1] = mp->len & 0xff;
+ buf[l + 2] = 0;
+ buf[l + 3] = '@';
+ buf[l + 4] = '@';
+ buf[l + 5] = mp->flag[0];
+ buf[l + 6] = mp->flag[1];
+ if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
+ if (!strcmp(mp->flag, "Cb"))
+ n = 35;
+ else
+ n = 4;
+ for (i=1; i<n; i++) {
+ buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
+ buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
+ buf[l + i * (mp->len+3) + 2] = 0;
+ buf[l + i * (mp->len+3) + 3] = '@';
+ buf[l + i * (mp->len+3) + 4] = '@';
+ buf[l + i * (mp->len+3) + 5] = mp->flag[0];
+ buf[l + i * (mp->len+3) + 6] = mp->flag[1];
+ }
+ }
+ }
+
+ /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
+ * copying the data in shmem_old to buf.
+ * When we are done we write it out and free both buffers.
+ * If the structure sizes dont agree, I will not copy.
+ * This could be due to an addition/deletion or a problem with the disk file.
+ */
+
+ if (shmem_old) {
+ if (shmem_old_size == shmem_length) {
+ for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) {
+ n1 = 256*(*(cp1-3)) + *(cp1-2);
+ if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4))
+ break;
+
+ memcpy(cp, cp1, (size_t) n);
+ }
+ }
+ free(shmem_old);
+ }
+
+ i = write(instance->shmemfd, buf, shmem_length);
+ free(buf);
+
+ if (i != shmem_length) {
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: error writing shmem");
+ close(instance->shmemfd);
+ return;
+ }
+
+ instance->shmem = (u_char *) mmap(0, shmem_length,
+ PROT_READ | PROT_WRITE,
+#ifdef MAP_HASSEMAPHORE
+ MAP_HASSEMAPHORE |
+#endif
+ MAP_SHARED, instance->shmemfd, (off_t)0);
+
+ if (instance->shmem == (u_char *)MAP_FAILED) {
+ instance->shmem = 0;
+ close(instance->shmemfd);
+ return;
+ }
+
+ sprintf(Msg, "SHMEM (size = %ld) is CONFIGURED and available as %s",
+ (u_long) shmem_length, instance->shmem_fname);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+#endif /* ONCORE_SHMEM_STATUS */
+
+
+
+/*
+ * Read Input file if it exists.
+ */
+
+static void
+oncore_read_config(
+ struct instance *instance
+ )
+{
+/*
+ * First we try to open the configuration file
+ * /etc/oncoreN
+ * where N is the unit number viz 127.127.30.N.
+ * If we don't find it we try
+ * /etc/ntp.oncore.N
+ * and then
+ * /etc/ntp.oncore
+ *
+ * If we don't find any then we don't have the cable delay or PPS offset
+ * and we choose MODE (4) below.
+ *
+ * Five Choices for MODE
+ * (0) ONCORE is preinitialized, don't do anything to change it.
+ * nb, DON'T set 0D mode, DON'T set Delay, position...
+ * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
+ * then this position is set as the INITIAL position of the ONCORE.
+ * This can reduce the time to first fix.
+ * -------------------------------------------------------------------------------
+ * Note that an Oncore UT without a battery backup retains NO information if it is
+ * power cycled, with a Battery Backup it remembers the almanac, etc.
+ * For an Oncore VP, there is an eeprom that will contain this data, along with the
+ * option of Battery Backup.
+ * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
+ * power cycle, since there is nowhere to store the data.
+ * -------------------------------------------------------------------------------
+ *
+ * If we open one or the other of the files, we read it looking for
+ * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
+ * STATUS, POSN3D, POSN2D, CHAN, TRAIM
+ * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must
+ * be present or mode reverts to (2,4).
+ *
+ * Read input file.
+ *
+ * # is comment to end of line
+ * = allowed between 1st and 2nd fields.
+ *
+ * Expect to see one line with 'MODE' as first field, followed by an integer
+ * in the range 0-4 (default = 4).
+ *
+ * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
+ * All numbers are floating point.
+ * DDD.ddd
+ * DDD MMM.mmm
+ * DDD MMM SSS.sss
+ *
+ * Expect to see one line with 'HT' as first field,
+ * followed by 1-2 fields. First is a number, the second is 'FT' or 'M'
+ * for feet or meters. HT is the height above the GPS ellipsoid.
+ * If the receiver reports height in both GPS and MSL, then we will report
+ * the difference GPS-MSL on the clockstats file.
+ *
+ * There is an optional line, starting with DELAY, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * DELAY is cable delay, typically a few tens of ns.
+ *
+ * There is an optional line, starting with OFFSET, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * OFFSET is the offset of the PPS pulse from 0. (only fully implemented
+ * with the PPSAPI, we need to be able to tell the Kernel about this
+ * offset if the Kernel PLL is in use, but can only do this presently
+ * when using the PPSAPI interface. If not using the Kernel PLL,
+ * then there is no problem.
+ *
+ * There is an optional line, with either ASSERT or CLEAR on it, which
+ * determine which transition of the PPS signal is used for timing by the
+ * PPSAPI. If neither is present, then ASSERT is assumed.
+ * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
+ * For Flag2, ASSERT=0, and hence is default.
+ *
+ * There is an optional line, with HARDPPS on it. Including this line causes
+ * the PPS signal to control the kernel PLL.
+ * HARDPPS can also be set with FLAG3 of the ntp.conf input.
+ * For Flag3, 0 is disabled, and the default.
+ *
+ * There are three options that have to do with using the shared memory option.
+ * First, to enable the option there must be a SHMEM line with a file name.
+ * The file name is the file associated with the shared memory.
+ *
+ * In shared memory, there is one 'record' for each returned variable.
+ * For the @@Ea data there are three 'records' containing position data.
+ * There will always be data in the record corresponding to the '0D' @@Ea record,
+ * and the user has a choice of filling the '3D' record by specifying POSN3D,
+ * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D'
+ * record is filled once every 15s.
+ *
+ * Two additional variables that can be set are CHAN and TRAIM. These should be
+ * set correctly by the code examining the @@Cj record, but we bring them out here
+ * to allow the user to override either the # of channels, or the existence of TRAIM.
+ * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
+ * followed by YES or NO.
+ *
+ * There is an optional line with MASK on it followed by one integer field in the
+ * range 0 to 89. This sets the satellite mask angle and will determine the minimum
+ * elevation angle for satellites to be tracked by the receiver. The default value
+ * is 10 deg for the VP and 0 deg for all other receivers.
+ *
+ * So acceptable input would be
+ * # these are my coordinates (RWC)
+ * LON -106 34.610
+ * LAT 35 08.999
+ * HT 1589 # could equally well say HT 5215 FT
+ * DELAY 60 ns
+ */
+
+ FILE *fd;
+ char *cp, *cc, *ca, line[100], units[2], device[20], Msg[160], **cpp;
+ char *dirs[] = { "/etc/ntp", "/etc", 0 };
+ int i, sign, lat_flg, long_flg, ht_flg, mode, mask;
+ double f1, f2, f3;
+
+ fd = NULL; /* just to shutup gcc complaint */
+ for (cpp=dirs; *cpp; cpp++) {
+ cp = *cpp;
+ sprintf(device, "%s/ntp.oncore.%d", cp, instance->unit); /* try "ntp.oncore.0 */
+ if ((fd=fopen(device, "r")))
+ break;
+ sprintf(device, "%s/ntp.oncore%d", cp, instance->unit); /* try "ntp.oncore0" */
+ if ((fd=fopen(device, "r")))
+ break;
+ sprintf(device, "%s/ntp.oncore", cp); /* and finally "ntp.oncore" */
+ if ((fd=fopen(device, "r")))
+ break;
+ }
+
+ if (!fd) { /* no inputfile, default to the works ... */
+ instance->init_type = 4;
+ return;
+ }
+
+ mode = mask = 0;
+ lat_flg = long_flg = ht_flg = 0;
+ while (fgets(line, 100, fd)) {
+
+ /* Remove comments */
+ if ((cp = strchr(line, '#')))
+ *cp = '\0';
+
+ /* Remove trailing space */
+ for (i = strlen(line);
+ i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
+ )
+ line[--i] = '\0';
+
+ /* Remove leading space */
+ for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
+ continue;
+
+ /* Stop if nothing left */
+ if (!*cc)
+ continue;
+
+ /* Uppercase the command and find the arg */
+ for (ca = cc; *ca; ca++) {
+ if (isascii((int)*ca)) {
+ if (islower((int)*ca)) {
+ *ca = toupper(*ca);
+ } else if (isspace((int)*ca) || (*ca == '='))
+ break;
+ }
+ }
+
+ /* Remove space (and possible =) leading the arg */
+ for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
+ continue;
+
+ if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
+ i = strlen(ca);
+ instance->shmem_fname = (char *) malloc((unsigned) (i+1));
+ strcpy(instance->shmem_fname, ca);
+ continue;
+ }
+
+ /* Uppercase argument as well */
+ for (cp = ca; *cp; cp++)
+ if (isascii((int)*cp) && islower((int)*cp))
+ *cp = toupper(*cp);
+
+ if (!strncmp(cc, "LAT", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ lat_flg++;
+ } else if (!strncmp(cc, "LON", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ long_flg++;
+ } else if (!strncmp(cc, "HT", (size_t) 2)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'F')
+ f1 = 0.3048 * f1;
+ instance->ss_ht = 100 * f1; /* cm */
+ ht_flg++;
+ } else if (!strncmp(cc, "DELAY", (size_t) 5)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999) {
+ sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ } else
+ instance->delay = f1; /* delay in ns */
+ } else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999999.) {
+ sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ } else
+ instance->offset = f1; /* offset in ns */
+ } else if (!strncmp(cc, "MODE", (size_t) 4)) {
+ sscanf(ca, "%d", &mode);
+ if (mode < 0 || mode > 4)
+ mode = 4;
+ } else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
+ instance->assert = 1;
+ } else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
+ instance->assert = 0;
+ } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
+ instance->hardpps = 1;
+ } else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
+ instance->shmem_Posn = 2;
+ } else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
+ instance->shmem_Posn = 3;
+ } else if (!strncmp(cc, "CHAN", (size_t) 4)) {
+ sscanf(ca, "%d", &i);
+ if ((i == 6) || (i == 8) || (i == 12))
+ instance->chan_in = i;
+ } else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
+ instance->traim_in = 1; /* so TRAIM alone is YES */
+ if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */
+ instance->traim_in = 0;
+ } else if (!strncmp(cc, "MASK", (size_t) 4)) {
+ sscanf(ca, "%d", &mask);
+ if (mask > -1 && mask < 90)
+ instance->Ag = mask; /* Satellite mask angle */
+ }
+ }
+ fclose(fd);
+
+ /*
+ * OK, have read all of data file, and extracted the good stuff.
+ * If lat/long/ht specified they ALL must be specified for mode = (1,3).
+ */
+
+ instance->posn_set = 1;
+ if (!( lat_flg && long_flg && ht_flg )) {
+ printf("ONCORE: incomplete data on %s\n", device);
+ instance->posn_set = 0;
+ if (mode == 1 || mode == 3) {
+ sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ mode++;
+ }
+ }
+ instance->init_type = mode;
+
+ sprintf(Msg, "Input mode = %d", mode);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
+ */
+
+static void
+oncore_receive(
+ struct recvbuf *rbufp
+ )
+{
+ size_t i;
+ u_char *p;
+ struct peer *peer;
+ struct instance *instance;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ instance = (struct instance *) peer->procptr->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+
+#if 0
+ if (debug > 4) {
+ int i;
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%02x ", p[i]);
+ printf("\n");
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%03o ", p[i]);
+ printf("\n");
+ }
+#endif
+
+ i = rbufp->recv_length;
+ if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
+ i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */
+ memcpy(rcvbuf+rcvptr, p, i);
+ rcvptr += i;
+ oncore_consume(instance);
+}
+
+
+
+/*
+ * Deal with any complete messages
+ */
+
+static void
+oncore_consume(
+ struct instance *instance
+ )
+{
+ int i, m;
+ unsigned l;
+
+ while (rcvptr >= 7) {
+ if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
+ /* We're not in sync, lets try to get there */
+ for (i=1; i < rcvptr-1; i++)
+ if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
+ break;
+#ifdef DEBUG
+ if (debug > 4)
+ printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i);
+#endif
+ if (i != rcvptr)
+ memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
+ rcvptr -= i;
+ continue;
+ }
+
+ /* Ok, we have a header now */
+ l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
+ for(m=0; m<l; m++)
+ if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
+ break;
+ if (m == l) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]);
+#endif
+ memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
+ rcvptr -= 4;
+ continue;
+ }
+
+ l = oncore_messages[m].len;
+#if 0
+ if (debug > 3)
+ printf("ONCORE[%d]: GOT: %c%c %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m);
+#endif
+ /* Got the entire message ? */
+
+ if (rcvptr < l)
+ return;
+
+ /* are we at the end of message? should be <Cksum><CR><LF> */
+
+ if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
+#ifdef DEBUG
+ if (debug)
+ printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit);
+#endif
+ } else { /* check the CheckSum */
+ if (oncore_checksum_ok(rcvbuf, l)) {
+ if (instance->shmem != NULL) {
+ instance->shmem[oncore_messages[m].shmem + 2]++;
+ memcpy(instance->shmem + oncore_messages[m].shmem + 3,
+ rcvbuf, (size_t) l);
+ }
+ oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
+ if (oncore_messages[m].handler)
+ oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
+ }
+#ifdef DEBUG
+ else if (debug) {
+ printf("ONCORE[%d]: Checksum mismatch!\n", instance->unit);
+ printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]);
+ for (i=4; i<l; i++)
+ printf("%03o ", rcvbuf[i]);
+ printf("\n");
+ }
+#endif
+ }
+
+ if (l != rcvptr)
+ memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
+ rcvptr -= l;
+ }
+}
+
+
+
+static void
+oncore_get_timestamp(
+ struct instance *instance,
+ long dt1, /* tick offset THIS time step */
+ long dt2 /* tick offset NEXT time step */
+ )
+{
+ int Rsm;
+ u_long j;
+ l_fp ts, ts_tmp;
+ double dmy;
+#ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec *tsp = 0;
+#else
+ struct timeval *tsp = 0;
+#endif
+ int current_mode;
+ u_long i;
+ pps_params_t current_params;
+ struct timespec timeout;
+ pps_info_t pps_i;
+
+#if 1
+ /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
+ * If we have Finished the SiteSurvey, then we fall thru for the 14/15
+ * times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
+ * This gives good time, which gets better when the SS is done.
+ */
+
+ if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D))
+#else
+ /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
+
+ if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D))
+#endif
+ return;
+
+ /* Don't do anything without an almanac to define the GPS->UTC delta */
+
+ if (instance->rsm.bad_almanac)
+ return;
+
+ /* Once the Almanac is valid, the M12+T does not produce valid UTC
+ * immediately.
+ * Wait for UTC offset decode valid, then wait one message more
+ * so we are not off by 13 seconds after reset.
+ */
+
+ if (instance->count5) {
+ instance->count5--;
+ return;
+ }
+
+ j = instance->ev_serial;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
+ &timeout) < 0) {
+ printf("ONCORE: time_pps_fetch failed\n");
+ return;
+ }
+
+ if (instance->assert) {
+ tsp = &pps_i.assert_timestamp;
+
+#ifdef DEBUG
+ if (debug > 2) {
+ i = (u_long) pps_i.assert_sequence;
+# ifdef HAVE_STRUCT_TIMESPEC
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
+ instance->unit, i, j,
+ (long)tsp->tv_sec, (long)tsp->tv_nsec);
+# else
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
+ instance->unit, i, j,
+ (long)tsp->tv_sec, (long)tsp->tv_usec);
+# endif
+ }
+#endif
+
+ if (pps_i.assert_sequence == j) {
+ printf("ONCORE: oncore_get_timestamp, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.assert_sequence;
+ } else {
+ tsp = &pps_i.clear_timestamp;
+
+#ifdef DEBUG
+ if (debug > 2) {
+ i = (u_long) pps_i.clear_sequence;
+# ifdef HAVE_STRUCT_TIMESPEC
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
+ instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_nsec);
+# else
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
+ instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_usec);
+# endif
+ }
+#endif
+
+ if (pps_i.clear_sequence == j) {
+ printf("ONCORE: oncore_get_timestamp, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.clear_sequence;
+ }
+
+ /* convert timespec -> ntp l_fp */
+
+ dmy = tsp->tv_nsec;
+ dmy /= 1e9;
+ ts.l_uf = dmy * 4294967296.0;
+ ts.l_ui = tsp->tv_sec;
+
+#if 0
+ alternate code for previous 4 lines is
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ dmy = tsp->tv_sec; /* integer part */
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+ or more simply
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ ts.l_ui = tsp->tv_sec;
+#endif /* 0 */
+
+ /* now have timestamp in ts */
+ /* add in saw_tooth and offset, these will be ZERO if no TRAIM */
+ /* they will be IGNORED if the PPSAPI cant do PPS_OFFSET/ASSERT/CLEAR */
+ /* we just try to add them in and dont test for that here */
+
+ /* saw_tooth not really necessary if using TIMEVAL */
+ /* since its only precise to us, but do it anyway. */
+
+ /* offset in ns, and is positive (late), we subtract */
+ /* to put the PPS time transition back where it belongs */
+
+ /* must hand the offset for the NEXT sec off to the Kernel to do */
+ /* the addition, so that the Kernel PLL sees the offset too */
+
+ if (instance->assert)
+ instance->pps_p.assert_offset.tv_nsec = -dt2;
+ else
+ instance->pps_p.clear_offset.tv_nsec = -dt2;
+
+ /* The following code is necessary, and not just a time_pps_setparams,
+ * using the saved instance->pps_p, since some other process on the
+ * machine may have diddled with the mode bits (say adding something
+ * that it needs). We take what is there and ADD what we need.
+ * [[ The results from the time_pps_getcap is unlikely to change so
+ * we could probably just save it, but I choose to do the call ]]
+ * Unfortunately, there is only ONE set of mode bits in the kernel per
+ * interface, and not one set for each open handle.
+ *
+ * There is still a race condition here where we might mess up someone
+ * elses mode, but if he is being careful too, he should survive.
+ */
+
+ if (time_pps_getcap(instance->pps_h, &current_mode) < 0) {
+ msyslog(LOG_ERR, "time_pps_getcap failed: %m");
+ return;
+ }
+
+ if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
+ msyslog(LOG_ERR, "time_pps_getparams failed: %m");
+ return;
+ }
+
+ /* or current and mine */
+ current_params.mode |= instance->pps_p.mode;
+ /* but only set whats legal */
+ current_params.mode &= current_mode;
+
+ current_params.assert_offset.tv_sec = 0;
+ current_params.assert_offset.tv_nsec = -dt2;
+ current_params.clear_offset.tv_sec = 0;
+ current_params.clear_offset.tv_nsec = -dt2;
+
+ if (time_pps_setparams(instance->pps_h, &current_params))
+ record_clock_stats(&(instance->peer->srcadr), "ONCORE: Error doing time_pps_setparams");
+
+ /* have time from UNIX origin, convert to NTP origin. */
+
+ ts.l_ui += JAN_1970;
+ instance->pp->lastrec = ts;
+
+ /* print out information about this timestamp (long line) */
+
+ ts_tmp = ts;
+ ts_tmp.l_ui = 0; /* zero integer part */
+ LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */
+ j = 1.0e9*dmy; /* then to integer ns */
+
+ Rsm = 0;
+ if (instance->chan == 6)
+ Rsm = instance->BEHa[64];
+ else if (instance->chan == 8)
+ Rsm = instance->BEHa[72];
+ else if (instance->chan == 12)
+ Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]);
+
+ if (instance->chan == 6 || instance->chan == 8) {
+ char f1[5], f2[5], f3[5], f4[5];
+ if (instance->traim) {
+ sprintf(f1, "%d", instance->BEHn[21]);
+ sprintf(f2, "%d", instance->BEHn[22]);
+ sprintf(f3, "%2d", instance->BEHn[23]*256+instance->BEHn[24]);
+ sprintf(f4, "%3d", (s_char) instance->BEHn[25]);
+ } else {
+ strcpy(f1, "x");
+ strcpy(f2, "x");
+ strcpy(f3, "xx");
+ strcpy(f4, "xxx");
+ }
+ sprintf(instance->pp->a_lastcode, /* MAX length 128, currently at 121 */
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]),
+ /*rsat dop */
+ instance->BEHa[38], instance->BEHa[39], instance->traim, f1, f2,
+ /* nsat visible, nsat tracked, traim,traim,traim */
+ f3, f4,
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53],
+ instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69]
+ ); /* will be 0 for 6 chan */
+ } else if (instance->chan == 12) {
+ char f1[5], f2[5], f3[5], f4[5];
+ if (instance->traim) {
+ sprintf(f1, "%d", instance->BEHn[6]);
+ sprintf(f2, "%d", instance->BEHn[7]);
+ sprintf(f3, "%d", instance->BEHn[12]*256+instance->BEHn[13]);
+ sprintf(f4, "%3d", (s_char) instance->BEHn[14]);
+ } else {
+ strcpy(f1, "x");
+ strcpy(f2, "x");
+ strcpy(f3, "x");
+ strcpy(f4, "xxx");
+ }
+ sprintf(instance->pp->a_lastcode,
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]),
+ /*rsat dop */
+ instance->BEHa[55], instance->BEHa[56], instance->traim, f1, f2,
+ /* nsat visible, nsat tracked traim,traim,traim */
+ f3, f4,
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76],
+ instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100],
+ instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124]
+ );
+ }
+
+#ifdef DEBUG
+ if (debug > 2) {
+ int n;
+
+ n = strlen(instance->pp->a_lastcode);
+ printf("ONCORE[%d]: len = %d %s\n", instance->unit, n, instance->pp->a_lastcode);
+ }
+#endif
+
+ /* and some things I dont understand (magic ntp things) */
+
+ if (!refclock_process(instance->pp)) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return;
+ }
+
+ record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
+ instance->pollcnt = 2;
+
+ if (instance->polled) {
+ instance->polled = 0;
+ /* instance->pp->dispersion = instance->pp->skew = 0; */
+ instance->pp->lastref = instance->pp->lastrec;
+ refclock_receive(instance->peer);
+ }
+}
+
+
+/*************** oncore_msg_XX routines start here *******************/
+
+
+/*
+ * print Oncore response message.
+ */
+
+static void
+oncore_msg_any(
+ struct instance *instance,
+ u_char *buf,
+ size_t len,
+ int idx
+ )
+{
+ int i;
+ const char *fmt = oncore_messages[idx].fmt;
+ const char *p;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ struct timeval tv;
+
+#ifdef DEBUG
+ if (debug > 3) {
+# ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+# else
+ GETTIMEOFDAY(&tv, 0);
+# endif
+ printf("ONCORE[%d]: %ld.%06ld\n", instance->unit, (long) tv.tv_sec, (long) tv.tv_usec);
+
+ if (!*fmt) {
+ printf(">>@@%c%c ", buf[2], buf[3]);
+ for(i=2; i < len && i < 2400 ; i++)
+ printf("%02x", buf[i]);
+ printf("\n");
+ return;
+ } else {
+ printf("##");
+ for (p = fmt; *p; p++) {
+ putchar(*p);
+ putchar('_');
+ }
+ printf("\n%c%c", buf[2], buf[3]);
+ i = 4;
+ for (p = fmt; *p; p++) {
+ printf("%02x", buf[i++]);
+ }
+ printf("\n");
+ }
+ }
+#endif
+}
+
+
+
+/* Latitude, Longitude, Height */
+
+static void
+oncore_msg_Adef(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+}
+
+
+
+/* Mask Angle */
+
+static void
+oncore_msg_Ag(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{ char Msg[160], *cp;
+
+ cp = "set to";
+ if (instance->o_state == ONCORE_RUN)
+ cp = "is";
+
+ instance->Ag = buf[4];
+ sprintf(Msg, "Satellite mask angle %s %d degrees", cp, (int) instance->Ag);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * get Position hold position
+ */
+
+static void
+oncore_msg_As(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ instance->ss_lat = buf_w32(&buf[4]);
+ instance->ss_long = buf_w32(&buf[8]);
+ instance->ss_ht = buf_w32(&buf[12]);
+
+ /* Print out Position */
+ oncore_print_posn(instance);
+}
+
+
+
+/*
+ * Try to use Oncore UT+ Auto Survey Feature
+ * If its not there (VP), set flag to do it ourselves.
+ */
+
+static void
+oncore_msg_At(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+
+ instance->saw_At = 1;
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (buf[4] == 2) {
+ record_clock_stats(&(instance->peer->srcadr),
+ "Initiating hardware 3D site survey");
+
+ cp = "SSstate = ONCORE_SS_HW";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_HW;
+ }
+ }
+}
+
+
+
+/*
+ * get PPS Offset
+ * Nb. @@Ay is not supported for early UT (no plus) model
+ */
+
+static void
+oncore_msg_Ay(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[120];
+
+ if (instance->saw_Ay)
+ return;
+
+ instance->saw_Ay = 1;
+
+ instance->offset = buf_w32(&buf[4]);
+
+ sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * get Cable Delay
+ */
+
+static void
+oncore_msg_Az(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[120];
+
+ if (instance->saw_Az)
+ return;
+
+ instance->saw_Az = 1;
+
+ instance->delay = buf_w32(&buf[4]);
+
+ sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* Ba, Ea and Ha come here, these contain Position */
+
+static void
+oncore_msg_BaEaHa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+ char Msg[160];
+ int mode;
+
+ /* OK, we are close to the RUN state now.
+ * But we have a few more items to initialize first.
+ *
+ * At the beginning of this routine there are several 'timers'.
+ * We enter this routine 1/sec, and since the upper levels of NTP have usurped
+ * the use of timers, we use the 1/sec entry to do things that
+ * we would normally do with timers...
+ */
+
+ if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */
+ if (buf[2] == 'B') { /* 6chan */
+ if (instance->chan_ck < 6) instance->chan_ck = 6;
+ } else if (buf[2] == 'E') { /* 8chan */
+ if (instance->chan_ck < 8) instance->chan_ck = 8;
+ } else if (buf[2] == 'H') { /* 12chan */
+ if (instance->chan_ck < 12) instance->chan_ck = 12;
+ }
+
+ if (instance->count3++ < 5)
+ return;
+
+ instance->count3 = 0;
+
+ if (instance->chan_in != -1) /* set in Input */
+ instance->chan = instance->chan_in;
+ else /* set from test */
+ instance->chan = instance->chan_ck;
+
+ sprintf(Msg, "Input says chan = %d", instance->chan_in);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Model # says chan = %d", instance->chan_id);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Testing says chan = %d", instance->chan_ck);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Using chan = %d", instance->chan);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->o_state = ONCORE_HAVE_CHAN;
+ cp = "state = ONCORE_HAVE_CHAN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ return;
+ }
+
+ if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
+ return;
+
+ /* PAUSE 5sec - make sure results are stable, before using position */
+
+ if (instance->count) {
+ if (instance->count++ < 5)
+ return;
+ instance->count = 0;
+ }
+
+ memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */
+
+ /* check the antenna (did it get unplugged) and almanac (is it ready) for changes. */
+
+ oncore_check_almanac(instance);
+ oncore_check_antenna(instance);
+
+ /* If we are in Almanac mode, waiting for Almanac, we can't do anything till we have it */
+ /* When we have an almanac, we will start the Bn/En/@@Hn messages */
+
+ if (instance->o_state == ONCORE_ALMANAC)
+ if (oncore_wait_almanac(instance))
+ return;
+
+ /* do some things once when we get this far in BaEaHa */
+
+ if (instance->once) {
+ instance->once = 0;
+ instance->count2 = 1;
+
+ /* Have we seen an @@At (position hold) command response */
+ /* if not, message out */
+
+ if (instance->chan != 12 && !instance->saw_At) {
+ cp = "Not Good, no @@At command (no Position Hold), must be a GT/GT+";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+
+ /* have an Almanac, can start the SiteSurvey
+ * (actually only need to get past the almanac_load where we diddle with At
+ * command,- we can't change it after we start the HW_SS below
+ */
+
+ mode = instance->init_type;
+ switch (mode) {
+ case 0: /* NO initialization, don't change anything */
+ case 1: /* Use given Position */
+ case 3:
+ instance->site_survey = ONCORE_SS_DONE;
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ break;
+
+ case 2:
+ case 4: /* Site Survey */
+ cp = "SSstate = ONCORE_SS_TESTING";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_TESTING;
+ instance->count1 = 1;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */
+ break;
+ }
+
+ /* Read back PPS Offset for Output */
+ /* Nb. This will fail silently for early UT (no plus) and M12 models */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx));
+
+ /* Read back Cable Delay for Output */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx, sizeof(oncore_cmd_Azx));
+
+ /* Read back Satellite Mask Angle for Output */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Agx, sizeof(oncore_cmd_Agx));
+ }
+
+
+ /* Unfortunately, the Gd3 command returns '3' for the M12 v1.3 firmware where it is
+ * out-of-range and it should return 0-2. (v1.3 can't do a HW Site Survey)
+ * We must do the Gd3, and then wait a cycle or two for things to settle,
+ * then check Ha[130]&0x10 to see if a SS is in progress.
+ * We will set SW if HW has not been set after an appropriate delay.
+ */
+
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (instance->chan == 12) {
+ if (instance->count1) {
+ if (instance->count1++ > 5 || instance->BEHa[130]&0x10) {
+ instance->count1 = 0;
+ if (instance->BEHa[130]&0x10) {
+ record_clock_stats(&(instance->peer->srcadr),
+ "Initiating hardware 3D site survey");
+
+ record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_HW");
+ instance->site_survey = ONCORE_SS_HW;
+ } else {
+ record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_SW");
+ instance->site_survey = ONCORE_SS_SW;
+ }
+ }
+ }
+ } else {
+ if (instance->count1) {
+ if (instance->count1++ > 5) {
+ instance->count1 = 0;
+ /*
+ * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec
+ * wait after the @@At2/@@Gd3 command we have not changed the state to
+ * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then
+ * the variable would have been changed by now.
+ * There are three possibilities:
+ * 6/8chan
+ * (a) We did not get a response to the @@At0 or @@At2 commands,
+ * and it must be a GT/GT+/SL with no position hold mode.
+ * We will have to do it ourselves.
+ * (b) We saw the @@At0, @@At2 commands, but @@At2 failed,
+ * must be a VP or older UT which doesn't have Site Survey mode.
+ * We will have to do it ourselves.
+ * 12chan
+ * (c) We saw the @@Gd command, and saw H[13]*0x10
+ * We will have to do it ourselves (done above)
+ */
+
+ sprintf(Msg, "Initiating software 3D site survey (%d samples)",
+ POS_HOLD_AVERAGE);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_SW");
+ instance->site_survey = ONCORE_SS_SW;
+
+ instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
+ else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */
+ }
+ }
+ }
+ }
+ }
+
+ /* check the mode we are in 0/2/3D */
+
+ if (instance->chan == 6) {
+ if (instance->BEHa[64]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[64]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[64]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 8) {
+ if (instance->BEHa[72]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[72]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[72]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 12) {
+ int bits;
+
+ bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ if (bits == 0x4)
+ instance->mode = MODE_0D;
+ else if (bits == 0x6)
+ instance->mode = MODE_2D;
+ else if (bits == 0x7)
+ instance->mode = MODE_3D;
+ }
+
+ /* copy the record to the (extra) location in SHMEM */
+
+ if (instance->shmem) {
+ int i;
+ u_char *smp; /* pointer to start of shared mem for Ba/Ea/Ha */
+
+ switch(instance->chan) {
+ case 6: smp = &instance->shmem[instance->shmem_Ba]; break;
+ case 8: smp = &instance->shmem[instance->shmem_Ea]; break;
+ case 12: smp = &instance->shmem[instance->shmem_Ha]; break;
+ default: smp = (u_char *) NULL; break;
+ }
+
+ switch (instance->mode) {
+ case MODE_0D: i = 1; break; /* 0D, Position Hold */
+ case MODE_2D: i = 2; break; /* 2D, Altitude Hold */
+ case MODE_3D: i = 3; break; /* 3D fix */
+ default: i = 0; break;
+ }
+
+ if (i && smp != NULL) {
+ i *= (len+6);
+ smp[i + 2]++;
+ memcpy(&smp[i+3], buf, (size_t) (len+3));
+ }
+ }
+
+ /*
+ * check if traim timer active
+ * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond
+ */
+
+ if (instance->traim_delay) {
+ if (instance->traim_delay++ > 5) {
+ instance->traim = 0;
+ instance->traim_delay = 0;
+ cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_set_traim(instance);
+ } else
+ return;
+
+ }
+
+ /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */
+
+ if (!instance->have_dH && !instance->traim_delay)
+ oncore_compute_dH(instance);
+
+ /*
+ * must be ONCORE_RUN if we are here.
+ * Have # chan and TRAIM by now.
+ */
+
+ instance->pp->year = buf[6]*256+buf[7];
+ instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
+ instance->pp->hour = buf[8];
+ instance->pp->minute = buf[9];
+ instance->pp->second = buf[10];
+
+ /*
+ * Are we doing a Hardware or Software Site Survey?
+ */
+
+ if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW)
+ oncore_ss(instance);
+
+ /* see if we ever saw a response from the @@Ayx above */
+
+ if (instance->count2) {
+ if (instance->count2++ > 5) { /* this delay to check on @@Ay command */
+ instance->count2 = 0;
+
+ /* Have we seen an Ay (1PPS time offset) command response */
+ /* if not, and non-zero offset, zero the offset, and send message */
+
+ if (!instance->saw_Ay && instance->offset) {
+ cp = "No @@Ay command, PPS OFFSET ignored";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->offset = 0;
+ }
+ }
+ }
+
+ /*
+ * Check the leap second status once per day.
+ */
+
+ oncore_check_leap_sec(instance);
+
+ /*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+ if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE))
+ oncore_shmem_get_3D(instance);
+
+ if (!instance->traim) /* NO traim, no BnEnHn, go get tick */
+ oncore_get_timestamp(instance, instance->offset, instance->offset);
+}
+
+
+
+/* Almanac Status */
+
+static void
+oncore_msg_Bd(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160];
+
+ sprintf(Msg, "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
+ ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], buf[7], w32(&buf[8]) );
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* get leap-second warning message */
+
+/*
+ * @@Bj does NOT behave as documented in current Oncore firmware.
+ * It turns on the LEAP indicator when the data is set, and does not,
+ * as documented, wait until the beginning of the month when the
+ * leap second will occur.
+ * Since this firmware bug will never be fixed in all the outstanding Oncore receivers
+ * @@Bj is only called in June/December.
+ */
+
+static void
+oncore_msg_Bj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ switch(buf[4]) {
+ case 1:
+ instance->pp->leap = LEAP_ADDSECOND;
+ cp = "Set pp.leap to LEAP_ADDSECOND";
+ break;
+ case 2:
+ instance->pp->leap = LEAP_DELSECOND;
+ cp = "Set pp.leap to LEAP_DELSECOND";
+ break;
+ case 0:
+ default:
+ instance->pp->leap = LEAP_NOWARNING;
+ cp = "Set pp.leap to LEAP_NOWARNING";
+ break;
+ }
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+static void
+oncore_msg_BnEnHn(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ long dt1, dt2;
+ char *cp;
+
+ if (instance->o_state != ONCORE_RUN)
+ return;
+
+ if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */
+ instance->traim_ck = 1;
+ instance->traim_delay = 0;
+ cp = "ONCORE: Detected TRAIM, TRAIM = ON";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_set_traim(instance);
+ }
+
+ memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */
+
+ if (!instance->traim) /* BnEnHn will be turned off in any case */
+ return;
+
+ /* If Time RAIM doesn't like it, don't trust it */
+
+ if (buf[2] == 'H') {
+ if (instance->BEHn[6]) /* bad TRAIM */
+ return;
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[14]; /* update for next time Hn[14] */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ } else {
+ if (instance->BEHn[21]) /* bad TRAIM */
+ return;
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time Bn[25], En[25] */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ }
+
+ oncore_get_timestamp(instance, dt1, dt2);
+}
+
+
+
+/* Here for @@Ca, @@Fa and @@Ia messages */
+
+/* These are Self test Commands for 6, 8, and 12 chan receivers.
+ * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
+ * It was found that under some circumstances the following
+ * command would fail if issued immediately after the return from the
+ * @@Fa, but a 2sec delay seemed to fix things. Since simply calling
+ * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
+ * itimer, we set a flag, and test it at the next POLL. If it hasn't
+ * been cleared, we reissue the @@Cj that is issued below.
+ * Note that we do a @@Cj at the beginning, and again here.
+ * The first is to get the info, the 2nd is just used as a safe command
+ * after the @@Fa for all Oncores (and it was in this posn in the
+ * original code).
+ */
+
+static void
+oncore_msg_CaFaIa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+ int i;
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ enum antenna_state antenna;
+
+ instance->timeout = 0;
+
+#ifdef DEBUG
+ if (debug > 2) {
+ if (buf[2] == 'I')
+ printf("ONCORE[%d]: >>@@%ca %x %x %x\n", instance->unit, buf[2], buf[4], buf[5], buf[6]);
+ else
+ printf("ONCORE[%d]: >>@@%ca %x %x\n", instance->unit, buf[2], buf[4], buf[5]);
+ }
+#endif
+
+ antenna = (buf[4] & 0xc0) >> 6;
+ buf[4] &= ~0xc0;
+
+ i = buf[4] || buf[5];
+ if (buf[2] == 'I') i = i || buf[6];
+ if (i) {
+ if (buf[2] == 'I') {
+ msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x %02x",
+ instance->unit, buf[4], buf[5], buf[6]);
+ } else {
+ msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x",
+ instance->unit, buf[4], buf[5]);
+ }
+ cp = "ONCORE: self test failed, shutting down driver";
+ record_clock_stats(&instance->peer->srcadr, cp);
+
+ refclock_report(instance->peer, CEVNT_FAULT);
+ oncore_shutdown(instance->unit, instance->peer);
+ return;
+ }
+
+ /* report the current antenna state */
+
+ oncore_antenna_report(instance, antenna);
+
+ instance->o_state = ONCORE_INIT;
+ cp = "state = ONCORE_INIT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * Demultiplex the almanac into shmem
+ */
+
+static void
+oncore_msg_Cb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int i;
+
+ if (instance->shmem == NULL)
+ return;
+
+ if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
+ i = buf[5];
+ else if (buf[4] == 4 && buf[5] <= 5)
+ i = buf[5] + 24;
+ else if (buf[4] == 4 && buf[5] <= 10)
+ i = buf[5] + 23;
+ else if (buf[4] == 4 && buf[5] == 25)
+ i = 34;
+ else {
+ char *cp;
+
+ cp = "Cb: Response is NO ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ return;
+ }
+
+ i *= 36;
+ instance->shmem[instance->shmem_Cb + i + 2]++;
+ memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
+
+#if 1
+ {
+ char Msg[160];
+ sprintf(Msg, "See Cb [%d,%d]", buf[4], buf[5]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+#endif
+}
+
+
+
+/*
+ * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
+ * not so for VP (eeprom) or any unit with a battery
+ */
+
+static void
+oncore_msg_Cf(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_RESET_SENT) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ /* Reset set VP to IDLE */
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * This is the Grand Central Station for the Preliminary Initialization.
+ * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
+ *
+ * We do an @@Cj whenever we need a safe command for all Oncores.
+ * The @@Cj gets us back here where we can switch to the next phase of setup.
+ *
+ * o Once at the very beginning (in start) to get the Model number.
+ * This info is printed, but no longer used.
+ * o Again after we have determined the number of Channels in the receiver.
+ * o And once later after we have done a reset and test, (which may hang),
+ * as we are about to initialize the Oncore and start it running.
+ * o We have one routine below for each case.
+ */
+
+static void
+oncore_msg_Cj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int mode;
+ char *cp;
+
+ memcpy(instance->Cj, buf, len);
+
+ instance->timeout = 0;
+ if (instance->o_state == ONCORE_CHECK_ID) {
+ oncore_msg_Cj_id(instance, buf, len);
+ oncore_chan_test(instance);
+ } else if (instance->o_state == ONCORE_HAVE_CHAN) {
+ mode = instance->init_type;
+ if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */
+ instance->o_state = ONCORE_RESET_SENT;
+ cp = "state = ONCORE_RESET_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
+ } else {
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ }
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
+ else if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
+ } else if (instance->o_state == ONCORE_INIT)
+ oncore_msg_Cj_init(instance, buf, len);
+}
+
+
+
+/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
+ * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
+ * and from Motorola. Until recently Rick was the only source of
+ * this information as Motorola didn't give the information out.
+ *
+ * Determine the Type from the Model #, this determines #chan and if TRAIM is
+ * available.
+ *
+ * The Information from this routine is NO LONGER USED.
+ * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
+ */
+
+static void
+oncore_msg_Cj_id(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp, *cp1, *cp2, Model[21], Msg[160];
+
+ /* Write Receiver ID message to clockstats file */
+
+ instance->Cj[294] = '\0';
+ for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
+ cp1 = strchr(cp, '\r');
+ if (!cp1)
+ cp1 = (char *)&instance->Cj[294];
+ *cp1 = '\0';
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ *cp1 = '\r';
+ cp = cp1+2;
+ }
+
+ /* next, the Firmware Version and Revision numbers */
+
+ instance->version = atoi((char *) &instance->Cj[83]);
+ instance->revision = atoi((char *) &instance->Cj[111]);
+
+ /* from model number decide which Oncore this is,
+ and then the number of channels */
+
+ for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */
+ ;
+ cp1 = cp;
+ cp2 = Model;
+ for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++)
+ *cp2 = *cp;
+ *cp2 = '\0';
+
+ cp = 0;
+ if (!strncmp(Model, "PVT6", (size_t) 4)) {
+ cp = "PVT6";
+ instance->model = ONCORE_PVT6;
+ } else if (Model[0] == 'A') {
+ cp = "Basic";
+ instance->model = ONCORE_BASIC;
+ } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
+ cp = "VP";
+ instance->model = ONCORE_VP;
+ } else if (Model[0] == 'P') {
+ cp = "M12";
+ instance->model = ONCORE_M12;
+ } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
+ if (Model[5] == 'N') {
+ cp = "GT";
+ instance->model = ONCORE_GT;
+ } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
+ cp = "GT+";
+ instance->model = ONCORE_GTPLUS;
+ } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
+ cp = "UT";
+ instance->model = ONCORE_UT;
+ } else if (Model[1] == '5' && Model[5] == 'G') {
+ cp = "UT+";
+ instance->model = ONCORE_UTPLUS;
+ } else if (Model[1] == '6' && Model[5] == 'G') {
+ cp = "SL";
+ instance->model = ONCORE_SL;
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+
+ /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
+
+ sprintf(Msg, "This looks like an Oncore %s with version %d.%d firmware.", cp, instance->version, instance->revision);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->chan_id = 8; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->chan_id = 6;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->chan_id = 8;
+ else if (instance->model == ONCORE_M12)
+ instance->chan_id = 12;
+
+ instance->traim_id = 0; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->traim_id = 0;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->traim_id = 1;
+ else if (instance->model == ONCORE_M12)
+ instance->traim_id = -1;
+
+ sprintf(Msg, "Channels = %d, TRAIM = %s", instance->chan_id,
+ ((instance->traim_id < 0) ? "UNKNOWN" : ((instance->traim_id > 0) ? "ON" : "OFF")));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* OK, know type of Oncore, have possibly reset it, and have tested it.
+ * We know the number of channels.
+ * We will determine whether we have TRAIM before we actually start.
+ * Now initialize.
+ */
+
+static void
+oncore_msg_Cj_init(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp, Msg[160];
+ u_char Cmd[20];
+ int mode;
+
+
+ /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
+ * start again if we go from 0D -> 3D, then loses them again when we
+ * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM.
+ * For NOW we will turn this aspect of filling SHMEM off for the M12
+ */
+
+ if (instance->chan == 12) {
+ instance->shmem_bad_Ea = 1;
+ sprintf(Msg, "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***", instance->version, instance->revision);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
+
+ mode = instance->init_type;
+
+ /* If there is Position input in the Config file
+ * and mode = (1,3) set it as posn hold posn, goto 0D mode.
+ * or mode = (2,4) set it as INITIAL position, and do Site Survey.
+ */
+
+ if (instance->posn_set) {
+ record_clock_stats(&(instance->peer->srcadr), "Setting Posn from input data");
+ oncore_set_posn(instance); /* this should print posn indirectly thru the As cmd */
+ } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
+ if (instance->chan != 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
+
+ if (mode != 0) {
+ /* cable delay in ns */
+ memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
+ w32_buf(&Cmd[-2+4], instance->delay);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */
+
+ /* PPS offset in ns */
+ if (instance->offset) {
+ memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */
+ w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ay));
+ }
+
+ /* Satellite mask angle */
+
+ if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */
+ memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
+ Cmd[-2+4] = instance->Ag;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ag));
+ }
+ }
+
+ /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
+ * now we're really running
+ * these were ALL started in the chan test,
+ * However, if we had mode=3,4 then commands got turned off, so we turn
+ * them on again here just in case
+ */
+
+ if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba ));
+ } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea ));
+ } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha ));
+ }
+
+ instance->count = 1;
+ instance->o_state = ONCORE_ALMANAC;
+ cp = "state = ONCORE_ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+/* 12chan position */
+
+static void
+oncore_msg_Ga(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160];
+ long lat, lon, ht;
+ double Lat, Lon, Ht;
+
+
+ lat = buf_w32(&buf[4]);
+ lon = buf_w32(&buf[8]);
+ ht = buf_w32(&buf[12]); /* GPS ellipsoid */
+
+ Lat = lat;
+ Lon = lon;
+ Ht = ht;
+
+ Lat /= 3600000;
+ Lon /= 3600000;
+ Ht /= 100;
+
+
+ sprintf(Msg, "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat, Lon, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->ss_lat = lat;
+ instance->ss_long = lon;
+ instance->ss_ht = ht;
+
+ oncore_print_posn(instance);
+}
+
+
+
+/* 12 chan time/date */
+
+static void
+oncore_msg_Gb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160], *gmts;
+ int mo, d, y, h, m, s, gmth, gmtm;
+
+ mo = buf[4];
+ d = buf[5];
+ y = 256*buf[6]+buf[7];
+
+ h = buf[8];
+ m = buf[9];
+ s = buf[10];
+
+ gmts = ((buf[11] == 0) ? "+" : "-");
+ gmth = buf[12];
+ gmtm = buf[13];
+
+ sprintf(Msg, "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
+ d, Month[mo-1], y, h, m, s, gmts, gmth, gmtm);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* Leap Second for M12, gives all info from satellite message */
+/* also in UT v3.0 */
+
+static void
+oncore_msg_Gj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int dt;
+ char Msg[160], *cp;
+
+ instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
+
+ /* print the message to verify whats there */
+
+ dt = buf[5] - buf[4];
+
+#if 1
+ sprintf(Msg, "ONCORE[%d]: Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
+ instance->unit,
+ buf[4], buf[5], 256*buf[6]+buf[7], buf[8], buf[9], buf[10],
+ (buf[14]+256*(buf[13]+256*(buf[12]+256*buf[11]))),
+ buf[15], buf[16], buf[17]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+#endif
+ if (dt) {
+ sprintf(Msg, "ONCORE[%d]: Leap second (%d) scheduled for %d%s%d at %d:%d:%d",
+ instance->unit,
+ dt, buf[9], Month[buf[8]-1], 256*buf[6]+buf[7],
+ buf[15], buf[16], buf[17]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+
+ /* Only raise warning within a month of the leap second */
+
+ instance->pp->leap = LEAP_NOWARNING;
+ cp = "Set pp.leap to LEAP_NOWARNING";
+
+ if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
+ buf[8] == instance->BEHa[4]) { /* month */
+ if (dt) {
+ if (dt < 0) {
+ instance->pp->leap = LEAP_DELSECOND;
+ cp = "Set pp.leap to LEAP_DELSECOND";
+ } else {
+ instance->pp->leap = LEAP_ADDSECOND;
+ cp = "Set pp.leap to LEAP_ADDSECOND";
+ }
+ }
+ }
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+/* Power on failure */
+
+static void
+oncore_msg_Sz(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ cp = "Oncore: System Failure at Power On";
+ if (instance && instance->peer) {
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_shutdown(instance->unit, instance->peer);
+ }
+}
+
+/************** Small Subroutines ***************/
+
+
+static void
+oncore_antenna_report(
+ struct instance *instance,
+ enum antenna_state new_state)
+{
+ char *cp;
+
+ if (instance->ant_state == new_state)
+ return;
+
+ switch (new_state) {
+ case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break;
+ case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break;
+ case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
+ case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break;
+ default: cp = "GPS antenna: ?"; break;
+ }
+
+ instance->ant_state = new_state;
+ record_clock_stats(&instance->peer->srcadr, cp);
+}
+
+
+
+static void
+oncore_chan_test(
+ struct instance *instance
+ )
+{
+ char *cp;
+
+ /* subroutine oncore_Cj_id has determined the number of channels from the
+ * model number of the attached oncore. This is not always correct since
+ * the oncore could have non-standard firmware. Here we check (independently) by
+ * trying a 6, 8, and 12 chan command, and see which responds.
+ * Caution: more than one CAN respond.
+ *
+ * This #chan is used by the code rather than that calculated from the model number.
+ */
+
+ instance->o_state = ONCORE_CHECK_CHAN;
+ cp = "state = ONCORE_CHECK_CHAN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->count3 = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
+}
+
+
+
+/* check for a GOOD Almanac, have we got one yet? */
+
+static void
+oncore_check_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->chan == 6) {
+ instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[64]&0x52;
+ } else if (instance->chan == 8) {
+ instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[72]&0x52;
+ } else if (instance->chan == 12) {
+ int bits1, bits2, bits3;
+
+ bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ bits2 = instance->BEHa[130];
+ instance->rsm.bad_almanac = (bits2 & 0x80);
+ instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2);
+ /* too few sat Bad Geom */
+
+ bits3 = instance->BEHa[141]; /* UTC parameters */
+ if (!instance->count5_set && (bits3 & 0xC0)) {
+ instance->count5 = 2;
+ instance->count5_set = 1;
+ }
+#if 0
+{
+ char Msg[160];
+
+ sprintf(Msg, "ONCORE[%d]: DEBUG BITS: (%x %x), (%x %x %x), %x %x %x %x %x\n",
+ instance->unit,
+ instance->BEHa[129], instance->BEHa[130], bits1, bits2, bits3, instance->mode == MODE_0D,
+ instance->mode == MODE_2D, instance->mode == MODE_3D,
+ instance->rsm.bad_almanac, instance->rsm.bad_fix);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+#endif
+ }
+}
+
+
+
+/* check the antenna for changes (did it get unplugged?) */
+
+static void
+oncore_check_antenna(
+ struct instance *instance
+ )
+{
+ enum antenna_state antenna; /* antenna state */
+
+ antenna = instance->ant_state;
+ if (instance->chan == 12)
+ antenna = (instance->BEHa[130] & 0x6 ) >> 1;
+ else
+ antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */
+
+ oncore_antenna_report (instance, antenna);
+}
+
+
+
+/*
+ * Check the leap second status once per day.
+ *
+ * Note that the ONCORE firmware for the Bj command is wrong at
+ * least in the VP.
+ * It starts advertising a LEAP SECOND as soon as the GPS satellite
+ * data message (page 18, subframe 4) is updated to a date in the
+ * future, and does not wait for the month that it will occur.
+ * The event will usually be advertised several months in advance.
+ * Since there is a one bit flag, there is no way to tell if it is
+ * this month, or when...
+ *
+ * As such, we have the workaround below, of only checking for leap
+ * seconds with the Bj command in June/December.
+ *
+ * The Gj command gives more information, and we can tell in which
+ * month to apply the correction.
+ *
+ * Note that with the VP we COULD read the raw data message, and
+ * interpret it ourselves, but since this is specific to this receiver
+ * only, and the above workaround is adequate, we don't bother.
+ */
+
+static void
+oncore_check_leap_sec(
+ struct instance *instance
+ )
+{
+ if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */
+ instance->Bj_day = instance->BEHa[5];
+
+ if (instance->saw_Gj < 0) { /* -1 DONT have Gj use Bj */
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ return;
+ }
+
+ if (instance->saw_Gj == 0) /* 0 is dont know if we have Gj */
+ instance->count4 = 1;
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
+ return;
+ }
+
+ /* Gj works for some 6/8 chan UT and the M12 */
+ /* if no response from Gj in 5 sec, we try Bj */
+ /* which isnt implemented in all the GT/UT either */
+
+ if (instance->count4) { /* delay, waiting for Gj response */
+ if (instance->saw_Gj == 1)
+ instance->count4 = 0;
+ else if (instance->count4++ > 5) { /* delay, waiting for Gj response */
+ instance->saw_Gj = -1; /* didnt see it, will use Bj */
+ instance->count4 = 0;
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ }
+ }
+}
+
+
+
+/* check the message checksum,
+ * buf points to START of message ( @@ )
+ * len is length WITH CR/LF.
+ */
+
+static int
+oncore_checksum_ok(
+ u_char *buf,
+ int len
+ )
+{
+ int i, j;
+
+ j = 0;
+ for (i = 2; i < len-3; i++)
+ j ^= buf[i];
+
+ return(j == buf[len-3]);
+}
+
+
+
+static void
+oncore_compute_dH(
+ struct instance *instance
+ )
+{
+ int GPS, MSL;
+ char Msg[160];
+
+ /* Here calculate dH = GPS - MSL for output message */
+ /* also set Altitude Hold mode if GT */
+
+ instance->have_dH = 1;
+ if (instance->chan == 12) {
+ GPS = buf_w32(&instance->BEHa[39]);
+ MSL = buf_w32(&instance->BEHa[43]);
+ } else {
+ GPS = buf_w32(&instance->BEHa[23]);
+ MSL = buf_w32(&instance->BEHa[27]);
+ }
+ instance->dH = GPS - MSL;
+ instance->dH /= 100.;
+
+ /* if MSL is not set, the calculation is meaningless */
+
+ if (MSL) { /* not set ! */
+ sprintf(Msg, "dH = (GPS - MSL) = %.2fm", instance->dH);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+}
+
+
+
+/*
+ * try loading Almanac from shmem (where it was copied from shmem_old
+ */
+
+static void
+oncore_load_almanac(
+ struct instance *instance
+ )
+{
+ u_char *cp, Cmd[20];
+ int n;
+ struct timeval tv;
+ struct tm *tm;
+
+ if (!instance->shmem)
+ return;
+
+#if 1
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ if (!strncmp((char *) cp, "@@Cb", 4) &&
+ oncore_checksum_ok(cp, 33) &&
+ (*(cp+4) == 4 || *(cp+4) == 5)) {
+ write(instance->ttyfd, cp, n);
+#if 1
+ oncore_print_Cb(instance, cp);
+#endif
+ }
+ }
+#else
+/************DEBUG************/
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ char Msg[160];
+
+ sprintf(Msg, "See %c%c%c%c %d", *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ if (!strncmp(cp, "@@Cb", 4)) {
+ oncore_print_Cb(instance, cp);
+ if (oncore_checksum_ok(cp, 33)) {
+ if (*(cp+4) == 4 || *(cp+4) == 5) {
+ record_clock_stats(&(instance->peer->srcadr), "GOOD SF");
+ write(instance->ttyfd, cp, n);
+ } else
+ record_clock_stats(&(instance->peer->srcadr), "BAD SF");
+ } else
+ record_clock_stats(&(instance->peer->srcadr), "BAD CHECKSUM");
+ }
+ }
+/************DEBUG************/
+#endif
+
+ /* Must load position and time or the Almanac doesn't do us any good */
+
+ if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */
+ record_clock_stats(&(instance->peer->srcadr), "Loading Posn from SHMEM");
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ if ((instance->chan == 6 && (!strncmp((char *) cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) ||
+ (instance->chan == 8 && (!strncmp((char *) cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) ||
+ (instance->chan == 12 && (!strncmp((char *) cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) {
+ int ii, jj, kk;
+
+ instance->posn_set = 1;
+ ii = buf_w32(cp + 15);
+ jj = buf_w32(cp + 19);
+ kk = buf_w32(cp + 23);
+#if 0
+{
+char Msg[160];
+sprintf(Msg, "SHMEM posn = %ld (%d, %d, %d)", (long) (cp-instance->shmem), ii, jj, kk);
+record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+#endif
+ if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */
+ instance->ss_lat = ii;
+ instance->ss_long = jj;
+ instance->ss_ht = kk;
+ }
+ }
+ }
+ }
+ oncore_set_posn(instance);
+
+ /* and set time to time from Computer clock */
+
+ gettimeofday(&tv, 0);
+ tm = gmtime((const time_t *) &tv.tv_sec);
+#if 1
+ {
+ char Msg[160];
+ sprintf(Msg, "DATE %d %d %d, %d %d %d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+#endif
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb));
+ Cmd[-2+4] = tm->tm_mon + 1;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ Cmd[-2+8] = tm->tm_hour;
+ Cmd[-2+9] = tm->tm_min;
+ Cmd[-2+10] = tm->tm_sec;
+ Cmd[-2+11] = 0;
+ Cmd[-2+12] = 0;
+ Cmd[-2+13] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Gb));
+ } else {
+ /* First set GMT offset to zero */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ab, sizeof(oncore_cmd_Ab));
+
+ memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac));
+ Cmd[-2+4] = tm->tm_mon + 1;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ac));
+
+ memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa));
+ Cmd[-2+4] = tm->tm_hour;
+ Cmd[-2+5] = tm->tm_min;
+ Cmd[-2+6] = tm->tm_sec;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Aa));
+ }
+
+ record_clock_stats(&(instance->peer->srcadr), "Setting Posn and Time after Loading Almanac");
+}
+
+
+
+/* Almanac data input */
+
+static void
+oncore_print_Cb(
+ struct instance *instance,
+ u_char *cp
+ )
+{
+#if 0
+ int ii;
+ char Msg[160];
+
+ printf("DEBUG: See: %c%c%c%c\n", *(cp), *(cp+1), *(cp+2), *(cp+3));
+ printf("DEBUG: Cb: [%d,%d]", *(cp+4), *(cp+5));
+ for(ii=0; ii<33; ii++)
+ printf(" %d", *(cp+ii));
+ printf("\n");
+
+ sprintf(Msg, "Debug: Cb: [%d,%d]", *(cp+4), *(cp+5));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+#endif
+}
+
+
+#if 0
+static void
+oncore_print_array(
+ u_char *cp,
+ int n
+ )
+{
+ int jj, i, j, nn;
+
+ nn = 0;
+ printf("\nTOP\n");
+ jj = n/16;
+ for (j=0; j<jj; j++) {
+ printf("%4d: ", nn);
+ nn += 16;
+ for (i=0; i<16; i++)
+ printf(" %o", *cp++);
+ printf("\n");
+ }
+}
+#endif
+
+
+static void
+oncore_print_posn(
+ struct instance *instance
+ )
+{
+ char Msg[120], ew, ns;
+ double xd, xm, xs, yd, ym, ys, hm, hft;
+ int idx, idy, is, imx, imy;
+ long lat, lon;
+
+ record_clock_stats(&(instance->peer->srcadr), "Posn:");
+ ew = 'E';
+ lon = instance->ss_long;
+ if (lon < 0) {
+ ew = 'W';
+ lon = -lon;
+ }
+
+ ns = 'N';
+ lat = instance->ss_lat;
+ if (lat < 0) {
+ ns = 'S';
+ lat = -lat;
+ }
+
+ hm = instance->ss_ht/100.;
+ hft= hm/0.3048;
+
+ xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */
+ yd = lon/3600000.;
+ sprintf(Msg, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ idx = xd;
+ idy = yd;
+ imx = lat%3600000;
+ imy = lon%3600000;
+ xm = imx/60000.;
+ ym = imy/60000.;
+ sprintf(Msg,
+ "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ imx = xm;
+ imy = ym;
+ is = lat%60000;
+ xs = is/1000.;
+ is = lon%60000;
+ ys = is/1000.;
+ sprintf(Msg,
+ "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * write message to Oncore.
+ */
+
+static void
+oncore_sendmsg(
+ int fd,
+ u_char *ptr,
+ size_t len
+ )
+{
+ u_char cs = 0;
+
+#ifdef DEBUG
+ if (debug > 4)
+ printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], (int) len);
+#endif
+ write(fd, "@@", (size_t) 2);
+ write(fd, ptr, len);
+ while (len--)
+ cs ^= *ptr++;
+ write(fd, &cs, (size_t) 1);
+ write(fd, "\r\n", (size_t) 2);
+}
+
+
+
+static void
+oncore_set_posn(
+ struct instance *instance
+ )
+{
+ int mode;
+ u_char Cmd[20];
+
+ /* Turn OFF position hold, it needs to be off to set position (for some units),
+ will get set ON in @@Ea later */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
+ else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */
+ }
+
+ mode = instance->init_type;
+
+ if (mode != 0) { /* first set posn hold position */
+ memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12], (int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */
+
+ memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */
+
+ /* next set current position */
+
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12],(int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */
+ } else {
+ memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_long);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */
+ }
+
+ /* Finally, turn on position hold */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1));
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1));
+ }
+}
+
+
+
+static void
+oncore_set_traim(
+ struct instance *instance
+ )
+{
+ char Msg[160];
+
+ if (instance->traim_in != -1) /* set in Input */
+ instance->traim = instance->traim_in;
+ else
+ instance->traim = instance->traim_ck;
+
+ sprintf(Msg, "Input says TRAIM = %d", instance->traim_in);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Model # says TRAIM = %d", instance->traim_id);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Testing says TRAIM = %d", instance->traim_ck);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Using TRAIM = %d", instance->traim);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ if (instance->traim_ck == 1 && instance->traim == 0) {
+ /* if it should be off, and I turned it on during testing,
+ then turn it off again */
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
+ else /* chan == 12 */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ }
+}
+
+
+
+/*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+static void
+oncore_shmem_get_3D(
+ struct instance *instance
+ )
+{
+ if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */
+ instance->shmem_reset = 1;
+ if (instance->chan == 12) {
+ if (instance->shmem_Posn == 2)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */
+ } else {
+ if (instance->saw_At) { /* out of 0D -> 3D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0));
+ if (instance->shmem_Posn == 2) /* 3D -> 2D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ } else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ }
+ } else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
+ instance->shmem_reset = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */
+ else {
+ if (instance->saw_At) {
+ if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */
+ } else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+ }
+}
+
+
+
+/*
+ * Here we do the Software SiteSurvey.
+ * We have to average our own position for the Position Hold Mode
+ * We use Heights from the GPS ellipsoid.
+ * We check for the END of either HW or SW SiteSurvey.
+ */
+
+static void
+oncore_ss(
+ struct instance *instance
+ )
+{
+ char *cp, Msg[160];
+ double lat, lon, ht;
+
+
+ if (instance->site_survey == ONCORE_SS_HW) {
+ /*
+ * Check to see if Hardware SiteSurvey has Finished.
+ */
+
+ if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) ||
+ (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) {
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
+
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+ } else {
+ /*
+ * Must be a Software Site Survey.
+ */
+
+ if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */
+ return;
+
+ if (instance->mode != MODE_3D) /* Use only 3D Fixes */
+ return;
+
+ instance->ss_lat += buf_w32(&instance->BEHa[15]);
+ instance->ss_long += buf_w32(&instance->BEHa[19]);
+ instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */
+ instance->ss_count++;
+
+ if (instance->ss_count != POS_HOLD_AVERAGE)
+ return;
+
+ instance->ss_lat /= POS_HOLD_AVERAGE;
+ instance->ss_long /= POS_HOLD_AVERAGE;
+ instance->ss_ht /= POS_HOLD_AVERAGE;
+
+ sprintf(Msg, "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
+ instance->ss_lat, instance->ss_long, instance->ss_ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ lat = instance->ss_lat/3600000.;
+ lon = instance->ss_long/3600000.;
+ ht = instance->ss_ht/100;
+ sprintf(Msg, "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
+ lat, lon, ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ oncore_set_posn(instance);
+
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+}
+
+
+
+static int
+oncore_wait_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->rsm.bad_almanac) {
+#ifdef DEBUG
+ if (debug)
+ printf("ONCORE[%d]: waiting for almanac\n", instance->unit);
+#endif
+
+ /*
+ * If we get here (first time) then we don't have an almanac in memory.
+ * Check if we have a SHMEM, and if so try to load whatever is there.
+ */
+
+ if (!instance->almanac_from_shmem) {
+ instance->almanac_from_shmem = 1;
+ oncore_load_almanac(instance);
+ }
+ return(1);
+ } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn
+ commands, and can finally check for TRAIM. Again, we set a delay
+ (5sec) and wait for things to settle down */
+
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof(oncore_cmd_En));
+ else if (instance->chan == 12) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */
+ }
+ instance->traim_delay = 1;
+
+ record_clock_stats(&(instance->peer->srcadr), "Have now loaded an ALMANAC");
+
+ instance->o_state = ONCORE_RUN;
+ record_clock_stats(&(instance->peer->srcadr), "state = ONCORE_RUN");
+ }
+ return(0);
+}
+
+
+
+#else
+int refclock_oncore_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_palisade.c b/contrib/ntp/ntpd/refclock_palisade.c
new file mode 100644
index 0000000..217ec10
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_palisade.c
@@ -0,0 +1,954 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ * Version 2.45; July 14, 1999
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
+
+#include "refclock_palisade.h"
+/* Table to get from month to day of the year */
+const int days_of_year [12] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+#ifdef DEBUG
+const char * Tracking_Status[15][15] = {
+ { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
+ {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
+ { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
+ { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
+ { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_palisade = {
+ palisade_start, /* start up driver */
+ palisade_shutdown, /* shut down driver */
+ palisade_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+int day_of_year P((char *dt));
+
+/* Extract the clock type from the mode setting */
+#define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
+
+/* Supported clock types */
+#define CLK_TRIMBLE 0 /* Trimble Palisade */
+#define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
+
+int praecis_msg;
+static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
+
+/*
+ * palisade_start - open the devices and initialize data for processing
+ */
+static int
+palisade_start (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else /* ANSI */
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ struct termios tio;
+#ifdef SYS_WINNT
+ (void) sprintf(gpsdev, "COM%d:", unit);
+#else
+ (void) sprintf(gpsdev, DEVICE, unit);
+#endif
+ /*
+ * Open serial port.
+ */
+#if defined PALISADE
+ fd = open(gpsdev, O_RDWR
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ );
+#else /* NTP 4.x */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+#endif
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("Palisade(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "Palisade(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+#if defined PALISADE
+ tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD);
+ tio.c_iflag = (IGNBRK);
+ tio.c_oflag = (0);
+ tio.c_lflag = (0);
+
+ if (cfsetispeed(&tio, SPEED232) == -1) {
+ msyslog(LOG_ERR,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit);
+#endif
+ return 0;
+ }
+ if (cfsetospeed(&tio, SPEED232) == -1) {
+#ifdef DEBUG
+ printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit);
+#endif
+ msyslog(LOG_ERR,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit);
+ return 0;
+ }
+#else /* NTP 4.x */
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR,
+ "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit);
+#endif
+ return (0);
+ }
+
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+#endif /* NTP 4.x */
+
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit);
+#endif
+ return 0;
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct palisade_unit *) emalloc(sizeof(struct palisade_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "Palisade(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) emalloc\n",unit);
+#endif
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct palisade_unit));
+
+ up->type = CLK_TYPE(peer);
+ switch (up->type) {
+ case CLK_TRIMBLE:
+ /* Normal mode, do nothing */
+ break;
+ case CLK_PRAECIS:
+ msyslog(LOG_NOTICE, "Palisade(%d) Praecis mode enabled\n",unit);
+ break;
+ default:
+ msyslog(LOG_NOTICE, "Palisade(%d) mode unknown\n",unit);
+ break;
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = palisade_io;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("Palisade(%d) io_addclock\n",unit);
+#endif
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->unitptr = (caddr_t)up;
+ pp->clockdesc = DESCRIPTION;
+
+ peer->precision = PRECISION;
+ peer->sstclktype = CTL_SST_TS_UHF;
+ peer->minpoll = TRMB_MINPOLL;
+ peer->maxpoll = TRMB_MAXPOLL;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ up->rpt_cnt = 0;
+
+ return 1;
+}
+
+
+/*
+ * palisade_shutdown - shut down the clock
+ */
+static void
+palisade_shutdown (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else /* ANSI */
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+
+/*
+ * unpack_date - get day and year from date
+ */
+int
+day_of_year (
+#ifdef PALISADE
+ dt
+ )
+ char * dt;
+#else
+ char * dt
+ )
+#endif
+{
+ int day, mon, year;
+
+ mon = dt[1];
+ /* Check month is inside array bounds */
+ if ((mon < 1) || (mon > 12))
+ return -1;
+
+ day = dt[0] + days_of_year[mon - 1];
+ year = getint((u_char *) (dt + 2));
+
+ if ( !(year % 4) && ((year % 100) ||
+ (!(year % 100) && !(year%400)))
+ &&(mon > 2))
+ day ++; /* leap year and March or later */
+
+ return day;
+}
+
+
+/*
+ * TSIP_decode - decode the TSIP data packets
+ */
+int
+TSIP_decode (
+#ifdef PALISADE
+ peer
+ )
+ struct peer *peer;
+#else
+ struct peer *peer
+ )
+#endif
+{
+ int st;
+ long secint;
+ double secs;
+ double secfrac;
+ unsigned short event = 0;
+
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ /*
+ * Check the time packet, decode its contents.
+ * If the timecode has invalid length or is not in
+ * proper format, declare bad format and exit.
+ */
+
+ if ((up->rpt_buf[0] == (char) 0x41) ||
+ (up->rpt_buf[0] == (char) 0x46) ||
+ (up->rpt_buf[0] == (char) 0x54) ||
+ (up->rpt_buf[0] == (char) 0x4B) ||
+ (up->rpt_buf[0] == (char) 0x6D)) {
+
+ /* standard time packet - GPS time and GPS week number */
+#ifdef DEBUG
+ printf("Palisade Port B packets detected. Connect to Port A\n");
+#endif
+
+ return 0;
+ }
+
+ /*
+ * We cast both to u_char to as 0x8f uses the sign bit on a char
+ */
+ if ((u_char) up->rpt_buf[0] == (u_char) 0x8f) {
+ /*
+ * Superpackets
+ */
+ event = (unsigned short) (getint((u_char *) &mb(1)) & 0xffff);
+ if (!((pp->sloppyclockflag & CLK_FLAG2) || event))
+ /* Ignore Packet */
+ return 0;
+
+ switch (mb(0) & 0xff) {
+ int GPS_UTC_Offset;
+ case PACKET_8F0B:
+
+ if (up->polled <= 0)
+ return 0;
+
+ if (up->rpt_cnt != LENCODE_8F0B) /* check length */
+ break;
+
+#ifdef DEBUG
+if (debug > 1) {
+ int ts;
+ double lat, lon, alt;
+ lat = getdbl((u_char *) &mb(42)) * R2D;
+ lon = getdbl((u_char *) &mb(50)) * R2D;
+ alt = getdbl((u_char *) &mb(58));
+
+ printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
+ up->unit, lat,lon,alt);
+ printf("TSIP_decode: unit %d: Sats:", up->unit);
+ for (st = 66, ts = 0; st <= 73; st++) if (mb(st)) {
+ if (mb(st) > 0) ts++;
+ printf(" %02d", mb(st));
+ }
+ printf(" : Tracking %d\n", ts);
+ }
+#endif
+
+ GPS_UTC_Offset = getint((u_char *) &mb(16));
+ if (GPS_UTC_Offset == 0) { /* Check UTC offset */
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Offset Unknown\n");
+#endif
+ break;
+ }
+
+ secs = getdbl((u_char *) &mb(3));
+ secint = (long) secs;
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+
+ pp->nsec = (long) (secfrac * 1000000000);
+
+ secint %= 86400; /* Only care about today */
+ pp->hour = secint / 3600;
+ secint %= 3600;
+ pp->minute = secint / 60;
+ secint %= 60;
+ pp->second = secint % 60;
+
+ if ((pp->day = day_of_year(&mb(11))) < 0) break;
+
+ pp->year = getint((u_char *) &mb(13));
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02d\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(12), mb(11), pp->year, GPS_UTC_Offset);
+#endif
+ /* Only use this packet when no
+ * 8F-AD's are being received
+ */
+
+ if (up->leap_status) {
+ up->leap_status = 0;
+ return 0;
+ }
+
+ return 2;
+ break;
+
+ case PACKET_NTP:
+ /* Palisade-NTP Packet */
+
+ if (up->rpt_cnt != LENCODE_NTP) /* check length */
+ break;
+
+ up->leap_status = mb(19);
+
+ if (up->polled <= 0)
+ return 0;
+
+ /* Check Tracking Status */
+ st = mb(18);
+ if (st < 0 || st > 14) st = 14;
+ if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
+#ifdef DEBUG
+ printf("TSIP_decode: Not Tracking Sats : %s\n",
+ *Tracking_Status[st]);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ break;
+ }
+
+ if (up->leap_status & PALISADE_LEAP_PENDING) {
+ if (up->leap_status & PALISADE_UTC_TIME)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_DELSECOND;
+ }
+ else if (up->leap_status)
+ pp->leap = LEAP_NOWARNING;
+
+ else { /* UTC flag is not set:
+ * Receiver may have been reset, and lost
+ * its UTC almanac data */
+ pp->leap = LEAP_NOTINSYNC;
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Almanac unavailable: %d\n",
+ mb(19));
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ }
+
+ pp->nsec = (long) (getdbl((u_char *) &mb(3)) * 1000000000);
+
+ if ((pp->day = day_of_year(&mb(14))) < 0)
+ break;
+ pp->year = getint((u_char *) &mb(16));
+ pp->hour = mb(11);
+ pp->minute = mb(12);
+ pp->second = mb(13);
+
+#ifdef DEBUG
+ if (debug > 1)
+printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(15), mb(14), pp->year,
+ mb(19), *Tracking_Status[st]);
+#endif
+ return 1;
+ break;
+
+ default:
+ /* Ignore Packet */
+ return 0;
+ } /* switch */
+ }/* if 8F packets */
+
+ refclock_report(peer, CEVNT_BADREPLY);
+ up->polled = -1;
+#ifdef DEBUG
+ printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
+ up->unit, up->rpt_buf[0] & 0xff, mb(0) & 0xff,
+ event, up->rpt_cnt);
+#endif
+ return 0;
+}
+
+/*
+ * palisade__receive - receive data from the serial interface
+ */
+
+static void
+palisade_receive (
+#ifdef PALISADE
+ peer
+ )
+ struct peer * peer;
+#else /* ANSI */
+ struct peer * peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ if (! TSIP_decode(peer)) return;
+
+ if (up->polled <= 0)
+ return; /* no poll pending, already received or timeout */
+
+ up->polled = 0; /* Poll reply received */
+ pp->lencode = 0; /* clear time code */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%06ld\n",
+ up->unit, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, pp->nsec);
+#endif
+
+ /*
+ * Process the sample
+ * Generate timecode: YYYY DoY HH:MM:SS.microsec
+ * report and process
+ */
+
+ (void) sprintf(pp->a_lastcode,"%4d %03d %02d:%02d:%02d.%06ld",
+ pp->year,pp->day,pp->hour,pp->minute, pp->second,pp->nsec);
+ pp->lencode = 24;
+
+#ifdef PALISADE
+ pp->lasttime = current_time;
+#endif
+ if (!refclock_process(pp
+#ifdef PALISADE
+ , PALISADE_SAMPLES, PALISADE_SAMPLES * 3 / 5
+#endif
+ )) {
+ refclock_report(peer, CEVNT_BADTIME);
+
+#ifdef DEBUG
+ printf("palisade_receive: unit %d: refclock_process failed!\n",
+ up->unit);
+#endif
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_receive: unit %d: %s\n",
+ up->unit, prettydate(&pp->lastrec));
+#endif
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer
+#ifdef PALISADE
+ , &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap
+#endif
+ );
+}
+
+
+/*
+ * palisade_poll - called by the transmit procedure
+ *
+ */
+static void
+palisade_poll (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ pp->polls++;
+ if (up->polled > 0) /* last reply never arrived or error */
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ up->polled = 2; /* synchronous packet + 1 event */
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_poll: unit %d: polling %s\n", unit,
+ (pp->sloppyclockflag & CLK_FLAG2) ?
+ "synchronous packet" : "event");
+#endif
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ return; /* using synchronous packet input */
+
+ if(up->type == CLK_PRAECIS) {
+ if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
+ msyslog(LOG_ERR, "Palisade(%d) write: %m:",unit);
+ else {
+ praecis_msg = 1;
+ return;
+ }
+ }
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+}
+
+static void
+praecis_parse(struct recvbuf *rbufp, struct peer *peer)
+{
+ static char buf[100];
+ static int p = 0;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
+ p += rbufp->recv_length;
+
+ if(buf[p-2] == '\r' && buf[p-1] == '\n') {
+ buf[p-2] = '\0';
+ record_clock_stats(&peer->srcadr, buf);
+
+ p = 0;
+ praecis_msg = 0;
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+
+ }
+}
+
+static void
+palisade_io (
+#ifdef PALISADE
+ rbufp
+ )
+ struct recvbuf *rbufp;
+#else /* ANSI */
+ struct recvbuf *rbufp
+ )
+#endif
+{
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ char * c, * d;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ if(up->type == CLK_PRAECIS) {
+ if(praecis_msg) {
+ praecis_parse(rbufp,peer);
+ return;
+ }
+ }
+
+ c = (char *) &rbufp->recv_space;
+ d = c + rbufp->recv_length;
+
+ while (c != d) {
+
+ /* Build time packet */
+ switch (up->rpt_status) {
+
+ case TSIP_PARSED_DLE_1:
+ switch (*c)
+ {
+ case 0:
+ case DLE:
+ case ETX:
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ break;
+
+ default:
+ up->rpt_status = TSIP_PARSED_DATA;
+ /* save packet ID */
+ up->rpt_buf[0] = *c;
+ break;
+ }
+ break;
+
+ case TSIP_PARSED_DATA:
+ if (*c == DLE)
+ up->rpt_status = TSIP_PARSED_DLE_2;
+ else
+ mb(up->rpt_cnt++) = *c;
+ break;
+
+ case TSIP_PARSED_DLE_2:
+ if (*c == DLE) {
+ up->rpt_status = TSIP_PARSED_DATA;
+ mb(up->rpt_cnt++) =
+ *c;
+ }
+ else if (*c == ETX)
+ up->rpt_status = TSIP_PARSED_FULL;
+ else {
+ /* error: start new report packet */
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ up->rpt_buf[0] = *c;
+ }
+ break;
+
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ if ( *c != DLE)
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ else
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ break;
+ }
+
+ c++;
+
+ if (up->rpt_status == TSIP_PARSED_DLE_1) {
+ up->rpt_cnt = 0;
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ /* stamp it */
+ get_systime(&pp->lastrec);
+ }
+ else if (up->rpt_status == TSIP_PARSED_EMPTY)
+ up->rpt_cnt = 0;
+
+ else if (up->rpt_cnt > BMAX)
+ up->rpt_status =TSIP_PARSED_EMPTY;
+
+ if (up->rpt_status == TSIP_PARSED_FULL)
+ palisade_receive(peer);
+
+ } /* while chars in buffer */
+}
+
+
+/*
+ * Trigger the Palisade's event input, which is driven off the RTS
+ *
+ * Take a system time stamp to match the GPS time stamp.
+ *
+ */
+long
+HW_poll (
+#ifdef PALISADE
+ pp /* pointer to unit structure */
+ )
+ struct refclockproc * pp; /* pointer to unit structure */
+#else
+ struct refclockproc * pp /* pointer to unit structure */
+ )
+#endif
+{
+ int x; /* state before & after RTS set */
+ struct palisade_unit *up;
+
+ up = (struct palisade_unit *) pp->unitptr;
+
+ /* read the current status, so we put things back right */
+ if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: GET %s\n", up->unit, strerror(errno));
+#endif
+ msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
+ up->unit);
+ return -1;
+ }
+
+ x |= TIOCM_RTS; /* turn on RTS */
+
+ /* Edge trigger */
+ if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: SET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
+ up->unit);
+ return -1;
+ }
+
+ x &= ~TIOCM_RTS; /* turn off RTS */
+
+ /* poll timestamp */
+ get_systime(&pp->lastrec);
+
+ if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: UNSET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
+ up->unit);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if 0 /* unused */
+/*
+ * this 'casts' a character array into a float
+ */
+float
+getfloat (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+ float sval;
+#ifdef WORDS_BIGENDIAN
+ ((char *) &sval)[0] = *bp++;
+ ((char *) &sval)[1] = *bp++;
+ ((char *) &sval)[2] = *bp++;
+ ((char *) &sval)[3] = *bp++;
+#else
+ ((char *) &sval)[3] = *bp++;
+ ((char *) &sval)[2] = *bp++;
+ ((char *) &sval)[1] = *bp++;
+ ((char *) &sval)[0] = *bp;
+#endif /* ! XNTP_BIG_ENDIAN */
+ return sval;
+}
+#endif
+
+/*
+ * this 'casts' a character array into a double
+ */
+double
+getdbl (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+ double dval;
+#ifdef WORDS_BIGENDIAN
+ ((char *) &dval)[0] = *bp++;
+ ((char *) &dval)[1] = *bp++;
+ ((char *) &dval)[2] = *bp++;
+ ((char *) &dval)[3] = *bp++;
+ ((char *) &dval)[4] = *bp++;
+ ((char *) &dval)[5] = *bp++;
+ ((char *) &dval)[6] = *bp++;
+ ((char *) &dval)[7] = *bp;
+#else
+ ((char *) &dval)[7] = *bp++;
+ ((char *) &dval)[6] = *bp++;
+ ((char *) &dval)[5] = *bp++;
+ ((char *) &dval)[4] = *bp++;
+ ((char *) &dval)[3] = *bp++;
+ ((char *) &dval)[2] = *bp++;
+ ((char *) &dval)[1] = *bp++;
+ ((char *) &dval)[0] = *bp;
+#endif /* ! XNTP_BIG_ENDIAN */
+ return dval;
+}
+
+/*
+ * cast a 16 bit character array into a short (16 bit) int
+ */
+short
+getint (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+return (short) (bp[1] + (bp[0] << 8));
+}
+
+#else
+int refclock_palisade_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_palisade.h b/contrib/ntp/ntpd/refclock_palisade.h
new file mode 100644
index 0000000..e63f3da
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_palisade.h
@@ -0,0 +1,170 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ */
+
+#ifndef _REFCLOCK_PALISADE_H
+#define _REFCLOCK_PALISADE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined HAVE_SYS_MODEM_H
+#include <sys/modem.h>
+#ifndef __QNXNTO__
+#define TIOCMSET MCSETA
+#define TIOCMGET MCGETA
+#define TIOCM_RTS MRTS
+#endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * GPS Definitions
+ */
+#define DESCRIPTION "Trimble Palisade GPS" /* Long name */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define TRMB_MINPOLL 4 /* 16 seconds */
+#define TRMB_MAXPOLL 5 /* 32 seconds */
+
+/*
+ * I/O Definitions
+ */
+#define DEVICE "/dev/palisade%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * TSIP Report Definitions
+ */
+#define LENCODE_8F0B 74 /* Length of TSIP 8F-0B Packet & header */
+#define LENCODE_NTP 22 /* Length of Palisade NTP Packet */
+
+/* Allowed Sub-Packet ID's */
+#define PACKET_8F0B 0x0B
+#define PACKET_NTP 0xAD
+
+#define DLE 0x10
+#define ETX 0x03
+
+/* parse states */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+/*
+ * Leap-Insert and Leap-Delete are encoded as follows:
+ * PALISADE_UTC_TIME set and PALISADE_LEAP_PENDING set: INSERT leap
+ */
+
+#define PALISADE_LEAP_INPROGRESS 0x08 /* This is the leap flag */
+#define PALISADE_LEAP_WARNING 0x04 /* GPS Leap Warning (see ICD-200) */
+#define PALISADE_LEAP_PENDING 0x02 /* Leap Pending (24 hours) */
+#define PALISADE_UTC_TIME 0x01 /* UTC time available */
+
+#define mb(_X_) (up->rpt_buf[(_X_ + 1)]) /* shortcut for buffer access */
+
+/* Conversion Definitions */
+#define GPS_PI (3.1415926535898)
+#define R2D (180.0/GPS_PI)
+
+/*
+ * Palisade unit control structure.
+ */
+struct palisade_unit {
+ short unit; /* NTP refclock unit number */
+ int polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ char rpt_status; /* TSIP Parser State */
+ short rpt_cnt; /* TSIP packet length so far */
+ char rpt_buf[BMAX]; /* packet assembly buffer */
+ int type; /* Clock mode type */
+};
+
+/*
+ * Function prototypes
+ */
+
+static int palisade_start P((int, struct peer *));
+static void palisade_shutdown P((int, struct peer *));
+static void palisade_receive P((struct peer *));
+static void palisade_poll P((int, struct peer *));
+static void palisade_io P((struct recvbuf *));
+int palisade_configure P((int, struct peer *));
+int TSIP_decode P((struct peer *));
+long HW_poll P((struct refclockproc *));
+float getfloat P((u_char *));
+double getdbl P((u_char *));
+short getint P((u_char *));
+
+#endif /* PALISADE_H */
diff --git a/contrib/ntp/ntpd/refclock_parse.c b/contrib/ntp/ntpd/refclock_parse.c
new file mode 100644
index 0000000..0b0109c
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_parse.c
@@ -0,0 +1,6045 @@
+/*
+ * /src/NTP/REPOSITORY/ntp4-dev/ntpd/refclock_parse.c,v 4.80 2007/08/11 12:06:29 kardel Exp
+ *
+ * refclock_parse.c,v 4.80 2007/08/11 12:06:29 kardel Exp
+ *
+ * generic reference clock driver for several DCF/GPS/MSF/... receivers
+ *
+ * PPS notes:
+ * On systems that support PPSAPI (RFC2783) PPSAPI is the
+ * preferred interface.
+ *
+ * Optionally make use of a STREAMS module for input processing where
+ * available and configured. This STREAMS module reduces the time
+ * stamp latency for serial and PPS events.
+ * Currently the STREAMS module is only available for Suns running
+ * SunOS 4.x and SunOS5.x.
+ *
+ * Copyright (c) 1995-2007 by Frank Kardel <kardel <AT> ntp.org>
+ * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PARSE)
+
+/*
+ * This driver currently provides the support for
+ * - Meinberg receiver DCF77 PZF 535 (TCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 535 (OCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 509 (DCF)
+ * - Meinberg receiver DCF77 AM receivers (e.g. C51) (DCF)
+ * - IGEL CLOCK (DCF)
+ * - ELV DCF7000 (DCF)
+ * - Schmid clock (DCF)
+ * - Conrad DCF77 receiver module (DCF)
+ * - FAU DCF77 NTP receiver (TimeBrick) (DCF)
+ * - WHARTON 400A Series clock (DCF)
+ *
+ * - Meinberg GPS166/GPS167 (GPS)
+ * - Trimble (TSIP and TAIP protocol) (GPS)
+ *
+ * - RCC8000 MSF Receiver (MSF)
+ * - VARITEXT clock (MSF)
+ */
+
+/*
+ * Meinberg receivers are usually connected via a
+ * 9600 baud serial line
+ *
+ * The Meinberg GPS receivers also have a special NTP time stamp
+ * format. The firmware release is Uni-Erlangen.
+ *
+ * Meinberg generic receiver setup:
+ * output time code every second
+ * Baud rate 9600 7E2S
+ *
+ * Meinberg GPS16x setup:
+ * output time code every second
+ * Baudrate 19200 8N1
+ *
+ * This software supports the standard data formats used
+ * in Meinberg receivers.
+ *
+ * Special software versions are only sensible for the
+ * GPS 16x family of receivers.
+ *
+ * Meinberg can be reached via: http://www.meinberg.de/
+ */
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h" /* includes <sys/time.h> */
+#include "ntp_control.h"
+#include "ntp_string.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef TM_IN_SYS_TIME
+# include <time.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
+# include "Bletch: Define one of {STREAM,HAVE_SYSV_TTYS,HAVE_TERMIOS}"
+#endif
+
+#ifdef STREAM
+# include <sys/stream.h>
+# include <sys/stropts.h>
+#endif
+
+#ifdef HAVE_TERMIOS
+# define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+# undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+# define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifdef HAVE_BSD_TTYS
+/* #error CURRENTLY NO BSD TTY SUPPORT */
+# include "Bletch: BSD TTY not currently supported"
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+#ifdef PPS
+# ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+# endif
+# ifdef HAVE_TIO_SERIAL_STUFF
+# include <linux/serial.h>
+# endif
+#endif
+
+#define BUFFER_SIZE(_BUF, _PTR) ((_BUF) + sizeof(_BUF) - (_PTR))
+#define BUFFER_SIZES(_BUF, _PTR, _SZ) ((_BUF) + (_SZ) - (_PTR))
+
+/*
+ * document type of PPS interfacing - copy of ifdef mechanism in local_input()
+ */
+#undef PPS_METHOD
+
+#ifdef HAVE_PPSAPI
+#define PPS_METHOD "PPS API"
+#else
+#ifdef TIOCDCDTIMESTAMP
+#define PPS_METHOD "TIOCDCDTIMESTAMP"
+#else /* TIOCDCDTIMESTAMP */
+#if defined(HAVE_STRUCT_PPSCLOCKEV) && (defined(HAVE_CIOGETEV) || defined(HAVE_TIOCGPPSEV))
+#ifdef HAVE_CIOGETEV
+#define PPS_METHOD "CIOGETEV"
+#endif
+#ifdef HAVE_TIOCGPPSEV
+#define PPS_METHOD "TIOCGPPSEV"
+#endif
+#endif
+#endif /* TIOCDCDTIMESTAMP */
+#endif /* HAVE_PPSAPI */
+
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#include "parse.h"
+#include "mbg_gps166.h"
+#include "trimble.h"
+#include "binio.h"
+#include "ascii.h"
+#include "ieee754io.h"
+#include "recvbuff.h"
+
+static char rcsid[] = "refclock_parse.c,v 4.80 2007/08/11 12:06:29 kardel Exp";
+
+/**===========================================================================
+ ** external interface to ntp mechanism
+ **/
+
+static int parse_start P((int, struct peer *));
+static void parse_shutdown P((int, struct peer *));
+static void parse_poll P((int, struct peer *));
+static void parse_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+
+struct refclock refclock_parse = {
+ parse_start,
+ parse_shutdown,
+ parse_poll,
+ parse_control,
+ noentry,
+ noentry,
+ NOFLAGS
+};
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
+#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
+#define PARSEPPSDEVICE "/dev/refclockpps-%d" /* optional pps device to open %d is unit number */
+
+#undef ABS
+#define ABS(_X_) (((_X_) < 0) ? -(_X_) : (_X_))
+
+#define PARSE_HARDPPS_DISABLE 0
+#define PARSE_HARDPPS_ENABLE 1
+
+/**===========================================================================
+ ** function vector for dynamically binding io handling mechanism
+ **/
+
+struct parseunit; /* to keep inquiring minds happy */
+
+typedef struct bind
+{
+ const char *bd_description; /* name of type of binding */
+ int (*bd_init) P((struct parseunit *)); /* initialize */
+ void (*bd_end) P((struct parseunit *)); /* end */
+ int (*bd_setcs) P((struct parseunit *, parsectl_t *)); /* set character size */
+ int (*bd_disable) P((struct parseunit *)); /* disable */
+ int (*bd_enable) P((struct parseunit *)); /* enable */
+ int (*bd_getfmt) P((struct parseunit *, parsectl_t *)); /* get format */
+ int (*bd_setfmt) P((struct parseunit *, parsectl_t *)); /* setfmt */
+ int (*bd_timecode) P((struct parseunit *, parsectl_t *)); /* get time code */
+ void (*bd_receive) P((struct recvbuf *)); /* receive operation */
+ int (*bd_io_input) P((struct recvbuf *)); /* input operation */
+} bind_t;
+
+#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
+#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
+#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
+#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
+#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
+#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
+#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
+
+/*
+ * io modes
+ */
+#define PARSE_F_PPSPPS 0x0001 /* use loopfilter PPS code (CIOGETEV) */
+#define PARSE_F_PPSONSECOND 0x0002 /* PPS pulses are on second */
+
+
+/**===========================================================================
+ ** error message regression handling
+ **
+ ** there are quite a few errors that can occur in rapid succession such as
+ ** noisy input data or no data at all. in order to reduce the amount of
+ ** syslog messages in such case, we are using a backoff algorithm. We limit
+ ** the number of error messages of a certain class to 1 per time unit. if a
+ ** configurable number of messages is displayed that way, we move on to the
+ ** next time unit / count for that class. a count of messages that have been
+ ** suppressed is held and displayed whenever a corresponding message is
+ ** displayed. the time units for a message class will also be displayed.
+ ** whenever an error condition clears we reset the error message state,
+ ** thus we would still generate much output on pathological conditions
+ ** where the system oscillates between OK and NOT OK states. coping
+ ** with that condition is currently considered too complicated.
+ **/
+
+#define ERR_ALL (unsigned)~0 /* "all" errors */
+#define ERR_BADDATA (unsigned)0 /* unusable input data/conversion errors */
+#define ERR_NODATA (unsigned)1 /* no input data */
+#define ERR_BADIO (unsigned)2 /* read/write/select errors */
+#define ERR_BADSTATUS (unsigned)3 /* unsync states */
+#define ERR_BADEVENT (unsigned)4 /* non nominal events */
+#define ERR_INTERNAL (unsigned)5 /* internal error */
+#define ERR_CNT (unsigned)(ERR_INTERNAL+1)
+
+#define ERR(_X_) if (list_err(parse, (_X_)))
+
+struct errorregression
+{
+ u_long err_count; /* number of repititions per class */
+ u_long err_delay; /* minimum delay between messages */
+};
+
+static struct errorregression
+err_baddata[] = /* error messages for bad input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_nodata[] = /* error messages for missing input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badstatus[] = /* unsynchronized state messages */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badio[] = /* io failures (bad reads, selects, ...) */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badevent[] = /* non nominal events */
+{
+ { 20, 0 }, /* output first message immediately */
+ { 6, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_internal[] = /* really bad things - basically coding/OS errors */
+{
+ { 0, 0 }, /* output all messages immediately */
+};
+
+static struct errorregression *
+err_tbl[] =
+{
+ err_baddata,
+ err_nodata,
+ err_badio,
+ err_badstatus,
+ err_badevent,
+ err_internal
+};
+
+struct errorinfo
+{
+ u_long err_started; /* begin time (ntp) of error condition */
+ u_long err_last; /* last time (ntp) error occurred */
+ u_long err_cnt; /* number of error repititions */
+ u_long err_suppressed; /* number of suppressed messages */
+ struct errorregression *err_stage; /* current error stage */
+};
+
+/**===========================================================================
+ ** refclock instance data
+ **/
+
+struct parseunit
+{
+ /*
+ * NTP management
+ */
+ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
+ struct refclockproc *generic; /* backlink to refclockproc structure */
+
+ /*
+ * PARSE io
+ */
+ bind_t *binding; /* io handling binding */
+
+ /*
+ * parse state
+ */
+ parse_t parseio; /* io handling structure (user level parsing) */
+
+ /*
+ * type specific parameters
+ */
+ struct parse_clockinfo *parse_type; /* link to clock description */
+
+ /*
+ * clock state handling/reporting
+ */
+ u_char flags; /* flags (leap_control) */
+ u_long lastchange; /* time (ntp) when last state change accured */
+ u_long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
+ u_long pollneeddata; /* current_time(!=0) for receive sample expected in PPS mode */
+ u_short lastformat; /* last format used */
+ u_long lastsync; /* time (ntp) when clock was last seen fully synchronized */
+ u_long maxunsync; /* max time in seconds a receiver is trusted after loosing synchronisation */
+ double ppsphaseadjust; /* phase adjustment of PPS time stamp */
+ u_long lastmissed; /* time (ntp) when poll didn't get data (powerup heuristic) */
+ u_long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
+ int ppsfd; /* fd to ise for PPS io */
+#ifdef HAVE_PPSAPI
+ pps_handle_t ppshandle; /* store PPSAPI handle */
+ pps_params_t ppsparams; /* current PPS parameters */
+ int hardppsstate; /* current hard pps state */
+#endif
+ parsetime_t timedata; /* last (parse module) data */
+ void *localdata; /* optional local, receiver-specific data */
+ unsigned long localstate; /* private local state */
+ struct errorinfo errors[ERR_CNT]; /* error state table for suppressing excessive error messages */
+ struct ctl_var *kv; /* additional pseudo variables */
+ u_long laststatistic; /* time when staticstics where output */
+};
+
+
+/**===========================================================================
+ ** Clockinfo section all parameter for specific clock types
+ ** includes NTP parameters, TTY parameters and IO handling parameters
+ **/
+
+static void poll_dpoll P((struct parseunit *));
+static void poll_poll P((struct peer *));
+static int poll_init P((struct parseunit *));
+
+typedef struct poll_info
+{
+ u_long rate; /* poll rate - once every "rate" seconds - 0 off */
+ const char *string; /* string to send for polling */
+ u_long count; /* number of characters in string */
+} poll_info_t;
+
+#define NO_CL_FLAGS 0
+#define NO_POLL 0
+#define NO_INIT 0
+#define NO_END 0
+#define NO_EVENT 0
+#define NO_LCLDATA 0
+#define NO_MESSAGE 0
+#define NO_PPSDELAY 0
+
+#define DCF_ID "DCF" /* generic DCF */
+#define DCF_A_ID "DCFa" /* AM demodulation */
+#define DCF_P_ID "DCFp" /* psuedo random phase shift */
+#define GPS_ID "GPS" /* GPS receiver */
+
+#define NOCLOCK_ROOTDELAY 0.0
+#define NOCLOCK_BASEDELAY 0.0
+#define NOCLOCK_DESCRIPTION 0
+#define NOCLOCK_MAXUNSYNC 0
+#define NOCLOCK_CFLAG 0
+#define NOCLOCK_IFLAG 0
+#define NOCLOCK_OFLAG 0
+#define NOCLOCK_LFLAG 0
+#define NOCLOCK_ID "TILT"
+#define NOCLOCK_POLL NO_POLL
+#define NOCLOCK_INIT NO_INIT
+#define NOCLOCK_END NO_END
+#define NOCLOCK_DATA NO_LCLDATA
+#define NOCLOCK_FORMAT ""
+#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
+#define NOCLOCK_SAMPLES 0
+#define NOCLOCK_KEEP 0
+
+#define DCF_TYPE CTL_SST_TS_LF
+#define GPS_TYPE CTL_SST_TS_UHF
+
+/*
+ * receiver specific constants
+ */
+#define MBG_SPEED (B9600)
+#define MBG_CFLAG (CS7|PARENB|CREAD|CLOCAL|HUPCL|CSTOPB)
+#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define MBG_OFLAG 0
+#define MBG_LFLAG 0
+#define MBG_FLAGS PARSE_F_PPSONSECOND
+
+/*
+ * Meinberg DCF77 receivers
+ */
+#define DCFUA31_ROOTDELAY 0.0 /* 0 */
+#define DCFUA31_BASEDELAY 0.010 /* 10.7421875ms: 10 ms (+/- 3 ms) */
+#define DCFUA31_DESCRIPTION "Meinberg DCF77 C51 or compatible"
+#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
+#define DCFUA31_SPEED MBG_SPEED
+#define DCFUA31_CFLAG MBG_CFLAG
+#define DCFUA31_IFLAG MBG_IFLAG
+#define DCFUA31_OFLAG MBG_OFLAG
+#define DCFUA31_LFLAG MBG_LFLAG
+#define DCFUA31_SAMPLES 5
+#define DCFUA31_KEEP 3
+#define DCFUA31_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/TCXO (FM/PZF) receiver
+ */
+#define DCFPZF535_ROOTDELAY 0.0
+#define DCFPZF535_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/509 / TCXO"
+#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
+ * @ 5e-8df/f we have accumulated
+ * at most 2.16 ms (thus we move to
+ * NTP synchronisation */
+#define DCFPZF535_SPEED MBG_SPEED
+#define DCFPZF535_CFLAG MBG_CFLAG
+#define DCFPZF535_IFLAG MBG_IFLAG
+#define DCFPZF535_OFLAG MBG_OFLAG
+#define DCFPZF535_LFLAG MBG_LFLAG
+#define DCFPZF535_SAMPLES 5
+#define DCFPZF535_KEEP 3
+#define DCFPZF535_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/OCXO receiver
+ */
+#define DCFPZF535OCXO_ROOTDELAY 0.0
+#define DCFPZF535OCXO_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/509 / OCXO"
+#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define DCFPZF535OCXO_SPEED MBG_SPEED
+#define DCFPZF535OCXO_CFLAG MBG_CFLAG
+#define DCFPZF535OCXO_IFLAG MBG_IFLAG
+#define DCFPZF535OCXO_OFLAG MBG_OFLAG
+#define DCFPZF535OCXO_LFLAG MBG_LFLAG
+#define DCFPZF535OCXO_SAMPLES 5
+#define DCFPZF535OCXO_KEEP 3
+#define DCFPZF535OCXO_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg GPS16X receiver
+ */
+static void gps16x_message P((struct parseunit *, parsetime_t *));
+static int gps16x_poll_init P((struct parseunit *));
+
+#define GPS16X_ROOTDELAY 0.0 /* nothing here */
+#define GPS16X_BASEDELAY 0.001968 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define GPS16X_DESCRIPTION "Meinberg GPS16x receiver"
+#define GPS16X_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define GPS16X_SPEED B19200
+#define GPS16X_CFLAG (CS8|CREAD|CLOCAL|HUPCL)
+#define GPS16X_IFLAG (IGNBRK|IGNPAR)
+#define GPS16X_OFLAG MBG_OFLAG
+#define GPS16X_LFLAG MBG_LFLAG
+#define GPS16X_POLLRATE 6
+#define GPS16X_POLLCMD ""
+#define GPS16X_CMDSIZE 0
+
+static poll_info_t gps16x_pollinfo = { GPS16X_POLLRATE, GPS16X_POLLCMD, GPS16X_CMDSIZE };
+
+#define GPS16X_INIT gps16x_poll_init
+#define GPS16X_POLL 0
+#define GPS16X_END 0
+#define GPS16X_DATA ((void *)(&gps16x_pollinfo))
+#define GPS16X_MESSAGE gps16x_message
+#define GPS16X_ID GPS_ID
+#define GPS16X_FORMAT "Meinberg GPS Extended"
+#define GPS16X_SAMPLES 5
+#define GPS16X_KEEP 3
+
+/*
+ * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
+ *
+ * This is really not the hottest clock - but before you have nothing ...
+ */
+#define DCF7000_ROOTDELAY 0.0 /* 0 */
+#define DCF7000_BASEDELAY 0.405 /* slow blow */
+#define DCF7000_DESCRIPTION "ELV DCF7000"
+#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
+#define DCF7000_SPEED (B9600)
+#define DCF7000_CFLAG (CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
+#define DCF7000_IFLAG (IGNBRK)
+#define DCF7000_OFLAG 0
+#define DCF7000_LFLAG 0
+#define DCF7000_SAMPLES 5
+#define DCF7000_KEEP 3
+#define DCF7000_FORMAT "ELV DCF7000"
+
+/*
+ * Schmid DCF Receiver Kit
+ *
+ * When the WSDCF clock is operating optimally we want the primary clock
+ * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
+ * structure is set to 290 ms and we compute delays which are at least
+ * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
+#define WS_POLLCMD "\163"
+#define WS_CMDSIZE 1
+
+static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
+
+#define WSDCF_INIT poll_init
+#define WSDCF_POLL poll_dpoll
+#define WSDCF_END 0
+#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
+#define WSDCF_ROOTDELAY 0.0 /* 0 */
+#define WSDCF_BASEDELAY 0.010 /* ~ 10ms */
+#define WSDCF_DESCRIPTION "WS/DCF Receiver"
+#define WSDCF_FORMAT "Schmid"
+#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
+#define WSDCF_SPEED (B1200)
+#define WSDCF_CFLAG (CS8|CREAD|CLOCAL)
+#define WSDCF_IFLAG 0
+#define WSDCF_OFLAG 0
+#define WSDCF_LFLAG 0
+#define WSDCF_SAMPLES 5
+#define WSDCF_KEEP 3
+
+/*
+ * RAW DCF77 - input of DCF marks via RS232 - many variants
+ */
+#define RAWDCF_FLAGS 0
+#define RAWDCF_ROOTDELAY 0.0 /* 0 */
+#define RAWDCF_BASEDELAY 0.258
+#define RAWDCF_FORMAT "RAW DCF77 Timecode"
+#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
+#define RAWDCF_SPEED (B50)
+#ifdef NO_PARENB_IGNPAR /* Was: defined(SYS_IRIX4) || defined(SYS_IRIX5) */
+/* somehow doesn't grok PARENB & IGNPAR (mj) */
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
+#else
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL|PARENB)
+#endif
+#ifdef RAWDCF_NO_IGNPAR /* Was: defined(SYS_LINUX) && defined(CLOCK_RAWDCF) */
+# define RAWDCF_IFLAG 0
+#else
+# define RAWDCF_IFLAG (IGNPAR)
+#endif
+#define RAWDCF_OFLAG 0
+#define RAWDCF_LFLAG 0
+#define RAWDCF_SAMPLES 20
+#define RAWDCF_KEEP 12
+#define RAWDCF_INIT 0
+
+/*
+ * RAW DCF variants
+ */
+/*
+ * Conrad receiver
+ *
+ * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
+ * (~40DM - roughly $30 ) followed by a level converter for RS232
+ */
+#define CONRAD_BASEDELAY 0.292 /* Conrad receiver @ 50 Baud on a Sun */
+#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
+
+/* Gude Analog- und Digitalsystem GmbH 'Expert mouseCLOCK USB v2.0' */
+#define GUDE_EMC_USB_V20_SPEED (B4800)
+#define GUDE_EMC_USB_V20_BASEDELAY 0.425 /* USB serial<->USB converter FTDI232R */
+#define GUDE_EMC_USB_V20_DESCRIPTION "RAW DCF77 CODE (Expert mouseCLOCK USB v2.0)"
+
+/*
+ * TimeBrick receiver
+ */
+#define TIMEBRICK_BASEDELAY 0.210 /* TimeBrick @ 50 Baud on a Sun */
+#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
+
+/*
+ * IGEL:clock receiver
+ */
+#define IGELCLOCK_BASEDELAY 0.258 /* IGEL:clock receiver */
+#define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)"
+#define IGELCLOCK_SPEED (B1200)
+#define IGELCLOCK_CFLAG (CS8|CREAD|HUPCL|CLOCAL)
+
+/*
+ * RAWDCF receivers that need to be powered from DTR
+ * (like Expert mouse clock)
+ */
+static int rawdcf_init_1 P((struct parseunit *));
+#define RAWDCFDTRSET_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR)"
+#define RAWDCFDTRSET_INIT rawdcf_init_1
+
+/*
+ * RAWDCF receivers that need to be powered from
+ * DTR CLR and RTS SET
+ */
+static int rawdcf_init_2 P((struct parseunit *));
+#define RAWDCFDTRCLRRTSSET_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET)"
+#define RAWDCFDTRCLRRTSSET_INIT rawdcf_init_2
+
+/*
+ * Trimble GPS receivers (TAIP and TSIP protocols)
+ */
+#ifndef TRIM_POLLRATE
+#define TRIM_POLLRATE 0 /* only true direct polling */
+#endif
+
+#define TRIM_TAIPPOLLCMD ">SRM;FR_FLAG=F;EC_FLAG=F<>QTM<"
+#define TRIM_TAIPCMDSIZE (sizeof(TRIM_TAIPPOLLCMD)-1)
+
+static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
+static int trimbletaip_init P((struct parseunit *));
+static void trimbletaip_event P((struct parseunit *, int));
+
+/* query time & UTC correction data */
+static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
+
+static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
+static int trimbletsip_init P((struct parseunit *));
+static void trimbletsip_end P((struct parseunit *));
+static void trimbletsip_message P((struct parseunit *, parsetime_t *));
+static void trimbletsip_event P((struct parseunit *, int));
+
+#define TRIMBLETSIP_IDLE_TIME (300) /* 5 minutes silence at most */
+#define TRIMBLE_RESET_HOLDOFF TRIMBLETSIP_IDLE_TIME
+
+#define TRIMBLETAIP_SPEED (B4800)
+#define TRIMBLETAIP_CFLAG (CS8|CREAD|CLOCAL)
+#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
+#define TRIMBLETAIP_LFLAG (0)
+
+#define TRIMBLETSIP_SPEED (B9600)
+#define TRIMBLETSIP_CFLAG (CS8|CLOCAL|CREAD|PARENB|PARODD)
+#define TRIMBLETSIP_IFLAG (IGNBRK)
+#define TRIMBLETSIP_OFLAG (0)
+#define TRIMBLETSIP_LFLAG (ICANON)
+
+#define TRIMBLETSIP_SAMPLES 5
+#define TRIMBLETSIP_KEEP 3
+#define TRIMBLETAIP_SAMPLES 5
+#define TRIMBLETAIP_KEEP 3
+
+#define TRIMBLETAIP_FLAGS (PARSE_F_PPSONSECOND)
+#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS)
+
+#define TRIMBLETAIP_POLL poll_dpoll
+#define TRIMBLETSIP_POLL poll_dpoll
+
+#define TRIMBLETAIP_INIT trimbletaip_init
+#define TRIMBLETSIP_INIT trimbletsip_init
+
+#define TRIMBLETAIP_EVENT trimbletaip_event
+
+#define TRIMBLETSIP_EVENT trimbletsip_event
+#define TRIMBLETSIP_MESSAGE trimbletsip_message
+
+#define TRIMBLETAIP_END 0
+#define TRIMBLETSIP_END trimbletsip_end
+
+#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
+#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
+
+#define TRIMBLETAIP_ID GPS_ID
+#define TRIMBLETSIP_ID GPS_ID
+
+#define TRIMBLETAIP_FORMAT "Trimble TAIP"
+#define TRIMBLETSIP_FORMAT "Trimble TSIP"
+
+#define TRIMBLETAIP_ROOTDELAY 0x0
+#define TRIMBLETSIP_ROOTDELAY 0x0
+
+#define TRIMBLETAIP_BASEDELAY 0.0
+#define TRIMBLETSIP_BASEDELAY 0.020 /* GPS time message latency */
+
+#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
+#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
+
+#define TRIMBLETAIP_MAXUNSYNC 0
+#define TRIMBLETSIP_MAXUNSYNC 0
+
+#define TRIMBLETAIP_EOL '<'
+
+/*
+ * RadioCode Clocks RCC 800 receiver
+ */
+#define RCC_POLLRATE 0 /* only true direct polling */
+#define RCC_POLLCMD "\r"
+#define RCC_CMDSIZE 1
+
+static poll_info_t rcc8000_pollinfo = { RCC_POLLRATE, RCC_POLLCMD, RCC_CMDSIZE };
+#define RCC8000_FLAGS 0
+#define RCC8000_POLL poll_dpoll
+#define RCC8000_INIT poll_init
+#define RCC8000_END 0
+#define RCC8000_DATA ((void *)(&rcc8000_pollinfo))
+#define RCC8000_ROOTDELAY 0.0
+#define RCC8000_BASEDELAY 0.0
+#define RCC8000_ID "MSF"
+#define RCC8000_DESCRIPTION "RCC 8000 MSF Receiver"
+#define RCC8000_FORMAT "Radiocode RCC8000"
+#define RCC8000_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define RCC8000_SPEED (B2400)
+#define RCC8000_CFLAG (CS8|CREAD|CLOCAL)
+#define RCC8000_IFLAG (IGNBRK|IGNPAR)
+#define RCC8000_OFLAG 0
+#define RCC8000_LFLAG 0
+#define RCC8000_SAMPLES 5
+#define RCC8000_KEEP 3
+
+/*
+ * Hopf Radio clock 6021 Format
+ *
+ */
+#define HOPF6021_ROOTDELAY 0.0
+#define HOPF6021_BASEDELAY 0.0
+#define HOPF6021_DESCRIPTION "HOPF 6021"
+#define HOPF6021_FORMAT "hopf Funkuhr 6021"
+#define HOPF6021_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define HOPF6021_SPEED (B9600)
+#define HOPF6021_CFLAG (CS8|CREAD|CLOCAL)
+#define HOPF6021_IFLAG (IGNBRK|ISTRIP)
+#define HOPF6021_OFLAG 0
+#define HOPF6021_LFLAG 0
+#define HOPF6021_FLAGS 0
+#define HOPF6021_SAMPLES 5
+#define HOPF6021_KEEP 3
+
+/*
+ * Diem's Computime Radio Clock Receiver
+ */
+#define COMPUTIME_FLAGS 0
+#define COMPUTIME_ROOTDELAY 0.0
+#define COMPUTIME_BASEDELAY 0.0
+#define COMPUTIME_ID DCF_ID
+#define COMPUTIME_DESCRIPTION "Diem's Computime receiver"
+#define COMPUTIME_FORMAT "Diem's Computime Radio Clock"
+#define COMPUTIME_TYPE DCF_TYPE
+#define COMPUTIME_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define COMPUTIME_SPEED (B9600)
+#define COMPUTIME_CFLAG (CSTOPB|CS7|CREAD|CLOCAL)
+#define COMPUTIME_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define COMPUTIME_OFLAG 0
+#define COMPUTIME_LFLAG 0
+#define COMPUTIME_SAMPLES 5
+#define COMPUTIME_KEEP 3
+
+/*
+ * Varitext Radio Clock Receiver
+ */
+#define VARITEXT_FLAGS 0
+#define VARITEXT_ROOTDELAY 0.0
+#define VARITEXT_BASEDELAY 0.0
+#define VARITEXT_ID "MSF"
+#define VARITEXT_DESCRIPTION "Varitext receiver"
+#define VARITEXT_FORMAT "Varitext Radio Clock"
+#define VARITEXT_TYPE DCF_TYPE
+#define VARITEXT_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define VARITEXT_SPEED (B9600)
+#define VARITEXT_CFLAG (CS7|CREAD|CLOCAL|PARENB|PARODD)
+#define VARITEXT_IFLAG (IGNPAR|IGNBRK|INPCK) /*|ISTRIP)*/
+#define VARITEXT_OFLAG 0
+#define VARITEXT_LFLAG 0
+#define VARITEXT_SAMPLES 32
+#define VARITEXT_KEEP 20
+
+static struct parse_clockinfo
+{
+ u_long cl_flags; /* operation flags (io modes) */
+ void (*cl_poll) P((struct parseunit *)); /* active poll routine */
+ int (*cl_init) P((struct parseunit *)); /* active poll init routine */
+ void (*cl_event) P((struct parseunit *, int)); /* special event handling (e.g. reset clock) */
+ void (*cl_end) P((struct parseunit *)); /* active poll end routine */
+ void (*cl_message) P((struct parseunit *, parsetime_t *)); /* process a lower layer message */
+ void *cl_data; /* local data area for "poll" mechanism */
+ double cl_rootdelay; /* rootdelay */
+ double cl_basedelay; /* current offset by which the RS232
+ time code is delayed from the actual time */
+ const char *cl_id; /* ID code */
+ const char *cl_description; /* device name */
+ const char *cl_format; /* fixed format */
+ u_char cl_type; /* clock type (ntp control) */
+ u_long cl_maxunsync; /* time to trust oscillator after losing synch */
+ u_long cl_speed; /* terminal input & output baudrate */
+ u_long cl_cflag; /* terminal control flags */
+ u_long cl_iflag; /* terminal input flags */
+ u_long cl_oflag; /* terminal output flags */
+ u_long cl_lflag; /* terminal local flags */
+ u_long cl_samples; /* samples for median filter */
+ u_long cl_keep; /* samples for median filter to keep */
+} parse_clockinfo[] =
+{
+ { /* mode 0 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFPZF535_ROOTDELAY,
+ DCFPZF535_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535_DESCRIPTION,
+ DCFPZF535_FORMAT,
+ DCF_TYPE,
+ DCFPZF535_MAXUNSYNC,
+ DCFPZF535_SPEED,
+ DCFPZF535_CFLAG,
+ DCFPZF535_IFLAG,
+ DCFPZF535_OFLAG,
+ DCFPZF535_LFLAG,
+ DCFPZF535_SAMPLES,
+ DCFPZF535_KEEP
+ },
+ { /* mode 1 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFPZF535OCXO_ROOTDELAY,
+ DCFPZF535OCXO_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535OCXO_DESCRIPTION,
+ DCFPZF535OCXO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535OCXO_MAXUNSYNC,
+ DCFPZF535OCXO_SPEED,
+ DCFPZF535OCXO_CFLAG,
+ DCFPZF535OCXO_IFLAG,
+ DCFPZF535OCXO_OFLAG,
+ DCFPZF535OCXO_LFLAG,
+ DCFPZF535OCXO_SAMPLES,
+ DCFPZF535OCXO_KEEP
+ },
+ { /* mode 2 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ DCFUA31_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_SPEED,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG,
+ DCFUA31_SAMPLES,
+ DCFUA31_KEEP
+ },
+ { /* mode 3 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ DCF7000_ROOTDELAY,
+ DCF7000_BASEDELAY,
+ DCF_A_ID,
+ DCF7000_DESCRIPTION,
+ DCF7000_FORMAT,
+ DCF_TYPE,
+ DCF7000_MAXUNSYNC,
+ DCF7000_SPEED,
+ DCF7000_CFLAG,
+ DCF7000_IFLAG,
+ DCF7000_OFLAG,
+ DCF7000_LFLAG,
+ DCF7000_SAMPLES,
+ DCF7000_KEEP
+ },
+ { /* mode 4 */
+ NO_CL_FLAGS,
+ WSDCF_POLL,
+ WSDCF_INIT,
+ NO_EVENT,
+ WSDCF_END,
+ NO_MESSAGE,
+ WSDCF_DATA,
+ WSDCF_ROOTDELAY,
+ WSDCF_BASEDELAY,
+ DCF_A_ID,
+ WSDCF_DESCRIPTION,
+ WSDCF_FORMAT,
+ DCF_TYPE,
+ WSDCF_MAXUNSYNC,
+ WSDCF_SPEED,
+ WSDCF_CFLAG,
+ WSDCF_IFLAG,
+ WSDCF_OFLAG,
+ WSDCF_LFLAG,
+ WSDCF_SAMPLES,
+ WSDCF_KEEP
+ },
+ { /* mode 5 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ CONRAD_BASEDELAY,
+ DCF_A_ID,
+ CONRAD_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 6 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ TIMEBRICK_BASEDELAY,
+ DCF_A_ID,
+ TIMEBRICK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 7 */
+ MBG_FLAGS,
+ GPS16X_POLL,
+ GPS16X_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 8 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ IGELCLOCK_BASEDELAY,
+ DCF_A_ID,
+ IGELCLOCK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ IGELCLOCK_SPEED,
+ IGELCLOCK_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 9 */
+ TRIMBLETAIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETAIP_POLL,
+#endif
+ TRIMBLETAIP_INIT,
+ TRIMBLETAIP_EVENT,
+ TRIMBLETAIP_END,
+ NO_MESSAGE,
+ TRIMBLETAIP_DATA,
+ TRIMBLETAIP_ROOTDELAY,
+ TRIMBLETAIP_BASEDELAY,
+ TRIMBLETAIP_ID,
+ TRIMBLETAIP_DESCRIPTION,
+ TRIMBLETAIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETAIP_MAXUNSYNC,
+ TRIMBLETAIP_SPEED,
+ TRIMBLETAIP_CFLAG,
+ TRIMBLETAIP_IFLAG,
+ TRIMBLETAIP_OFLAG,
+ TRIMBLETAIP_LFLAG,
+ TRIMBLETAIP_SAMPLES,
+ TRIMBLETAIP_KEEP
+ },
+ { /* mode 10 */
+ TRIMBLETSIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETSIP_POLL,
+#endif
+ TRIMBLETSIP_INIT,
+ TRIMBLETSIP_EVENT,
+ TRIMBLETSIP_END,
+ TRIMBLETSIP_MESSAGE,
+ TRIMBLETSIP_DATA,
+ TRIMBLETSIP_ROOTDELAY,
+ TRIMBLETSIP_BASEDELAY,
+ TRIMBLETSIP_ID,
+ TRIMBLETSIP_DESCRIPTION,
+ TRIMBLETSIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETSIP_MAXUNSYNC,
+ TRIMBLETSIP_SPEED,
+ TRIMBLETSIP_CFLAG,
+ TRIMBLETSIP_IFLAG,
+ TRIMBLETSIP_OFLAG,
+ TRIMBLETSIP_LFLAG,
+ TRIMBLETSIP_SAMPLES,
+ TRIMBLETSIP_KEEP
+ },
+ { /* mode 11 */
+ NO_CL_FLAGS,
+ RCC8000_POLL,
+ RCC8000_INIT,
+ NO_EVENT,
+ RCC8000_END,
+ NO_MESSAGE,
+ RCC8000_DATA,
+ RCC8000_ROOTDELAY,
+ RCC8000_BASEDELAY,
+ RCC8000_ID,
+ RCC8000_DESCRIPTION,
+ RCC8000_FORMAT,
+ DCF_TYPE,
+ RCC8000_MAXUNSYNC,
+ RCC8000_SPEED,
+ RCC8000_CFLAG,
+ RCC8000_IFLAG,
+ RCC8000_OFLAG,
+ RCC8000_LFLAG,
+ RCC8000_SAMPLES,
+ RCC8000_KEEP
+ },
+ { /* mode 12 */
+ HOPF6021_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ HOPF6021_ROOTDELAY,
+ HOPF6021_BASEDELAY,
+ DCF_ID,
+ HOPF6021_DESCRIPTION,
+ HOPF6021_FORMAT,
+ DCF_TYPE,
+ HOPF6021_MAXUNSYNC,
+ HOPF6021_SPEED,
+ HOPF6021_CFLAG,
+ HOPF6021_IFLAG,
+ HOPF6021_OFLAG,
+ HOPF6021_LFLAG,
+ HOPF6021_SAMPLES,
+ HOPF6021_KEEP
+ },
+ { /* mode 13 */
+ COMPUTIME_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ COMPUTIME_ROOTDELAY,
+ COMPUTIME_BASEDELAY,
+ COMPUTIME_ID,
+ COMPUTIME_DESCRIPTION,
+ COMPUTIME_FORMAT,
+ COMPUTIME_TYPE,
+ COMPUTIME_MAXUNSYNC,
+ COMPUTIME_SPEED,
+ COMPUTIME_CFLAG,
+ COMPUTIME_IFLAG,
+ COMPUTIME_OFLAG,
+ COMPUTIME_LFLAG,
+ COMPUTIME_SAMPLES,
+ COMPUTIME_KEEP
+ },
+ { /* mode 14 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 15 */
+ 0, /* operation flags (io modes) */
+ NO_POLL, /* active poll routine */
+ NO_INIT, /* active poll init routine */
+ NO_EVENT, /* special event handling (e.g. reset clock) */
+ NO_END, /* active poll end routine */
+ NO_MESSAGE, /* process a lower layer message */
+ NO_LCLDATA, /* local data area for "poll" mechanism */
+ 0, /* rootdelay */
+ 11.0 /* bits */ / 9600, /* current offset by which the RS232
+ time code is delayed from the actual time */
+ DCF_ID, /* ID code */
+ "WHARTON 400A Series clock", /* device name */
+ "WHARTON 400A Series clock Output Format 1", /* fixed format */
+ /* Must match a format-name in a libparse/clk_xxx.c file */
+ DCF_TYPE, /* clock type (ntp control) */
+ (1*60*60), /* time to trust oscillator after losing synch */
+ B9600, /* terminal input & output baudrate */
+ (CS8|CREAD|PARENB|CLOCAL|HUPCL),/* terminal control flags */
+ 0, /* terminal input flags */
+ 0, /* terminal output flags */
+ 0, /* terminal local flags */
+ 5, /* samples for median filter */
+ 3, /* samples for median filter to keep */
+ },
+ { /* mode 16 - RAWDCF RTS set, DTR clr */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRCLRRTSSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRCLRRTSSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 17 */
+ VARITEXT_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ VARITEXT_ROOTDELAY,
+ VARITEXT_BASEDELAY,
+ VARITEXT_ID,
+ VARITEXT_DESCRIPTION,
+ VARITEXT_FORMAT,
+ VARITEXT_TYPE,
+ VARITEXT_MAXUNSYNC,
+ VARITEXT_SPEED,
+ VARITEXT_CFLAG,
+ VARITEXT_IFLAG,
+ VARITEXT_OFLAG,
+ VARITEXT_LFLAG,
+ VARITEXT_SAMPLES,
+ VARITEXT_KEEP
+ },
+ { /* mode 18 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 19 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_LCLDATA,
+ RAWDCF_ROOTDELAY,
+ GUDE_EMC_USB_V20_BASEDELAY,
+ DCF_A_ID,
+ GUDE_EMC_USB_V20_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ GUDE_EMC_USB_V20_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+};
+
+static int ncltypes = sizeof(parse_clockinfo) / sizeof(struct parse_clockinfo);
+
+#define CLK_REALTYPE(x) ((int)(((x)->ttl) & 0x7F))
+#define CLK_TYPE(x) ((CLK_REALTYPE(x) >= ncltypes) ? ~0 : CLK_REALTYPE(x))
+#define CLK_UNIT(x) ((int)REFCLOCKUNIT(&(x)->srcadr))
+#define CLK_PPS(x) (((x)->ttl) & 0x80)
+
+/*
+ * Other constant stuff
+ */
+#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
+
+#define PARSESTATISTICS (60*60) /* output state statistics every hour */
+
+static int notice = 0;
+
+#define PARSE_STATETIME(parse, i) ((parse->generic->currentstatus == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
+
+static void parse_event P((struct parseunit *, int));
+static void parse_process P((struct parseunit *, parsetime_t *));
+static void clear_err P((struct parseunit *, u_long));
+static int list_err P((struct parseunit *, u_long));
+static char * l_mktime P((u_long));
+
+/**===========================================================================
+ ** implementation error message regression module
+ **/
+static void
+clear_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ if (lstate == ERR_ALL)
+ {
+ int i;
+
+ for (i = 0; i < ERR_CNT; i++)
+ {
+ parse->errors[i].err_stage = err_tbl[i];
+ parse->errors[i].err_cnt = 0;
+ parse->errors[i].err_last = 0;
+ parse->errors[i].err_started = 0;
+ parse->errors[i].err_suppressed = 0;
+ }
+ }
+ else
+ {
+ parse->errors[lstate].err_stage = err_tbl[lstate];
+ parse->errors[lstate].err_cnt = 0;
+ parse->errors[lstate].err_last = 0;
+ parse->errors[lstate].err_started = 0;
+ parse->errors[lstate].err_suppressed = 0;
+ }
+}
+
+static int
+list_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ int do_it;
+ struct errorinfo *err = &parse->errors[lstate];
+
+ if (err->err_started == 0)
+ {
+ err->err_started = current_time;
+ }
+
+ do_it = (current_time - err->err_last) >= err->err_stage->err_delay;
+
+ if (do_it)
+ err->err_cnt++;
+
+ if (err->err_stage->err_count &&
+ (err->err_cnt >= err->err_stage->err_count))
+ {
+ err->err_stage++;
+ err->err_cnt = 0;
+ }
+
+ if (!err->err_cnt && do_it)
+ msyslog(LOG_INFO, "PARSE receiver #%d: interval for following error message class is at least %s",
+ CLK_UNIT(parse->peer), l_mktime(err->err_stage->err_delay));
+
+ if (!do_it)
+ err->err_suppressed++;
+ else
+ err->err_last = current_time;
+
+ if (do_it && err->err_suppressed)
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: %ld message%s suppressed, error condition class persists for %s",
+ CLK_UNIT(parse->peer), err->err_suppressed, (err->err_suppressed == 1) ? " was" : "s where",
+ l_mktime(current_time - err->err_started));
+ err->err_suppressed = 0;
+ }
+
+ return do_it;
+}
+
+/*--------------------------------------------------
+ * mkreadable - make a printable ascii string (without
+ * embedded quotes so that the ntpq protocol isn't
+ * fooled
+ */
+#ifndef isprint
+#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
+#endif
+
+static char *
+mkreadable(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen,
+ int hex
+ )
+{
+ char *b = buffer;
+ char *endb = (char *)0;
+
+ if (blen < 4)
+ return (char *)0; /* don't bother with mini buffers */
+
+ endb = buffer + blen - 4;
+
+ blen--; /* account for '\0' */
+
+ while (blen && srclen--)
+ {
+ if (!hex && /* no binary only */
+ (*src != '\\') && /* no plain \ */
+ (*src != '"') && /* no " */
+ isprint((int)*src)) /* only printables */
+ { /* they are easy... */
+ *buffer++ = *src++;
+ blen--;
+ }
+ else
+ {
+ if (blen < 4)
+ {
+ while (blen--)
+ {
+ *buffer++ = '.';
+ }
+ *buffer = '\0';
+ return b;
+ }
+ else
+ {
+ if (*src == '\\')
+ {
+ strcpy(buffer,"\\\\");
+ buffer += 2;
+ blen -= 2;
+ src++;
+ }
+ else
+ {
+ sprintf(buffer, "\\x%02x", *src++);
+ blen -= 4;
+ buffer += 4;
+ }
+ }
+ }
+ if (srclen && !blen && endb) /* overflow - set last chars to ... */
+ strcpy(endb, "...");
+ }
+
+ *buffer = '\0';
+ return b;
+}
+
+
+/*--------------------------------------------------
+ * mkascii - make a printable ascii string
+ * assumes (unless defined better) 7-bit ASCII
+ */
+static char *
+mkascii(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen
+ )
+{
+ return mkreadable(buffer, blen, src, srclen, 0);
+}
+
+/**===========================================================================
+ ** implementation of i/o handling methods
+ ** (all STREAM, partial STREAM, user level)
+ **/
+
+/*
+ * define possible io handling methods
+ */
+#ifdef STREAM
+static int ppsclock_init P((struct parseunit *));
+static int stream_init P((struct parseunit *));
+static void stream_end P((struct parseunit *));
+static int stream_enable P((struct parseunit *));
+static int stream_disable P((struct parseunit *));
+static int stream_setcs P((struct parseunit *, parsectl_t *));
+static int stream_getfmt P((struct parseunit *, parsectl_t *));
+static int stream_setfmt P((struct parseunit *, parsectl_t *));
+static int stream_timecode P((struct parseunit *, parsectl_t *));
+static void stream_receive P((struct recvbuf *));
+#endif
+
+static int local_init P((struct parseunit *));
+static void local_end P((struct parseunit *));
+static int local_nop P((struct parseunit *));
+static int local_setcs P((struct parseunit *, parsectl_t *));
+static int local_getfmt P((struct parseunit *, parsectl_t *));
+static int local_setfmt P((struct parseunit *, parsectl_t *));
+static int local_timecode P((struct parseunit *, parsectl_t *));
+static void local_receive P((struct recvbuf *));
+static int local_input P((struct recvbuf *));
+
+static bind_t io_bindings[] =
+{
+#ifdef STREAM
+ {
+ "parse STREAM",
+ stream_init,
+ stream_end,
+ stream_setcs,
+ stream_disable,
+ stream_enable,
+ stream_getfmt,
+ stream_setfmt,
+ stream_timecode,
+ stream_receive,
+ 0,
+ },
+ {
+ "ppsclock STREAM",
+ ppsclock_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+#endif
+ {
+ "normal",
+ local_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+ {
+ (char *)0,
+ }
+};
+
+#ifdef STREAM
+
+#define fix_ts(_X_) \
+ if ((&(_X_))->tv.tv_usec >= 1000000) \
+ { \
+ (&(_X_))->tv.tv_usec -= 1000000; \
+ (&(_X_))->tv.tv_sec += 1; \
+ }
+
+#define cvt_ts(_X_, _Y_) \
+ { \
+ l_fp ts; \
+ fix_ts((_X_)); \
+ if (!buftvtots((const char *)&(&(_X_))->tv, &ts)) \
+ { \
+ ERR(ERR_BADDATA) \
+ msyslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%ld.%06ld) ", (_Y_), (long)(&(_X_))->tv.tv_sec, (long)(&(_X_))->tv.tv_usec);\
+ return; \
+ } \
+ else \
+ { \
+ (&(_X_))->fp = ts; \
+ } \
+ }
+
+/*--------------------------------------------------
+ * ppsclock STREAM init
+ */
+static int
+ppsclock_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "ppsclocd";
+ static char m2[] = "ppsclock";
+
+ /*
+ * now push the parse streams module
+ * it will ensure exclusive access to the device
+ */
+ if (ioctl(parse->ppsfd, I_PUSH, (caddr_t)m1) == -1 &&
+ ioctl(parse->ppsfd, I_PUSH, (caddr_t)m2) == -1)
+ {
+ if (errno != EINVAL)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
+ CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ if (!local_init(parse))
+ {
+ (void)ioctl(parse->ppsfd, I_POP, (caddr_t)0);
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse STREAM init
+ */
+static int
+stream_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "parse";
+ /*
+ * now push the parse streams module
+ * to test whether it is there (neat interface 8-( )
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ if (errno != EINVAL) /* accept non-existence */
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ else
+ {
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+
+ /*
+ * now push it a second time after we have removed all
+ * module garbage
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * parse STREAM end
+ */
+static void
+stream_end(
+ struct parseunit *parse
+ )
+{
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+}
+
+/*--------------------------------------------------
+ * STREAM setcs
+ */
+static int
+stream_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETCS;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM enable
+ */
+static int
+stream_enable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_ENABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = stream_receive; /* ok - parse input in kernel */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM disable
+ */
+static int
+stream_disable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_DISABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = local_receive; /* ok - parse input in daemon */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getfmt
+ */
+static int
+stream_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setfmt
+ */
+static int
+stream_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+
+/*--------------------------------------------------
+ * STREAM timecode
+ */
+static int
+stream_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_TIMECODE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_timecode: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ clear_err(parse, ERR_INTERNAL);
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM receive
+ */
+static void
+stream_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ parsetime_t parsetime;
+
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: stream_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time %lx.%08lx, stime %lx.%08lx, ptime %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (unsigned long)parsetime.parse_time.tv.tv_sec,
+ (unsigned long)parsetime.parse_time.tv.tv_usec,
+ (unsigned long)parsetime.parse_stime.tv.tv_sec,
+ (unsigned long)parsetime.parse_stime.tv.tv_usec,
+ (unsigned long)parsetime.parse_ptime.tv.tv_sec,
+ (unsigned long)parsetime.parse_ptime.tv.tv_usec);
+ }
+#endif
+
+ /*
+ * switch time stamp world - be sure to normalize small usec field
+ * errors.
+ */
+
+ cvt_ts(parsetime.parse_stime, "parse_stime");
+
+ if (PARSE_TIMECODE(parsetime.parse_state))
+ {
+ cvt_ts(parsetime.parse_time, "parse_time");
+ }
+
+ if (PARSE_PPS(parsetime.parse_state))
+ cvt_ts(parsetime.parse_ptime, "parse_ptime");
+
+ parse_process(parse, &parsetime);
+}
+#endif
+
+/*--------------------------------------------------
+ * local init
+ */
+static int
+local_init(
+ struct parseunit *parse
+ )
+{
+ return parse_ioinit(&parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local end
+ */
+static void
+local_end(
+ struct parseunit *parse
+ )
+{
+ parse_ioend(&parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local nop
+ */
+static int
+local_nop(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+
+/*--------------------------------------------------
+ * local setcs
+ */
+static int
+local_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setcs(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getfmt
+ */
+static int
+local_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_getfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setfmt
+ */
+static int
+local_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local timecode
+ */
+static int
+local_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_timecode(tcl, &parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local input
+ */
+static int
+local_input(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ int count;
+ unsigned char *s;
+ timestamp_t ts;
+
+ if (!parse->peer)
+ return 0;
+
+ /*
+ * eat all characters, parsing then and feeding complete samples
+ */
+ count = rbufp->recv_length;
+ s = (unsigned char *)rbufp->recv_buffer;
+ ts.fp = rbufp->recv_time;
+
+ while (count--)
+ {
+ if (parse_ioread(&parse->parseio, (unsigned int)(*s++), &ts))
+ {
+ struct recvbuf *buf;
+
+ /*
+ * got something good to eat
+ */
+ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state))
+ {
+#ifdef HAVE_PPSAPI
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ struct timespec pps_timeout;
+ pps_info_t pps_info;
+
+ pps_timeout.tv_sec = 0;
+ pps_timeout.tv_nsec = 0;
+
+ if (time_pps_fetch(parse->ppshandle, PPS_TSFMT_TSPEC, &pps_info,
+ &pps_timeout) == 0)
+ {
+ if (pps_info.assert_sequence + pps_info.clear_sequence != parse->ppsserial)
+ {
+ double dtemp;
+
+ struct timespec pts;
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (parse->flags & PARSE_CLEAR)
+ pts = pps_info.clear_timestamp;
+ else
+ pts = pps_info.assert_timestamp;
+
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui = pts.tv_sec + JAN_1970;
+
+ dtemp = pts.tv_nsec / 1e9;
+ if (dtemp < 0.) {
+ dtemp += 1;
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui--;
+ }
+ if (dtemp > 1.) {
+ dtemp -= 1;
+ parse->parseio.parse_dtime.parse_ptime.fp.l_ui++;
+ }
+ parse->parseio.parse_dtime.parse_ptime.fp.l_uf = dtemp * FRAC;
+
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI seq %ld - PPS %s\n",
+ rbufp->fd,
+ (long)pps_info.assert_sequence + (long)pps_info.clear_sequence,
+ lfptoa(&parse->parseio.parse_dtime.parse_ptime.fp, 6));
+ }
+#endif
+ }
+#ifdef DEBUG
+ else
+ {
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI seq assert %ld, seq clear %ld - NO PPS event\n",
+ rbufp->fd,
+ (long)pps_info.assert_sequence, (long)pps_info.clear_sequence);
+ }
+ }
+#endif
+ parse->ppsserial = pps_info.assert_sequence + pps_info.clear_sequence;
+ }
+#ifdef DEBUG
+ else
+ {
+ if (debug > 3)
+ {
+ printf(
+ "parse: local_receive: fd %d PPSAPI time_pps_fetch errno = %d\n",
+ rbufp->fd,
+ errno);
+ }
+ }
+#endif
+ }
+#else
+#ifdef TIOCDCDTIMESTAMP
+ struct timeval dcd_time;
+
+ if (ioctl(parse->ppsfd, TIOCDCDTIMESTAMP, &dcd_time) != -1)
+ {
+ l_fp tstmp;
+
+ TVTOTS(&dcd_time, &tstmp);
+ tstmp.l_ui += JAN_1970;
+ L_SUB(&ts.fp, &tstmp);
+ if (ts.fp.l_ui == 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ {
+ printf(
+ "parse: local_receive: fd %d DCDTIMESTAMP %s\n",
+ parse->ppsfd,
+ lfptoa(&tstmp, 6));
+ printf(" sigio %s\n",
+ lfptoa(&ts.fp, 6));
+ }
+#endif
+ parse->parseio.parse_dtime.parse_ptime.fp = tstmp;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+#else /* TIOCDCDTIMESTAMP */
+#if defined(HAVE_STRUCT_PPSCLOCKEV) && (defined(HAVE_CIOGETEV) || defined(HAVE_TIOCGPPSEV))
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ l_fp tts;
+ struct ppsclockev ev;
+
+#ifdef HAVE_CIOGETEV
+ if (ioctl(parse->ppsfd, CIOGETEV, (caddr_t)&ev) == 0)
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ if (ioctl(parse->ppsfd, TIOCGPPSEV, (caddr_t)&ev) == 0)
+#endif
+ {
+ if (ev.serial != parse->ppsserial)
+ {
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (!buftvtots((const char *)&ev.tv, &tts))
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
+ }
+ else
+ {
+ parse->parseio.parse_dtime.parse_ptime.fp = tts;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+ parse->ppsserial = ev.serial;
+ }
+ }
+#endif
+#endif /* TIOCDCDTIMESTAMP */
+#endif /* !HAVE_PPSAPI */
+ }
+ if (count)
+ { /* simulate receive */
+ buf = get_free_recv_buffer();
+ if (buf != NULL) {
+ memmove((caddr_t)buf->recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ buf->recv_length = sizeof(parsetime_t);
+ buf->recv_time = rbufp->recv_time;
+ buf->srcadr = rbufp->srcadr;
+ buf->dstadr = rbufp->dstadr;
+ buf->receiver = rbufp->receiver;
+ buf->fd = rbufp->fd;
+ buf->X_from_where = rbufp->X_from_where;
+ add_full_recv_buffer(buf);
+ }
+ parse_iodone(&parse->parseio);
+ }
+ else
+ {
+ memmove((caddr_t)rbufp->recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ parse_iodone(&parse->parseio);
+ rbufp->recv_length = sizeof(parsetime_t);
+ return 1; /* got something & in place return */
+ }
+ }
+ }
+ return 0; /* nothing to pass up */
+}
+
+/*--------------------------------------------------
+ * local receive
+ */
+static void
+local_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ parsetime_t parsetime;
+
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: local_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time(fp) %lx.%08lx, stime(fp) %lx.%08lx, ptime(fp) %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (unsigned long)parsetime.parse_time.fp.l_ui,
+ (unsigned long)parsetime.parse_time.fp.l_uf,
+ (unsigned long)parsetime.parse_stime.fp.l_ui,
+ (unsigned long)parsetime.parse_stime.fp.l_uf,
+ (unsigned long)parsetime.parse_ptime.fp.l_ui,
+ (unsigned long)parsetime.parse_ptime.fp.l_uf);
+ }
+#endif
+
+ parse_process(parse, &parsetime);
+}
+
+/*--------------------------------------------------
+ * init_iobinding - find and initialize lower layers
+ */
+static bind_t *
+init_iobinding(
+ struct parseunit *parse
+ )
+{
+ bind_t *b = io_bindings;
+
+ while (b->bd_description != (char *)0)
+ {
+ if ((*b->bd_init)(parse))
+ {
+ return b;
+ }
+ b++;
+ }
+ return (bind_t *)0;
+}
+
+/**===========================================================================
+ ** support routines
+ **/
+
+/*--------------------------------------------------
+ * convert a flag field to a string
+ */
+static char *
+parsestate(
+ u_long lstate,
+ char *buffer,
+ int size
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADD WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE, "ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ u_long bit;
+ const char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+ char *s, *t;
+
+
+ *buffer = '\0';
+ s = t = buffer;
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (s != t)
+ strncpy(t, "; ", BUFFER_SIZES(buffer, t, size));
+ strncat(t, flagstrings[i].name, BUFFER_SIZES(buffer, t, size));
+ t += strlen(t);
+ }
+ i++;
+ }
+
+ if (lstate & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ if (s != t)
+ strncpy(t, "; ", BUFFER_SIZES(buffer, t, size));
+
+ t += strlen(t);
+
+ strncpy(t, "(", BUFFER_SIZES(buffer, t, size));
+
+ s = t = t + strlen(t);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & lstate)
+ {
+ if (t != s)
+ {
+ strncpy(t, "; ", BUFFER_SIZES(buffer, t, size));
+ t += 2;
+ }
+
+ strncpy(t, sflagstrings[i].name, BUFFER_SIZES(buffer, t, size));
+ t += strlen(t);
+ }
+ i++;
+ }
+ strncpy(t, ")", BUFFER_SIZES(buffer, t, size));
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *
+parsestatus(
+ u_long lstate,
+ char *buffer,
+ int size
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { CVT_ADDITIONAL, "ADDITIONAL DATA" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (buffer[0])
+ strncat(buffer, "; ", size);
+ strncat(buffer, flagstrings[i].name, size);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a clock status flag field to a string
+ */
+static const char *
+clockstatus(
+ u_long lstate
+ )
+{
+ static char buffer[20];
+ static struct status
+ {
+ u_long value;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CEVNT_NOMINAL, "NOMINAL" },
+ { CEVNT_TIMEOUT, "NO RESPONSE" },
+ { CEVNT_BADREPLY,"BAD FORMAT" },
+ { CEVNT_FAULT, "FAULT" },
+ { CEVNT_PROP, "PROPAGATION DELAY" },
+ { CEVNT_BADDATE, "ILLEGAL DATE" },
+ { CEVNT_BADTIME, "ILLEGAL TIME" },
+ { (unsigned)~0L }
+ };
+ int i;
+
+ i = 0;
+ while (flagstrings[i].value != ~0)
+ {
+ if (flagstrings[i].value == lstate)
+ {
+ return flagstrings[i].name;
+ }
+ i++;
+ }
+
+ snprintf(buffer, sizeof(buffer), "unknown #%ld", (u_long)lstate);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * l_mktime - make representation of a relative time
+ */
+static char *
+l_mktime(
+ u_long delta
+ )
+{
+ u_long tmp, m, s;
+ static char buffer[40];
+ char *t;
+
+ buffer[0] = '\0';
+
+ if ((tmp = delta / (60*60*24)) != 0)
+ {
+ snprintf(buffer, BUFFER_SIZE(buffer, buffer), "%ldd+", (u_long)tmp);
+ delta -= tmp * 60*60*24;
+ }
+
+ s = delta % 60;
+ delta /= 60;
+ m = delta % 60;
+ delta /= 60;
+
+ t = buffer + strlen(buffer);
+
+ snprintf(t, BUFFER_SIZE(buffer, t), "%02d:%02d:%02d",
+ (int)delta, (int)m, (int)s);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * parse_statistics - list summary of clock states
+ */
+static void
+parse_statistics(
+ struct parseunit *parse
+ )
+{
+ int i;
+
+ NLOG(NLOG_CLOCKSTATIST) /* conditional if clause for conditional syslog */
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
+ CLK_UNIT(parse->peer),
+ l_mktime(current_time - parse->generic->timestarted));
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
+ CLK_UNIT(parse->peer),
+ clockstatus(parse->generic->currentstatus));
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long percent, d = current_time - parse->generic->timestarted;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ msyslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3ld.%02ld%%)",
+ CLK_UNIT(parse->peer),
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ percent / 100, percent % 100);
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * cparse_statistics - wrapper for statistics call
+ */
+static void
+cparse_statistics(
+ struct parseunit *parse
+ )
+{
+ if (parse->laststatistic + PARSESTATISTICS < current_time)
+ parse_statistics(parse);
+ parse->laststatistic = current_time;
+}
+
+/**===========================================================================
+ ** ntp interface routines
+ **/
+
+/*--------------------------------------------------
+ * parse_shutdown - shut down a PARSE clock
+ */
+static void
+parse_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)0;
+
+ if (peer && peer->procptr)
+ parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (!parse)
+ {
+ /* nothing to clean up */
+ return;
+ }
+
+ if (!parse->peer)
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: INTERNAL ERROR - unit already inactive - shutdown ignored", unit);
+ return;
+ }
+
+#ifdef HAVE_PPSAPI
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ (void)time_pps_destroy(parse->ppshandle);
+ }
+#endif
+ if (parse->generic->io.fd != parse->ppsfd && parse->ppsfd != -1)
+ (void)close(parse->ppsfd); /* close separate PPS source */
+
+ /*
+ * print statistics a last time and
+ * stop statistics machine
+ */
+ parse_statistics(parse);
+
+ if (parse->parse_type->cl_end)
+ {
+ parse->parse_type->cl_end(parse);
+ }
+
+ /*
+ * cleanup before leaving this world
+ */
+ if (parse->binding)
+ PARSE_END(parse);
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ io_closeclock(&parse->generic->io);
+
+ free_varlist(parse->kv);
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
+ CLK_UNIT(parse->peer), parse->parse_type->cl_description);
+
+ parse->peer = (struct peer *)0; /* unused now */
+ peer->procptr->unitptr = (caddr_t)0;
+ free(parse);
+}
+
+#ifdef HAVE_PPSAPI
+/*----------------------------------------
+ * set up HARDPPS via PPSAPI
+ */
+static void
+parse_hardpps(
+ struct parseunit *parse,
+ int mode
+ )
+{
+ if (parse->hardppsstate == mode)
+ return;
+
+ if (CLK_PPS(parse->peer) && (parse->flags & PARSE_PPSKERNEL)) {
+ int i = 0;
+
+ if (mode == PARSE_HARDPPS_ENABLE)
+ {
+ if (parse->flags & PARSE_CLEAR)
+ i = PPS_CAPTURECLEAR;
+ else
+ i = PPS_CAPTUREASSERT;
+ }
+
+ if (time_pps_kcbind(parse->ppshandle, PPS_KC_HARDPPS, i,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: time_pps_kcbind failed: %m",
+ CLK_UNIT(parse->peer));
+ } else {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "PARSE receiver #%d: kernel PPS synchronisation %sabled",
+ CLK_UNIT(parse->peer), (mode == PARSE_HARDPPS_ENABLE) ? "en" : "dis");
+ /*
+ * tell the rest, that we have a kernel PPS source, iff we ever enable HARDPPS
+ */
+ if (mode == PARSE_HARDPPS_ENABLE)
+ pps_enable = 1;
+ }
+ }
+
+ parse->hardppsstate = mode;
+}
+
+/*----------------------------------------
+ * set up PPS via PPSAPI
+ */
+static int
+parse_ppsapi(
+ struct parseunit *parse
+ )
+{
+ int cap, mode, mode1;
+ char *cp;
+
+ parse->flags &= ~PARSE_PPSCLOCK;
+
+ if (time_pps_getcap(parse->ppshandle, &cap) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_ppsapi: time_pps_getcap failed: %m",
+ CLK_UNIT(parse->peer));
+
+ return 0;
+ }
+
+ if (time_pps_getparams(parse->ppshandle, &parse->ppsparams) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_ppsapi: time_pps_getparams failed: %m",
+ CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ if (parse->flags & PARSE_CLEAR) {
+ cp = "CLEAR";
+ mode = PPS_CAPTURECLEAR;
+ mode1 = PPS_OFFSETCLEAR;
+ } else {
+ cp = "ASSERT";
+ mode = PPS_CAPTUREASSERT;
+ mode1 = PPS_OFFSETASSERT;
+ }
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: initializing PPS to %s",
+ CLK_UNIT(parse->peer), cp);
+
+ if (!(mode & cap)) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: FAILED to initialize PPS to %s (PPS API capabilities=0x%x)",
+ CLK_UNIT(parse->peer), cp, cap);
+
+ return 0;
+ }
+
+ if (!(mode1 & cap)) {
+ msyslog(LOG_WARNING, "PARSE receiver #%d: Cannot set PPS_%sCLEAR, this will increase jitter (PPS API capabilities=0x%x)",
+ CLK_UNIT(parse->peer), cp, cap);
+ mode1 = 0;
+ } else {
+ if (mode1 == PPS_OFFSETCLEAR)
+ {
+ parse->ppsparams.clear_offset.tv_sec = -parse->ppsphaseadjust;
+ parse->ppsparams.clear_offset.tv_nsec = -1e9*(parse->ppsphaseadjust - (long)parse->ppsphaseadjust);
+ }
+
+ if (mode1 == PPS_OFFSETASSERT)
+ {
+ parse->ppsparams.assert_offset.tv_sec = -parse->ppsphaseadjust;
+ parse->ppsparams.assert_offset.tv_nsec = -1e9*(parse->ppsphaseadjust - (long)parse->ppsphaseadjust);
+ }
+ }
+
+ /* only set what is legal */
+
+ parse->ppsparams.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
+
+ if (time_pps_setparams(parse->ppshandle, &parse->ppsparams) < 0) {
+ msyslog(LOG_ERR, "PARSE receiver #%d: FAILED set PPS parameters: %m",
+ CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+#else
+#define parse_hardpps(_PARSE_, _MODE_) /* empty */
+#endif
+
+/*--------------------------------------------------
+ * parse_start - open the PARSE devices and initialize data for processing
+ */
+static int
+parse_start(
+ int sysunit,
+ struct peer *peer
+ )
+{
+ u_int unit;
+ int fd232;
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ struct parseunit * parse;
+ char parsedev[sizeof(PARSEDEVICE)+20];
+ char parseppsdev[sizeof(PARSEPPSDEVICE)+20];
+ parsectl_t tmp_ctl;
+ u_int type;
+
+ /*
+ * get out Copyright information once
+ */
+ if (!notice)
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-2006, Frank Kardel");
+ notice = 1;
+ }
+
+ type = CLK_TYPE(peer);
+ unit = CLK_UNIT(peer);
+
+ if ((type == ~0) || (parse_clockinfo[type].cl_description == (char *)0))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
+ unit, CLK_REALTYPE(peer), ncltypes-1);
+ return 0;
+ }
+
+ /*
+ * Unit okay, attempt to open the device.
+ */
+ (void) snprintf(parsedev, sizeof(parsedev), PARSEDEVICE, unit);
+ (void) snprintf(parseppsdev, sizeof(parsedev), PARSEPPSDEVICE, unit);
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+ fd232 = open(parsedev, O_RDWR | O_NOCTTY
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ , 0777);
+
+ if (fd232 == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
+ return 0;
+ }
+
+ parse = (struct parseunit *)emalloc(sizeof(struct parseunit));
+
+ memset((char *)parse, 0, sizeof(struct parseunit));
+
+ parse->generic = peer->procptr; /* link up */
+ parse->generic->unitptr = (caddr_t)parse; /* link down */
+
+ /*
+ * Set up the structures
+ */
+ parse->generic->timestarted = current_time;
+ parse->lastchange = current_time;
+
+ parse->flags = 0;
+ parse->pollneeddata = 0;
+ parse->laststatistic = current_time;
+ parse->lastformat = (unsigned short)~0; /* assume no format known */
+ parse->timedata.parse_status = (unsigned short)~0; /* be sure to mark initial status change */
+ parse->lastmissed = 0; /* assume got everything */
+ parse->ppsserial = 0;
+ parse->ppsfd = -1;
+ parse->localdata = (void *)0;
+ parse->localstate = 0;
+ parse->kv = (struct ctl_var *)0;
+
+ clear_err(parse, ERR_ALL);
+
+ parse->parse_type = &parse_clockinfo[type];
+
+ parse->maxunsync = parse->parse_type->cl_maxunsync;
+
+ parse->generic->fudgetime1 = parse->parse_type->cl_basedelay;
+
+ parse->generic->fudgetime2 = 0.0;
+ parse->ppsphaseadjust = parse->generic->fudgetime2;
+
+ parse->generic->clockdesc = parse->parse_type->cl_description;
+
+ peer->rootdelay = parse->parse_type->cl_rootdelay;
+ peer->sstclktype = parse->parse_type->cl_type;
+ peer->precision = sys_precision;
+
+ peer->stratum = STRATUM_REFCLOCK;
+
+ if (peer->stratum <= 1)
+ memmove((char *)&parse->generic->refid, parse->parse_type->cl_id, 4);
+ else
+ parse->generic->refid = htonl(PARSEHSREFID);
+
+ parse->generic->io.fd = fd232;
+
+ parse->peer = peer; /* marks it also as busy */
+
+ /*
+ * configure terminal line
+ */
+ if (TTY_GETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+#ifndef _PC_VDISABLE
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc));
+#else
+ int disablec;
+ errno = 0; /* pathconf can deliver -1 without changing errno ! */
+
+ disablec = fpathconf(parse->generic->io.fd, _PC_VDISABLE);
+ if (disablec == -1 && errno)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CLK_UNIT(parse->peer));
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); /* best guess */
+ }
+ else
+ if (disablec != -1)
+ memset((char *)tio.c_cc, disablec, sizeof(tio.c_cc));
+#endif
+
+#if defined (VMIN) || defined(VTIME)
+ if ((parse_clockinfo[type].cl_lflag & ICANON) == 0)
+ {
+#ifdef VMIN
+ tio.c_cc[VMIN] = 1;
+#endif
+#ifdef VTIME
+ tio.c_cc[VTIME] = 0;
+#endif
+ }
+#endif
+
+ tio.c_cflag = parse_clockinfo[type].cl_cflag;
+ tio.c_iflag = parse_clockinfo[type].cl_iflag;
+ tio.c_oflag = parse_clockinfo[type].cl_oflag;
+ tio.c_lflag = parse_clockinfo[type].cl_lflag;
+
+
+#ifdef HAVE_TERMIOS
+ if ((cfsetospeed(&tio, parse_clockinfo[type].cl_speed) == -1) ||
+ (cfsetispeed(&tio, parse_clockinfo[type].cl_speed) == -1))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcset{i,o}speed(&tio, speed): %m", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#else
+ tio.c_cflag |= parse_clockinfo[type].cl_speed;
+#endif
+
+ /*
+ * set up pps device
+ * if the PARSEPPSDEVICE can be opened that will be used
+ * for PPS else PARSEDEVICE will be used
+ */
+ parse->ppsfd = open(parseppsdev, O_RDWR | O_NOCTTY
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ , 0777);
+
+ if (parse->ppsfd == -1)
+ {
+ parse->ppsfd = fd232;
+ }
+
+/*
+ * Linux PPS - the old way
+ */
+#if defined(HAVE_TIO_SERIAL_STUFF) /* Linux hack: define PPS interface */
+ {
+ struct serial_struct ss;
+ if (ioctl(parse->ppsfd, TIOCGSERIAL, &ss) < 0 ||
+ (
+#ifdef ASYNC_LOW_LATENCY
+ ss.flags |= ASYNC_LOW_LATENCY,
+#endif
+#ifndef HAVE_PPSAPI
+#ifdef ASYNC_PPS_CD_NEG
+ ss.flags |= ASYNC_PPS_CD_NEG,
+#endif
+#endif
+ ioctl(parse->ppsfd, TIOCSSERIAL, &ss)) < 0) {
+ msyslog(LOG_NOTICE, "refclock_parse: TIOCSSERIAL fd %d, %m", parse->ppsfd);
+ msyslog(LOG_NOTICE,
+ "refclock_parse: optional PPS processing not available");
+ } else {
+ parse->flags |= PARSE_PPSCLOCK;
+#ifdef ASYNC_PPS_CD_NEG
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO,
+ "refclock_parse: PPS detection on");
+#endif
+ }
+ }
+#endif
+
+/*
+ * SUN the Solaris way
+ */
+#ifdef HAVE_TIOCSPPS /* SUN PPS support */
+ if (CLK_PPS(parse->peer))
+ {
+ int i = 1;
+
+ if (ioctl(parse->ppsfd, TIOCSPPS, (caddr_t)&i) == 0)
+ {
+ parse->flags |= PARSE_PPSCLOCK;
+ }
+ }
+#endif
+
+/*
+ * PPS via PPSAPI
+ */
+#if defined(HAVE_PPSAPI)
+ parse->hardppsstate = PARSE_HARDPPS_DISABLE;
+ if (CLK_PPS(parse->peer))
+ {
+ if (time_pps_create(parse->ppsfd, &parse->ppshandle) < 0)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: could not set up PPS: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ parse_ppsapi(parse);
+ }
+ }
+#endif
+
+ if (TTY_SETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+
+ /*
+ * pick correct input machine
+ */
+ parse->generic->io.srcclock = (caddr_t)parse;
+ parse->generic->io.datalen = 0;
+
+ parse->binding = init_iobinding(parse);
+
+ if (parse->binding == (bind_t *)0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.", CLK_UNIT(parse->peer));
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ parse->generic->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
+ parse->generic->io.io_input = parse->binding->bd_io_input; /* pick correct input routine */
+
+ /*
+ * as we always(?) get 8 bit chars we want to be
+ * sure, that the upper bits are zero for less
+ * than 8 bit I/O - so we pass that information on.
+ * note that there can be only one bit count format
+ * per file descriptor
+ */
+
+ switch (tio.c_cflag & CSIZE)
+ {
+ case CS5:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
+ break;
+
+ case CS6:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
+ break;
+
+ case CS7:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
+ break;
+
+ case CS8:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
+ break;
+ }
+
+ if (!PARSE_SETCS(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ strncpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format, sizeof(tmp_ctl.parseformat.parse_buffer));
+ tmp_ctl.parseformat.parse_count = strlen(tmp_ctl.parseformat.parse_buffer);
+
+ if (!PARSE_SETFMT(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * get rid of all IO accumulated so far
+ */
+#ifdef HAVE_TERMIOS
+ (void) tcflush(parse->generic->io.fd, TCIOFLUSH);
+#else
+#if defined(TCFLSH) && defined(TCIOFLUSH)
+ {
+ int flshcmd = TCIOFLUSH;
+
+ (void) ioctl(parse->generic->io.fd, TCFLSH, (caddr_t)&flshcmd);
+ }
+#endif
+#endif
+
+ /*
+ * try to do any special initializations
+ */
+ if (parse->parse_type->cl_init)
+ {
+ if (parse->parse_type->cl_init(parse))
+ {
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+ }
+
+ /*
+ * Insert in async io device list.
+ */
+ if (!io_addclock(&parse->generic->io))
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CLK_UNIT(parse->peer), parsedev);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+
+ /*
+ * print out configuration
+ */
+ NLOG(NLOG_CLOCKINFO)
+ {
+ /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (I/O device %s, PPS device %s) added",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_description, parsedev,
+ (parse->ppsfd != parse->generic->io.fd) ? parseppsdev : parsedev);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, trust time %s, precision %d",
+ CLK_UNIT(parse->peer),
+ parse->peer->stratum,
+ l_mktime(parse->maxunsync), parse->peer->precision);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: rootdelay %.6f s, phase adjustment %.6f s, PPS phase adjustment %.6f s, %s IO handling",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_rootdelay,
+ parse->generic->fudgetime1,
+ parse->ppsphaseadjust,
+ parse->binding->bd_description);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CLK_UNIT(parse->peer),
+ parse->parse_type->cl_format);
+ msyslog(LOG_INFO, "PARSE receiver #%d: %sPPS support%s", CLK_UNIT(parse->peer),
+ CLK_PPS(parse->peer) ? "" : "NO ",
+ CLK_PPS(parse->peer) ?
+#ifdef PPS_METHOD
+ " (implementation " PPS_METHOD ")"
+#else
+ ""
+#endif
+ : ""
+ );
+ }
+
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse_ctl - process changes on flags/time values
+ */
+static void
+parse_ctl(
+ struct parseunit *parse,
+ struct refclockstat *in
+ )
+{
+ if (in)
+ {
+ if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parse->flags = (parse->flags & ~(CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) |
+ (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4));
+#if defined(HAVE_PPSAPI)
+ if (CLK_PPS(parse->peer))
+ {
+ parse_ppsapi(parse);
+ }
+#endif
+ }
+
+ if (in->haveflags & CLK_HAVETIME1)
+ {
+ parse->generic->fudgetime1 = in->fudgetime1;
+ msyslog(LOG_INFO, "PARSE receiver #%d: new phase adjustment %.6f s",
+ CLK_UNIT(parse->peer),
+ parse->generic->fudgetime1);
+ }
+
+ if (in->haveflags & CLK_HAVETIME2)
+ {
+ parse->generic->fudgetime2 = in->fudgetime2;
+ if (parse->flags & PARSE_TRUSTTIME)
+ {
+ parse->maxunsync = (u_long)ABS(in->fudgetime2);
+ msyslog(LOG_INFO, "PARSE receiver #%d: new trust time %s",
+ CLK_UNIT(parse->peer),
+ l_mktime(parse->maxunsync));
+ }
+ else
+ {
+ parse->ppsphaseadjust = in->fudgetime2;
+ msyslog(LOG_INFO, "PARSE receiver #%d: new PPS phase adjustment %.6f s",
+ CLK_UNIT(parse->peer),
+ parse->ppsphaseadjust);
+#if defined(HAVE_PPSAPI)
+ if (CLK_PPS(parse->peer))
+ {
+ parse_ppsapi(parse);
+ }
+#endif
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * parse_poll - called by the transmit procedure
+ */
+static void
+parse_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (peer != parse->peer)
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: poll: INTERNAL: peer incorrect",
+ unit);
+ return;
+ }
+
+ /*
+ * Update clock stat counters
+ */
+ parse->generic->polls++;
+
+ if (parse->pollneeddata &&
+ ((current_time - parse->pollneeddata) > (1<<(max(min(parse->peer->hpoll, parse->peer->ppoll), parse->peer->minpoll)))))
+ {
+ /*
+ * start worrying when exceeding a poll inteval
+ * bad news - didn't get a response last time
+ */
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ ERR(ERR_NODATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval (check receiver / wiring)", CLK_UNIT(parse->peer));
+ }
+
+ /*
+ * we just mark that we want the next sample for the clock filter
+ */
+ parse->pollneeddata = current_time;
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ cparse_statistics(parse);
+
+ return;
+}
+
+#define LEN_STATES 300 /* length of state string */
+
+/*--------------------------------------------------
+ * parse_control - set fudge factors, return statistics
+ */
+static void
+parse_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+ parsectl_t tmpctl;
+
+ static char outstatus[400]; /* status output buffer */
+
+ if (out)
+ {
+ out->lencode = 0;
+ out->p_lastcode = 0;
+ out->kv_list = (struct ctl_var *)0;
+ }
+
+ if (!parse || !parse->peer)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
+ unit);
+ return;
+ }
+
+ unit = CLK_UNIT(parse->peer);
+
+ /*
+ * handle changes
+ */
+ parse_ctl(parse, in);
+
+ /*
+ * supply data
+ */
+ if (out)
+ {
+ u_long sum = 0;
+ char *tt, *start;
+ int i;
+
+ outstatus[0] = '\0';
+
+ out->type = REFCLK_PARSE;
+
+ /*
+ * keep fudgetime2 in sync with TRUSTTIME/MAXUNSYNC flag1
+ */
+ parse->generic->fudgetime2 = (parse->flags & PARSE_TRUSTTIME) ? (double)parse->maxunsync : parse->ppsphaseadjust;
+
+ /*
+ * figure out skew between PPS and RS232 - just for informational
+ * purposes
+ */
+ if (PARSE_SYNC(parse->timedata.parse_state))
+ {
+ if (PARSE_PPS(parse->timedata.parse_state) && PARSE_TIMECODE(parse->timedata.parse_state))
+ {
+ l_fp off;
+
+ /*
+ * we have a PPS and RS232 signal - calculate the skew
+ * WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
+ */
+ off = parse->timedata.parse_stime.fp;
+ L_SUB(&off, &parse->timedata.parse_ptime.fp); /* true offset */
+ tt = add_var(&out->kv_list, 80, RO);
+ snprintf(tt, 80, "refclock_ppsskew=%s", lfptoms(&off, 6));
+ }
+ }
+
+ if (PARSE_PPS(parse->timedata.parse_state))
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 80, "refclock_ppstime=\"%s\"", gmprettydate(&parse->timedata.parse_ptime.fp));
+ }
+
+ start = tt = add_var(&out->kv_list, 128, RO|DEF);
+ snprintf(tt, 128, "refclock_time=\"");
+ tt += strlen(tt);
+
+ if (parse->timedata.parse_time.fp.l_ui == 0)
+ {
+ strncpy(tt, "<UNDEFINED>\"", BUFFER_SIZES(start, tt, 128));
+ }
+ else
+ {
+ snprintf(tt, 128, "%s\"", gmprettydate(&parse->timedata.parse_time.fp));
+ }
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
+ }
+ else
+ {
+ start = tt = add_var(&out->kv_list, 512, RO|DEF);
+ snprintf(tt, 512, "refclock_status=\"");
+ tt += strlen(tt);
+
+ /*
+ * copy PPS flags from last read transaction (informational only)
+ */
+ tmpctl.parsegettc.parse_state |= parse->timedata.parse_state &
+ (PARSEB_PPS|PARSEB_S_PPS);
+
+ (void) parsestate(tmpctl.parsegettc.parse_state, tt, BUFFER_SIZES(start, tt, 512));
+
+ strncat(tt, "\"", BUFFER_SIZES(start, tt, 512));
+
+ if (tmpctl.parsegettc.parse_count)
+ mkascii(outstatus+strlen(outstatus), (int)(sizeof(outstatus)- strlen(outstatus) - 1),
+ tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count));
+
+ }
+
+ tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 80, "refclock_format=\"");
+
+ strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count);
+ strncat(tt,"\"", 80);
+ }
+
+ /*
+ * gather state statistics
+ */
+
+ start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF);
+ strncpy(tt, "refclock_states=\"", LEN_STATES);
+ tt += strlen(tt);
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long d = current_time - parse->generic->timestarted;
+ u_long percent;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ {
+ char item[80];
+ int count;
+
+ snprintf(item, 80, "%s%s%s: %s (%d.%02d%%)",
+ sum ? "; " : "",
+ (parse->generic->currentstatus == i) ? "*" : "",
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ (int)(percent / 100), (int)(percent % 100));
+ if ((count = strlen(item)) < (LEN_STATES - 40 - (tt - start)))
+ {
+ strncpy(tt, item, BUFFER_SIZES(start, tt, LEN_STATES));
+ tt += count;
+ }
+ sum += s_time;
+ }
+ }
+
+ snprintf(tt, BUFFER_SIZES(start, tt, LEN_STATES), "; running time: %s\"", l_mktime(sum));
+
+ tt = add_var(&out->kv_list, 32, RO);
+ snprintf(tt, 32, "refclock_id=\"%s\"", parse->parse_type->cl_id);
+
+ tt = add_var(&out->kv_list, 80, RO);
+ snprintf(tt, 80, "refclock_iomode=\"%s\"", parse->binding->bd_description);
+
+ tt = add_var(&out->kv_list, 128, RO);
+ snprintf(tt, 128, "refclock_driver_version=\"%s\"", rcsid);
+
+ {
+ struct ctl_var *k;
+
+ k = parse->kv;
+ while (k && !(k->flags & EOV))
+ {
+ set_var(&out->kv_list, k->text, strlen(k->text)+1, k->flags);
+ k++;
+ }
+ }
+
+ out->lencode = strlen(outstatus);
+ out->p_lastcode = outstatus;
+ }
+}
+
+/**===========================================================================
+ ** processing routines
+ **/
+
+/*--------------------------------------------------
+ * event handling - note that nominal events will also be posted
+ * keep track of state dwelling times
+ */
+static void
+parse_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ if (parse->generic->currentstatus != (u_char) event)
+ {
+ parse->statetime[parse->generic->currentstatus] += current_time - parse->lastchange;
+ parse->lastchange = current_time;
+
+ if (parse->parse_type->cl_event)
+ parse->parse_type->cl_event(parse, event);
+
+ if (event == CEVNT_NOMINAL)
+ {
+ NLOG(NLOG_CLOCKSTATUS)
+ msyslog(LOG_INFO, "PARSE receiver #%d: SYNCHRONIZED",
+ CLK_UNIT(parse->peer));
+ }
+
+ refclock_report(parse->peer, event);
+ }
+}
+
+/*--------------------------------------------------
+ * process a PARSE time sample
+ */
+static void
+parse_process(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ l_fp off, rectime, reftime;
+ double fudge;
+
+ /*
+ * check for changes in conversion status
+ * (only one for each new status !)
+ */
+ if (((parsetime->parse_status & CVT_MASK) != CVT_OK) &&
+ ((parsetime->parse_status & CVT_MASK) != CVT_NONE) &&
+ (parse->timedata.parse_status != parsetime->parse_status))
+ {
+ char buffer[400];
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
+ CLK_UNIT(parse->peer), parsestatus(parsetime->parse_status, buffer, sizeof(buffer)));
+
+ if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
+ {
+ /*
+ * tell more about the story - list time code
+ * there is a slight change for a race condition and
+ * the time code might be overwritten by the next packet
+ */
+ parsectl_t tmpctl;
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\" (check receiver configuration / wiring)",
+ CLK_UNIT(parse->peer), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1)));
+ }
+ }
+ }
+
+ /*
+ * examine status and post appropriate events
+ */
+ if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
+ {
+ /*
+ * got bad data - tell the rest of the system
+ */
+ switch (parsetime->parse_status & CVT_MASK)
+ {
+ case CVT_NONE:
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ /*
+ * save PPS information that comes piggyback
+ */
+ if (PARSE_PPS(parsetime->parse_state))
+ {
+ parse->timedata.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ parse->timedata.parse_ptime = parsetime->parse_ptime;
+ }
+ break; /* well, still waiting - timeout is handled at higher levels */
+
+ case CVT_FAIL:
+ if (parsetime->parse_status & CVT_BADFMT)
+ {
+ parse_event(parse, CEVNT_BADREPLY);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADDATE)
+ {
+ parse_event(parse, CEVNT_BADDATE);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADTIME)
+ {
+ parse_event(parse, CEVNT_BADTIME);
+ }
+ else
+ {
+ parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
+ }
+ }
+ return; /* skip the rest - useless */
+ }
+
+ /*
+ * check for format changes
+ * (in case somebody has swapped clocks 8-)
+ */
+ if (parse->lastformat != parsetime->parse_format)
+ {
+ parsectl_t tmpctl;
+
+ tmpctl.parseformat.parse_format = parsetime->parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: packet format \"%s\"",
+ CLK_UNIT(parse->peer), tmpctl.parseformat.parse_buffer);
+ }
+ parse->lastformat = parsetime->parse_format;
+ }
+
+ /*
+ * now, any changes ?
+ */
+ if ((parse->timedata.parse_state ^ parsetime->parse_state) &
+ ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS))
+ {
+ char tmp1[200];
+ char tmp2[200];
+ /*
+ * something happend - except for PPS events
+ */
+
+ (void) parsestate(parsetime->parse_state, tmp1, sizeof(tmp1));
+ (void) parsestate(parse->timedata.parse_state, tmp2, sizeof(tmp2));
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
+ CLK_UNIT(parse->peer), tmp2, tmp1);
+ }
+
+ /*
+ * carry on PPS information if still usable
+ */
+ if (PARSE_PPS(parse->timedata.parse_state) && !PARSE_PPS(parsetime->parse_state))
+ {
+ parsetime->parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ parsetime->parse_ptime = parse->timedata.parse_ptime;
+ }
+
+ /*
+ * remember for future
+ */
+ parse->timedata = *parsetime;
+
+ /*
+ * check to see, whether the clock did a complete powerup or lost PZF signal
+ * and post correct events for current condition
+ */
+ if (PARSE_POWERUP(parsetime->parse_state))
+ {
+ /*
+ * this is bad, as we have completely lost synchronisation
+ * well this is a problem with the receiver here
+ * for PARSE Meinberg DCF77 receivers the lost synchronisation
+ * is true as it is the powerup state and the time is taken
+ * from a crude real time clock chip
+ * for the PZF series this is only partly true, as
+ * PARSE_POWERUP only means that the pseudo random
+ * phase shift sequence cannot be found. this is only
+ * bad, if we have never seen the clock in the SYNC
+ * state, where the PHASE and EPOCH are correct.
+ * for reporting events the above business does not
+ * really matter, but we can use the time code
+ * even in the POWERUP state after having seen
+ * the clock in the synchronized state (PZF class
+ * receivers) unless we have had a telegram disruption
+ * after having seen the clock in the SYNC state. we
+ * thus require having seen the clock in SYNC state
+ * *after* having missed telegrams (noresponse) from
+ * the clock. one problem remains: we might use erroneously
+ * POWERUP data if the disruption is shorter than 1 polling
+ * interval. fortunately powerdowns last usually longer than 64
+ * seconds and the receiver is at least 2 minutes in the
+ * POWERUP or NOSYNC state before switching to SYNC
+ */
+ parse_event(parse, CEVNT_FAULT);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED",
+ CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ /*
+ * we have two states left
+ *
+ * SYNC:
+ * this state means that the EPOCH (timecode) and PHASE
+ * information has be read correctly (at least two
+ * successive PARSE timecodes were received correctly)
+ * this is the best possible state - full trust
+ *
+ * NOSYNC:
+ * The clock should be on phase with respect to the second
+ * signal, but the timecode has not been received correctly within
+ * at least the last two minutes. this is a sort of half baked state
+ * for PARSE Meinberg DCF77 clocks this is bad news (clock running
+ * without timecode confirmation)
+ * PZF 535 has also no time confirmation, but the phase should be
+ * very precise as the PZF signal can be decoded
+ */
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * currently completely synchronized - best possible state
+ */
+ parse->lastsync = current_time;
+ clear_err(parse, ERR_BADSTATUS);
+ }
+ else
+ {
+ /*
+ * we have had some problems receiving the time code
+ */
+ parse_event(parse, CEVNT_PROP);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: TIMECODE NOT CONFIRMED",
+ CLK_UNIT(parse->peer));
+ }
+ }
+
+ fudge = parse->generic->fudgetime1; /* standard RS232 Fudgefactor */
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ rectime = parsetime->parse_stime.fp;
+ off = reftime = parsetime->parse_time.fp;
+
+ L_SUB(&off, &rectime); /* prepare for PPS adjustments logic */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - initial offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+ }
+
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ l_fp offset;
+ double ppsphaseadjust = parse->ppsphaseadjust;
+
+#ifdef HAVE_PPSAPI
+ /*
+ * set fudge = 0.0 if already included in PPS time stamps
+ */
+ if (parse->ppsparams.mode & (PPS_OFFSETCLEAR|PPS_OFFSETASSERT))
+ {
+ ppsphaseadjust = 0.0;
+ }
+#endif
+
+ /*
+ * we have a PPS signal - much better than the RS232 stuff (we hope)
+ */
+ offset = parsetime->parse_ptime.fp;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: PPStime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&offset));
+#endif
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ if (M_ISGEQ(off.l_i, off.l_f, -1, 0x80000000) &&
+ M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_f))
+ {
+ fudge = ppsphaseadjust; /* pick PPS fudge factor */
+
+ /*
+ * RS232 offsets within [-0.5..0.5[ - take PPS offsets
+ */
+
+ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
+ {
+ reftime = off = offset;
+ if (reftime.l_uf & (unsigned)0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+
+
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ else
+ {
+ /*
+ * time code describes pulse
+ */
+ reftime = off = parsetime->parse_time.fp;
+
+ L_SUB(&off, &offset); /* true offset */
+ }
+ }
+ /*
+ * take RS232 offset when PPS when out of bounds
+ */
+ }
+ else
+ {
+ fudge = ppsphaseadjust; /* pick PPS fudge factor */
+ /*
+ * Well, no time code to guide us - assume on second pulse
+ * and pray, that we are within [-0.5..0.5[
+ */
+ off = offset;
+ reftime = offset;
+ if (reftime.l_uf & (unsigned)0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ }
+ else
+ {
+ if (!PARSE_TIMECODE(parsetime->parse_state))
+ {
+ /*
+ * Well, no PPS, no TIMECODE, no more work ...
+ */
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - final offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+
+
+ rectime = reftime;
+ L_SUB(&rectime, &off); /* just to keep the ntp interface happy */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: calculated Reftime %s, Recvtime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime));
+#endif
+
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * log OK status
+ */
+ parse_event(parse, CEVNT_NOMINAL);
+ }
+
+ clear_err(parse, ERR_BADIO);
+ clear_err(parse, ERR_BADDATA);
+ clear_err(parse, ERR_NODATA);
+ clear_err(parse, ERR_INTERNAL);
+
+ /*
+ * and now stick it into the clock machine
+ * samples are only valid iff lastsync is not too old and
+ * we have seen the clock in sync at least once
+ * after the last time we didn't see an expected data telegram
+ * at startup being not in sync is also bad just like
+ * POWERUP state
+ * see the clock states section above for more reasoning
+ */
+ if (((current_time - parse->lastsync) > parse->maxunsync) ||
+ (parse->lastsync < parse->lastmissed) ||
+ ((parse->lastsync == 0) && !PARSE_SYNC(parsetime->parse_state)) ||
+ PARSE_POWERUP(parsetime->parse_state))
+ {
+ parse->generic->leap = LEAP_NOTINSYNC;
+ parse->lastsync = 0; /* wait for full sync again */
+ }
+ else
+ {
+ if (PARSE_LEAPADD(parsetime->parse_state))
+ {
+ /*
+ * we pick this state also for time code that pass leap warnings
+ * without direction information (as earth is currently slowing
+ * down).
+ */
+ parse->generic->leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
+ }
+ else
+ if (PARSE_LEAPDEL(parsetime->parse_state))
+ {
+ parse->generic->leap = LEAP_DELSECOND;
+ }
+ else
+ {
+ parse->generic->leap = LEAP_NOWARNING;
+ }
+ }
+
+ if (parse->generic->leap != LEAP_NOTINSYNC)
+ {
+ /*
+ * only good/trusted samples are interesting
+ */
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ printf("PARSE receiver #%d: refclock_process_offset(reftime=%s, rectime=%s, Fudge=%f)\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ fudge);
+ }
+#endif
+ parse->generic->lastref = reftime;
+
+ refclock_process_offset(parse->generic, reftime, rectime, fudge);
+
+ /*
+ * pass PPS information on to PPS clock
+ */
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ (void) pps_sample(&parse->timedata.parse_ptime.fp);
+ parse_hardpps(parse, PARSE_HARDPPS_ENABLE);
+ }
+ } else {
+ parse_hardpps(parse, PARSE_HARDPPS_DISABLE);
+ }
+
+ /*
+ * ready, unless the machine wants a sample or
+ * we are in fast startup mode (peer->dist > MAXDISTANCE)
+ */
+ if (!parse->pollneeddata && parse->peer->disp <= MAXDISTANCE)
+ return;
+
+ parse->pollneeddata = 0;
+
+ parse->timedata.parse_state &= ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS);
+
+ refclock_receive(parse->peer);
+}
+
+/**===========================================================================
+ ** special code for special clocks
+ **/
+
+static void
+mk_utcinfo(
+ char *t,
+ int wnt,
+ int wnlsf,
+ int dn,
+ int dtls,
+ int dtlsf,
+ int size
+ )
+{
+ l_fp leapdate;
+ char *start = t;
+
+ snprintf(t, size, "current correction %d sec", dtls);
+ t += strlen(t);
+
+ if (wnlsf < 990)
+ wnlsf += 1024;
+
+ if (wnt < 990)
+ wnt += 1024;
+
+ gpstolfp((unsigned short)wnlsf, (unsigned short)dn, 0, &leapdate);
+
+ if ((dtlsf != dtls) &&
+ ((wnlsf - wnt) < 52))
+ {
+ snprintf(t, BUFFER_SIZES(start, t, size), ", next correction %d sec on %s, new GPS-UTC offset %d",
+ dtlsf - dtls, gmprettydate(&leapdate), dtlsf);
+ }
+ else
+ {
+ snprintf(t, BUFFER_SIZES(start, t, size), ", last correction on %s",
+ gmprettydate(&leapdate));
+ }
+}
+
+#ifdef CLOCK_MEINBERG
+/**===========================================================================
+ ** Meinberg GPS166/GPS167 support
+ **/
+
+/*------------------------------------------------------------
+ * gps16x_message - process GPS16x messages
+ */
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ if (parse->timedata.parse_msglen && parsetime->parse_msg[0] == SOH)
+ {
+ GPS_MSG_HDR header;
+ unsigned char *bufp = (unsigned char *)parsetime->parse_msg + 1;
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char msgbuffer[600];
+
+ mkreadable(msgbuffer, sizeof(msgbuffer), (char *)parsetime->parse_msg, parsetime->parse_msglen, 1);
+ printf("PARSE receiver #%d: received message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parsetime->parse_msglen,
+ msgbuffer);
+ }
+#endif
+ get_mbg_header(&bufp, &header);
+ if (header.gps_hdr_csum == mbg_csum(parsetime->parse_msg + 1, 6) &&
+ (header.gps_len == 0 ||
+ (header.gps_len < sizeof(parsetime->parse_msg) &&
+ header.gps_data_csum == mbg_csum(bufp, header.gps_len))))
+ {
+ /*
+ * clean message
+ */
+ switch (header.gps_cmd)
+ {
+ case GPS_SW_REV:
+ {
+ char buffer[64];
+ SW_REV gps_sw_rev;
+
+ get_mbg_sw_rev(&bufp, &gps_sw_rev);
+ snprintf(buffer, sizeof(buffer), "meinberg_gps_version=\"%x.%02x%s%s\"",
+ (gps_sw_rev.code >> 8) & 0xFF,
+ gps_sw_rev.code & 0xFF,
+ gps_sw_rev.name[0] ? " " : "",
+ gps_sw_rev.name);
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_STAT:
+ {
+ static struct state
+ {
+ unsigned short flag; /* status flag */
+ unsigned const char *string; /* bit name */
+ } states[] =
+ {
+ { TM_ANT_DISCONN, (const unsigned char *)"ANTENNA FAULTY" },
+ { TM_SYN_FLAG, (const unsigned char *)"NO SYNC SIGNAL" },
+ { TM_NO_SYNC, (const unsigned char *)"NO SYNC POWERUP" },
+ { TM_NO_POS, (const unsigned char *)"NO POSITION" },
+ { 0, (const unsigned char *)"" }
+ };
+ unsigned short status;
+ struct state *s = states;
+ char buffer[512];
+ char *p, *b;
+
+ status = get_lsb_short(&bufp);
+ snprintf(buffer, sizeof(buffer), "meinberg_gps_status=\"[0x%04x] ", status);
+
+ if (status)
+ {
+ p = b = buffer + strlen(buffer);
+ while (s->flag)
+ {
+ if (status & s->flag)
+ {
+ if (p != b)
+ {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+
+ strncat(p, (const char *)s->string, sizeof(buffer));
+ }
+ s++;
+ }
+
+ *p++ = '"';
+ *p = '\0';
+ }
+ else
+ {
+ strncat(buffer, "<OK>\"", sizeof(buffer));
+ }
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_POS_XYZ:
+ {
+ XYZ xyz;
+ char buffer[256];
+
+ get_mbg_xyz(&bufp, xyz);
+ snprintf(buffer, sizeof(buffer), "gps_position(XYZ)=\"%s m, %s m, %s m\"",
+ mfptoa(xyz[XP].l_ui, xyz[XP].l_uf, 1),
+ mfptoa(xyz[YP].l_ui, xyz[YP].l_uf, 1),
+ mfptoa(xyz[ZP].l_ui, xyz[ZP].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_POS_LLA:
+ {
+ LLA lla;
+ char buffer[256];
+
+ get_mbg_lla(&bufp, lla);
+
+ snprintf(buffer, sizeof(buffer), "gps_position(LLA)=\"%s deg, %s deg, %s m\"",
+ mfptoa(lla[LAT].l_ui, lla[LAT].l_uf, 4),
+ mfptoa(lla[LON].l_ui, lla[LON].l_uf, 4),
+ mfptoa(lla[ALT].l_ui, lla[ALT].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_TZDL:
+ break;
+
+ case GPS_PORT_PARM:
+ break;
+
+ case GPS_SYNTH:
+ break;
+
+ case GPS_ANT_INFO:
+ {
+ ANT_INFO antinfo;
+ char buffer[512];
+ char *p;
+
+ get_mbg_antinfo(&bufp, &antinfo);
+ snprintf(buffer, sizeof(buffer), "meinberg_antenna_status=\"");
+ p = buffer + strlen(buffer);
+
+ switch (antinfo.status)
+ {
+ case ANT_INVALID:
+ strncat(p, "<OK>", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ break;
+
+ case ANT_DISCONN:
+ strncat(p, "DISCONNECTED since ", BUFFER_SIZE(buffer, p));
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: ANTENNA FAILURE: %s",
+ CLK_UNIT(parse->peer), p);
+
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p));
+ *p = '\0';
+ break;
+
+ case ANT_RECONN:
+ strncat(p, "RECONNECTED on ", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_reconn, BUFFER_SIZE(buffer, p));
+ snprintf(p, BUFFER_SIZE(buffer, p), ", reconnect clockoffset %c%ld.%07ld s, disconnect time ",
+ (antinfo.delta_t < 0) ? '-' : '+',
+ ABS(antinfo.delta_t) / 10000,
+ ABS(antinfo.delta_t) % 10000);
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p));
+ *p = '\0';
+ break;
+
+ default:
+ snprintf(p, BUFFER_SIZE(buffer, p), "bad status 0x%04x", antinfo.status);
+ p += strlen(p);
+ break;
+ }
+
+ strncat(p, "\"", BUFFER_SIZE(buffer, p));
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_UCAP:
+ break;
+
+ case GPS_CFGH:
+ {
+ CFGH cfgh;
+ char buffer[512];
+ char *p;
+
+ get_mbg_cfgh(&bufp, &cfgh);
+ if (cfgh.valid)
+ {
+ int i;
+
+ p = buffer;
+ strncpy(buffer, "gps_tot_51=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_51, BUFFER_SIZE(buffer, p));
+ strncpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ strncpy(buffer, "gps_tot_63=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_63, BUFFER_SIZE(buffer, p));
+ strncpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ strncpy(buffer, "gps_t0a=\"", BUFFER_SIZE(buffer, p));
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.t0a, BUFFER_SIZE(buffer, p));
+ strncpy(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ for (i = MIN_SVNO; i < MAX_SVNO; i++)
+ {
+ p = buffer;
+ snprintf(p, BUFFER_SIZE(buffer, p), "gps_cfg[%d]=\"[0x%x] ", i, cfgh.cfg[i]);
+ p += strlen(p);
+ switch (cfgh.cfg[i] & 0x7)
+ {
+ case 0:
+ strncpy(p, "BLOCK I", BUFFER_SIZE(buffer, p));
+ break;
+ case 1:
+ strncpy(p, "BLOCK II", BUFFER_SIZE(buffer, p));
+ break;
+ default:
+ strncpy(p, "bad CFG", BUFFER_SIZE(buffer, p));
+ break;
+ }
+ strncat(p, "\"", BUFFER_SIZE(buffer, p));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+
+ p = buffer;
+ snprintf(p, BUFFER_SIZE(buffer, p), "gps_health[%d]=\"[0x%x] ", i, cfgh.health[i]);
+ p += strlen(p);
+ switch ((cfgh.health[i] >> 5) & 0x7 )
+ {
+ case 0:
+ strncpy(p, "OK;", BUFFER_SIZE(buffer, p));
+ break;
+ case 1:
+ strncpy(p, "PARITY;", BUFFER_SIZE(buffer, p));
+ break;
+ case 2:
+ strncpy(p, "TLM/HOW;", BUFFER_SIZE(buffer, p));
+ break;
+ case 3:
+ strncpy(p, "Z-COUNT;", BUFFER_SIZE(buffer, p));
+ break;
+ case 4:
+ strncpy(p, "SUBFRAME 1,2,3;", BUFFER_SIZE(buffer, p));
+ break;
+ case 5:
+ strncpy(p, "SUBFRAME 4,5;", BUFFER_SIZE(buffer, p));
+ break;
+ case 6:
+ strncpy(p, "UPLOAD BAD;", BUFFER_SIZE(buffer, p));
+ break;
+ case 7:
+ strncpy(p, "DATA BAD;", BUFFER_SIZE(buffer, p));
+ break;
+ }
+
+ p += strlen(p);
+
+ switch (cfgh.health[i] & 0x1F)
+ {
+ case 0:
+ strncpy(p, "SIGNAL OK", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1C:
+ strncpy(p, "SV TEMP OUT", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1D:
+ strncpy(p, "SV WILL BE TEMP OUT", BUFFER_SIZE(buffer, p));
+ break;
+ case 0x1E:
+ break;
+ case 0x1F:
+ strncpy(p, "MULTIPLE ERRS", BUFFER_SIZE(buffer, p));
+ break;
+ default:
+ strncpy(p, "TRANSMISSION PROBLEMS", BUFFER_SIZE(buffer, p));
+ break;
+ }
+
+ strncat(p, "\"", sizeof(buffer));
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO);
+ }
+ }
+ }
+ break;
+
+ case GPS_ALM:
+ break;
+
+ case GPS_EPH:
+ break;
+
+ case GPS_UTC:
+ {
+ UTC utc;
+ char buffer[512];
+ char *p;
+
+ p = buffer;
+
+ get_mbg_utc(&bufp, &utc);
+
+ if (utc.valid)
+ {
+ strncpy(p, "gps_utc_correction=\"", sizeof(buffer));
+ p += strlen(p);
+ mk_utcinfo(p, utc.t0t.wn, utc.WNlsf, utc.DNt, utc.delta_tls, utc.delta_tlsf, BUFFER_SIZE(buffer, p));
+ strncat(p, "\"", BUFFER_SIZE(buffer, p));
+ }
+ else
+ {
+ strncpy(p, "gps_utc_correction=\"<NO UTC DATA>\"", BUFFER_SIZE(buffer, p));
+ }
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+ break;
+
+ case GPS_IONO:
+ break;
+
+ case GPS_ASCII_MSG:
+ {
+ ASCII_MSG gps_ascii_msg;
+ char buffer[128];
+
+ get_mbg_ascii_msg(&bufp, &gps_ascii_msg);
+
+ if (gps_ascii_msg.valid)
+ {
+ char buffer1[128];
+ mkreadable(buffer1, sizeof(buffer1), gps_ascii_msg.s, strlen(gps_ascii_msg.s), (int)0);
+
+ snprintf(buffer, sizeof(buffer), "gps_message=\"%s\"", buffer1);
+ }
+ else
+ strncpy(buffer, "gps_message=<NONE>", sizeof(buffer));
+
+ set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_DEBUG, "PARSE receiver #%d: gps16x_message: message checksum error: hdr_csum = 0x%x (expected 0x%lx), data_len = %d, data_csum = 0x%x (expected 0x%lx)",
+ CLK_UNIT(parse->peer),
+ header.gps_hdr_csum, mbg_csum(parsetime->parse_msg + 1, 6),
+ header.gps_len,
+ header.gps_data_csum, mbg_csum(bufp, (unsigned)((header.gps_len < sizeof(parsetime->parse_msg)) ? header.gps_len : 0)));
+ }
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------
+ * gps16x_poll - query the reciver peridically
+ */
+static void
+gps16x_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ static GPS_MSG_HDR sequence[] =
+ {
+ { GPS_SW_REV, 0, 0, 0 },
+ { GPS_STAT, 0, 0, 0 },
+ { GPS_UTC, 0, 0, 0 },
+ { GPS_ASCII_MSG, 0, 0, 0 },
+ { GPS_ANT_INFO, 0, 0, 0 },
+ { GPS_CFGH, 0, 0, 0 },
+ { GPS_POS_XYZ, 0, 0, 0 },
+ { GPS_POS_LLA, 0, 0, 0 },
+ { (unsigned short)~0, 0, 0, 0 }
+ };
+
+ int rtc;
+ unsigned char cmd_buffer[64];
+ unsigned char *outp = cmd_buffer;
+ GPS_MSG_HDR *header;
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+
+ if (sequence[parse->localstate].gps_cmd == (unsigned short)~0)
+ parse->localstate = 0;
+
+ header = sequence + parse->localstate++;
+
+ *outp++ = SOH; /* start command */
+
+ put_mbg_header(&outp, header);
+ outp = cmd_buffer + 1;
+
+ header->gps_hdr_csum = (short)mbg_csum(outp, 6);
+ put_mbg_header(&outp, header);
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[128];
+
+ mkreadable(buffer, sizeof(buffer), (char *)cmd_buffer, (unsigned)(outp - cmd_buffer), 1);
+ printf("PARSE receiver #%d: transmitted message #%ld (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parse->localstate - 1,
+ (int)(outp - cmd_buffer),
+ buffer);
+ }
+#endif
+
+ rtc = write(parse->generic->io.fd, cmd_buffer, (unsigned long)(outp - cmd_buffer));
+
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != outp - cmd_buffer)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, (int)(outp - cmd_buffer));
+ }
+
+ clear_err(parse, ERR_BADIO);
+ return;
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->action = gps16x_poll;
+ gps16x_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+#else
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{}
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+#endif /* CLOCK_MEINBERG */
+
+/**===========================================================================
+ ** clock polling support
+ **/
+
+/*--------------------------------------------------
+ * direct poll routine
+ */
+static void
+poll_dpoll(
+ struct parseunit *parse
+ )
+{
+ int rtc;
+ const char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
+ int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
+
+ rtc = write(parse->generic->io.fd, ps, (unsigned long)ct);
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != ct)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, ct);
+ }
+ clear_err(parse, ERR_BADIO);
+}
+
+/*--------------------------------------------------
+ * periodic poll routine
+ */
+static void
+poll_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (parse->parse_type->cl_poll)
+ parse->parse_type->cl_poll(parse);
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->action = poll_poll;
+ poll_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+/**===========================================================================
+ ** Trimble support
+ **/
+
+/*-------------------------------------------------------------
+ * trimble TAIP init routine - setup EOL and then do poll_init.
+ */
+static int
+trimbletaip_init(
+ struct parseunit *parse
+ )
+{
+#ifdef HAVE_TERMIOS
+ struct termios tio;
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio;
+#endif
+ /*
+ * configure terminal line for trimble receiver
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ tio.c_cc[VEOL] = TRIMBLETAIP_EOL;
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ }
+ return poll_init(parse);
+}
+
+/*--------------------------------------------------
+ * trimble TAIP event routine - reset receiver upon data format trouble
+ */
+static const char *taipinit[] = {
+ ">FPV00000000<",
+ ">SRM;ID_FLAG=F;CS_FLAG=T;EC_FLAG=F;FR_FLAG=T;CR_FLAG=F<",
+ ">FTM00020001<",
+ (char *)0
+};
+
+static void
+trimbletaip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ {
+ const char **iv;
+
+ iv = taipinit;
+ while (*iv)
+ {
+ int rtc = write(parse->generic->io.fd, *iv, strlen(*iv));
+ if (rtc < 0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ return;
+ }
+ else
+ {
+ if (rtc != strlen(*iv))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd incomplete (%d of %d bytes sent)",
+ CLK_UNIT(parse->peer), rtc, (int)strlen(*iv));
+ return;
+ }
+ }
+ iv++;
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: RECEIVER INITIALIZED",
+ CLK_UNIT(parse->peer));
+ }
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * This driver supports the Trimble SVee Six Plus GPS receiver module.
+ * It should support other Trimble receivers which use the Trimble Standard
+ * Interface Protocol (see below).
+ *
+ * The module has a serial I/O port for command/data and a 1 pulse-per-second
+ * output, about 1 microsecond wide. The leading edge of the pulse is
+ * coincident with the change of the GPS second. This is the same as
+ * the change of the UTC second +/- ~1 microsecond. Some other clocks
+ * specifically use a feature in the data message as a timing reference, but
+ * the SVee Six Plus does not do this. In fact there is considerable jitter
+ * on the timing of the messages, so this driver only supports the use
+ * of the PPS pulse for accurate timing. Where it is determined that
+ * the offset is way off, when first starting up ntpd for example,
+ * the timing of the data stream is used until the offset becomes low enough
+ * (|offset| < CLOCK_MAX), at which point the pps offset is used.
+ *
+ * It can use either option for receiving PPS information - the 'ppsclock'
+ * stream pushed onto the serial data interface to timestamp the Carrier
+ * Detect interrupts, where the 1PPS connects to the CD line. This only
+ * works on SunOS 4.1.x currently. To select this, define PPSPPS in
+ * Config.local. The other option is to use a pulse-stretcher/level-converter
+ * to convert the PPS pulse into a RS232 start pulse & feed this into another
+ * tty port. To use this option, define PPSCLK in Config.local. The pps input,
+ * by whichever method, is handled in ntp_loopfilter.c
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+#define PI 3.1415926535898 /* lots of sig figs */
+#define D2R PI/180.0
+
+/*-------------------------------------------------------------------
+ * sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
+ * interface to the receiver.
+ *
+ * CAVEAT: the sendflt, sendint routines are byte order dependend and
+ * float implementation dependend - these must be converted to portable
+ * versions !
+ *
+ * CURRENT LIMITATION: float implementation. This runs only on systems
+ * with IEEE754 floats as native floats
+ */
+
+typedef struct trimble
+{
+ u_long last_msg; /* last message received */
+ u_long last_reset; /* last time a reset was issued */
+ u_char qtracking; /* query tracking status */
+ u_long ctrack; /* current tracking set */
+ u_long ltrack; /* last tracking set */
+} trimble_t;
+
+union uval {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+};
+
+struct txbuf
+{
+ short idx; /* index to first unused byte */
+ u_char *txt; /* pointer to actual data buffer */
+};
+
+void sendcmd P((struct txbuf *buf, int c));
+void sendbyte P((struct txbuf *buf, int b));
+void sendetx P((struct txbuf *buf, struct parseunit *parse));
+void sendint P((struct txbuf *buf, int a));
+void sendflt P((struct txbuf *buf, double a));
+
+void
+sendcmd(
+ struct txbuf *buf,
+ int c
+ )
+{
+ buf->txt[0] = DLE;
+ buf->txt[1] = (u_char)c;
+ buf->idx = 2;
+}
+
+void sendcmd P((struct txbuf *buf, int c));
+void sendbyte P((struct txbuf *buf, int b));
+void sendetx P((struct txbuf *buf, struct parseunit *parse));
+void sendint P((struct txbuf *buf, int a));
+void sendflt P((struct txbuf *buf, double a));
+
+void
+sendbyte(
+ struct txbuf *buf,
+ int b
+ )
+{
+ if (b == DLE)
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = (u_char)b;
+}
+
+void
+sendetx(
+ struct txbuf *buf,
+ struct parseunit *parse
+ )
+{
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = ETX;
+
+ if (write(parse->generic->io.fd, buf->txt, (unsigned long)buf->idx) != buf->idx)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[256];
+
+ mkreadable(buffer, sizeof(buffer), (char *)buf->txt, (unsigned)buf->idx, 1);
+ printf("PARSE receiver #%d: transmitted message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ buf->idx, buffer);
+ }
+#endif
+ clear_err(parse, ERR_BADIO);
+ }
+}
+
+void
+sendint(
+ struct txbuf *buf,
+ int a
+ )
+{
+ /* send 16bit int, msbyte first */
+ sendbyte(buf, (u_char)((a>>8) & 0xff));
+ sendbyte(buf, (u_char)(a & 0xff));
+}
+
+void
+sendflt(
+ struct txbuf *buf,
+ double a
+ )
+{
+ int i;
+ union uval uval;
+
+ uval.fv = a;
+#ifdef WORDS_BIGENDIAN
+ for (i=0; i<=3; i++)
+#else
+ for (i=3; i>=0; i--)
+#endif
+ sendbyte(buf, uval.bd[i]);
+}
+
+#define TRIM_POS_OPT 0x13 /* output position with high precision */
+#define TRIM_TIME_OPT 0x03 /* use UTC time stamps, on second */
+
+/*--------------------------------------------------
+ * trimble TSIP setup routine
+ */
+static int
+trimbletsip_setup(
+ struct parseunit *parse,
+ const char *reason
+ )
+{
+ u_char buffer[256];
+ struct txbuf buf;
+ trimble_t *t = parse->localdata;
+
+ if (t && t->last_reset &&
+ ((t->last_reset + TRIMBLE_RESET_HOLDOFF) > current_time)) {
+ return 1; /* not yet */
+ }
+
+ if (t)
+ t->last_reset = current_time;
+
+ buf.txt = buffer;
+
+ sendcmd(&buf, CMD_CVERSION); /* request software versions */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_COPERPARAM); /* set operating parameters */
+ sendbyte(&buf, 4); /* static */
+ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
+ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
+ sendflt(&buf, 12.0); /* PDOP mask = 12 */
+ sendflt(&buf, 8.0); /* PDOP switch level = 8 */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMODESEL); /* fix mode select */
+ sendbyte(&buf, 1); /* time transfer mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMESSAGE); /* request system message */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CSUPER); /* superpacket fix */
+ sendbyte(&buf, 0x2); /* binary mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CIOOPTIONS); /* set I/O options */
+ sendbyte(&buf, TRIM_POS_OPT); /* position output */
+ sendbyte(&buf, 0x00); /* no velocity output */
+ sendbyte(&buf, TRIM_TIME_OPT); /* UTC, compute on seconds */
+ sendbyte(&buf, 0x00); /* no raw measurements */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CUTCPARAM); /* request UTC correction data */
+ sendetx(&buf, parse);
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_setup: RECEIVER RE-INITIALIZED (%s)", CLK_UNIT(parse->peer), reason);
+
+ return 0;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP check routine
+ */
+static void
+trimble_check(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+ trimble_t *t = parse->localdata;
+ u_char buffer[256];
+ struct txbuf buf;
+ buf.txt = buffer;
+
+ if (t)
+ {
+ if (current_time > t->last_msg + TRIMBLETSIP_IDLE_TIME)
+ (void)trimbletsip_setup(parse, "message timeout");
+ }
+
+ poll_poll(parse->peer); /* emit query string and re-arm timer */
+
+ if (t && t->qtracking)
+ {
+ u_long oldsats = t->ltrack & ~t->ctrack;
+
+ t->qtracking = 0;
+ t->ltrack = t->ctrack;
+
+ if (oldsats)
+ {
+ int i;
+
+ for (i = 0; oldsats; i++) {
+ if (oldsats & (1 << i))
+ {
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, i+1); /* old sat */
+ sendetx(&buf, parse);
+ }
+ oldsats &= ~(1 << i);
+ }
+ }
+
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, 0x00); /* current tracking set */
+ sendetx(&buf, parse);
+ }
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP end routine
+ */
+static void
+trimbletsip_end(
+ struct parseunit *parse
+ )
+{ trimble_t *t = parse->localdata;
+
+ if (t)
+ {
+ free(t);
+ parse->localdata = (void *)0;
+ }
+ parse->peer->nextaction = 0;
+ parse->peer->action = (void (*) P((struct peer *)))0;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP init routine
+ */
+static int
+trimbletsip_init(
+ struct parseunit *parse
+ )
+{
+#if defined(VEOL) || defined(VEOL2)
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ /*
+ * allocate local data area
+ */
+ if (!parse->localdata)
+ {
+ trimble_t *t;
+
+ t = (trimble_t *)(parse->localdata = emalloc(sizeof(trimble_t)));
+
+ if (t)
+ {
+ memset((char *)t, 0, sizeof(trimble_t));
+ t->last_msg = current_time;
+ }
+ }
+
+ parse->peer->action = trimble_check;
+ parse->peer->nextaction = current_time;
+
+ /*
+ * configure terminal line for ICANON mode with VEOL characters
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcgetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ else
+ {
+ if ((parse_clockinfo[CLK_TYPE(parse->peer)].cl_lflag & ICANON))
+ {
+#ifdef VEOL
+ tio.c_cc[VEOL] = ETX;
+#endif
+#ifdef VEOL2
+ tio.c_cc[VEOL2] = DLE;
+#endif
+ }
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcsetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ }
+#endif
+ return trimbletsip_setup(parse, "initial startup");
+}
+
+/*------------------------------------------------------------
+ * trimbletsip_event - handle Trimble events
+ * simple evente handler - attempt to re-initialize receiver
+ */
+static void
+trimbletsip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ (void)trimbletsip_setup(parse, "event BAD_REPLY/TIMEOUT");
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * getflt, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.fv;
+}
+
+static double
+getdbl(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[7] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.dv;
+}
+
+static int
+getshort(
+ unsigned char *p
+ )
+{
+ return get_msb_short(&p);
+}
+
+/*--------------------------------------------------
+ * trimbletsip_message - process trimble messages
+ */
+#define RTOD (180.0 / 3.1415926535898)
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+
+static void
+trimbletsip_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ unsigned char *buffer = parsetime->parse_msg;
+ unsigned int size = parsetime->parse_msglen;
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("TRIMBLE BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return;
+ }
+ else
+ {
+ int var_flag;
+ trimble_t *tr = parse->localdata;
+ unsigned int cmd = buffer[1];
+ char pbuffer[200];
+ char *t = pbuffer;
+ cmd_info_t *s;
+
+#ifdef DEBUG
+ if (debug > 3) {
+ int i;
+
+ printf("TRIMBLE packet 0x%02x, size %d:\n ", cmd, size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (tr)
+ tr->last_msg = current_time;
+
+ s = trimble_convert(cmd, trimble_rcmds);
+
+ if (s)
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%s=\"", s->varname);
+ }
+ else
+ {
+ DPRINTF(1, ("TRIMBLE UNKNOWN COMMAND 0x%02x\n", cmd));
+ return;
+ }
+
+ var_flag = s->varmode;
+
+ t += strlen(t);
+
+ switch(cmd)
+ {
+ case CMD_RCURTIME:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%f, %d, %f",
+ getflt((unsigned char *)&mb(0)), getshort((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(6)));
+ break;
+
+ case CMD_RBEST4:
+ strncpy(t, "mode: ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+ switch (mb(0) & 0xF)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "0x%x", mb(0) & 0x7);
+ break;
+
+ case 1:
+ strncpy(t, "0D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 3:
+ strncpy(t, "2D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 4:
+ strncpy(t, "3D", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x10)
+ strncpy(t, "-MANUAL, ", BUFFER_SIZE(pbuffer, t));
+ else
+ strncpy(t, "-AUTO, ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "satellites %02d %02d %02d %02d, PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f",
+ mb(1), mb(2), mb(3), mb(4),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ getflt((unsigned char *)&mb(17)));
+
+ break;
+
+ case CMD_RVERSION:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%d.%d (%d/%d/%d)",
+ mb(0)&0xff, mb(1)&0xff, 1900+(mb(4)&0xff), mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case CMD_RRECVHEALTH:
+ {
+ static const char *msgs[] =
+ {
+ "Battery backup failed",
+ "Signal processor error",
+ "Alignment error, channel or chip 1",
+ "Alignment error, channel or chip 2",
+ "Antenna feed line fault",
+ "Excessive ref freq. error",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ switch (mb(0) & 0xFF)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "illegal value 0x%02x", mb(0) & 0xFF);
+ break;
+ case 0x00:
+ strncpy(t, "doing position fixes", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x01:
+ strncpy(t, "no GPS time yet", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x03:
+ strncpy(t, "PDOP too high", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x08:
+ strncpy(t, "no usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x09:
+ strncpy(t, "only ONE usable satellite", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0A:
+ strncpy(t, "only TWO usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0B:
+ strncpy(t, "only THREE usable satellites", BUFFER_SIZE(pbuffer, t));
+ break;
+ case 0x0C:
+ strncpy(t, "the chosen satellite is unusable", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", %s", msgs[i]);
+ t += strlen(t);
+ }
+ }
+ break;
+
+ case CMD_RMESSAGE:
+ mkreadable(t, (int)BUFFER_SIZE(pbuffer, t), (char *)&mb(0), (unsigned)(size - 2 - (&mb(0) - buffer)), 0);
+ break;
+
+ case CMD_RMACHSTAT:
+ {
+ static const char *msgs[] =
+ {
+ "Synthesizer Fault",
+ "Battery Powered Time Clock Fault",
+ "A-to-D Converter Fault",
+ "The almanac stored in the receiver is not complete and current",
+ "<BIT 4>",
+ "<BIT 5",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "machine id 0x%02x", mb(0) & 0xFF);
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", %s", msgs[i]);
+ t += strlen(t);
+ }
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), ", Superpackets %ssupported", (mb(2) & 0xFF) ? "" :"un" );
+ }
+ break;
+
+ case CMD_ROPERPARAM:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%2x %.1f %.1f %.1f %.1f",
+ mb(0), getflt((unsigned char *)&mb(1)), getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)), getflt((unsigned char *)&mb(13)));
+ break;
+
+ case CMD_RUTCPARAM:
+ {
+ float t0t = getflt((unsigned char *)&mb(14));
+ short wnt = getshort((unsigned char *)&mb(18));
+ short dtls = getshort((unsigned char *)&mb(12));
+ short wnlsf = getshort((unsigned char *)&mb(20));
+ short dn = getshort((unsigned char *)&mb(22));
+ short dtlsf = getshort((unsigned char *)&mb(24));
+
+ if ((int)t0t != 0)
+ {
+ mk_utcinfo(t, wnt, wnlsf, dn, dtls, dtlsf, BUFFER_SIZE(pbuffer, t));
+ }
+ else
+ {
+ strncpy(t, "<NO UTC DATA>", BUFFER_SIZE(pbuffer, t));
+ }
+ }
+ break;
+
+ case CMD_RSAT1BIAS:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%.1fm %.2fm/s at %.1fs",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8)));
+ break;
+
+ case CMD_RIOOPTIONS:
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%02x %02x %02x %02x",
+ mb(0), mb(1), mb(2), mb(3));
+ if (mb(0) != TRIM_POS_OPT ||
+ mb(2) != TRIM_TIME_OPT)
+ {
+ (void)trimbletsip_setup(parse, "bad io options");
+ }
+ }
+ break;
+
+ case CMD_RSPOSXYZ:
+ {
+ double x = getflt((unsigned char *)&mb(0));
+ double y = getflt((unsigned char *)&mb(4));
+ double z = getflt((unsigned char *)&mb(8));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "x= %.1fm, y= %.1fm, z= %.1fm, time_of_fix= %f sec",
+ x, y, z,
+ f);
+ else
+ return;
+ }
+ break;
+
+ case CMD_RSLLAPOS:
+ {
+ double lat = getflt((unsigned char *)&mb(0));
+ double lng = getflt((unsigned char *)&mb(4));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "lat %f %c, long %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getflt((unsigned char *)&mb(8)));
+ else
+ return;
+ }
+ break;
+
+ case CMD_RDOUBLEXYZ:
+ {
+ double x = getdbl((unsigned char *)&mb(0));
+ double y = getdbl((unsigned char *)&mb(8));
+ double z = getdbl((unsigned char *)&mb(16));
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "x= %.1fm, y= %.1fm, z= %.1fm",
+ x, y, z);
+ }
+ break;
+
+ case CMD_RDOUBLELLA:
+ {
+ double lat = getdbl((unsigned char *)&mb(0));
+ double lng = getdbl((unsigned char *)&mb(8));
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "lat %f %c, lon %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getdbl((unsigned char *)&mb(16)));
+ }
+ break;
+
+ case CMD_RALLINVIEW:
+ {
+ int i, sats;
+
+ strncpy(t, "mode: ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+ switch (mb(0) & 0x7)
+ {
+ default:
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "0x%x", mb(0) & 0x7);
+ break;
+
+ case 3:
+ strncpy(t, "2D", BUFFER_SIZE(pbuffer, t));
+ break;
+
+ case 4:
+ strncpy(t, "3D", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x8)
+ strncpy(t, "-MANUAL, ", BUFFER_SIZE(pbuffer, t));
+ else
+ strncpy(t, "-AUTO, ", BUFFER_SIZE(pbuffer, t));
+ t += strlen(t);
+
+ sats = (mb(0)>>4) & 0xF;
+
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f, %d satellite%s in view: ",
+ getflt((unsigned char *)&mb(1)),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ sats, (sats == 1) ? "" : "s");
+ t += strlen(t);
+
+ for (i=0; i < sats; i++)
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "%s%02d", i ? ", " : "", mb(17+i));
+ t += strlen(t);
+ if (tr)
+ tr->ctrack |= (1 << (mb(17+i)-1));
+ }
+
+ if (tr)
+ { /* mark for tracking status query */
+ tr->qtracking = 1;
+ }
+ }
+ break;
+
+ case CMD_RSTATTRACK:
+ {
+ snprintf(t-2, BUFFER_SIZE(pbuffer, t-2), "[%02d]=\"", mb(0)); /* add index to var name */
+ t += strlen(t);
+
+ if (getflt((unsigned char *)&mb(4)) < 0.0)
+ {
+ strncpy(t, "<NO MEASUREMENTS>", BUFFER_SIZE(pbuffer, t));
+ var_flag &= ~DEF;
+ }
+ else
+ {
+ snprintf(t, BUFFER_SIZE(pbuffer, t), "ch=%d, acq=%s, eph=%d, signal_level= %5.2f, elevation= %5.2f, azimuth= %6.2f",
+ (mb(1) & 0xFF)>>3,
+ mb(2) ? ((mb(2) == 1) ? "ACQ" : "SRCH") : "NEVER",
+ mb(3),
+ getflt((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(12)) * RTOD,
+ getflt((unsigned char *)&mb(16)) * RTOD);
+ t += strlen(t);
+ if (mb(20))
+ {
+ var_flag &= ~DEF;
+ strncpy(t, ", OLD", BUFFER_SIZE(pbuffer, t));
+ }
+ t += strlen(t);
+ if (mb(22))
+ {
+ if (mb(22) == 1)
+ strncpy(t, ", BAD PARITY", BUFFER_SIZE(pbuffer, t));
+ else
+ if (mb(22) == 2)
+ strncpy(t, ", BAD EPH HEALTH", BUFFER_SIZE(pbuffer, t));
+ }
+ t += strlen(t);
+ if (mb(23))
+ strncpy(t, ", collecting data", BUFFER_SIZE(pbuffer, t));
+ }
+ }
+ break;
+
+ default:
+ strncpy(t, "<UNDECODED>", BUFFER_SIZE(pbuffer, t));
+ break;
+ }
+ t += strlen(t);
+
+ strncpy(t,"\"", BUFFER_SIZE(pbuffer, t));
+ set_var(&parse->kv, pbuffer, sizeof(pbuffer), var_flag);
+ }
+}
+
+
+/**============================================================
+ ** RAWDCF support
+ **/
+
+/*--------------------------------------------------
+ * rawdcf_init_1 - set up modem lines for RAWDCF receivers
+ * SET DTR line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR))
+static int
+rawdcf_init_1(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_DTR
+ sl232 = (sl232 & ~TIOCM_RTS) | TIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_RTS) | CIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcfdtr_init_1(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+/*--------------------------------------------------
+ * rawdcf_init_2 - set up modem lines for RAWDCF receivers
+ * CLR DTR line, SET RTS line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_RTS
+ sl232 = (sl232 & ~TIOCM_DTR) | TIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_DTR) | CIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: OS interface incapable of setting RTS to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+#else /* defined(REFCLOCK) && defined(PARSE) */
+int refclock_parse_bs;
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * Revision 4.80 2007/08/11 12:06:29 kardel
+ * update comments wrt/ to PPS
+ *
+ * Revision 4.79 2007/08/11 11:52:23 kardel
+ * - terminate io bindings before io_closeclock() will close our file descriptor
+ *
+ * Revision 4.78 2006/12/22 20:08:27 kardel
+ * Bug 746 (RFE): add configuration for Expert mouseCLOCK USB v2.0 as mode 19
+ *
+ * Revision 4.77 2006/08/05 07:44:49 kardel
+ * support optionally separate PPS devices via /dev/refclockpps-{0..3}
+ *
+ * Revision 4.76 2006/06/22 18:40:47 kardel
+ * clean up signedness (gcc 4)
+ *
+ * Revision 4.75 2006/06/22 16:58:10 kardel
+ * Bug #632: call parse_ppsapi() in parse_ctl() when updating
+ * the PPS offset. Fix sign of offset passed to kernel.
+ *
+ * Revision 4.74 2006/06/18 21:18:37 kardel
+ * NetBSD Coverity CID 3796: possible NULL deref
+ *
+ * Revision 4.73 2006/05/26 14:23:46 kardel
+ * cleanup of copyright info
+ *
+ * Revision 4.72 2006/05/26 14:19:43 kardel
+ * cleanup of ioctl cruft
+ *
+ * Revision 4.71 2006/05/26 14:15:57 kardel
+ * delay adding refclock to async refclock io after all initializations
+ *
+ * Revision 4.70 2006/05/25 18:20:50 kardel
+ * bug #619
+ * terminate parse io engine after de-registering
+ * from refclock io engine
+ *
+ * Revision 4.69 2006/05/25 17:28:02 kardel
+ * complete refclock io structure initialization *before* inserting it into the
+ * refclock input machine (avoids null pointer deref) (bug #619)
+ *
+ * Revision 4.68 2006/05/01 17:02:51 kardel
+ * copy receiver method also for newlwy created receive buffers
+ *
+ * Revision 4.67 2006/05/01 14:37:29 kardel
+ * If an input buffer parses into more than one message do insert the
+ * parsed message in a new input buffer instead of processing it
+ * directly. This avoids deed complicated processing in signal
+ * handling.
+ *
+ * Revision 4.66 2006/03/18 00:45:30 kardel
+ * coverity fixes found in NetBSD coverity scan
+ *
+ * Revision 4.65 2006/01/26 06:08:33 kardel
+ * output errno on PPS setup failure
+ *
+ * Revision 4.64 2005/11/09 20:44:47 kardel
+ * utilize full PPS timestamp resolution from PPS API
+ *
+ * Revision 4.63 2005/10/07 22:10:25 kardel
+ * bounded buffer implementation
+ *
+ * Revision 4.62.2.2 2005/09/25 10:20:16 kardel
+ * avoid unexpected buffer overflows due to sprintf("%f") on strange floats:
+ * replace almost all str* and *printf functions be their buffer bounded
+ * counterparts
+ *
+ * Revision 4.62.2.1 2005/08/27 16:19:27 kardel
+ * limit re-set rate of trimble clocks
+ *
+ * Revision 4.62 2005/08/06 17:40:00 kardel
+ * cleanup size handling wrt/ to buffer boundaries
+ *
+ * Revision 4.61 2005/07/27 21:16:19 kardel
+ * fix a long (> 11 years) misconfiguration wrt/ Meinberg cflag factory
+ * default setup. CSTOPB was missing for the 7E2 default data format of
+ * the DCF77 clocks.
+ *
+ * Revision 4.60 2005/07/17 21:14:44 kardel
+ * change contents of version string to include the RCS/CVS Id
+ *
+ * Revision 4.59 2005/07/06 06:56:38 kardel
+ * syntax error
+ *
+ * Revision 4.58 2005/07/04 13:10:40 kardel
+ * fix bug 455: tripping over NULL pointer on cleanup
+ * fix shadow storage logic for ppsphaseadjust and trustime wrt/ time2
+ * fix compiler warnings for some platforms wrt/ printf formatstrings and
+ * varying structure element sizes
+ * reorder assignment in binding to avoid tripping over NULL pointers
+ *
+ * Revision 4.57 2005/06/25 09:25:19 kardel
+ * sort out log output sequence
+ *
+ * Revision 4.56 2005/06/14 21:47:27 kardel
+ * collect samples only if samples are ok (sync or trusted flywheel)
+ * propagate pps phase adjustment value to kernel via PPSAPI to help HARDPPS
+ * en- and dis-able HARDPPS in correlation to receiver sync state
+ *
+ * Revision 4.55 2005/06/02 21:28:31 kardel
+ * clarify trust logic
+ *
+ * Revision 4.54 2005/06/02 17:06:49 kardel
+ * change status reporting to use fixed refclock_report()
+ *
+ * Revision 4.53 2005/06/02 16:33:31 kardel
+ * fix acceptance of clocks unsync clocks right at start
+ *
+ * Revision 4.52 2005/05/26 21:55:06 kardel
+ * cleanup status reporting
+ *
+ * Revision 4.51 2005/05/26 19:19:14 kardel
+ * implement fast refclock startup
+ *
+ * Revision 4.50 2005/04/16 20:51:35 kardel
+ * set pps_enable = 1 when binding a kernel PPS source
+ *
+ * Revision 4.49 2005/04/16 17:29:26 kardel
+ * add non polling clock type 18 for just listenning to Meinberg clocks
+ *
+ * Revision 4.48 2005/04/16 16:22:27 kardel
+ * bk sync 20050415 ntp-dev
+ *
+ * Revision 4.47 2004/11/29 10:42:48 kardel
+ * bk sync ntp-dev 20041129
+ *
+ * Revision 4.46 2004/11/29 10:26:29 kardel
+ * keep fudgetime2 in sync with trusttime/ppsphaseadjust depending in flag1
+ *
+ * Revision 4.45 2004/11/14 20:53:20 kardel
+ * clear PPS flags after using them
+ *
+ * Revision 4.44 2004/11/14 15:29:41 kardel
+ * support PPSAPI, upgrade Copyright to Berkeley style
+ *
+ * Revision 4.43 2001/05/26 22:53:16 kardel
+ * 20010526 reconcilation
+ *
+ * Revision 4.42 2000/05/14 15:31:51 kardel
+ * PPSAPI && RAWDCF modemline support
+ *
+ * Revision 4.41 2000/04/09 19:50:45 kardel
+ * fixed rawdcfdtr_init() -> rawdcf_init_1
+ *
+ * Revision 4.40 2000/04/09 15:27:55 kardel
+ * modem line fiddle in rawdcf_init_2
+ *
+ * Revision 4.39 2000/03/18 09:16:55 kardel
+ * PPSAPI integration
+ *
+ * Revision 4.38 2000/03/05 20:25:06 kardel
+ * support PPSAPI
+ *
+ * Revision 4.37 2000/03/05 20:11:14 kardel
+ * 4.0.99g reconcilation
+ *
+ * Revision 4.36 1999/11/28 17:18:20 kardel
+ * disabled burst mode
+ *
+ * Revision 4.35 1999/11/28 09:14:14 kardel
+ * RECON_4_0_98F
+ *
+ * Revision 4.34 1999/05/14 06:08:05 kardel
+ * store current_time in a suitable container (u_long)
+ *
+ * Revision 4.33 1999/05/13 21:48:38 kardel
+ * double the no response timeout interval
+ *
+ * Revision 4.32 1999/05/13 20:09:13 kardel
+ * complain only about missing polls after a full poll interval
+ *
+ * Revision 4.31 1999/05/13 19:59:32 kardel
+ * add clock type 16 for RTS set DTR clr in RAWDCF
+ *
+ * Revision 4.30 1999/02/28 20:36:43 kardel
+ * fixed printf fmt
+ *
+ * Revision 4.29 1999/02/28 19:58:23 kardel
+ * updated copyright information
+ *
+ * Revision 4.28 1999/02/28 19:01:50 kardel
+ * improved debug out on sent Meinberg messages
+ *
+ * Revision 4.27 1999/02/28 18:05:55 kardel
+ * no linux/ppsclock.h stuff
+ *
+ * Revision 4.26 1999/02/28 15:27:27 kardel
+ * wharton clock integration
+ *
+ * Revision 4.25 1999/02/28 14:04:46 kardel
+ * added missing double quotes to UTC information string
+ *
+ * Revision 4.24 1999/02/28 12:06:50 kardel
+ * (parse_control): using gmprettydate instead of prettydate()
+ * (mk_utcinfo): new function for formatting GPS derived UTC information
+ * (gps16x_message): changed to use mk_utcinfo()
+ * (trimbletsip_message): changed to use mk_utcinfo()
+ * ignoring position information in unsynchronized mode
+ * (parse_start): augument linux support for optional ASYNC_LOW_LATENCY
+ *
+ * Revision 4.23 1999/02/23 19:47:53 kardel
+ * fixed #endifs
+ * (stream_receive): fixed formats
+ *
+ * Revision 4.22 1999/02/22 06:21:02 kardel
+ * use new autoconfig symbols
+ *
+ * Revision 4.21 1999/02/21 12:18:13 kardel
+ * 4.91f reconcilation
+ *
+ * Revision 4.20 1999/02/21 10:53:36 kardel
+ * initial Linux PPSkit version
+ *
+ * Revision 4.19 1999/02/07 09:10:45 kardel
+ * clarify STREAMS mitigation rules in comment
+ *
+ * Revision 4.18 1998/12/20 23:45:34 kardel
+ * fix types and warnings
+ *
+ * Revision 4.17 1998/11/15 21:24:51 kardel
+ * cannot access mbg_ routines when CLOCK_MEINBERG
+ * is not defined
+ *
+ * Revision 4.16 1998/11/15 20:28:17 kardel
+ * Release 4.0.73e13 reconcilation
+ *
+ * Revision 4.15 1998/08/22 21:56:08 kardel
+ * fixed IO handling for non-STREAM IO
+ *
+ * Revision 4.14 1998/08/16 19:00:48 kardel
+ * (gps16x_message): reduced UTC parameter information (dropped A0,A1)
+ * made uval a local variable (killed one of the last globals)
+ * (sendetx): added logging of messages when in debug mode
+ * (trimble_check): added periodic checks to facilitate re-initialization
+ * (trimbletsip_init): made use of EOL character if in non-kernel operation
+ * (trimbletsip_message): extended message interpretation
+ * (getdbl): fixed data conversion
+ *
+ * Revision 4.13 1998/08/09 22:29:13 kardel
+ * Trimble TSIP support
+ *
+ * Revision 4.12 1998/07/11 10:05:34 kardel
+ * Release 4.0.73d reconcilation
+ *
+ * Revision 4.11 1998/06/14 21:09:42 kardel
+ * Sun acc cleanup
+ *
+ * Revision 4.10 1998/06/13 12:36:45 kardel
+ * signed/unsigned, name clashes
+ *
+ * Revision 4.9 1998/06/12 15:30:00 kardel
+ * prototype fixes
+ *
+ * Revision 4.8 1998/06/12 11:19:42 kardel
+ * added direct input processing routine for refclocks in
+ * order to avaiod that single character io gobbles up all
+ * receive buffers and drops input data. (Problem started
+ * with fast machines so a character a buffer was possible
+ * one of the few cases where faster machines break existing
+ * allocation algorithms)
+ *
+ * Revision 4.7 1998/06/06 18:35:20 kardel
+ * (parse_start): added BURST mode initialisation
+ *
+ * Revision 4.6 1998/05/27 06:12:46 kardel
+ * RAWDCF_BASEDELAY default added
+ * old comment removed
+ * casts for ioctl()
+ *
+ * Revision 4.5 1998/05/25 22:05:09 kardel
+ * RAWDCF_SETDTR option removed
+ * clock type 14 attempts to set DTR for
+ * power supply of RAWDCF receivers
+ *
+ * Revision 4.4 1998/05/24 16:20:47 kardel
+ * updated comments referencing Meinberg clocks
+ * added RAWDCF clock with DTR set option as type 14
+ *
+ * Revision 4.3 1998/05/24 10:48:33 kardel
+ * calibrated CONRAD RAWDCF default fudge factor
+ *
+ * Revision 4.2 1998/05/24 09:59:35 kardel
+ * corrected version information (ntpq support)
+ *
+ * Revision 4.1 1998/05/24 09:52:31 kardel
+ * use fixed format only (new IO model)
+ * output debug to stdout instead of msyslog()
+ * don't include >"< in ASCII output in order not to confuse
+ * ntpq parsing
+ *
+ * Revision 4.0 1998/04/10 19:52:11 kardel
+ * Start 4.0 release version numbering
+ *
+ * Revision 1.2 1998/04/10 19:28:04 kardel
+ * initial NTP VERSION 4 integration of PARSE with GPS166 binary support
+ * derived from 3.105.1.2 from V3 tree
+ *
+ * Revision information 3.1 - 3.105 from log deleted 1998/04/10 kardel
+ *
+ */
diff --git a/contrib/ntp/ntpd/refclock_pcf.c b/contrib/ntp/ntpd/refclock_pcf.c
new file mode 100644
index 0000000..d4e9fd1
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_pcf.c
@@ -0,0 +1,224 @@
+/*
+ * refclock_pcf - clock driver for the Conrad parallel port radio clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PCF)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the parallel port radio clock sold by Conrad
+ * Electronic under order numbers 967602 and 642002.
+ *
+ * It requires that the local timezone be CET/CEST and that the pcfclock
+ * device driver be installed. A device driver for Linux is available at
+ * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD
+ * driver is available at http://schumann.cx/pcfclock/.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pcfclocks/%d"
+#define OLDDEVICE "/dev/pcfclock%d"
+#define PRECISION (-1) /* precision assumed (about 0.5 s) */
+#define REFID "PCF"
+#define DESCRIPTION "Conrad parallel port radio clock"
+
+#define LENPCF 18 /* timecode length */
+
+/*
+ * Function prototypes
+ */
+static int pcf_start P((int, struct peer *));
+static void pcf_shutdown P((int, struct peer *));
+static void pcf_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pcf = {
+ pcf_start, /* start up driver */
+ pcf_shutdown, /* shut down driver */
+ pcf_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pcf_start - open the device and initialize data for processing
+ */
+static int
+pcf_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[128];
+
+ /*
+ * Open device file for reading.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY);
+ if (fd == -1) {
+ (void)sprintf(device, OLDDEVICE, unit);
+ fd = open(device, O_RDONLY);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf ("starting PCF with device %s\n",device);
+#endif
+ if (fd == -1) {
+ return (0);
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ /* one transmission takes 172.5 milliseconds since the radio clock
+ transmits 69 bits with a period of 2.5 milliseconds per bit */
+ pp->fudgetime1 = 0.1725;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ return (1);
+}
+
+
+/*
+ * pcf_shutdown - shut down the clock
+ */
+static void
+pcf_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ (void)close(pp->io.fd);
+}
+
+
+/*
+ * pcf_poll - called by the transmit procedure
+ */
+static void
+pcf_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char buf[LENPCF];
+ struct tm tm, *tp;
+ time_t t;
+
+ pp = peer->procptr;
+
+ buf[0] = 0;
+ if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ tm.tm_mday = buf[11] * 10 + buf[10];
+ tm.tm_mon = buf[13] * 10 + buf[12] - 1;
+ tm.tm_year = buf[15] * 10 + buf[14];
+ tm.tm_hour = buf[7] * 10 + buf[6];
+ tm.tm_min = buf[5] * 10 + buf[4];
+ tm.tm_sec = buf[3] * 10 + buf[2];
+ tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
+
+ /*
+ * Y2K convert the 2-digit year
+ */
+ if (tm.tm_year < 99)
+ tm.tm_year += 100;
+
+ t = mktime(&tm);
+ if (t == (time_t) -1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+#if defined(__GLIBC__) && defined(_BSD_SOURCE)
+ if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
+ || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
+ || tm.tm_isdst < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf ("local time zone not set to CET/CEST\n");
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+#endif
+
+ pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
+
+#if defined(_REENTRANT) || defined(_THREAD_SAFE)
+ tp = gmtime_r(&t, &tm);
+#else
+ tp = gmtime(&t);
+#endif
+ if (!tp) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ pp->year = tp->tm_year + 1900;
+ pp->day = tp->tm_yday + 1;
+ pp->hour = tp->tm_hour;
+ pp->minute = tp->tm_min;
+ pp->second = tp->tm_sec;
+ pp->nsec = buf[16] * 31250000;
+ if (buf[17] & 1)
+ pp->nsec += 500000000;
+
+#ifdef DEBUG
+ if (debug)
+ printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
+ pp->minute, pp->second);
+#endif
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_pcf_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_pst.c b/contrib/ntp/ntpd/refclock_pst.c
new file mode 100644
index 0000000..776e28e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_pst.c
@@ -0,0 +1,322 @@
+/*
+ * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PST)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ * Receivers. No specific claim of accuracy is made for these receiver,
+ * but actual experience suggests that 10 ms would be a conservative
+ * assumption.
+ *
+ * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ * of-year format and UTC time zone. Automatic correction for DST should
+ * be disabled. It is very important that the year be set correctly in
+ * the DIPswitches; otherwise, the day of year will be incorrect after
+ * 28 April of a normal or leap year. The propagation delay DIPswitches
+ * should be set according to the distance from the transmitter for both
+ * WWV and WWVH, as described in the instructions. While the delay can
+ * be set only to within 11 ms, the fudge time1 parameter can be used
+ * for vernier corrections.
+ *
+ * Using the poll sequence QTQDQM, the response timecode is in three
+ * sections totalling 50 ASCII printing characters, as concatenated by
+ * the driver, in the following format:
+ *
+ * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ * a = AM/PM indicator (' ' for 24-hour mode)
+ * yy = year (from internal switches)
+ * dd/mm/ddd = day of month, month, day of year
+ * s = daylight-saving indicator (' ' for 24-hour mode)
+ * f = frequency enable (O = all frequencies enabled)
+ * r = baud rate (3 = 1200, 6 = 9600)
+ * d = features indicator (@ = month/day display enabled)
+ * z = time zone (0 = UTC)
+ * y = year (5 = 91)
+ * cc = WWV propagation delay (52 = 22 ms)
+ * hh = WWVH propagation delay (81 = 33 ms)
+ * SS = status (80 or 82 = operating correctly)
+ * F = current receive frequency (4 = 15 MHz)
+ * T = transmitter (C = WWV, H = WWVH)
+ * tttt = time since last update (0000 = minutes)
+ * uu = flush character (03 = ^c)
+ * xx = 94 (unknown)
+ *
+ * The alarm condition is indicated by other than '8' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * an extended period; unlock condition is indicated by other than
+ * "0000" in the tttt subfield at Q.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwv%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define WWVREFID "WWV\0" /* WWV reference ID */
+#define WWVHREFID "WWVH" /* WWVH reference ID */
+#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
+#define PST_PHI (10e-6) /* max clock oscillator offset */
+#define LENPST 46 /* min timecode length */
+
+/*
+ * Unit control structure
+ */
+struct pstunit {
+ int tcswitch; /* timecode switch */
+ char *lastptr; /* pointer to timecode data */
+};
+
+/*
+ * Function prototypes
+ */
+static int pst_start P((int, struct peer *));
+static void pst_shutdown P((int, struct peer *));
+static void pst_receive P((struct recvbuf *));
+static void pst_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pst = {
+ pst_start, /* start up driver */
+ pst_shutdown, /* shut down driver */
+ pst_poll, /* transmit poll message */
+ noentry, /* not used (old pst_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old pst_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pst_start - open the devices and initialize data for processing
+ */
+static int
+pst_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct pstunit *)emalloc(sizeof(struct pstunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct pstunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = pst_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ peer->burst = MAXSTAGE;
+ return (1);
+}
+
+
+/*
+ * pst_shutdown - shut down the clock
+ */
+static void
+pst_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * pst_receive - receive data from the serial interface
+ */
+static void
+pst_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ char ampmchar; /* AM/PM indicator */
+ char daychar; /* standard/daylight indicator */
+ char junque[10]; /* "yy/dd/mm/" discard */
+ char info[14]; /* "frdzycchhSSFT" clock info */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
+ + BMAX - 2 - up->lastptr, &trtmp);
+ *up->lastptr++ = ' ';
+ *up->lastptr = '\0';
+
+ /*
+ * Note we get a buffer and timestamp for each <cr>, but only
+ * the first timestamp is retained.
+ */
+ if (up->tcswitch == 0)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->a_lastcode;
+ if (up->tcswitch < 3)
+ return;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENPST) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format:
+ * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &daychar, junque, &pp->day, info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Decode synchronization, quality and last update. If
+ * unsynchronized, set the leap bits accordingly and exit. Once
+ * synchronized, the dispersion depends only on when the clock
+ * was last heard, which depends on the time since last update,
+ * as reported by the clock.
+ */
+ if (info[9] != '8')
+ pp->leap = LEAP_NOTINSYNC;
+ if (info[12] == 'H')
+ memcpy((char *)&pp->refid, WWVHREFID, 4);
+ else
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ if (ltemp == 0)
+ pp->lastref = pp->lastrec;
+ pp->disp = PST_PHI * ltemp * 60;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ else if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+}
+
+
+/*
+ * pst_poll - called by the transmit procedure
+ */
+static void
+pst_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The PSTI/Traconex clock responds to a
+ * "QTQDQMT" by returning a timecode in the format specified
+ * above. Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in. If the clock
+ * becomes unreachable, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->tcswitch = 0;
+ up->lastptr = pp->a_lastcode;
+ if (write(pp->io.fd, "QTQDQMT", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("pst: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ peer->burst = MAXSTAGE;
+ pp->polls++;
+}
+
+#else
+int refclock_pst_int;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_ripencc.c b/contrib/ntp/ntpd/refclock_ripencc.c
new file mode 100644
index 0000000..d9fa204
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_ripencc.c
@@ -0,0 +1,4866 @@
+/*
+ * $Id: refclock_ripencc.c,v 1.13 2002/06/18 14:20:55 marks Exp marks $
+ *
+ * Copyright (c) 2002 RIPE NCC
+ *
+ * All Rights Reserved
+ *
+ * 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 appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of the author not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ *
+ * This driver was developed for use with the RIPE NCC TTM project.
+ *
+ *
+ * The initial driver was developed by Daniel Karrenberg <dfk@ripe.net>
+ * using the code made available by Trimble. This was for xntpd-3.x.x
+ *
+ * Rewrite of the driver for ntpd-4.x.x by Mark Santcroos <marks@ripe.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if defined(REFCLOCK) && defined(CLOCK_RIPENCC)
+
+#include "ntp_stdlib.h"
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_io.h"
+
+#ifdef HAVE_PPSAPI
+# include "ppsapi_timepps.h"
+#endif
+
+/*
+ * Definitions
+ */
+
+/* we are on little endian */
+#define BYTESWAP
+
+/*
+ * DEBUG statements: uncomment if necessary
+ */
+/* #define DEBUG_NCC */ /* general debug statements */
+/* #define DEBUG_PPS */ /* debug pps */
+/* #define DEBUG_RAW */ /* print raw packets */
+
+#define TRIMBLE_OUTPUT_FUNC
+#define TSIP_VERNUM "7.12a"
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif /* FALSE */
+
+#define GPS_PI (3.1415926535898)
+#define GPS_C (299792458.)
+#define D2R (GPS_PI/180.0)
+#define R2D (180.0/GPS_PI)
+#define WEEK (604800.)
+#define MAXCHAN (8)
+
+/* control characters for TSIP packets */
+#define DLE (0x10)
+#define ETX (0x03)
+
+#define MAX_RPTBUF (256)
+
+/* values of TSIPPKT.status */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+#define UTCF_UTC_AVAIL (unsigned char) (1) /* UTC available */
+#define UTCF_LEAP_SCHD (unsigned char) (1<<4) /* Leap scheduled */
+#define UTCF_LEAP_PNDG (unsigned char) (1<<5) /* Leap pending, will occur at end of day */
+
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define REFID_LEN 4
+#define DESCRIPTION "RIPE NCC GPS (Palisade)" /* Description */
+#define SPEED232 B9600 /* 9600 baud */
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/* Structures */
+
+/* TSIP packets have the following structure, whether report or command. */
+typedef struct {
+ short
+ counter, /* counter */
+ len; /* size of buf; < MAX_RPTBUF unsigned chars */
+ unsigned char
+ status, /* TSIP packet format/parse status */
+ code, /* TSIP code */
+ buf[MAX_RPTBUF];/* report or command string */
+} TSIPPKT;
+
+/* TSIP binary data structures */
+typedef struct {
+ unsigned char
+ t_oa_raw, SV_health;
+ float
+ e, t_oa, i_0, OMEGADOT, sqrt_A,
+ OMEGA_0, omega, M_0, a_f0, a_f1,
+ Axis, n, OMEGA_n, ODOT_n, t_zc;
+ short
+ weeknum, wn_oa;
+} ALM_INFO;
+
+typedef struct { /* Almanac health page (25) parameters */
+ unsigned char
+ WN_a, SV_health[32], t_oa;
+} ALH_PARMS;
+
+typedef struct { /* Universal Coordinated Time (UTC) parms */
+ double
+ A_0;
+ float
+ A_1;
+ short
+ delta_t_LS;
+ float
+ t_ot;
+ short
+ WN_t, WN_LSF, DN, delta_t_LSF;
+} UTC_INFO;
+
+typedef struct { /* Ionospheric info (float) */
+ float
+ alpha_0, alpha_1, alpha_2, alpha_3,
+ beta_0, beta_1, beta_2, beta_3;
+} ION_INFO;
+
+typedef struct { /* Subframe 1 info (float) */
+ short
+ weeknum;
+ unsigned char
+ codeL2, L2Pdata, SVacc_raw, SV_health;
+ short
+ IODC;
+ float
+ T_GD, t_oc, a_f2, a_f1, a_f0, SVacc;
+} EPHEM_CLOCK;
+
+typedef struct { /* Ephemeris info (float) */
+ unsigned char
+ IODE, fit_interval;
+ float
+ C_rs, delta_n;
+ double
+ M_0;
+ float
+ C_uc;
+ double
+ e;
+ float
+ C_us;
+ double
+ sqrt_A;
+ float
+ t_oe, C_ic;
+ double
+ OMEGA_0;
+ float
+ C_is;
+ double
+ i_0;
+ float
+ C_rc;
+ double
+ omega;
+ float
+ OMEGADOT, IDOT;
+ double
+ Axis, n, r1me2, OMEGA_n, ODOT_n;
+} EPHEM_ORBIT;
+
+typedef struct { /* Navigation data structure */
+ short
+ sv_number; /* SV number (0 = no entry) */
+ float
+ t_ephem; /* time of ephemeris collection */
+ EPHEM_CLOCK
+ ephclk; /* subframe 1 data */
+ EPHEM_ORBIT
+ ephorb; /* ephemeris data */
+} NAV_INFO;
+
+typedef struct {
+ unsigned char
+ bSubcode,
+ operating_mode,
+ dgps_mode,
+ dyn_code,
+ trackmode;
+ float
+ elev_mask,
+ cno_mask,
+ dop_mask,
+ dop_switch;
+ unsigned char
+ dgps_age_limit;
+} TSIP_RCVR_CFG;
+
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static char
+ *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
+ old_baudnum[] = {0, 1, 4, 5, 6, 8, 9, 11, 28, 12},
+ *st_baud_text_app [] = {"", "", " 300", " 600", " 1200", " 2400",
+ " 4800", " 9600", "19200", "38400"},
+ *old_parity_text[] = {"EVEN", "ODD", "", "", "NONE"},
+ *parity_text [] = {"NONE", "ODD", "EVEN"},
+ *old_input_ch[] = { "TSIP", "RTCM (6 of 8 bits)"},
+ *old_output_ch[] = { "TSIP", "No output", "", "", "", "NMEA 0183"},
+ *protocols_in_text[] = { "", "TSIP", "", ""},
+ *protocols_out_text[] = { "", "TSIP", "NMEA"},
+ *rcvr_port_text [] = { "Port A ", "Port B ", "Current Port"},
+ *dyn_text [] = {"Unchanged", "Land", "Sea", "Air", "Static"},
+ *NavModeText0xBB[] = {"automatic", "time only (0-D)", "", "2-D",
+ "3-D", "", "", "OverDetermined Time"},
+ *PPSTimeBaseText[] = {"GPS", "UTC", "USER"},
+ *PPSPolarityText[] = {"Positive", "Negative"},
+ *MaskText[] = { "Almanac ", "Ephemeris", "UTC ", "Iono ",
+ "GPS Msg ", "Alm Hlth ", "Time Fix ", "SV Select",
+ "Ext Event", "Pos Fix ", "Raw Meas "};
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+/*
+ * Unit control structure
+ */
+struct ripencc_unit {
+ int unit; /* unit number */
+ int pollcnt; /* poll message counter */
+ int polled; /* Hand in a sample? */
+ char leapdelta; /* delta of next leap event */
+ unsigned char utcflags; /* delta of next leap event */
+ l_fp tstamp; /* timestamp of last poll */
+
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+
+};
+
+
+/******************* PROTOYPES *****************/
+
+/* prototypes for report parsing primitives */
+short rpt_0x3D (TSIPPKT *rpt, unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index, unsigned char *char_format_index,
+ unsigned char *stop_bits, unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index);
+short rpt_0x40 (TSIPPKT *rpt, unsigned char *sv_prn, short *week_num,
+ float *t_zc, float *eccentricity, float *t_oa, float *i_0,
+ float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega,
+ float *M_0);
+short rpt_0x41 (TSIPPKT *rpt, float *time_of_week, float *UTC_offset,
+ short *week_num);
+short rpt_0x42 (TSIPPKT *rpt, float ECEF_pos[3], float *time_of_fix);
+short rpt_0x43 (TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x45 (TSIPPKT *rpt, unsigned char *major_nav_version,
+ unsigned char *minor_nav_version, unsigned char *nav_day,
+ unsigned char *nav_month, unsigned char *nav_year,
+ unsigned char *major_dsp_version, unsigned char *minor_dsp_version,
+ unsigned char *dsp_day, unsigned char *dsp_month,
+ unsigned char *dsp_year);
+short rpt_0x46 (TSIPPKT *rpt, unsigned char *status1, unsigned char *status2);
+short rpt_0x47 (TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn,
+ float *snr);
+short rpt_0x48 (TSIPPKT *rpt, unsigned char *message);
+short rpt_0x49 (TSIPPKT *rpt, unsigned char *sv_health);
+short rpt_0x4A (TSIPPKT *rpt, float *lat, float *lon, float *alt,
+ float *clock_bias, float *time_of_fix);
+short rpt_0x4A_2 (TSIPPKT *rpt, float *alt, float *dummy,
+ unsigned char *alt_flag);
+short rpt_0x4B (TSIPPKT *rpt, unsigned char *machine_id,
+ unsigned char *status3, unsigned char *status4);
+short rpt_0x4C (TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask,
+ float *snr_mask, float *dop_mask, float *dop_switch);
+short rpt_0x4D (TSIPPKT *rpt, float *osc_offset);
+short rpt_0x4E (TSIPPKT *rpt, unsigned char *response);
+short rpt_0x4F (TSIPPKT *rpt, double *a0, float *a1, float *time_of_data,
+ short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf);
+short rpt_0x54 (TSIPPKT *rpt, float *clock_bias, float *freq_offset,
+ float *time_of_fix);
+short rpt_0x55 (TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code,
+ unsigned char *time_code, unsigned char *aux_code);
+short rpt_0x56 (TSIPPKT *rpt, float vel_ENU[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x57 (TSIPPKT *rpt, unsigned char *source_code,
+ unsigned char *diag_code, short *week_num, float *time_of_fix);
+short rpt_0x58 (TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type,
+ unsigned char *sv_prn, unsigned char *data_length,
+ unsigned char *data_packet);
+short rpt_0x59 (TSIPPKT *rpt, unsigned char *code_type,
+ unsigned char status_code[32]);
+short rpt_0x5A (TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length,
+ float *signal_level, float *code_phase, float *Doppler,
+ double *time_of_fix);
+short rpt_0x5B (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health,
+ unsigned char *sv_iode, unsigned char *fit_interval_flag,
+ float *time_of_collection, float *time_of_eph, float *sv_accy);
+short rpt_0x5C (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot,
+ unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag,
+ float *signal_level, float *time_of_last_msmt, float *elev,
+ float *azim, unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag, unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag);
+short rpt_0x6D (TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs,
+ unsigned char *ndim, unsigned char sv_prn[], float *pdop,
+ float *hdop, float *vdop, float *tdop);
+short rpt_0x82 (TSIPPKT *rpt, unsigned char *diff_mode);
+short rpt_0x83 (TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias,
+ float *time_of_fix);
+short rpt_0x84 (TSIPPKT *rpt, double *lat, double *lon, double *alt,
+ double *clock_bias, float *time_of_fix);
+short rpt_Paly0xBB(TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB);
+short rpt_0xBC (TSIPPKT *rpt, unsigned char *port_num,
+ unsigned char *in_baud, unsigned char *out_baud,
+ unsigned char *data_bits, unsigned char *parity,
+ unsigned char *stop_bits, unsigned char *flow_control,
+ unsigned char *protocols_in, unsigned char *protocols_out,
+ unsigned char *reserved);
+
+/* prototypes for superpacket parsers */
+
+short rpt_0x8F0B (TSIPPKT *rpt, unsigned short *event, double *tow,
+ unsigned char *date, unsigned char *month, short *year,
+ unsigned char *dim_mode, short *utc_offset, double *bias, double *drift,
+ float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt,
+ char sv_id[8]);
+short rpt_0x8F14 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F15 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F20 (TSIPPKT *rpt, unsigned char *info, double *lat,
+ double *lon, double *alt, double vel_enu[], double *time_of_fix,
+ short *week_num, unsigned char *nsvs, unsigned char sv_prn[],
+ short sv_IODC[], short *datum_index);
+short rpt_0x8F41 (TSIPPKT *rpt, unsigned char *bSearchRange,
+ unsigned char *bBoardOptions, unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear, unsigned char *bBuildMonth,
+ unsigned char *bBuildDay, unsigned char *bBuildHour,
+ float *fOscOffset, unsigned short *iTestCodeId);
+short rpt_0x8F42 (TSIPPKT *rpt, unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions, unsigned short *iMachineID,
+ unsigned short *iKey);
+short rpt_0x8F45 (TSIPPKT *rpt, unsigned char *bSegMask);
+short rpt_0x8F4A_16 (TSIPPKT *rpt, unsigned char *pps_enabled,
+ unsigned char *pps_timebase, unsigned char *pos_polarity,
+ double *pps_offset, float *bias_unc_threshold);
+short rpt_0x8F4B (TSIPPKT *rpt, unsigned long *decorr_max);
+short rpt_0x8F4D (TSIPPKT *rpt, unsigned long *event_mask);
+short rpt_0x8FA5 (TSIPPKT *rpt, unsigned char *spktmask);
+short rpt_0x8FAD (TSIPPKT *rpt, unsigned short *COUNT, double *FracSec,
+ unsigned char *Hour, unsigned char *Minute, unsigned char *Second,
+ unsigned char *Day, unsigned char *Month, unsigned short *Year,
+ unsigned char *Status, unsigned char *Flags);
+
+/**/
+/* prototypes for command-encode primitives with suffix convention: */
+/* c = clear, s = set, q = query, e = enable, d = disable */
+void cmd_0x1F (TSIPPKT *cmd);
+void cmd_0x26 (TSIPPKT *cmd);
+void cmd_0x2F (TSIPPKT *cmd);
+void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
+ unsigned char time_code, unsigned char opts_code);
+void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn);
+void cmd_0x3Ds (TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp,
+ unsigned char char_code, unsigned char stopbitcode,
+ unsigned char output_mode, unsigned char input_mode);
+void cmd_0xBBq (TSIPPKT *cmd, unsigned char subcode) ;
+
+/* prototypes 8E commands */
+void cmd_0x8E0Bq (TSIPPKT *cmd);
+void cmd_0x8E41q (TSIPPKT *cmd);
+void cmd_0x8E42q (TSIPPKT *cmd);
+void cmd_0x8E4Aq (TSIPPKT *cmd);
+void cmd_0x8E4As (TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase,
+ unsigned char Polarity, double PPSOffset, float Uncertainty);
+void cmd_0x8E4Bq (TSIPPKT *cmd);
+void cmd_0x8E4Ds (TSIPPKT *cmd, unsigned long AutoOutputMask);
+void cmd_0x8EADq (TSIPPKT *cmd);
+
+/* header/source border XXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+/* Trimble parse functions */
+static int parse0x8FAD P((TSIPPKT *, struct peer *));
+static int parse0x8F0B P((TSIPPKT *, struct peer *));
+#ifdef TRIMBLE_OUTPUT_FUNC
+static int parseany P((TSIPPKT *, struct peer *));
+static void TranslateTSIPReportToText P((TSIPPKT *, char *));
+#endif /* TRIMBLE_OUTPUT_FUNC */
+static int parse0x5C P((TSIPPKT *, struct peer *));
+static int parse0x4F P((TSIPPKT *, struct peer *));
+static void tsip_input_proc P((TSIPPKT *, int));
+
+/* Trimble helper functions */
+static void bPutFloat P((float *, unsigned char *));
+static void bPutDouble P((double *, unsigned char *));
+static void bPutULong P((unsigned long *, unsigned char *));
+static int print_msg_table_header P((int rptcode, char *HdrStr, int force));
+static char * show_time P((float time_of_week));
+
+/* RIPE NCC functions */
+static void ripencc_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int ripencc_ppsapi P((struct peer *, int, int));
+static int ripencc_get_pps_ts P((struct ripencc_unit *, l_fp *));
+static int ripencc_start P((int, struct peer *));
+static void ripencc_shutdown P((int, struct peer *));
+static void ripencc_poll P((int, struct peer *));
+static void ripencc_send P((struct peer *, TSIPPKT spt));
+static void ripencc_receive P((struct recvbuf *));
+
+/* fill in reflock structure for our clock */
+struct refclock refclock_ripencc = {
+ ripencc_start, /* start up driver */
+ ripencc_shutdown, /* shut down driver */
+ ripencc_poll, /* transmit poll message */
+ ripencc_control, /* control function */
+ noentry, /* initialize driver */
+ noentry, /* debug info */
+ NOFLAGS /* clock flags */
+};
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+
+/*
+ * ripencc_start - open the GPS devices and initialize data for processing
+ */
+static int
+ripencc_start(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+ struct termios tio;
+ TSIPPKT spt;
+
+ /*
+ * Open serial port
+ */
+ (void)snprintf(device, sizeof(device), DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_RAW)))
+ return (0);
+
+ /* from refclock_palisade.c */
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR, "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * set flags
+ */
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct ripencc_unit *)
+ emalloc(sizeof(struct ripencc_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct ripencc_unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = ripencc_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, REFID_LEN);
+ up->pollcnt = 2;
+ up->unit = unit;
+ up->leapdelta = 0;
+ up->utcflags = 0;
+
+ /*
+ * Initialize the Clock
+ */
+
+ /* query software versions */
+ cmd_0x1F(&spt);
+ ripencc_send(peer, spt);
+
+ /* query receiver health */
+ cmd_0x26(&spt);
+ ripencc_send(peer, spt);
+
+ /* query serial numbers */
+ cmd_0x8E42q(&spt);
+ ripencc_send(peer, spt);
+
+ /* query manuf params */
+ cmd_0x8E41q(&spt);
+ ripencc_send(peer, spt);
+
+ /* i/o opts */ /* trimble manual page A30 */
+ cmd_0x35s(&spt,
+ 0x1C, /* position */
+ 0x00, /* velocity */
+ 0x05, /* timing */
+ 0x0a); /* auxilary */
+ ripencc_send(peer, spt);
+
+ /* turn off port A */
+ cmd_0x3Ds (&spt,
+ 0x0B, /* baud_out */
+ 0x0B, /* baud_inp */
+ 0x07, /* char_code */
+ 0x07, /* stopbitcode */
+ 0x01, /* output_mode */
+ 0x00); /* input_mode */
+ ripencc_send(peer, spt);
+
+ /* set i/o options */
+ cmd_0x8E4As (&spt,
+ 0x01, /* PPS on */
+ 0x01, /* Timebase UTC */
+ 0x00, /* polarity positive */
+ 0., /* 100 ft. cable XXX make flag */
+ 1e-6 * GPS_C); /* turn of biasuncert. > (1us) */
+ ripencc_send(peer,spt);
+
+ /* all outomatic packet output off */
+ cmd_0x8E4Ds(&spt,
+ 0x00000000); /* AutoOutputMask */
+ ripencc_send(peer, spt);
+
+ cmd_0xBBq (&spt,
+ 0x00); /* query primary configuration */
+ ripencc_send(peer,spt);
+
+
+ /* query PPS parameters */
+ cmd_0x8E4Aq (&spt); /* query PPS params */
+ ripencc_send(peer,spt);
+
+ /* query survey limit */
+ cmd_0x8E4Bq (&spt); /* query survey limit */
+ ripencc_send(peer,spt);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("ripencc_start: success\n");
+#endif /* DEBUG_NCC */
+
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR, "refclock_ripencc: time_pps_create failed: %m");
+ return (1);
+ }
+
+ return(ripencc_ppsapi(peer, 0, 0));
+}
+
+/*
+ * ripencc_control - fudge control
+ */
+static void
+ripencc_control(
+ int unit, /* unit (not used) */
+ struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO,"%s()",__FUNCTION__);
+#endif /* DEBUG_NCC */
+
+ pp = peer->procptr;
+ ripencc_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+ripencc_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct ripencc_unit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG_NCC
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ripencc: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif /* DEBUG_NCC */
+
+ return (1);
+}
+
+/*
+ * This function is called every 64 seconds from ripencc_receive
+ * It will fetch the pps time
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+ripencc_get_pps_ts(
+ struct ripencc_unit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ripencc_get_pps_ts\n");
+#endif /* DEBUG_PPS */
+
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ts.tv_sec: %d\n",(int)ts.tv_sec);
+ msyslog(LOG_INFO,"ts.tv_nsec: %ld\n",ts.tv_nsec);
+#endif /* DEBUG_PPS */
+
+ *tsptr = tstmp;
+ return (1);
+}
+
+/*
+ * ripencc_shutdown - shut down a GPS clock
+ */
+static void
+ripencc_shutdown(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+
+ io_closeclock(&pp->io);
+
+ free(up);
+}
+
+/*
+ * ripencc_poll - called by the transmit procedure
+ */
+static void
+ripencc_poll(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ TSIPPKT spt;
+
+#ifdef DEBUG_NCC
+ if (debug)
+ fprintf(stderr, "ripencc_poll(%d)\n", unit);
+#endif /* DEBUG_NCC */
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+
+ pp->polls++;
+ up->polled = 1;
+
+ /* poll for UTC superpacket */
+ cmd_0x8EADq (&spt);
+ ripencc_send(peer,spt);
+}
+
+/*
+ * ripencc_send - send message to clock
+ * use the structures being created by the trimble functions!
+ * makes the code more readable/clean
+ */
+static void
+ripencc_send(struct peer *peer, TSIPPKT spt)
+{
+ unsigned char *ip, *op;
+ unsigned char obuf[512];
+
+#ifdef DEBUG_RAW
+ {
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (debug)
+ printf("ripencc_send(%d, %02X)\n", up->unit, cmd);
+ }
+#endif /* DEBUG_RAW */
+
+ ip = spt.buf;
+ op = obuf;
+
+ *op++ = 0x10;
+ *op++ = spt.code;
+
+ while (spt.len--) {
+ if (op-obuf > sizeof(obuf)-5) {
+ msyslog(LOG_ERR, "ripencc_send obuf overflow!");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ if (*ip == 0x10) /* byte stuffing */
+ *op++ = 0x10;
+ *op++ = *ip++;
+ }
+
+ *op++ = 0x10;
+ *op++ = 0x03;
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ unsigned char *cp;
+ int i;
+
+ printf("ripencc_send: len %d\n", op-obuf);
+ for (i=1, cp=obuf; cp<op; i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ if (write(peer->procptr->io.fd, obuf, op-obuf) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+/*
+ * ripencc_receive()
+ *
+ * called when a packet is received on the serial port
+ * takes care of further processing
+ *
+ */
+static void
+ripencc_receive(struct recvbuf *rbufp)
+{
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+ struct peer *peer;
+ static TSIPPKT rpt; /* structure for current incoming TSIP report */
+ TSIPPKT spt; /* send packet */
+ int ns_since_pps;
+ int i;
+ char *cp;
+ /* Use these variables to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /* msyslog(LOG_INFO, "%s",__FUNCTION__); */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+
+#ifdef DEBUG_RAW
+ if (debug)
+ fprintf(stderr, "ripencc_receive(%d)\n", up->unit);
+#endif /* DEBUG_RAW */
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ int i;
+ unsigned char *cp;
+
+ printf("ripencc_receive: len %d\n", rbufp->recv_length);
+ for (i=1, cp=(char*)&rbufp->recv_space; i <= rbufp->recv_length; i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ cp = (char*) &rbufp->recv_space;
+ i=rbufp->recv_length;
+
+ while (i--) { /* loop over received chars */
+
+ tsip_input_proc(&rpt, (unsigned char) *cp++);
+
+ if (rpt.status != TSIP_PARSED_FULL)
+ continue;
+
+ switch (rpt.code) {
+
+ case 0x8F: /* superpacket */
+
+ switch (rpt.buf[0]) {
+
+ case 0xAD: /* UTC Time */
+ /*
+ * When polling on port B the timecode
+ * is the time of the previous PPS.
+ * If we completed receiving the packet
+ * less than 150ms after the turn of the second,
+ * it may have the code of the previous second.
+ * We do not trust that and simply poll again
+ * without even parsing it.
+ *
+ * More elegant would be to re-schedule the poll,
+ * but I do not know (yet) how to do that cleanly.
+ *
+ */
+ /* BLA ns_since_pps = ncc_tstmp(rbufp, &trtmp); */
+/* if (up->polled && ns_since_pps > -1 && ns_since_pps < 150) { */
+
+ ns_since_pps=200;
+ if (up->polled && ns_since_pps < 150) {
+ msyslog(LOG_INFO, "%s(): up->polled",__FUNCTION__);
+ ripencc_poll(up->unit, peer);
+ break;
+ }
+
+ /*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ */
+ if (parse0x8FAD(&rpt, peer) < 0) {
+ msyslog(LOG_INFO, "%s(): parse0x8FAD < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If the PPSAPI is working, rather use its
+ * timestamps.
+ * assume that the PPS occurs on the second
+ * so blow any msec
+ */
+ if (ripencc_get_pps_ts(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->nsec = 0;
+ }
+ else
+ msyslog(LOG_INFO, "%s(): ripencc_get_pps_ts returns failure\n",__FUNCTION__);
+
+
+ if (!up->polled) {
+ msyslog(LOG_INFO, "%s(): unrequested packet\n",__FUNCTION__);
+ /* unrequested packet */
+ break;
+ }
+
+ /* we have been polled ! */
+ up->polled = 0;
+ up->pollcnt = 2;
+
+ /* poll for next packet */
+ cmd_0x8E0Bq(&spt);
+ ripencc_send(peer,spt);
+
+ if (ns_since_pps < 0) { /* no PPS */
+ msyslog(LOG_INFO, "%s(): ns_since_pps < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion.
+ */
+ if (!refclock_process(pp)) {
+ msyslog(LOG_INFO, "%s(): !refclock_process",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ refclock_receive(peer);
+ break;
+
+ case 0x0B: /* comprehensive time packet */
+ parse0x8F0B(&rpt, peer);
+ break;
+
+ default: /* other superpackets */
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO, "%s(): calling parseany",__FUNCTION__);
+#endif /* DEBUG_NCC */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ break;
+
+ case 0x4F: /* UTC parameters, for leap info */
+ parse0x4F(&rpt, peer);
+ break;
+
+ case 0x5C: /* sat tracking data */
+ parse0x5C(&rpt, peer);
+ break;
+
+ default: /* other packets */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ rpt.status = TSIP_PARSED_EMPTY;
+ }
+}
+
+/*
+ * All trimble functions that are directly referenced from driver code
+ * (so not from parseany)
+ */
+
+void cmd_0x1F (TSIPPKT *cmd)
+/* request software versions */
+{
+ cmd->len = 0;
+ cmd->code = 0x1F;
+}
+
+void cmd_0x26 (TSIPPKT *cmd)
+/* request receiver health */
+{
+ cmd->len = 0;
+ cmd->code = 0x26;
+}
+
+
+
+
+void cmd_0x2F (TSIPPKT *cmd)
+/* request UTC params */
+{
+ cmd->len = 0;
+ cmd->code = 0x2F;
+}
+
+void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
+ unsigned char time_code, unsigned char opts_code)
+/* set serial I/O options */
+{
+ cmd->buf[0] = pos_code;
+ cmd->buf[1] = vel_code;
+ cmd->buf[2] = time_code;
+ cmd->buf[3] = opts_code;
+ cmd->len = 4;
+ cmd->code = 0x35;
+}
+void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn)
+/* request tracking status */
+{
+ cmd->buf[0] = sv_prn;
+ cmd->len = 1;
+ cmd->code = 0x3C;
+}
+
+
+void cmd_0x3Ds (TSIPPKT *cmd,
+ unsigned char baud_out, unsigned char baud_inp,
+ unsigned char char_code, unsigned char stopbitcode,
+ unsigned char output_mode, unsigned char input_mode)
+/* set Channel A configuration for dual-port operation */
+{
+ cmd->buf[0] = baud_out; /* XMT baud rate */
+ cmd->buf[1] = baud_inp; /* RCV baud rate */
+ cmd->buf[2] = char_code; /* parity and #bits per byte */
+ cmd->buf[3] = stopbitcode; /* number of stop bits code */
+ cmd->buf[4] = output_mode; /* Ch. A transmission mode */
+ cmd->buf[5] = input_mode; /* Ch. A reception mode */
+ cmd->len = 6;
+ cmd->code = 0x3D;
+}
+
+
+/* query primary configuration */
+void cmd_0xBBq (TSIPPKT *cmd,
+ unsigned char subcode)
+{
+
+ cmd->len = 1;
+ cmd->code = 0xBB;
+ cmd->buf[0] = subcode;
+}
+
+
+/**** Superpackets ****/
+void cmd_0x8E0Bq (TSIPPKT *cmd)
+/* 8E-0B to query 8F-0B controls */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x0B;
+}
+
+
+void cmd_0x8E41q (TSIPPKT *cmd)
+/* 8F-41 to query board serial number */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x41;
+}
+
+
+void cmd_0x8E42q (TSIPPKT *cmd)
+/* 8F-42 to query product serial number */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x42;
+}
+void cmd_0x8E4Aq (TSIPPKT *cmd)
+/* 8F-4A to query PPS parameters */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+}
+
+
+/* set i/o options */
+void cmd_0x8E4As (TSIPPKT *cmd,
+ unsigned char PPSOnOff,
+ unsigned char TimeBase,
+ unsigned char Polarity,
+ double PPSOffset,
+ float Uncertainty)
+{
+ cmd->len = 16;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+ cmd->buf[1] = PPSOnOff;
+ cmd->buf[2] = TimeBase;
+ cmd->buf[3] = Polarity;
+ bPutDouble (&PPSOffset, &cmd->buf[4]);
+ bPutFloat (&Uncertainty, &cmd->buf[12]);
+}
+void cmd_0x8E4Bq (TSIPPKT *cmd)
+/* 8F-4B query survey limit */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4B;
+}
+
+
+/* poll for UTC superpacket */
+void cmd_0x8EADq (TSIPPKT *cmd)
+/* 8E-AD to query 8F-AD controls */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0xAD;
+}
+
+/* all outomatic packet output off */
+void cmd_0x8E4Ds (TSIPPKT *cmd,
+ unsigned long AutoOutputMask)
+{
+ cmd->len = 5;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4D;
+ bPutULong (&AutoOutputMask, &cmd->buf[1]);
+}
+
+
+
+
+/* for DOS machines, reverse order of bytes as they come through the
+ * serial port. */
+#ifdef BYTESWAP
+static short bGetShort (unsigned char *bp)
+{
+ short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static unsigned short bGetUShort (unsigned char *bp)
+{
+ unsigned short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static long bGetLong (unsigned char *bp)
+{
+ long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static unsigned long bGetULong (unsigned char *bp)
+{
+ unsigned long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+static float bGetSingle (unsigned char *bp)
+{
+ float outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static double bGetDouble (unsigned char *bp)
+{
+ double outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 7;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#else /* not BYTESWAP */
+
+#define bGetShort(bp) (*(short*)(bp))
+#define bGetLong(bp) (*(long*)(bp))
+#define bGetULong(bp) (*(unsigned long*)(bp))
+#define bGetSingle(bp) (*(float*)(bp))
+#define bGetDouble(bp) (*(double*)(bp))
+
+#endif /* BYTESWAP */
+/*
+ * Byte-reversal is necessary for little-endian (Intel-based) machines.
+ * TSIP streams are Big-endian (Motorola-based).
+ */
+#ifdef BYTESWAP
+
+void
+bPutFloat (float *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutULong (unsigned long *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutDouble (double *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 7;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+#else /* not BYTESWAP */
+
+void bPutShort (short a, unsigned char *cmdbuf) {*(short*) cmdbuf = a;}
+void bPutULong (long a, unsigned char *cmdbuf) {*(long*) cmdbuf = a;}
+void bPutFloat (float a, unsigned char *cmdbuf) {*(float*) cmdbuf = a;}
+void bPutDouble (double a, unsigned char *cmdbuf){*(double*) cmdbuf = a;}
+
+#endif /* BYTESWAP */
+
+/*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+static int
+parse0x8FAD(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+ register struct ripencc_unit *up;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned char trackstat, utcflags;
+
+ static char logbuf[1024]; /* logging string buffer */
+ int i;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 22)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: event count != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+
+ if (bGetDouble(&buf[3]) != 0.0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: fracsecs != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+ hour = (unsigned int) buf[11];
+ minute = (unsigned int) buf[12];
+ second = (unsigned int) buf[13];
+ day = (unsigned int) buf[14];
+ month = (unsigned int) buf[15];
+ year = bGetShort(&buf[16]);
+ trackstat = buf[18];
+ utcflags = buf[19];
+
+
+ sprintf(logbuf, "U1 %d.%d.%d %02d:%02d:%02d %d %02x",
+ day, month, year, hour, minute, second, trackstat, utcflags);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ if (!utcflags & UTCF_UTC_AVAIL)
+ return(-1);
+
+ /* poll for UTC parameters once and then if UTC flag changed */
+ up = (struct ripencc_unit *) pp->unitptr;
+ if (utcflags != up->utcflags) {
+ TSIPPKT spt; /* local structure for send packet */
+ cmd_0x2F (&spt); /* request UTC params */
+ ripencc_send(peer,spt);
+ up->utcflags = utcflags;
+ }
+
+ /*
+ * If we hit the leap second, we choose to skip this sample
+ * rather than rely on other code to be perfectly correct.
+ * No offense, just defense ;-).
+ */
+ if (second == 60)
+ return(-1);
+
+ /* now check and convert the time we received */
+
+ pp->year = year;
+ if (month < 1 || month > 12 || day < 1 || day > 31)
+ return(-1);
+
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp-> second = second;
+ pp->nsec = 0;
+
+ if ((utcflags&UTCF_LEAP_PNDG) && up->leapdelta != 0)
+ pp-> leap = (up->leapdelta > 0 ? LEAP_ADDSECOND : LEAP_DELSECOND);
+ else
+ pp-> leap = LEAP_NOWARNING;
+
+ return (0);
+}
+
+/*
+ * Parse comprehensive time packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x8F0B(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned utcoff;
+ unsigned char mode;
+ double bias, rate;
+ float biasunc, rateunc;
+ double lat, lon, alt;
+ short lat_deg, lon_deg;
+ float lat_min, lon_min;
+ unsigned char north_south, east_west;
+ char sv[9];
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char b;
+ int i;
+ unsigned char *buf;
+ double tow;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 74)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0)
+ return(-1);;
+
+ tow = bGetDouble(&buf[3]);
+
+ if (tow == -1.0) {
+ return(-1);
+ }
+ else if ((tow >= 604800.0) || (tow < 0.0)) {
+ return(-1);
+ }
+ else
+ {
+ if (tow < 604799.9) tow = tow + .00000001;
+ second = (unsigned int) fmod(tow, 60.);
+ minute = (unsigned int) fmod(tow/60., 60.);
+ hour = (unsigned int )fmod(tow / 3600., 24.);
+ }
+
+
+ day = (unsigned int) buf[11];
+ month = (unsigned int) buf[12];
+ year = bGetShort(&buf[13]);
+ mode = buf[15];
+ utcoff = bGetShort(&buf[16]);
+ bias = bGetDouble(&buf[18]) / GPS_C * 1e9; /* ns */
+ rate = bGetDouble(&buf[26]) / GPS_C * 1e9; /* ppb */
+ biasunc = bGetSingle(&buf[34]) / GPS_C * 1e9; /* ns */
+ rateunc = bGetSingle(&buf[38]) / GPS_C * 1e9; /* ppb */
+ lat = bGetDouble(&buf[42]) * R2D;
+ lon = bGetDouble(&buf[50]) * R2D;
+ alt = bGetDouble(&buf[58]);
+
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ for (i=0; i<8; i++) {
+ sv[i] = buf[i + 66];
+ if (sv[i]) {
+ TSIPPKT spt; /* local structure for sendpacket */
+ b = (unsigned char) (sv[i]<0 ? -sv[i] : sv[i]);
+ /* request tracking status */
+ cmd_0x3C (&spt, b);
+ ripencc_send(peer,spt);
+ }
+ }
+
+
+ sprintf(logbuf, "C1 %02d%02d%04d %02d%02d%02d %d %7.0f %.1f %.0f %.1f %d %02d%09.6f %c %02d%09.6f %c %.0f %d %d %d %d %d %d %d %d",
+ day, month, year, hour, minute, second, mode, bias, biasunc, rate, rateunc, utcoff,
+ lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt,
+ sv[0], sv[1], sv[2], sv[3], sv[4], sv[5], sv[6], sv[7]);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+/*
+ * Parse any packet using Trimble machinery
+ */
+int parseany(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ static char logbuf[1024]; /* logging string buffer */
+
+ TranslateTSIPReportToText (rpt, logbuf); /* anything else */
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(&logbuf[1]);
+#endif /* DEBUG_NCC */
+ record_clock_stats(&peer->srcadr, &logbuf[1]);
+ return(0);
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+
+/*
+ * Parse UTC Parameter Packet
+ *
+ * See the IDE for documentation!
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x4F(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct ripencc_unit *up;
+
+ double a0;
+ float a1, tot;
+ int dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 26)
+ return (-1);
+ a0 = bGetDouble (buf);
+ a1 = bGetSingle (&buf[8]);
+ dt_ls = bGetShort (&buf[12]);
+ tot = bGetSingle (&buf[14]);
+ wn_t = bGetShort (&buf[18]);
+ wn_lsf = bGetShort (&buf[20]);
+ dn = bGetShort (&buf[22]);
+ dt_lsf = bGetShort (&buf[24]);
+
+ sprintf(logbuf, "L1 %d %d %d %g %g %g %d %d %d",
+ dt_lsf - dt_ls, dt_ls, dt_lsf, a0, a1, tot, wn_t, wn_lsf, dn);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ up = (struct ripencc_unit *) peer->procptr->unitptr;
+ up->leapdelta = dt_lsf - dt_ls;
+
+ return (0);
+}
+
+/*
+ * Parse Tracking Status packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x5C(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ unsigned char prn, channel, aqflag, ephstat;
+ float snr, azinuth, elevation;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 24)
+ return(-1);
+
+ prn = buf[0];
+ channel = (unsigned char)(buf[1] >> 3);
+ if (channel == 0x10)
+ channel = 2;
+ else
+ channel++;
+ aqflag = buf[2];
+ ephstat = buf[3];
+ snr = bGetSingle(&buf[4]);
+ elevation = bGetSingle(&buf[12]) * R2D;
+ azinuth = bGetSingle(&buf[16]) * R2D;
+
+ sprintf(logbuf, "S1 %02d %d %d %02x %4.1f %5.1f %4.1f",
+ prn, channel, aqflag, ephstat, snr, azinuth, elevation);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+/******* Code below is from Trimble Tsipchat *************/
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * ------- BYTE-SWAPPING -------
+ * TSIP is big-endian (Motorola) protocol. To use on little-endian (Intel)
+ * systems, the bytes of all multi-byte types (shorts, floats, doubles, etc.)
+ * must be reversed. This is controlled by the MACRO BYTESWAP; if defined, it
+ * assumes little-endian protocol.
+ * --------------------------------
+ *
+ * T_PARSER.C and T_PARSER.H contains primitive functions that interpret
+ * reports received from the receiver. A second source file pair,
+ * T_FORMAT.C and T_FORMAT.H, contin the matching TSIP command formatters.
+ *
+ * The module is in very portable, basic C language. It can be used as is, or
+ * with minimal changes if a TSIP communications application is needed separate
+ * from TSIPCHAT. The construction of most argument lists avoid the use of
+ * structures, but the developer is encouraged to reconstruct them using such
+ * definitions to meet project requirements. Declarations of T_PARSER.C
+ * functions are included in T_PARSER.H to provide prototyping definitions.
+ *
+ * There are two types of functions: a serial input processing routine,
+ * tsip_input_proc()
+ * which assembles incoming bytes into a TSIPPKT structure, and the
+ * report parsers, rpt_0x??().
+ *
+ * 1) The function tsip_input_proc() accumulates bytes from the receiver,
+ * strips control bytes (DLE), and checks if the report end sequence (DLE ETX)
+ * has been received. rpt.status is defined as TSIP_PARSED_FULL (== 1)
+ * if a complete packet is available.
+ *
+ * 2) The functions rpt_0x??() are report string interpreters patterned after
+ * the document called "Trimble Standard Interface Protocol". It should be
+ * noted that if the report buffer is sent into the receiver with the wrong
+ * length (byte count), the rpt_0x??() returns the Boolean equivalence for
+ * TRUE.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+/**/
+static void tsip_input_proc (
+ TSIPPKT *rpt,
+ int inbyte)
+/* reads bytes until serial buffer is empty or a complete report
+ * has been received; end of report is signified by DLE ETX.
+ */
+{
+ unsigned char newbyte;
+
+ if (inbyte < 0 || inbyte > 0xFF) return;
+
+ newbyte = (unsigned char)(inbyte);
+ switch (rpt->status)
+ {
+ case TSIP_PARSED_DLE_1:
+ switch (newbyte)
+ {
+ case 0:
+ case ETX:
+ /* illegal TSIP IDs */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ break;
+ case DLE:
+ /* try normal message start again */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* legal TSIP ID; start message */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ }
+ break;
+ case TSIP_PARSED_DATA:
+ switch (newbyte) {
+ case DLE:
+ /* expect DLE or ETX next */
+ rpt->status = TSIP_PARSED_DLE_2;
+ break;
+ default:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ /* no change in rpt->status */
+ break;
+ }
+ break;
+ case TSIP_PARSED_DLE_2:
+ switch (newbyte) {
+ case DLE:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ case ETX:
+ /* end of message; return TRUE here. */
+ rpt->status = TSIP_PARSED_FULL;
+ break;
+ default:
+ /* error: treat as TSIP_PARSED_DLE_1; start new report packet */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ }
+ break;
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ switch (newbyte) {
+ case DLE:
+ /* normal message start */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* error: ignore newbyte */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ }
+ break;
+ }
+ if (rpt->len > MAX_RPTBUF) {
+ /* error: start new report packet */
+ rpt->status = TSIP_PARSED_EMPTY;
+ rpt->len = 0;
+ }
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+
+/**/
+short rpt_0x3D (TSIPPKT *rpt,
+ unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index,
+ unsigned char *char_format_index,
+ unsigned char *stop_bits,
+ unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index)
+/* Channel A configuration for dual port operation */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 6) return TRUE;
+ *tx_baud_index = buf[0];
+ *rx_baud_index = buf[1];
+ *char_format_index = buf[2];
+ *stop_bits = (unsigned char)((buf[3] == 0x07) ? 1 : 2);
+ *tx_mode_index = buf[4];
+ *rx_mode_index = buf[5];
+ return FALSE;
+}
+
+/**/
+short rpt_0x40 (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ short *week_num,
+ float *t_zc,
+ float *eccentricity,
+ float *t_oa,
+ float *i_0,
+ float *OMEGA_dot,
+ float *sqrt_A,
+ float *OMEGA_0,
+ float *omega,
+ float *M_0)
+/* almanac data for specified satellite */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 39) return TRUE;
+ *sv_prn = buf[0];
+ *t_zc = bGetSingle (&buf[1]);
+ *week_num = bGetShort (&buf[5]);
+ *eccentricity = bGetSingle (&buf[7]);
+ *t_oa = bGetSingle (&buf[11]);
+ *i_0 = bGetSingle (&buf[15]);
+ *OMEGA_dot = bGetSingle (&buf[19]);
+ *sqrt_A = bGetSingle (&buf[23]);
+ *OMEGA_0 = bGetSingle (&buf[27]);
+ *omega = bGetSingle (&buf[31]);
+ *M_0 = bGetSingle (&buf[35]);
+ return FALSE;
+}
+
+short rpt_0x41 (TSIPPKT *rpt,
+ float *time_of_week,
+ float *UTC_offset,
+ short *week_num)
+/* GPS time */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *time_of_week = bGetSingle (buf);
+ *week_num = bGetShort (&buf[4]);
+ *UTC_offset = bGetSingle (&buf[6]);
+ return FALSE;
+}
+
+short rpt_0x42 (TSIPPKT *rpt,
+ float pos_ECEF[3],
+ float *time_of_fix)
+/* position in ECEF, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ pos_ECEF[0] = bGetSingle (buf);
+ pos_ECEF[1]= bGetSingle (&buf[4]);
+ pos_ECEF[2]= bGetSingle (&buf[8]);
+ *time_of_fix = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x43 (TSIPPKT *rpt,
+ float ECEF_vel[3],
+ float *freq_offset,
+ float *time_of_fix)
+/* velocity in ECEF, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ ECEF_vel[0] = bGetSingle (buf);
+ ECEF_vel[1] = bGetSingle (&buf[4]);
+ ECEF_vel[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x45 (TSIPPKT *rpt,
+ unsigned char *major_nav_version,
+ unsigned char *minor_nav_version,
+ unsigned char *nav_day,
+ unsigned char *nav_month,
+ unsigned char *nav_year,
+ unsigned char *major_dsp_version,
+ unsigned char *minor_dsp_version,
+ unsigned char *dsp_day,
+ unsigned char *dsp_month,
+ unsigned char *dsp_year)
+/* software versions */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *major_nav_version = buf[0];
+ *minor_nav_version = buf[1];
+ *nav_day = buf[2];
+ *nav_month = buf[3];
+ *nav_year = buf[4];
+ *major_dsp_version = buf[5];
+ *minor_dsp_version = buf[6];
+ *dsp_day = buf[7];
+ *dsp_month = buf[8];
+ *dsp_year = buf[9];
+ return FALSE;
+}
+
+short rpt_0x46 (TSIPPKT *rpt,
+ unsigned char *status1,
+ unsigned char *status2)
+/* receiver health and status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 2) return TRUE;
+ *status1 = buf[0];
+ *status2 = buf[1];
+ return FALSE;
+}
+
+short rpt_0x47 (TSIPPKT *rpt,
+ unsigned char *nsvs, unsigned char *sv_prn,
+ float *snr)
+/* signal levels for all satellites tracked */
+{
+ short isv;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1 + 5*buf[0]) return TRUE;
+ *nsvs = buf[0];
+ for (isv = 0; isv < (*nsvs); isv++) {
+ sv_prn[isv] = buf[5*isv + 1];
+ snr[isv] = bGetSingle (&buf[5*isv + 2]);
+ }
+ return FALSE;
+}
+
+short rpt_0x48 (TSIPPKT *rpt,
+ unsigned char *message)
+/* GPS system message */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 22) return TRUE;
+ memcpy (message, buf, 22);
+ message[22] = 0;
+ return FALSE;
+}
+
+short rpt_0x49 (TSIPPKT *rpt,
+ unsigned char *sv_health)
+/* health for all satellites from almanac health page */
+{
+ short i;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 32) return TRUE;
+ for (i = 0; i < 32; i++) sv_health [i]= buf[i];
+ return FALSE;
+}
+
+short rpt_0x4A (TSIPPKT *rpt,
+ float *lat,
+ float *lon,
+ float *alt,
+ float *clock_bias,
+ float *time_of_fix)
+/* position in lat-lon-alt, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ *lat = bGetSingle (buf);
+ *lon = bGetSingle (&buf[4]);
+ *alt = bGetSingle (&buf[8]);
+ *clock_bias = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x4A_2 (TSIPPKT *rpt,
+ float *alt, float *dummy , unsigned char *alt_flag)
+/* reference altitude parameters */
+{
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 9) return TRUE;
+ *alt = bGetSingle (buf);
+ *dummy = bGetSingle (&buf[4]);
+ *alt_flag = buf[8];
+ return FALSE;
+}
+
+short rpt_0x4B (TSIPPKT *rpt,
+ unsigned char *machine_id,
+ unsigned char *status3,
+ unsigned char *status4)
+/* machine ID code, status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 3) return TRUE;
+ *machine_id = buf[0];
+ *status3 = buf[1];
+ *status4 = buf[2];
+ return FALSE;
+}
+
+short rpt_0x4C (TSIPPKT *rpt,
+ unsigned char *dyn_code,
+ float *el_mask,
+ float *snr_mask,
+ float *dop_mask,
+ float *dop_switch)
+/* operating parameters and masks */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 17) return TRUE;
+ *dyn_code = buf[0];
+ *el_mask = bGetSingle (&buf[1]);
+ *snr_mask = bGetSingle (&buf[5]);
+ *dop_mask = bGetSingle (&buf[9]);
+ *dop_switch = bGetSingle (&buf[13]);
+ return FALSE;
+}
+
+short rpt_0x4D (TSIPPKT *rpt,
+ float *osc_offset)
+/* oscillator offset */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *osc_offset = bGetSingle (buf);
+ return FALSE;
+}
+
+short rpt_0x4E (TSIPPKT *rpt,
+ unsigned char *response)
+/* yes/no response to command to set GPS time */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *response = buf[0];
+ return FALSE;
+}
+
+short rpt_0x4F (TSIPPKT *rpt,
+ double *a0,
+ float *a1,
+ float *time_of_data,
+ short *dt_ls,
+ short *wn_t,
+ short *wn_lsf,
+ short *dn,
+ short *dt_lsf)
+/* UTC data */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 26) return TRUE;
+ *a0 = bGetDouble (buf);
+ *a1 = bGetSingle (&buf[8]);
+ *dt_ls = bGetShort (&buf[12]);
+ *time_of_data = bGetSingle (&buf[14]);
+ *wn_t = bGetShort (&buf[18]);
+ *wn_lsf = bGetShort (&buf[20]);
+ *dn = bGetShort (&buf[22]);
+ *dt_lsf = bGetShort (&buf[24]);
+ return FALSE;
+}
+
+/**/
+short rpt_0x54 (TSIPPKT *rpt,
+ float *clock_bias,
+ float *freq_offset,
+ float *time_of_fix)
+/* clock offset and frequency offset in 1-SV (0-D) mode */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 12) return TRUE;
+ *clock_bias = bGetSingle (buf);
+ *freq_offset = bGetSingle (&buf[4]);
+ *time_of_fix = bGetSingle (&buf[8]);
+ return FALSE;
+}
+
+short rpt_0x55 (TSIPPKT *rpt,
+ unsigned char *pos_code,
+ unsigned char *vel_code,
+ unsigned char *time_code,
+ unsigned char *aux_code)
+/* I/O serial options */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *pos_code = buf[0];
+ *vel_code = buf[1];
+ *time_code = buf[2];
+ *aux_code = buf[3];
+ return FALSE;
+}
+
+short rpt_0x56 (TSIPPKT *rpt,
+ float vel_ENU[3], float *freq_offset, float *time_of_fix)
+/* velocity in east-north-up coordinates */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ /* east */
+ vel_ENU[0] = bGetSingle (buf);
+ /* north */
+ vel_ENU[1] = bGetSingle (&buf[4]);
+ /* up */
+ vel_ENU[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x57 (TSIPPKT *rpt,
+ unsigned char *source_code, unsigned char *diag_code,
+ short *week_num,
+ float *time_of_fix)
+/* info about last computed fix */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 8) return TRUE;
+ *source_code = buf[0];
+ *diag_code = buf[1];
+ *time_of_fix = bGetSingle (&buf[2]);
+ *week_num = bGetShort (&buf[6]);
+ return FALSE;
+}
+
+short rpt_0x58 (TSIPPKT *rpt,
+ unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn,
+ unsigned char *data_length, unsigned char *data_packet)
+/* GPS system data or acknowledgment of GPS system data load */
+{
+ unsigned char *buf, *buf4;
+ short dl;
+ ALM_INFO* alminfo;
+ ION_INFO* ioninfo;
+ UTC_INFO* utcinfo;
+ NAV_INFO* navinfo;
+
+ buf = rpt->buf;
+
+ if (buf[0] == 2) {
+ if (rpt->len < 4) return TRUE;
+ if (rpt->len != 4+buf[3]) return TRUE;
+ }
+ else if (rpt->len != 3) {
+ return TRUE;
+ }
+ *op_code = buf[0];
+ *data_type = buf[1];
+ *sv_prn = buf[2];
+ if (*op_code == 2) {
+ dl = buf[3];
+ *data_length = (unsigned char)dl;
+ buf4 = &buf[4];
+ switch (*data_type) {
+ case 2:
+ /* Almanac */
+ if (*data_length != sizeof (ALM_INFO)) return TRUE;
+ alminfo = (ALM_INFO*)data_packet;
+ alminfo->t_oa_raw = buf4[0];
+ alminfo->SV_health = buf4[1];
+ alminfo->e = bGetSingle(&buf4[2]);
+ alminfo->t_oa = bGetSingle(&buf4[6]);
+ alminfo->i_0 = bGetSingle(&buf4[10]);
+ alminfo->OMEGADOT = bGetSingle(&buf4[14]);
+ alminfo->sqrt_A = bGetSingle(&buf4[18]);
+ alminfo->OMEGA_0 = bGetSingle(&buf4[22]);
+ alminfo->omega = bGetSingle(&buf4[26]);
+ alminfo->M_0 = bGetSingle(&buf4[30]);
+ alminfo->a_f0 = bGetSingle(&buf4[34]);
+ alminfo->a_f1 = bGetSingle(&buf4[38]);
+ alminfo->Axis = bGetSingle(&buf4[42]);
+ alminfo->n = bGetSingle(&buf4[46]);
+ alminfo->OMEGA_n = bGetSingle(&buf4[50]);
+ alminfo->ODOT_n = bGetSingle(&buf4[54]);
+ alminfo->t_zc = bGetSingle(&buf4[58]);
+ alminfo->weeknum = bGetShort(&buf4[62]);
+ alminfo->wn_oa = bGetShort(&buf4[64]);
+ break;
+
+ case 3:
+ /* Almanac health page */
+ if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE;
+
+ /* this record is returned raw */
+ memcpy (data_packet, buf4, dl);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ if (*data_length != sizeof (ION_INFO) + 8) return TRUE;
+ ioninfo = (ION_INFO*)data_packet;
+ ioninfo->alpha_0 = bGetSingle (&buf4[8]);
+ ioninfo->alpha_1 = bGetSingle (&buf4[12]);
+ ioninfo->alpha_2 = bGetSingle (&buf4[16]);
+ ioninfo->alpha_3 = bGetSingle (&buf4[20]);
+ ioninfo->beta_0 = bGetSingle (&buf4[24]);
+ ioninfo->beta_1 = bGetSingle (&buf4[28]);
+ ioninfo->beta_2 = bGetSingle (&buf4[32]);
+ ioninfo->beta_3 = bGetSingle (&buf4[36]);
+ break;
+
+ case 5:
+ /* UTC */
+ if (*data_length != sizeof (UTC_INFO) + 13) return TRUE;
+ utcinfo = (UTC_INFO*)data_packet;
+ utcinfo->A_0 = bGetDouble (&buf4[13]);
+ utcinfo->A_1 = bGetSingle (&buf4[21]);
+ utcinfo->delta_t_LS = bGetShort (&buf4[25]);
+ utcinfo->t_ot = bGetSingle(&buf4[27]);
+ utcinfo->WN_t = bGetShort (&buf4[31]);
+ utcinfo->WN_LSF = bGetShort (&buf4[33]);
+ utcinfo->DN = bGetShort (&buf4[35]);
+ utcinfo->delta_t_LSF = bGetShort (&buf4[37]);
+ break;
+
+ case 6:
+ /* Ephemeris */
+ if (*data_length != sizeof (NAV_INFO) - 1) return TRUE;
+
+ navinfo = (NAV_INFO*)data_packet;
+
+ navinfo->sv_number = buf4[0];
+ navinfo->t_ephem = bGetSingle (&buf4[1]);
+ navinfo->ephclk.weeknum = bGetShort (&buf4[5]);
+
+ navinfo->ephclk.codeL2 = buf4[7];
+ navinfo->ephclk.L2Pdata = buf4[8];
+ navinfo->ephclk.SVacc_raw = buf4[9];
+ navinfo->ephclk.SV_health = buf4[10];
+ navinfo->ephclk.IODC = bGetShort (&buf4[11]);
+ navinfo->ephclk.T_GD = bGetSingle (&buf4[13]);
+ navinfo->ephclk.t_oc = bGetSingle (&buf4[17]);
+ navinfo->ephclk.a_f2 = bGetSingle (&buf4[21]);
+ navinfo->ephclk.a_f1 = bGetSingle (&buf4[25]);
+ navinfo->ephclk.a_f0 = bGetSingle (&buf4[29]);
+ navinfo->ephclk.SVacc = bGetSingle (&buf4[33]);
+
+ navinfo->ephorb.IODE = buf4[37];
+ navinfo->ephorb.fit_interval = buf4[38];
+ navinfo->ephorb.C_rs = bGetSingle (&buf4[39]);
+ navinfo->ephorb.delta_n = bGetSingle (&buf4[43]);
+ navinfo->ephorb.M_0 = bGetDouble (&buf4[47]);
+ navinfo->ephorb.C_uc = bGetSingle (&buf4[55]);
+ navinfo->ephorb.e = bGetDouble (&buf4[59]);
+ navinfo->ephorb.C_us = bGetSingle (&buf4[67]);
+ navinfo->ephorb.sqrt_A = bGetDouble (&buf4[71]);
+ navinfo->ephorb.t_oe = bGetSingle (&buf4[79]);
+ navinfo->ephorb.C_ic = bGetSingle (&buf4[83]);
+ navinfo->ephorb.OMEGA_0 = bGetDouble (&buf4[87]);
+ navinfo->ephorb.C_is = bGetSingle (&buf4[95]);
+ navinfo->ephorb.i_0 = bGetDouble (&buf4[99]);
+ navinfo->ephorb.C_rc = bGetSingle (&buf4[107]);
+ navinfo->ephorb.omega = bGetDouble (&buf4[111]);
+ navinfo->ephorb.OMEGADOT=bGetSingle (&buf4[119]);
+ navinfo->ephorb.IDOT = bGetSingle (&buf4[123]);
+ navinfo->ephorb.Axis = bGetDouble (&buf4[127]);
+ navinfo->ephorb.n = bGetDouble (&buf4[135]);
+ navinfo->ephorb.r1me2 = bGetDouble (&buf4[143]);
+ navinfo->ephorb.OMEGA_n=bGetDouble (&buf4[151]);
+ navinfo->ephorb.ODOT_n = bGetDouble (&buf4[159]);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+short rpt_0x59 (TSIPPKT *rpt,
+ unsigned char *code_type,
+ unsigned char status_code[32])
+/* satellite enable/disable or health heed/ignore list */
+{
+ short iprn;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 33) return TRUE;
+ *code_type = buf[0];
+ for (iprn = 0; iprn < 32; iprn++)
+ status_code[iprn] = buf[iprn + 1];
+ return FALSE;
+}
+
+short rpt_0x5A (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ float *sample_length,
+ float *signal_level,
+ float *code_phase,
+ float *Doppler,
+ double *time_of_fix)
+/* raw measurement data - code phase/Doppler */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 25) return TRUE;
+ *sv_prn = buf[0];
+ *sample_length = bGetSingle (&buf[1]);
+ *signal_level = bGetSingle (&buf[5]);
+ *code_phase = bGetSingle (&buf[9]);
+ *Doppler = bGetSingle (&buf[13]);
+ *time_of_fix = bGetDouble (&buf[17]);
+ return FALSE;
+}
+
+short rpt_0x5B (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *sv_health,
+ unsigned char *sv_iode,
+ unsigned char *fit_interval_flag,
+ float *time_of_collection,
+ float *time_of_eph,
+ float *sv_accy)
+/* satellite ephorb status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ *sv_prn = buf[0];
+ *time_of_collection = bGetSingle (&buf[1]);
+ *sv_health = buf[5];
+ *sv_iode = buf[6];
+ *time_of_eph = bGetSingle (&buf[7]);
+ *fit_interval_flag = buf[11];
+ *sv_accy = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x5C (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *slot,
+ unsigned char *chan,
+ unsigned char *acq_flag,
+ unsigned char *eph_flag,
+ float *signal_level,
+ float *time_of_last_msmt,
+ float *elev,
+ float *azim,
+ unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag,
+ unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag)
+/* satellite tracking status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 24) return TRUE;
+ *sv_prn = buf[0];
+ *slot = (unsigned char)((buf[1] & 0x07) + 1);
+ *chan = (unsigned char)(buf[1] >> 3);
+ if (*chan == 0x10) *chan = 2;
+ else (*chan)++;
+ *acq_flag = buf[2];
+ *eph_flag = buf[3];
+ *signal_level = bGetSingle (&buf[4]);
+ *time_of_last_msmt = bGetSingle (&buf[8]);
+ *elev = bGetSingle (&buf[12]);
+ *azim = bGetSingle (&buf[16]);
+ *old_msmt_flag = buf[20];
+ *integer_msec_flag = buf[21];
+ *bad_data_flag = buf[22];
+ *data_collect_flag = buf[23];
+ return FALSE;
+}
+
+/**/
+short rpt_0x6D (TSIPPKT *rpt,
+ unsigned char *manual_mode,
+ unsigned char *nsvs,
+ unsigned char *ndim,
+ unsigned char sv_prn[],
+ float *pdop,
+ float *hdop,
+ float *vdop,
+ float *tdop)
+/* over-determined satellite selection for position fixes, PDOP, fix mode */
+{
+ short islot;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ *nsvs = (unsigned char)((buf[0] & 0xF0) >> 4);
+ if ((*nsvs)>8) return TRUE;
+ if (rpt->len != 17 + (*nsvs) ) return TRUE;
+
+ *manual_mode = (unsigned char)(buf[0] & 0x08);
+ *ndim = (unsigned char)((buf[0] & 0x07));
+ *pdop = bGetSingle (&buf[1]);
+ *hdop = bGetSingle (&buf[5]);
+ *vdop = bGetSingle (&buf[9]);
+ *tdop = bGetSingle (&buf[13]);
+ for (islot = 0; islot < (*nsvs); islot++)
+ sv_prn[islot] = buf[islot + 17];
+ return FALSE;
+}
+
+/**/
+short rpt_0x82 (TSIPPKT *rpt,
+ unsigned char *diff_mode)
+/* differential fix mode */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *diff_mode = buf[0];
+ return FALSE;
+}
+
+short rpt_0x83 (TSIPPKT *rpt,
+ double ECEF_pos[3],
+ double *clock_bias,
+ float *time_of_fix)
+/* position, ECEF double precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ ECEF_pos[0] = bGetDouble (buf);
+ ECEF_pos[1] = bGetDouble (&buf[8]);
+ ECEF_pos[2] = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+short rpt_0x84 (TSIPPKT *rpt,
+ double *lat,
+ double *lon,
+ double *alt,
+ double *clock_bias,
+ float *time_of_fix)
+/* position, lat-lon-alt double precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ *lat = bGetDouble (buf);
+ *lon = bGetDouble (&buf[8]);
+ *alt = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+short rpt_Paly0xBB(TSIPPKT *rpt,
+ TSIP_RCVR_CFG *TsipxBB)
+{
+
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ /* Palisade is inconsistent with other TSIP, which has a kength of 40 */
+ /* if (rpt->len != 40) return TRUE; */
+ if (rpt->len != 43) return TRUE;
+
+ TsipxBB->bSubcode = buf[0];
+ TsipxBB->operating_mode = buf[1] ;
+ TsipxBB->dyn_code = buf[3] ;
+ TsipxBB->elev_mask = bGetSingle (&buf[5]);
+ TsipxBB->cno_mask = bGetSingle (&buf[9]);
+ TsipxBB->dop_mask = bGetSingle (&buf[13]);
+ TsipxBB->dop_switch = bGetSingle (&buf[17]);
+ return FALSE;
+}
+
+short rpt_0xBC (TSIPPKT *rpt,
+ unsigned char *port_num,
+ unsigned char *in_baud,
+ unsigned char *out_baud,
+ unsigned char *data_bits,
+ unsigned char *parity,
+ unsigned char *stop_bits,
+ unsigned char *flow_control,
+ unsigned char *protocols_in,
+ unsigned char *protocols_out,
+ unsigned char *reserved)
+/* Receiver serial port configuration */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *port_num = buf[0];
+ *in_baud = buf[1];
+ *out_baud = buf[2];
+ *data_bits = buf[3];
+ *parity = buf[4];
+ *stop_bits = buf[5];
+ *flow_control = buf[6];
+ *protocols_in = buf[7];
+ *protocols_out = buf[8];
+ *reserved = buf[9];
+
+ return FALSE;
+}
+
+/**** Superpackets ****/
+
+short rpt_0x8F0B(TSIPPKT *rpt,
+ unsigned short *event,
+ double *tow,
+ unsigned char *date,
+ unsigned char *month,
+ short *year,
+ unsigned char *dim_mode,
+ short *utc_offset,
+ double *bias,
+ double *drift,
+ float *bias_unc,
+ float *dr_unc,
+ double *lat,
+ double *lon,
+ double *alt,
+ char sv_id[8])
+{
+ short local_index;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 74) return TRUE;
+ *event = bGetShort(&buf[1]);
+ *tow = bGetDouble(&buf[3]);
+ *date = buf[11];
+ *month = buf[12];
+ *year = bGetShort(&buf[13]);
+ *dim_mode = buf[15];
+ *utc_offset = bGetShort(&buf[16]);
+ *bias = bGetDouble(&buf[18]);
+ *drift = bGetDouble(&buf[26]);
+ *bias_unc = bGetSingle(&buf[34]);
+ *dr_unc = bGetSingle(&buf[38]);
+ *lat = bGetDouble(&buf[42]);
+ *lon = bGetDouble(&buf[50]);
+ *alt = bGetDouble(&buf[58]);
+
+ for (local_index=0; local_index<8; local_index++) sv_id[local_index] = buf[local_index + 66];
+ return FALSE;
+}
+
+short rpt_0x8F14 (TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5])
+/* datum index and coefficients */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+short rpt_0x8F15 (TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5])
+/* datum index and coefficients */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+#define MAX_LONG (2147483648.) /* 2**31 */
+
+short rpt_0x8F20 (TSIPPKT *rpt,
+ unsigned char *info,
+ double *lat,
+ double *lon,
+ double *alt,
+ double vel_enu[],
+ double *time_of_fix,
+ short *week_num,
+ unsigned char *nsvs,
+ unsigned char sv_prn[],
+ short sv_IODC[],
+ short *datum_index)
+{
+ short
+ isv;
+ unsigned char
+ *buf, prnx, iode;
+ unsigned long
+ ulongtemp;
+ long
+ longtemp;
+ double
+ vel_scale;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 56) return TRUE;
+
+ vel_scale = (buf[24]&1)? 0.020 : 0.005;
+ vel_enu[0] = bGetShort (buf+2)*vel_scale;
+ vel_enu[1] = bGetShort (buf+4)*vel_scale;
+ vel_enu[2] = bGetShort (buf+6)*vel_scale;
+
+ *time_of_fix = bGetULong (buf+8)*.001;
+
+ longtemp = bGetLong (buf+12);
+ *lat = longtemp*(GPS_PI/MAX_LONG);
+
+ ulongtemp = bGetULong (buf+16);
+ *lon = ulongtemp*(GPS_PI/MAX_LONG);
+ if (*lon > GPS_PI) *lon -= 2.0*GPS_PI;
+
+ *alt = bGetLong (buf+20)*.001;
+ /* 25 blank; 29 = UTC */
+ (*datum_index) = (short)((short)buf[26]-1);
+ *info = buf[27];
+ *nsvs = buf[28];
+ *week_num = bGetShort (&buf[30]);
+ for (isv = 0; isv < 8; isv++) {
+ prnx = buf[32+2*isv];
+ sv_prn[isv] = (unsigned char)(prnx&0x3F);
+ iode = buf[33+2*isv];
+ sv_IODC[isv] = (short)(iode | ((prnx>>6)<<8));
+ }
+ return FALSE;
+}
+
+short rpt_0x8F41 (TSIPPKT *rpt,
+ unsigned char *bSearchRange,
+ unsigned char *bBoardOptions,
+ unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear,
+ unsigned char *bBuildMonth,
+ unsigned char *bBuildDay,
+ unsigned char *bBuildHour,
+ float *fOscOffset,
+ unsigned short *iTestCodeId)
+{
+ if(rpt->len != 17) return FALSE;
+ *bSearchRange = rpt->buf[1];
+ *bBoardOptions = rpt->buf[2];
+ *iiSerialNumber = bGetLong(&rpt->buf[3]);
+ *bBuildYear = rpt->buf[7];
+ *bBuildMonth = rpt->buf[8];
+ *bBuildDay = rpt->buf[9];
+ *bBuildHour = rpt->buf[10];
+ *fOscOffset = bGetSingle(&rpt->buf[11]);
+ *iTestCodeId = bGetShort(&rpt->buf[15]);
+/* Tsipx8E41Data = *Tsipx8E41; */
+ return TRUE;
+}
+
+short rpt_0x8F42 (TSIPPKT *rpt,
+ unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt,
+ unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber,
+ unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions,
+ unsigned short *iMachineID,
+ unsigned short *iKey)
+{
+ if(rpt->len != 19) return FALSE;
+ *bProdOptionsPre = rpt->buf[1];
+ *bProdNumberExt = rpt->buf[2];
+ *iCaseSerialNumberPre = bGetShort(&rpt->buf[3]);
+ *iiCaseSerialNumber = bGetLong(&rpt->buf[5]);
+ *iiProdNumber = bGetLong(&rpt->buf[9]);
+ *iPremiumOptions = bGetShort(&rpt->buf[13]);
+ *iMachineID = bGetShort(&rpt->buf[15]);
+ *iKey = bGetShort(&rpt->buf[17]);
+ return TRUE;
+}
+
+short rpt_0x8F45(TSIPPKT *rpt,
+ unsigned char *bSegMask)
+{
+ if(rpt->len != 2) return FALSE;
+ *bSegMask = rpt->buf[1];
+ return TRUE;
+}
+
+short rpt_0x8F4A_16(TSIPPKT *rpt,
+ unsigned char *pps_enabled,
+ unsigned char *pps_timebase,
+ unsigned char *pos_polarity,
+ double *pps_offset,
+ float *bias_unc_threshold)
+/* Stinger PPS definition */
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 16) return TRUE;
+ *pps_enabled = buf[1];
+ *pps_timebase = buf[2];
+ *pos_polarity = buf[3];
+ *pps_offset = bGetDouble(&buf[4]);
+ *bias_unc_threshold = bGetSingle(&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x8F4B(TSIPPKT *rpt,
+ unsigned long *decorr_max)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *decorr_max = bGetLong(&buf[1]);
+ return FALSE;
+}
+
+short rpt_0x8F4D(TSIPPKT *rpt,
+ unsigned long *event_mask)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *event_mask = bGetULong (&buf[1]);
+ return FALSE;
+}
+
+short rpt_0x8FA5(TSIPPKT *rpt,
+ unsigned char *spktmask)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ spktmask[0] = buf[1];
+ spktmask[1] = buf[2];
+ spktmask[2] = buf[3];
+ spktmask[3] = buf[4];
+ return FALSE;
+}
+
+short rpt_0x8FAD (TSIPPKT *rpt,
+ unsigned short *COUNT,
+ double *FracSec,
+ unsigned char *Hour,
+ unsigned char *Minute,
+ unsigned char *Second,
+ unsigned char *Day,
+ unsigned char *Month,
+ unsigned short *Year,
+ unsigned char *Status,
+ unsigned char *Flags)
+{
+
+ if (rpt->len != 22) return TRUE;
+
+ *COUNT = bGetUShort(&rpt->buf[1]);
+ *FracSec = bGetDouble(&rpt->buf[3]);
+ *Hour = rpt->buf[11];
+ *Minute = rpt->buf[12];
+ *Second = rpt->buf[13];
+ *Day = rpt->buf[14];
+ *Month = rpt->buf[15];
+ *Year = bGetUShort(&rpt->buf[16]);
+ *Status = rpt->buf[18];
+ *Flags = rpt->buf[19];
+ return FALSE;
+}
+
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * T_REPORT.C consists of a primary function TranslateTSIPReportToText()
+ * called by main().
+ *
+ * This function takes a character buffer that has been received as a report
+ * from a TSIP device and interprets it. The character buffer has been
+ * assembled using tsip_input_proc() in T_PARSER.C.
+ *
+ * A large case statement directs processing to one of many mid-level
+ * functions. The mid-level functions specific to the current report
+ * code passes the report buffer to the appropriate report decoder
+ * rpt_0x?? () in T_PARSER.C, which converts the byte stream in rpt.buf
+ * to data values approporaite for use.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+#define GOOD_PARSE 0
+#define BADID_PARSE 1
+#define BADLEN_PARSE 2
+#define BADDATA_PARSE 3
+
+#define B_TSIP 0x02
+#define B_NMEA 0x04
+
+
+/* pbuf is the pointer to the current location of the text output */
+static char
+ *pbuf;
+
+/* keep track of whether the message has been successfully parsed */
+static short
+ parsed;
+
+
+/* convert time of week into day-hour-minute-second and print */
+char* show_time (float time_of_week)
+{
+ short days, hours, minutes;
+ float seconds;
+ double tow = 0;
+ static char timestring [80];
+
+ if (time_of_week == -1.0)
+ {
+ sprintf(timestring, " <No time yet> ");
+ }
+ else if ((time_of_week >= 604800.0) || (time_of_week < 0.0))
+ {
+ sprintf(timestring, " <Bad time> ");
+ }
+ else
+ {
+ if (time_of_week < 604799.9)
+ tow = time_of_week + .00000001;
+ seconds = (float)fmod(tow, 60.);
+ minutes = (short) fmod(tow/60., 60.);
+ hours = (short)fmod(tow / 3600., 24.);
+ days = (short)(tow / 86400.0);
+ sprintf(timestring, " %s %02d:%02d:%05.2f ",
+ dayname[days], hours, minutes, seconds);
+ }
+ return timestring;
+}
+
+/**/
+/* 0x3D */
+static void rpt_chan_A_config (TSIPPKT *rpt)
+{
+ unsigned char
+ tx_baud_index, rx_baud_index,
+ char_format_index, stop_bits,
+ tx_mode_index, rx_mode_index,
+ databits, parity;
+ int
+ i, nbaud;
+
+ /* unload rptbuf */
+ if (rpt_0x3D (rpt,
+ &tx_baud_index, &rx_baud_index, &char_format_index,
+ &stop_bits, &tx_mode_index, &rx_mode_index)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nChannel A Configuration");
+
+ nbaud = sizeof(old_baudnum);
+
+ for (i = 0; i < nbaud; ++i) if (tx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Transmit speed: %s at %s",
+ old_output_ch[tx_mode_index], st_baud_text_app[i]);
+
+ for (i = 0; i < nbaud; ++i) if (rx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Receive speed: %s at %s",
+ old_input_ch[rx_mode_index], st_baud_text_app[i]);
+
+ databits = (unsigned char)((char_format_index & 0x03) + 5);
+
+ parity = (unsigned char)(char_format_index >> 2);
+ if (parity > 4) parity = 2;
+
+ pbuf += sprintf(pbuf, "\n Character format (bits/char, parity, stop bits): %d-%s-%d",
+ databits, old_parity_text[parity], stop_bits);
+}
+
+/**/
+/* 0x40 */
+static void rpt_almanac_data_page (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn;
+ short
+ week_num;
+ float
+ t_zc,
+ eccentricity,
+ t_oa,
+ i_0,
+ OMEGA_dot,
+ sqrt_A,
+ OMEGA_0,
+ omega,
+ M_0;
+
+ /* unload rptbuf */
+ if (rpt_0x40 (rpt,
+ &sv_prn, &week_num, &t_zc, &eccentricity, &t_oa,
+ &i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac for SV %02d", sv_prn);
+ pbuf += sprintf(pbuf, "\n Captured:%15.0f %s",
+ t_zc, show_time (t_zc));
+ pbuf += sprintf(pbuf, "\n week:%15d", week_num);
+ pbuf += sprintf(pbuf, "\n Eccentricity:%15g", eccentricity);
+ pbuf += sprintf(pbuf, "\n T_oa:%15.0f %s",
+ t_oa, show_time (t_oa));
+ pbuf += sprintf(pbuf, "\n i 0:%15g", i_0);
+ pbuf += sprintf(pbuf, "\n OMEGA dot:%15g", OMEGA_dot);
+ pbuf += sprintf(pbuf, "\n sqrt A:%15g", sqrt_A);
+ pbuf += sprintf(pbuf, "\n OMEGA 0:%15g", OMEGA_0);
+ pbuf += sprintf(pbuf, "\n omega:%15g", omega);
+ pbuf += sprintf(pbuf, "\n M 0:%15g", M_0);
+}
+
+/* 0x41 */
+static void rpt_GPS_time (TSIPPKT *rpt)
+{
+ float
+ time_of_week, UTC_offset;
+ short
+ week_num;
+
+ /* unload rptbuf */
+ if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time:%s GPS week: %d UTC offset %.1f",
+ show_time(time_of_week), week_num, UTC_offset);
+
+}
+
+/* 0x42 */
+static void rpt_single_ECEF_position (TSIPPKT *rpt)
+{
+ float
+ ECEF_pos[3], time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSXYZ: %15.0f %15.0f %15.0f %s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2],
+ show_time(time_of_fix));
+}
+
+/* 0x43 */
+static void rpt_single_ECEF_velocity (TSIPPKT *rpt)
+{
+
+ float
+ ECEF_vel[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVelECEF: %11.3f %11.3f %11.3f %12.3f%s",
+ ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset,
+ show_time(time_of_fix));
+}
+
+/* 0x45 */
+static void rpt_SW_version (TSIPPKT *rpt) {
+ unsigned char
+ major_nav_version, minor_nav_version,
+ nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version,
+ dsp_day, dsp_month, dsp_year;
+
+ /* unload rptbuf */
+ if (rpt_0x45 (rpt,
+ &major_nav_version, &minor_nav_version,
+ &nav_day, &nav_month, &nav_year,
+ &major_dsp_version, &minor_dsp_version,
+ &dsp_day, &dsp_month, &dsp_year)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+"\nFW Versions: Nav Proc %2d.%02d %2d/%2d/%2d Sig Proc %2d.%02d %2d/%2d/%2d",
+ major_nav_version, minor_nav_version, nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year);
+}
+
+/* 0x46 */
+static void rpt_rcvr_health (TSIPPKT *rpt)
+{
+ unsigned char
+ status1, status2;
+ static char
+ *sc_text[] = {
+ "Doing position fixes",
+ "Don't have GPS time yet",
+ "Waiting for almanac collection",
+ "DOP too high ",
+ "No satellites available",
+ "Only 1 satellite available",
+ "Only 2 satellites available",
+ "Only 3 satellites available",
+ "No satellites usable ",
+ "Only 1 satellite usable",
+ "Only 2 satellites usable",
+ "Only 3 satellites usable",
+ "Chosen satellite unusable"};
+
+
+ /* unload rptbuf */
+ if (rpt_0x46 (rpt, &status1, &status2))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nRcvr status1: %s (%02Xh); ",
+ sc_text[rpt->buf[0]], status1);
+
+ pbuf += sprintf(pbuf, "status2: %s, %s (%02Xh)",
+ (status2 & 0x01)?"No BBRAM":"BBRAM OK",
+ (status2 & 0x10)?"No Ant":"Ant OK",
+ status2);
+}
+
+/* 0x47 */
+static void rpt_SNR_all_SVs (TSIPPKT *rpt)
+{
+ unsigned char
+ nsvs, sv_prn[12];
+ short
+ isv;
+ float
+ snr[12];
+
+ /* unload rptbuf */
+ if (rpt_0x47 (rpt, &nsvs, sv_prn, snr))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSNR for satellites: %d", nsvs);
+ for (isv = 0; isv < nsvs; isv++)
+ {
+ pbuf += sprintf(pbuf, "\n SV %02d %6.2f",
+ sv_prn[isv], snr[isv]);
+ }
+}
+
+/* 0x48 */
+static void rpt_GPS_system_message (TSIPPKT *rpt)
+{
+ unsigned char
+ message[23];
+
+ /* unload rptbuf */
+ if (rpt_0x48 (rpt, message))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS message: %s", message);
+}
+
+/* 0x49 */
+static void rpt_almanac_health_page (TSIPPKT *rpt)
+{
+ short
+ iprn;
+ unsigned char
+ sv_health [32];
+
+ /* unload rptbuf */
+ if (rpt_0x49 (rpt, sv_health))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++)
+ {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , sv_health[iprn]);
+ }
+}
+
+/* 0x4A */
+static void rpt_single_lla_position (TSIPPKT *rpt) {
+ short
+ lat_deg, lon_deg;
+ float
+ lat, lon,
+ alt, clock_bias, time_of_fix;
+ double lat_min, lon_min;
+ unsigned char
+ north_south, east_west;
+
+ if (rpt_0x4A (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ /* convert from radians to degrees */
+ lat *= (float)R2D;
+ north_south = 'N';
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ lon *= (float)R2D;
+ east_west = 'E';
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ pbuf += sprintf(pbuf, "\nSLLA: %4d: %06.3f %c%5d:%06.3f %c%10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x4A */
+static void rpt_ref_alt (TSIPPKT *rpt) {
+
+ float
+ alt, dummy;
+ unsigned char
+ alt_flag;
+
+ if (rpt_0x4A_2 (rpt,
+ &alt, &dummy, &alt_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nReference Alt: %.1f m; %s",
+ alt, alt_flag?"ON":"OFF");
+}
+
+/* 0x4B */
+static void rpt_rcvr_id_and_status (TSIPPKT *rpt)
+{
+
+ unsigned char
+ machine_id, status3, status4;
+
+ /* unload rptbuf */
+ if (rpt_0x4B (rpt, &machine_id, &status3, &status4))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nRcvr Machine ID: %d; Status3 = %s, %s (%02Xh)",
+ machine_id,
+ (status3 & 0x02)?"No RTC":"RTC OK",
+ (status3 & 0x08)?"No Alm":"Alm OK",
+ status3);
+}
+
+/* 0x4C */
+static void rpt_operating_parameters (TSIPPKT *rpt)
+{
+ unsigned char
+ dyn_code;
+ float
+ el_mask, snr_mask, dop_mask, dop_switch;
+
+ /* unload rptbuf */
+ if (rpt_0x4C (rpt, &dyn_code, &el_mask,
+ &snr_mask, &dop_mask, &dop_switch))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOperating Parameters:");
+ pbuf += sprintf(pbuf, "\n Dynamics code = %d %s",
+ dyn_code, dyn_text[dyn_code]);
+ pbuf += sprintf(pbuf, "\n Elevation mask = %.2fø", el_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask = %.2f", snr_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask = %.2f", dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch = %.2f", dop_switch);
+}
+
+/* 0x4D */
+static void rpt_oscillator_offset (TSIPPKT *rpt)
+{
+ float
+ osc_offset;
+
+ /* unload rptbuf */
+ if (rpt_0x4D (rpt, &osc_offset))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOscillator offset: %.2f Hz = %.3f PPM",
+ osc_offset, osc_offset/1575.42);
+}
+
+/* 0x4E */
+static void rpt_GPS_time_set_response (TSIPPKT *rpt)
+{
+
+ unsigned char
+ response;
+
+ /* unload rptbuf */
+ if (rpt_0x4E (rpt, &response))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (response)
+ {
+ case 'Y':
+ pbuf += sprintf(pbuf, "\nTime set accepted");
+ break;
+
+ case 'N':
+ pbuf += sprintf(pbuf, "\nTime set rejected or not required");
+ break;
+
+ default:
+ parsed = BADDATA_PARSE;
+ }
+}
+
+/* 0x4F */
+static void rpt_UTC_offset (TSIPPKT *rpt)
+{
+ double
+ a0;
+ float
+ a1, time_of_data;
+ short
+ dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ /* unload rptbuf */
+ if (rpt_0x4F (rpt, &a0, &a1, &time_of_data,
+ &dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nUTC Correction Data");
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", a0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", a1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", dt_ls);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", time_of_data);
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", wn_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", wn_lsf );
+ pbuf += sprintf(pbuf, "\n DN = %d ", dn );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", dt_lsf );
+}
+
+/**/
+/* 0x54 */
+static void rpt_1SV_bias (TSIPPKT *rpt)
+{
+ float
+ clock_bias, freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf (pbuf, "\nTime Fix Clock Bias: %6.2f m Freq Bias: %6.2f m/s%s",
+ clock_bias, freq_offset, show_time (time_of_fix));
+}
+
+/* 0x55 */
+static void rpt_io_opt (TSIPPKT *rpt)
+{
+ unsigned char
+ pos_code, vel_code, time_code, aux_code;
+
+ /* unload rptbuf */
+ if (rpt_0x55 (rpt,
+ &pos_code, &vel_code, &time_code, &aux_code)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\nI/O Options: %2X %2X %2X %2X",
+ pos_code, vel_code, time_code, aux_code);
+
+ if (pos_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ position output");
+ }
+
+ if (pos_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n LLA position output");
+ }
+
+ pbuf += sprintf(pbuf, (pos_code & 0x04)?
+ "\n MSL altitude output (Geoid height) ":
+ "\n WGS-84 altitude output");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x08)?
+ "\n MSL altitude input":
+ "\n WGS-84 altitude input");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x10)?
+ "\n Double precision":
+ "\n Single precision");
+
+ if (pos_code & 0x20) {
+ pbuf += sprintf(pbuf, "\n All Enabled Superpackets");
+ }
+
+ if (vel_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ velocity output");
+ }
+
+ if (vel_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n ENU velocity output");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x01)?
+ "\n Time tags in UTC":
+ "\n Time tags in GPS time");
+
+ if (time_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Fixes delayed to integer seconds");
+ }
+
+ if (time_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Fixes sent only on request");
+ }
+
+ if (time_code & 0x08) {
+ pbuf += sprintf(pbuf, "\n Synchronized measurements");
+ }
+
+ if (time_code & 0x10) {
+ pbuf += sprintf(pbuf, "\n Minimize measurement propagation");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x20) ?
+ "\n PPS output at all times" :
+ "\n PPS output during fixes");
+
+ if (aux_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n Raw measurement output");
+ }
+
+ if (aux_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Code-phase smoothed before output");
+ }
+
+ if (aux_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Additional fix status");
+ }
+
+ pbuf += sprintf(pbuf, (aux_code & 0x08)?
+ "\n Signal Strength Output as dBHz" :
+ "\n Signal Strength Output as AMU");
+}
+
+/* 0x56 */
+static void rpt_ENU_velocity (TSIPPKT *rpt)
+{
+ float
+ vel_ENU[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVel ENU: %11.3f %11.3f %11.3f %12.3f%s",
+ vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset,
+ show_time (time_of_fix));
+}
+
+/* 0x57 */
+static void rpt_last_fix_info (TSIPPKT *rpt)
+{
+ unsigned char
+ source_code, diag_code;
+ short
+ week_num;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n source code %d; diag code: %2Xh",
+ source_code, diag_code);
+ pbuf += sprintf(pbuf, "\n Time of last fix:%s", show_time(time_of_fix));
+ pbuf += sprintf(pbuf, "\n Week of last fix: %d", week_num);
+}
+
+/* 0x58 */
+static void rpt_GPS_system_data (TSIPPKT *rpt)
+{
+ unsigned char
+ iprn,
+ op_code, data_type, sv_prn,
+ data_length, data_packet[250];
+ ALM_INFO
+ *almanac;
+ ALH_PARMS
+ *almh;
+ UTC_INFO
+ *utc;
+ ION_INFO
+ *ionosphere;
+ EPHEM_CLOCK
+ *cdata;
+ EPHEM_ORBIT
+ *edata;
+ NAV_INFO
+ *nav_data;
+ unsigned char
+ curr_t_oa;
+ unsigned short
+ curr_wn_oa;
+ static char
+ *datname[] =
+ {"", "", "Almanac Orbit",
+ "Health Page & Ref Time", "Ionosphere", "UTC ",
+ "Ephemeris"};
+
+ /* unload rptbuf */
+ if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn,
+ &data_length, data_packet))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSystem data [%d]: %s SV%02d",
+ data_type, datname[data_type], sv_prn);
+ switch (op_code)
+ {
+ case 1:
+ pbuf += sprintf(pbuf, " Acknowledgment");
+ break;
+ case 2:
+ pbuf += sprintf(pbuf, " length = %d bytes", data_length);
+ switch (data_type) {
+ case 2:
+ /* Almanac */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ almanac = (ALM_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa_raw = % -12d SV_hlth = % -12d ",
+ almanac->t_oa_raw , almanac->SV_health );
+ pbuf += sprintf(pbuf, "\n e = % -12g t_oa = % -12g ",
+ almanac->e , almanac->t_oa );
+ pbuf += sprintf(pbuf, "\n i_0 = % -12g OMEGADOT = % -12g ",
+ almanac->i_0 , almanac->OMEGADOT );
+ pbuf += sprintf(pbuf, "\n sqrt_A = % -12g OMEGA_0 = % -12g ",
+ almanac->sqrt_A , almanac->OMEGA_0 );
+ pbuf += sprintf(pbuf, "\n omega = % -12g M_0 = % -12g ",
+ almanac->omega , almanac->M_0 );
+ pbuf += sprintf(pbuf, "\n a_f0 = % -12g a_f1 = % -12g ",
+ almanac->a_f0 , almanac->a_f1 );
+ pbuf += sprintf(pbuf, "\n Axis = % -12g n = % -12g ",
+ almanac->Axis , almanac->n );
+ pbuf += sprintf(pbuf, "\n OMEGA_n = % -12g ODOT_n = % -12g ",
+ almanac->OMEGA_n , almanac->ODOT_n );
+ pbuf += sprintf(pbuf, "\n t_zc = % -12g weeknum = % -12d ",
+ almanac->t_zc , almanac->weeknum );
+ pbuf += sprintf(pbuf, "\n wn_oa = % -12d", almanac->wn_oa );
+ break;
+
+ case 3:
+ /* Almanac health page */
+ almh = (ALH_PARMS*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa = %d, wn_oa&0xFF = %d ",
+ almh->t_oa, almh->WN_a);
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++) {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , almh->SV_health[iprn]);
+ }
+ curr_t_oa = data_packet[34];
+ curr_wn_oa = (unsigned short)((data_packet[35]<<8) + data_packet[36]);
+ pbuf += sprintf(pbuf, "\n current t_oa = %d, wn_oa = %d ",
+ curr_t_oa, curr_wn_oa);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ ionosphere = (ION_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n alpha_0 = % -12g alpha_1 = % -12g ",
+ ionosphere->alpha_0, ionosphere->alpha_1);
+ pbuf += sprintf(pbuf, "\n alpha_2 = % -12g alpha_3 = % -12g ",
+ ionosphere->alpha_2, ionosphere->alpha_3);
+ pbuf += sprintf(pbuf, "\n beta_0 = % -12g beta_1 = % -12g ",
+ ionosphere->beta_0, ionosphere->beta_1);
+ pbuf += sprintf(pbuf, "\n beta_2 = % -12g beta_3 = % -12g ",
+ ionosphere->beta_2, ionosphere->beta_3);
+ break;
+
+ case 5:
+ /* UTC */
+ utc = (UTC_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", utc->A_0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", utc->A_1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", utc->delta_t_LS);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", utc->t_ot );
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", utc->WN_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", utc->WN_LSF );
+ pbuf += sprintf(pbuf, "\n DN = %d ", utc->DN );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", utc->delta_t_LSF );
+ break;
+
+ case 6: /* Ephemeris */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ nav_data = (NAV_INFO*)data_packet;
+
+ pbuf += sprintf(pbuf, "\n SV_PRN = % -12d . t_ephem = % -12g . ",
+ nav_data->sv_number , nav_data->t_ephem );
+ cdata = &(nav_data->ephclk);
+ pbuf += sprintf(pbuf,
+ "\n weeknum = % -12d . codeL2 = % -12d . L2Pdata = % -12d",
+ cdata->weeknum , cdata->codeL2 , cdata->L2Pdata );
+ pbuf += sprintf(pbuf,
+ "\n SVacc_raw = % -12d .SV_health = % -12d . IODC = % -12d",
+ cdata->SVacc_raw, cdata->SV_health, cdata->IODC );
+ pbuf += sprintf(pbuf,
+ "\n T_GD = % -12g . t_oc = % -12g . a_f2 = % -12g",
+ cdata->T_GD, cdata->t_oc, cdata->a_f2 );
+ pbuf += sprintf(pbuf,
+ "\n a_f1 = % -12g . a_f0 = % -12g . SVacc = % -12g",
+ cdata->a_f1, cdata->a_f0, cdata->SVacc );
+ edata = &(nav_data->ephorb);
+ pbuf += sprintf(pbuf,
+ "\n IODE = % -12d .fit_intvl = % -12d . C_rs = % -12g",
+ edata->IODE, edata->fit_interval, edata->C_rs );
+ pbuf += sprintf(pbuf,
+ "\n delta_n = % -12g . M_0 = % -12g . C_uc = % -12g",
+ edata->delta_n, edata->M_0, edata->C_uc );
+ pbuf += sprintf(pbuf,
+ "\n ecc = % -12g . C_us = % -12g . sqrt_A = % -12g",
+ edata->e, edata->C_us, edata->sqrt_A );
+ pbuf += sprintf(pbuf,
+ "\n t_oe = % -12g . C_ic = % -12g . OMEGA_0 = % -12g",
+ edata->t_oe, edata->C_ic, edata->OMEGA_0 );
+ pbuf += sprintf(pbuf,
+ "\n C_is = % -12g . i_0 = % -12g . C_rc = % -12g",
+ edata->C_is, edata->i_0, edata->C_rc );
+ pbuf += sprintf(pbuf,
+ "\n omega = % -12g . OMEGADOT = % -12g . IDOT = % -12g",
+ edata->omega, edata->OMEGADOT, edata->IDOT );
+ pbuf += sprintf(pbuf,
+ "\n Axis = % -12g . n = % -12g . r1me2 = % -12g",
+ edata->Axis, edata->n, edata->r1me2 );
+ pbuf += sprintf(pbuf,
+ "\n OMEGA_n = % -12g . ODOT_n = % -12g",
+ edata->OMEGA_n, edata->ODOT_n );
+ break;
+ }
+ }
+}
+
+
+/* 0x59: */
+static void rpt_SVs_enabled (TSIPPKT *rpt)
+{
+ unsigned char
+ numsvs,
+ code_type,
+ status_code[32];
+ short
+ iprn;
+
+ /* unload rptbuf */
+ if (rpt_0x59 (rpt, &code_type, status_code))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ switch (code_type)
+ {
+ case 3: pbuf += sprintf(pbuf, "\nSVs Disabled:\n"); break;
+ case 6: pbuf += sprintf(pbuf, "\nSVs with Health Ignored:\n"); break;
+ default: return;
+ }
+ numsvs = 0;
+ for (iprn=0; iprn<32; iprn++)
+ {
+ if (status_code[iprn])
+ {
+ pbuf += sprintf(pbuf, " %02d", iprn+1);
+ numsvs++;
+ }
+ }
+ if (numsvs == 0) pbuf += sprintf(pbuf, "None");
+}
+
+
+/* 0x5A */
+static void rpt_raw_msmt (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn;
+ float
+ sample_length, signal_level, code_phase, Doppler;
+ double
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level,
+ &code_phase, &Doppler, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d %5.0f %7.1f %10.2f %10.2f %12.3f %s",
+ sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix,
+ show_time ((float)time_of_fix));
+}
+
+/* 0x5B */
+static void rpt_SV_ephemeris_status (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn, sv_health, sv_iode, fit_interval_flag;
+ float
+ time_of_collection, time_of_eph, sv_accy;
+
+ /* unload rptbuf */
+ if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag,
+ &time_of_collection, &time_of_eph, &sv_accy))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n SV%02d %s %2Xh %2Xh ",
+ sv_prn, show_time (time_of_collection), sv_health, sv_iode);
+ /* note: cannot use show_time twice in same call */
+ pbuf += sprintf(pbuf, "%s %1d %4.1f",
+ show_time (time_of_eph), fit_interval_flag, sv_accy);
+}
+
+/* 0x5C */
+static void rpt_SV_tracking_status (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn, chan, slot, acq_flag, eph_flag,
+ old_msmt_flag, integer_msec_flag, bad_data_flag,
+ data_collect_flag;
+ float
+ signal_level, time_of_last_msmt,
+ elev, azim;
+
+ /* unload rptbuf */
+ if (rpt_0x5C (rpt,
+ &sv_prn, &slot, &chan, &acq_flag, &eph_flag,
+ &signal_level, &time_of_last_msmt, &elev, &azim,
+ &old_msmt_flag, &integer_msec_flag, &bad_data_flag,
+ &data_collect_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+"\n SV%2d %1d %1d %1d %4.1f %s %5.1f %5.1f",
+ sv_prn, chan,
+ acq_flag, eph_flag, signal_level,
+ show_time(time_of_last_msmt),
+ elev*R2D, azim*R2D);
+}
+
+/**/
+/* 0x6D */
+static void rpt_allSV_selection (TSIPPKT *rpt)
+{
+ unsigned char
+ manual_mode, nsvs, sv_prn[8], ndim;
+ short
+ islot;
+ float
+ pdop, hdop, vdop, tdop;
+
+ /* unload rptbuf */
+ if (rpt_0x6D (rpt,
+ &manual_mode, &nsvs, &ndim, sv_prn,
+ &pdop, &hdop, &vdop, &tdop))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (ndim)
+ {
+ case 0:
+ pbuf += sprintf(pbuf, "\nMode: Searching, %d-SV:", nsvs);
+ break;
+ case 1:
+ pbuf += sprintf(pbuf, "\nMode: One-SV Timing:");
+ break;
+ case 3: case 4:
+ pbuf += sprintf(pbuf, "\nMode: %c-%dD, %d-SV:",
+ manual_mode ? 'M' : 'A', ndim - 1, nsvs);
+ break;
+ case 5:
+ pbuf += sprintf(pbuf, "\nMode: Timing, %d-SV:", nsvs);
+ break;
+ default:
+ pbuf += sprintf(pbuf, "\nMode: Unknown = %d:", ndim);
+ break;
+ }
+
+ for (islot = 0; islot < nsvs; islot++)
+ {
+ if (sv_prn[islot]) pbuf += sprintf(pbuf, " %02d", sv_prn[islot]);
+ }
+ if (ndim == 3 || ndim == 4)
+ {
+ pbuf += sprintf(pbuf, "; DOPs: P %.1f H %.1f V %.1f T %.1f",
+ pdop, hdop, vdop, tdop);
+ }
+}
+
+/**/
+/* 0x82 */
+static void rpt_DGPS_position_mode (TSIPPKT *rpt)
+{
+ unsigned char
+ diff_mode;
+
+ /* unload rptbuf */
+ if (rpt_0x82 (rpt, &diff_mode)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nFix is%s DGPS-corrected (%s mode) (%d)",
+ (diff_mode&1) ? "" : " not",
+ (diff_mode&2) ? "auto" : "manual",
+ diff_mode);
+}
+
+/* 0x83 */
+static void rpt_double_ECEF_position (TSIPPKT *rpt)
+{
+
+ double
+ ECEF_pos[3], clock_bias;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nDXYZ:%12.2f %13.2f %13.2f %12.2f%s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x84 */
+static void rpt_double_lla_position (TSIPPKT *rpt)
+{
+ short
+ lat_deg, lon_deg;
+ double
+ lat, lon, lat_min, lon_min,
+ alt, clock_bias;
+ float
+ time_of_fix;
+ unsigned char
+ north_south, east_west;
+
+ /* unload rptbuf */
+ if (rpt_0x84 (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ lat *= R2D;
+ lon *= R2D;
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ } else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ } else {
+ east_west = 'E';
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nDLLA: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0xBB */
+static void rpt_complete_rcvr_config (TSIPPKT *rpt)
+{
+ TSIP_RCVR_CFG TsipxBB ;
+ /* unload rptbuf */
+ if (rpt_Paly0xBB (rpt, &TsipxBB))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n operating mode: %s",
+ NavModeText0xBB[TsipxBB.operating_mode]);
+ pbuf += sprintf(pbuf, "\n dynamics: %s",
+ dyn_text[TsipxBB.dyn_code]);
+ pbuf += sprintf(pbuf, "\n elev angle mask: %g deg",
+ TsipxBB.elev_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask: %g AMU",
+ TsipxBB.cno_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask: %g",
+ TsipxBB.dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch: %g",
+ TsipxBB.dop_switch);
+ return ;
+}
+
+/* 0xBC */
+static void rpt_rcvr_serial_port_config (TSIPPKT *rpt)
+{
+ unsigned char
+ port_num, in_baud, out_baud, data_bits, parity, stop_bits, flow_control,
+ protocols_in, protocols_out, reserved;
+ unsigned char known;
+
+ /* unload rptbuf */
+ if (rpt_0xBC (rpt, &port_num, &in_baud, &out_baud, &data_bits, &parity,
+ &stop_bits, &flow_control, &protocols_in, &protocols_out, &reserved)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\n RECEIVER serial port %s config:",
+ rcvr_port_text[port_num]);
+
+ pbuf += sprintf(pbuf, "\n I/O Baud %s/%s, %d - %s - %d",
+ st_baud_text_app[in_baud],
+ st_baud_text_app[out_baud],
+ data_bits+5,
+ parity_text[parity],
+ stop_bits=1);
+ pbuf += sprintf(pbuf, "\n Input protocols: ");
+ known = FALSE;
+ if (protocols_in&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_in_text[1]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+
+ pbuf += sprintf(pbuf, "\n Output protocols: ");
+ known = FALSE;
+ if (protocols_out&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[1]);
+ known = TRUE;
+ }
+ if (protocols_out&B_NMEA)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[2]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+ reserved = reserved;
+
+ }
+
+/* 0x8F */
+/* 8F0B */
+static void rpt_8F0B(TSIPPKT *rpt)
+{
+ const char
+ *oprtng_dim[7] = {
+ "horizontal (2-D)",
+ "full position (3-D)",
+ "single satellite (0-D)",
+ "automatic",
+ "N/A",
+ "N/A",
+ "overdetermined clock"};
+ char
+ sv_id[8];
+ unsigned char
+ month,
+ date,
+ dim_mode,
+ north_south,
+ east_west;
+ unsigned short
+ event;
+ short
+ utc_offset,
+ year,
+ local_index;
+ short
+ lat_deg,
+ lon_deg;
+ float
+ bias_unc,
+ dr_unc;
+ double
+ tow,
+ bias,
+ drift,
+ lat,
+ lon,
+ alt,
+ lat_min,
+ lon_min;
+ int
+ numfix,
+ numnotfix;
+
+ if (rpt_0x8F0B(rpt,
+ &event,
+ &tow,
+ &date,
+ &month,
+ &year,
+ &dim_mode,
+ &utc_offset,
+ &bias,
+ &drift,
+ &bias_unc,
+ &dr_unc,
+ &lat,
+ &lon,
+ &alt,
+ sv_id))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (event == 0)
+ {
+ pbuf += sprintf(pbuf, "\nNew partial+full meas");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nEvent count: %5d", event);
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time : %s %2d/%2d/%2d (DMY)",
+ show_time(tow), date, month, year);
+ pbuf += sprintf(pbuf, "\nMode : %s", oprtng_dim[dim_mode]);
+ pbuf += sprintf(pbuf, "\nUTC offset: %2d", utc_offset);
+ pbuf += sprintf(pbuf, "\nClock Bias: %6.2f m", bias);
+ pbuf += sprintf(pbuf, "\nFreq bias : %6.2f m/s", drift);
+ pbuf += sprintf(pbuf, "\nBias unc : %6.2f m", bias_unc);
+ pbuf += sprintf(pbuf, "\nFreq unc : %6.2f m/s", dr_unc);
+
+ lat *= R2D; /* convert from radians to degrees */
+ lon *= R2D;
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else
+ {
+ north_south = 'N';
+ }
+
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else
+ {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nPosition :");
+ pbuf += sprintf(pbuf, " %4d %6.3f %c", lat_deg, lat_min, north_south);
+ pbuf += sprintf(pbuf, " %5d %6.3f %c", lon_deg, lon_min, east_west);
+ pbuf += sprintf(pbuf, " %10.2f", alt);
+
+ numfix = numnotfix = 0;
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0) numnotfix++;
+ if (sv_id[local_index] > 0) numfix++;
+ }
+ if (numfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nSVs used in fix : ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] > 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+ if (numnotfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nOther SVs tracked: ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+}
+
+/* 0x8F14 */
+static void rpt_8F14 (TSIPPKT *rpt)
+/* Datum parameters */
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F15 */
+static void rpt_8F15 (TSIPPKT *rpt)
+/* Datum parameters */
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F15 (rpt, &datum_idx, datum_coeffs)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F20 */
+#define INFO_DGPS 0x02
+#define INFO_2D 0x04
+#define INFO_ALTSET 0x08
+#define INFO_FILTERED 0x10
+static void rpt_8F20 (TSIPPKT *rpt)
+{
+ unsigned char
+ info, nsvs, sv_prn[32];
+ short
+ week_num, datum_index, sv_IODC[32];
+ double
+ lat, lon, alt, time_of_fix;
+ double
+ londeg, latdeg, vel[3];
+ short
+ isv;
+ char
+ datum_string[20];
+
+ /* unload rptbuf */
+ if (rpt_0x8F20 (rpt,
+ &info, &lat, &lon, &alt, vel,
+ &time_of_fix,
+ &week_num, &nsvs, sv_prn, sv_IODC, &datum_index))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf,
+ "\nFix at: %04d:%3s:%02d:%02d:%06.3f GPS (=UTC+%2ds) FixType: %s%s%s",
+ week_num,
+ dayname[(short)(time_of_fix/86400.0)],
+ (short)fmod(time_of_fix/3600., 24.),
+ (short)fmod(time_of_fix/60., 60.),
+ fmod(time_of_fix, 60.),
+ (char)rpt->buf[29], /* UTC offset */
+ (info & INFO_DGPS)?"Diff":"",
+ (info & INFO_2D)?"2D":"3D",
+ (info & INFO_FILTERED)?"-Filtrd":"");
+
+ if (datum_index > 0)
+ {
+ sprintf(datum_string, "Datum%3d", datum_index);
+ }
+ else if (datum_index)
+ {
+ sprintf(datum_string, "Unknown ");
+ }
+ else
+ {
+ sprintf(datum_string, "WGS-84");
+ }
+
+ /* convert from radians to degrees */
+ latdeg = R2D * fabs(lat);
+ londeg = R2D * fabs(lon);
+ pbuf += sprintf(pbuf,
+ "\n Pos: %4d:%09.6f %c %5d:%09.6f %c %10.2f m HAE (%s)",
+ (short)latdeg, fmod (latdeg, 1.)*60.0,
+ (lat<0.0)?'S':'N',
+ (short)londeg, fmod (londeg, 1.)*60.0,
+ (lon<0.0)?'W':'E',
+ alt,
+ datum_string);
+ pbuf += sprintf(pbuf,
+ "\n Vel: %9.3f E %9.3f N %9.3f U (m/sec)",
+ vel[0], vel[1], vel[2]);
+
+ pbuf += sprintf(pbuf,
+ "\n SVs: ");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02d", sv_prn[isv]);
+ }
+ pbuf += sprintf(pbuf, " (IODEs:");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02X", sv_IODC[isv]&0xFF);
+ }
+ pbuf += sprintf(pbuf, ")");
+}
+
+/* 0x8F41 */
+static void rpt_8F41(TSIPPKT *rpt)
+{
+ unsigned char
+ bSearchRange,
+ bBoardOptions,
+ bBuildYear,
+ bBuildMonth,
+ bBuildDay,
+ bBuildHour;
+ float
+ fOscOffset;
+ unsigned short
+ iTestCodeId;
+ unsigned long
+ iiSerialNumber;
+
+ if (!rpt_0x8F41(rpt,
+ &bSearchRange,
+ &bBoardOptions,
+ &iiSerialNumber,
+ &bBuildYear,
+ &bBuildMonth,
+ &bBuildDay,
+ &bBuildHour,
+ &fOscOffset,
+ &iTestCodeId))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n search range: %d",
+ bSearchRange);
+ pbuf += sprintf(pbuf, "\n board options: %d",
+ bBoardOptions);
+ pbuf += sprintf(pbuf, "\n board serial #: %ld",
+ iiSerialNumber);
+ pbuf += sprintf(pbuf, "\n build date/hour: %02d/%02d/%02d %02d:00",
+ bBuildDay, bBuildMonth, bBuildYear, bBuildHour);
+ pbuf += sprintf(pbuf, "\n osc offset: %.3f PPM (%.0f Hz)",
+ fOscOffset/1575.42, fOscOffset);
+ pbuf += sprintf(pbuf, "\n test code: %d",
+ iTestCodeId);
+}
+
+/* 0x8F42 */
+static void rpt_8F42(TSIPPKT *rpt)
+{
+ unsigned char
+ bProdOptionsPre,
+ bProdNumberExt;
+ unsigned short
+ iCaseSerialNumberPre,
+ iPremiumOptions,
+ iMachineID,
+ iKey;
+ unsigned long
+ iiCaseSerialNumber,
+ iiProdNumber;
+
+ if (!rpt_0x8F42(rpt,
+ &bProdOptionsPre,
+ &bProdNumberExt,
+ &iCaseSerialNumberPre,
+ &iiCaseSerialNumber,
+ &iiProdNumber,
+ &iPremiumOptions,
+ &iMachineID,
+ &iKey))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nProduct ID 8F42");
+ pbuf += sprintf(pbuf, "\n extension: %d", bProdNumberExt);
+ pbuf += sprintf(pbuf, "\n case serial # prefix: %d", iCaseSerialNumberPre);
+ pbuf += sprintf(pbuf, "\n case serial #: %ld", iiCaseSerialNumber);
+ pbuf += sprintf(pbuf, "\n prod. #: %ld", iiProdNumber);
+ pbuf += sprintf(pbuf, "\n premium options: %Xh", iPremiumOptions);
+ pbuf += sprintf(pbuf, "\n machine ID: %d", iMachineID);
+ pbuf += sprintf(pbuf, "\n key: %Xh", iKey);
+}
+
+/* 0x8F45 */
+static void rpt_8F45(TSIPPKT *rpt)
+{
+ unsigned char bSegMask;
+
+ if (!rpt_0x8F45(rpt,
+ &bSegMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf, "\nCleared Segment Mask: %Xh", bSegMask);
+}
+
+static void rpt_8F4A(TSIPPKT *rpt)
+/* Stinger PPS def */
+{
+ unsigned char
+ pps_enabled,
+ pps_timebase,
+ pps_polarity;
+ float
+ bias_unc_threshold;
+ double
+ pps_offset;
+
+ if (rpt_0x8F4A_16 (rpt,
+ &pps_enabled,
+ &pps_timebase,
+ &pps_polarity,
+ &pps_offset,
+ &bias_unc_threshold))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nPPS is %s", pps_enabled?"enabled":"disabled");
+ pbuf += sprintf(pbuf, "\n timebase: %s", PPSTimeBaseText[pps_timebase]);
+ pbuf += sprintf(pbuf, "\n polarity: %s", PPSPolarityText[pps_polarity]);
+ pbuf += sprintf(pbuf, "\n offset: %.1f ns, ", pps_offset*1.e9);
+ pbuf += sprintf(pbuf, "\n biasunc: %.1f ns", bias_unc_threshold/GPS_C*1.e9);
+}
+
+static void rpt_8F4B(TSIPPKT *rpt)
+/* fast-SA decorrolation time for self-survey */
+{
+ unsigned long
+ decorr_max;
+
+ if (rpt_0x8F4B(rpt, &decorr_max))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+ "\nMax # of position fixes for self-survey : %ld",
+ decorr_max);
+}
+
+static void rpt_8F4D(TSIPPKT *rpt)
+{
+ static char
+ *linestart;
+ unsigned long
+ OutputMask;
+ static unsigned long
+ MaskBit[] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
+ 0x00000100L, 0x00000800L, 0x00001000L,
+ 0x40000000L, 0x80000000L};
+ int
+ ichoice,
+ numchoices;
+
+ if (rpt_0x8F4D(rpt, &OutputMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Report Mask: %02X %02X %02X %02X",
+ (unsigned char)(OutputMask>>24),
+ (unsigned char)(OutputMask>>16),
+ (unsigned char)(OutputMask>>8),
+ (unsigned char)OutputMask);
+
+ numchoices = sizeof(MaskText)/sizeof(char*);
+ pbuf += sprintf(pbuf, "\nAuto-Reports scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice=0; ichoice<numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice])
+ {
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Reports NOT scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice=0; ichoice<numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice]) continue;
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+}
+
+static void rpt_8FA5(TSIPPKT *rpt)
+{
+ unsigned char
+ spktmask[4];
+
+ if (rpt_0x8FA5(rpt, spktmask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSuperpacket auto-output mask: %02X %02X %02X %02X",
+ spktmask[0], spktmask[1], spktmask[2], spktmask[3]);
+
+ if (spktmask[0]&0x01) pbuf+= sprintf (pbuf, "\n PPS 8F-0B");
+ if (spktmask[0]&0x02) pbuf+= sprintf (pbuf, "\n Event 8F-0B");
+ if (spktmask[0]&0x10) pbuf+= sprintf (pbuf, "\n PPS 8F-AD");
+ if (spktmask[0]&0x20) pbuf+= sprintf (pbuf, "\n Event 8F-AD");
+ if (spktmask[2]&0x01) pbuf+= sprintf (pbuf, "\n ppos Fix 8F-20");
+}
+
+static void rpt_8FAD (TSIPPKT *rpt)
+{
+ unsigned short
+ Count,
+ Year;
+ double
+ FracSec;
+ unsigned char
+ Hour,
+ Minute,
+ Second,
+ Day,
+ Month,
+ Status,
+ Flags;
+ static char* Status8FADText[] = {
+ "CODE_DOING_FIXES",
+ "CODE_GOOD_1_SV",
+ "CODE_APPX_1SV",
+ "CODE_NEED_TIME",
+ "CODE_NEED_INITIALIZATION",
+ "CODE_PDOP_HIGH",
+ "CODE_BAD_1SV",
+ "CODE_0SVS",
+ "CODE_1SV",
+ "CODE_2SVS",
+ "CODE_3SVS",
+ "CODE_NO_INTEGRITY",
+ "CODE_DCORR_GEN",
+ "CODE_OVERDET_CLK",
+ "Invalid Status"},
+ *LeapStatusText[] = {
+ " UTC Avail", " ", " ", " ",
+ " Scheduled", " Pending", " Warning", " In Progress"};
+ int i;
+
+ if (rpt_0x8FAD (rpt,
+ &Count,
+ &FracSec,
+ &Hour,
+ &Minute,
+ &Second,
+ &Day,
+ &Month,
+ &Year,
+ &Status,
+ &Flags))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n8FAD Count: %d Status: %s",
+ Count, Status8FADText[Status]);
+
+ pbuf += sprintf(pbuf, "\n Leap Flags:");
+ if (Flags)
+ {
+ for (i=0; i<8; i++)
+ {
+ if (Flags&(1<<i)) pbuf += sprintf(pbuf, LeapStatusText[i]);
+ }
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, " UTC info not available");
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d/%02d/%04d (DMY) %02d:%02d:%02d.%09ld UTC",
+ Day, Month, Year, Hour, Minute, Second, (long)(FracSec*1.e9));
+}
+
+
+int print_msg_table_header (int rptcode, char *HdrStr, int force)
+{
+ /* force header is to help auto-output function */
+ /* last_rptcode is to determine whether to print a header */
+ /* for the first occurrence of a series of reports */
+ static int
+ last_rptcode = 0;
+ int
+ numchars;
+
+ numchars = 0;
+ if (force || rptcode!=last_rptcode)
+ {
+ /* supply a header in console output */
+ switch (rptcode)
+ {
+ case 0x5A:
+ numchars = sprintf(HdrStr, "\nRaw Measurement Data");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Sample SNR Code Phase Doppler Seconds Time of Meas");
+ break;
+
+ case 0x5B:
+ numchars = sprintf(HdrStr, "\nEphemeris Status");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Time collected Health IODE t oe Fit URA");
+ break;
+
+ case 0x5C:
+ numchars = sprintf(HdrStr, "\nTracking Info");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV C Acq Eph SNR Time of Meas Elev Azim ");
+ break;
+
+ }
+ }
+ last_rptcode = rptcode;
+ return (short)numchars;
+}
+
+static void unknown_rpt (TSIPPKT *rpt)
+{
+ int i;
+
+ /* app-specific rpt packets */
+ if (parsed == BADLEN_PARSE)
+ {
+ pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: Bad length",
+ rpt->code, rpt->len);
+ }
+ if (parsed == BADID_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: translation not supported",
+ rpt->code, rpt->len);
+ }
+
+ if (parsed == BADDATA_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: data content incorrect",
+ rpt->code, rpt->len);
+ }
+
+ for (i = 0; i < rpt->len; i++) {
+ if ((i % 20) == 0) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " %02X", rpt->buf[i]);
+ }
+}
+/**/
+/*
+** main subroutine, called from ProcessInputBytesWhileWaitingForKBHit()
+*/
+void TranslateTSIPReportToText (TSIPPKT *rpt, char *TextOutputBuffer)
+{
+
+ /* pbuf is the pointer to the current location of the text output */
+ pbuf = TextOutputBuffer;
+
+ /* keep track of whether the message has been successfully parsed */
+ parsed = GOOD_PARSE;
+
+ /* print a header if this is the first of a series of messages */
+ pbuf += print_msg_table_header (rpt->code, pbuf, FALSE);
+
+ /* process incoming TSIP report according to code */
+ switch (rpt->code)
+ {
+ case 0x3D: rpt_chan_A_config (rpt); break;
+ case 0x40: rpt_almanac_data_page (rpt); break;
+ case 0x41: rpt_GPS_time (rpt); break;
+ case 0x42: rpt_single_ECEF_position (rpt); break;
+ case 0x43: rpt_single_ECEF_velocity (rpt); break;
+ case 0x45: rpt_SW_version (rpt); break;
+ case 0x46: rpt_rcvr_health (rpt); break;
+ case 0x47: rpt_SNR_all_SVs (rpt); break;
+ case 0x48: rpt_GPS_system_message (rpt); break;
+ case 0x49: rpt_almanac_health_page (rpt); break;
+ case 0x4A: switch (rpt->len) {
+ /*
+ ** special case (=slip-up) in the TSIP protocol;
+ ** parsing method depends on length
+ */
+ case 20: rpt_single_lla_position (rpt); break;
+ case 9: rpt_ref_alt (rpt); break;
+ } break;
+ case 0x4B: rpt_rcvr_id_and_status (rpt);break;
+ case 0x4C: rpt_operating_parameters (rpt); break;
+ case 0x4D: rpt_oscillator_offset (rpt); break;
+ case 0x4E: rpt_GPS_time_set_response (rpt); break;
+ case 0x4F: rpt_UTC_offset (rpt); break;
+ case 0x54: rpt_1SV_bias (rpt); break;
+ case 0x55: rpt_io_opt (rpt); break;
+ case 0x56: rpt_ENU_velocity (rpt); break;
+ case 0x57: rpt_last_fix_info (rpt); break;
+ case 0x58: rpt_GPS_system_data (rpt); break;
+ case 0x59: rpt_SVs_enabled (rpt); break;
+ case 0x5A: rpt_raw_msmt (rpt); break;
+ case 0x5B: rpt_SV_ephemeris_status (rpt); break;
+ case 0x5C: rpt_SV_tracking_status (rpt); break;
+ case 0x6D: rpt_allSV_selection (rpt); break;
+ case 0x82: rpt_DGPS_position_mode (rpt); break;
+ case 0x83: rpt_double_ECEF_position (rpt); break;
+ case 0x84: rpt_double_lla_position (rpt); break;
+ case 0xBB: rpt_complete_rcvr_config (rpt); break;
+ case 0xBC: rpt_rcvr_serial_port_config (rpt); break;
+
+ case 0x8F: switch (rpt->buf[0])
+ {
+ /* superpackets; parsed according to subcodes */
+ case 0x0B: rpt_8F0B(rpt); break;
+ case 0x14: rpt_8F14(rpt); break;
+ case 0x15: rpt_8F15(rpt); break;
+ case 0x20: rpt_8F20(rpt); break;
+ case 0x41: rpt_8F41(rpt); break;
+ case 0x42: rpt_8F42(rpt); break;
+ case 0x45: rpt_8F45(rpt); break;
+ case 0x4A: rpt_8F4A(rpt); break;
+ case 0x4B: rpt_8F4B(rpt); break;
+ case 0x4D: rpt_8F4D(rpt); break;
+ case 0xA5: rpt_8FA5(rpt); break;
+ case 0xAD: rpt_8FAD(rpt); break;
+ default: parsed = BADID_PARSE; break;
+ }
+ break;
+
+ default: parsed = BADID_PARSE; break;
+ }
+
+ if (parsed != GOOD_PARSE)
+ {
+ /*
+ **The message has TSIP structure (DLEs, etc.)
+ ** but could not be parsed by above routines
+ */
+ unknown_rpt (rpt);
+ }
+
+ /* close TextOutputBuffer */
+ pbuf = '\0';
+}
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+#else /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+int refclock_ripencc_bs;
+#endif /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+
diff --git a/contrib/ntp/ntpd/refclock_shm.c b/contrib/ntp/ntpd/refclock_shm.c
new file mode 100644
index 0000000..7be263d
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_shm.c
@@ -0,0 +1,312 @@
+/*
+ * refclock_shm - clock driver for utc via shared memory
+ * - under construction -
+ * To add new modes: Extend or union the shmTime-struct. Do not
+ * extend/shrink size, because otherwise existing implementations
+ * will specify wrong size of shared memory-segment
+ * PB 18.3.97
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_SHM)
+
+#include "ntpd.h"
+#undef fileno
+#include "ntp_io.h"
+#undef fileno
+#include "ntp_refclock.h"
+#undef fileno
+#include "ntp_unixtime.h"
+#undef fileno
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/shm.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+#endif
+
+/*
+ * This driver supports a reference clock attached thru shared memory
+ */
+
+/*
+ * SHM interface definitions
+ */
+#define PRECISION (-1) /* precision assumed (0.5 s) */
+#define REFID "SHM" /* reference ID */
+#define DESCRIPTION "SHM/Shared memory interface"
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Function prototypes
+ */
+static int shm_start (int, struct peer *);
+static void shm_shutdown (int, struct peer *);
+static void shm_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_shm = {
+ shm_start, /* start up driver */
+ shm_shutdown, /* shut down driver */
+ shm_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+struct shmTime {
+ int mode; /* 0 - if valid set
+ * use values,
+ * clear valid
+ * 1 - if valid set
+ * if count before and after read of values is equal,
+ * use values
+ * clear valid
+ */
+ int count;
+ time_t clockTimeStampSec;
+ int clockTimeStampUSec;
+ time_t receiveTimeStampSec;
+ int receiveTimeStampUSec;
+ int leap;
+ int precision;
+ int nsamples;
+ int valid;
+ int dummy[10];
+};
+
+struct shmTime *getShmTime(int);
+
+struct shmTime *getShmTime (int unit) {
+#ifndef SYS_WINNT
+ int shmid=0;
+
+ assert (unit<10); /* MAXUNIT is 4, so should never happen */
+ shmid=shmget (0x4e545030+unit, sizeof (struct shmTime),
+ IPC_CREAT|(unit<2?0700:0777));
+ if (shmid==-1) { /*error */
+ msyslog(LOG_ERR,"SHM shmget (unit %d): %s",unit,strerror(errno));
+ return 0;
+ }
+ else { /* no error */
+ struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
+ if ((int)(long)p==-1) { /* error */
+ msyslog(LOG_ERR,"SHM shmat (unit %d): %s",unit,strerror(errno));
+ return 0;
+ }
+ return p;
+ }
+#else
+ char buf[10];
+ LPSECURITY_ATTRIBUTES psec=0;
+ HANDLE shmid=0;
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES sa;
+ sprintf (buf,"NTP%d",unit);
+ if (unit>=2) { /* world access */
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m",unit);
+ return 0;
+ }
+ if (!SetSecurityDescriptorDacl(&sd,1,0,0)) {
+ msyslog(LOG_ERR,"SHM SetSecurityDescriptorDacl (unit %d): %m",unit);
+ return 0;
+ }
+ sa.nLength=sizeof (SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor=&sd;
+ sa.bInheritHandle=0;
+ psec=&sa;
+ }
+ shmid=CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
+ 0, sizeof (struct shmTime),buf);
+ if (!shmid) { /*error*/
+ char buf[1000];
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR,"SHM CreateFileMapping (unit %d): %s",unit,buf);
+ return 0;
+ }
+ else {
+ struct shmTime *p=(struct shmTime *) MapViewOfFile (shmid,
+ FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
+ if (p==0) { /*error*/
+ char buf[1000];
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s",unit,buf);
+ return 0;
+ }
+ return p;
+ }
+#endif
+}
+/*
+ * shm_start - attach to shared memory
+ */
+static int
+shm_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+ pp->unitptr = (caddr_t)getShmTime(unit);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ memcpy((char *)&pp->refid, REFID, 4);
+ if (pp->unitptr!=0) {
+ ((struct shmTime*)pp->unitptr)->precision=PRECISION;
+ peer->precision = ((struct shmTime*)pp->unitptr)->precision;
+ ((struct shmTime*)pp->unitptr)->valid=0;
+ ((struct shmTime*)pp->unitptr)->nsamples=NSAMPLES;
+ pp->clockdesc = DESCRIPTION;
+ return (1);
+ }
+ else {
+ return 0;
+ }
+}
+
+
+/*
+ * shm_shutdown - shut down the clock
+ */
+static void
+shm_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct shmTime *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct shmTime *)pp->unitptr;
+#ifndef SYS_WINNT
+ /* HMS: shmdt()wants char* or const void * */
+ (void) shmdt (up);
+#else
+ UnmapViewOfFile (up);
+#endif
+}
+
+
+/*
+ * shm_poll - called by the transmit procedure
+ */
+static void
+shm_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct shmTime *up;
+ struct refclockproc *pp;
+
+ /*
+ * This is the main routine. It snatches the time from the shm
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct shmTime*)pp->unitptr;
+ if (up==0) { /* try to map again - this may succeed if meanwhile some-
+ body has ipcrm'ed the old (unaccessible) shared mem
+ segment */
+ pp->unitptr = (caddr_t)getShmTime(unit);
+ up = (struct shmTime*)pp->unitptr;
+ }
+ if (up==0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ if (up->valid) {
+ struct timeval tvr;
+ struct timeval tvt;
+ struct tm *t;
+ int ok=1;
+ tvr.tv_sec = 0;
+ tvr.tv_usec = 0;
+ tvt.tv_sec = 0;
+ tvt.tv_usec = 0;
+ switch (up->mode) {
+ case 0: {
+ tvr.tv_sec=up->receiveTimeStampSec;
+ tvr.tv_usec=up->receiveTimeStampUSec;
+ tvt.tv_sec=up->clockTimeStampSec;
+ tvt.tv_usec=up->clockTimeStampUSec;
+ }
+ break;
+ case 1: {
+ int cnt=up->count;
+ tvr.tv_sec=up->receiveTimeStampSec;
+ tvr.tv_usec=up->receiveTimeStampUSec;
+ tvt.tv_sec=up->clockTimeStampSec;
+ tvt.tv_usec=up->clockTimeStampUSec;
+ ok=(cnt==up->count);
+ }
+ break;
+ default:
+ msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",up->mode);
+ }
+ up->valid=0;
+ if (ok) {
+ time_t help; /* XXX NetBSD has incompatible tv_sec */
+
+ TVTOTS(&tvr,&pp->lastrec);
+ pp->lastrec.l_ui += JAN_1970;
+ /* pp->lasttime = current_time; */
+ pp->polls++;
+ help = tvt.tv_sec;
+ t = gmtime (&help);
+ pp->day=t->tm_yday+1;
+ pp->hour=t->tm_hour;
+ pp->minute=t->tm_min;
+ pp->second=t->tm_sec;
+ pp->nsec=tvt.tv_usec * 1000;
+ peer->precision=up->precision;
+ pp->leap=up->leap;
+ }
+ else {
+ refclock_report(peer, CEVNT_FAULT);
+ msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
+ return;
+ }
+ }
+ else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ /*
+ msyslog (LOG_NOTICE, "SHM: no new value found in shared memory");
+ */
+ return;
+ }
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+
+#else
+int refclock_shm_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_tpro.c b/contrib/ntp/ntpd/refclock_tpro.c
new file mode 100644
index 0000000..3c42568
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_tpro.c
@@ -0,0 +1,216 @@
+/*
+ * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TPRO)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
+ * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
+ * tpro.o device driver be installed and loaded.
+ */
+
+/*
+ * TPRO interface definitions
+ */
+#define DEVICE "/dev/tpro%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */
+
+/*
+ * Unit control structure
+ */
+struct tprounit {
+ struct tproval tprodata; /* data returned from tpro read */
+};
+
+/*
+ * Function prototypes
+ */
+static int tpro_start P((int, struct peer *));
+static void tpro_shutdown P((int, struct peer *));
+static void tpro_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tpro = {
+ tpro_start, /* start up driver */
+ tpro_shutdown, /* shut down driver */
+ tpro_poll, /* transmit poll message */
+ noentry, /* not used (old tpro_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tpro_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * tpro_start - open the TPRO device and initialize data for processing
+ */
+static int
+tpro_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+
+ /*
+ * Open TPRO device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tpro_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tprounit *) emalloc(sizeof(struct tprounit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tprounit));
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tpro_shutdown - shut down the clock
+ */
+static void
+tpro_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tpro_poll - called by the transmit procedure
+ */
+static void
+tpro_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ struct tproval *tp;
+
+ /*
+ * This is the main routine. It snatches the time from the TPRO
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+
+ tp = &up->tprodata;
+ if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ *
+ * Note that the refclockproc usec member has now become nsec.
+ * We could either multiply the read-in usec value by 1000 or
+ * we could pad the written string appropriately and read the
+ * resulting value in already scaled.
+ */
+ sprintf(pp->a_lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
+ tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
+ tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tpro: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->nsec *= 1000; /* Convert usec to nsec */
+ if (!tp->status & 0x3)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ pp->lastref = pp->lastrec;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+#else
+int refclock_tpro_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_trak.c b/contrib/ntp/ntpd/refclock_trak.c
new file mode 100644
index 0000000..3a4a54e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_trak.c
@@ -0,0 +1,359 @@
+/*
+ * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
+ *
+ * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
+ * original version Dec, 1993
+ * revised version Sep, 1994 for ntp3.4e or later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+/*
+ * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
+ * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * For best accuracy, this radio requires the LDISC_ACTS line
+ * discipline, which captures a timestamp at the '*' on-time character
+ * of the timecode. Using this discipline the jitter is in the order of
+ * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
+ * timestamp is used, which is captured at the \r ending the timecode
+ * message. This introduces a systematic error of 23 character times, or
+ * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
+ * IPC-class machines.
+ *
+ * Using the memus, the radio should be set for 9600 bps, one stop bit
+ * and no parity. It should be set to operate in computer (no echo)
+ * mode. The timecode format includes neither the year nor leap-second
+ * warning. No provisions are included in this preliminary version of
+ * the driver to read and record detailed internal radio status.
+ *
+ * In operation, this driver sends a RQTS\r request to the radio at
+ * initialization in order to put it in continuous time output mode. The
+ * radio then sends the following message once each second:
+ *
+ * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+ *
+ * on-time = '*' * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * q = quality indicator (phase error), 0-6:
+ * 0 > 20 us
+ * 6 > 10 us
+ * 5 > 1 us
+ * 4 > 100 ns
+ * 3 > 10 ns
+ * 2 < 10 ns
+ *
+ * The alarm condition is indicated by '0' at Q, which means the radio
+ * has a phase error than 20 usec relative to the broadcast time. The
+ * absence of year, DST and leap-second warning in this format is also
+ * alarming.
+ *
+ * The continuous time mode is disabled using the RQTX<cr> request,
+ * following which the radio sends a RQTX DONE<cr><lf> response. In the
+ * normal mode, other control and status requests are effective,
+ * including the leap-second status request RQLS<cr>. The radio responds
+ * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
+ * day. Presumably, this gives the epoch of the next leap second,
+ * RQLS 00,00,00 if none is specified in the GPS message. Specified in
+ * this form, the information is generally useless and is ignored by
+ * the driver.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/trak%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
+
+#define LENTRAK 24 /* timecode length */
+#define C_CTO "RQTS\r" /* start continuous time output */
+
+/*
+ * Unit control structure
+ */
+struct trakunit {
+ int polled; /* poll message flag */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int trak_start P((int, struct peer *));
+static void trak_shutdown P((int, struct peer *));
+static void trak_receive P((struct recvbuf *));
+static void trak_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_trak = {
+ trak_start, /* start up driver */
+ trak_shutdown, /* shut down driver */
+ trak_poll, /* transmit poll message */
+ noentry, /* not used (old trak_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old trak_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * trak_start - open the devices and initialize data for processing
+ */
+static int
+trak_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. The LDISC_ACTS line discipline inserts a
+ * timestamp following the "*" on-time character of the
+ * timecode.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (
+#ifdef PPS
+ !(fd = refclock_open(device, SPEED232, LDISC_CLK))
+#else
+ !(fd = refclock_open(device, SPEED232, 0))
+#endif /* PPS */
+ )
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct trakunit *)
+ emalloc(sizeof(struct trakunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct trakunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = trak_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->polled = 0;
+
+ /*
+ * Start continuous time output. If something breaks, fold the
+ * tent and go home.
+ */
+ if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
+ refclock_report(peer, CEVNT_FAULT);
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ return (1);
+}
+
+
+/*
+ * trak_shutdown - shut down the clock
+ */
+static void
+trak_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * trak_receive - receive data from the serial interface
+ */
+static void
+trak_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char *dpt, *dpend;
+ char qchar;
+#ifdef PPS
+ struct ppsclockev ppsev;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+#endif /* PPS */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. We
+ * then chuck out everything, including runts, except one
+ * message each poll interval.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * We get a buffer and timestamp following the '*' on-time
+ * character. If a valid timestamp, we use that in place of the
+ * buffer timestamp and edit out the timestamp for prettyprint
+ * billboards.
+ */
+ dpt = pp->a_lastcode;
+ dpend = dpt + pp->lencode;
+ if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
+ if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
+ pp->lastrec.l_i + 1) {
+ pp->lastrec = trtmp;
+ dpt += 9;
+ while (dpt < dpend) {
+ *(dpt - 8) = *dpt;
+ ++dpt;
+ }
+ }
+ }
+ if (up->polled == 0) return;
+ up->polled = 0;
+#ifndef PPS
+ get_systime(&up->tstamp);
+#endif
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("trak: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTRAK) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
+ */
+ if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality and leap characters. If unsynchronized, set
+ * the leap bits accordingly and exit.
+ */
+ if (qchar == '0') {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#ifdef PPS
+ if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
+ ppsev.tv.tv_sec += (u_int32) JAN_1970;
+ TVTOTS(&ppsev.tv,&up->tstamp);
+ }
+#endif /* PPS */
+ /* record the last ppsclock event time stamp */
+ pp->lastrec = up->tstamp;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+
+
+/*
+ * trak_poll - called by the transmit procedure
+ */
+static void
+trak_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ if (up->polled)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ pp->polls++;
+ up->polled = 1;
+}
+
+#else
+int refclock_trak_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_true.c b/contrib/ntp/ntpd/refclock_true.c
new file mode 100644
index 0000000..dd355d9
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_true.c
@@ -0,0 +1,873 @@
+/*
+ * refclock_true - clock driver for the Kinemetrics Truetime receivers
+ * Receiver Version 3.0C - tested plain, with CLKLDISC
+ * Developement work being done:
+ * - Properly handle varying satellite positions (more acurately)
+ * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* This should be an atom clock but those are very hard to build.
+ *
+ * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
+ * of TTL input and output pins, all brought out to the back panel. If you
+ * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
+ * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
+ * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
+ * number of uSecs since the last PPS upward swing, mediated by reading OUT0
+ * to find out if the counter has wrapped around (this happens if more than
+ * 65535us (65ms) elapses between the PPS event and our being called.)
+ */
+#ifdef CLOCK_PPS720
+# undef min /* XXX */
+# undef max /* XXX */
+# include <machine/inline.h>
+# include <sys/pcl720.h>
+# include <sys/i8253.h>
+# define PCL720_IOB 0x2a0 /* XXX */
+# define PCL720_CTR 0 /* XXX */
+#endif
+
+/*
+ * Support for Kinemetrics Truetime Receivers
+ * GOES
+ * GPS/TM-TMD
+ * XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD)
+ * GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module)
+ * OM-DC: getting stale ("OMEGA")
+ *
+ * Most of this code is originally from refclock_wwvb.c with thanks.
+ * It has been so mangled that wwvb is not a recognizable ancestor.
+ *
+ * Timcode format: ADDD:HH:MM:SSQCL
+ * A - control A (this is stripped before we see it)
+ * Q - Quality indication (see below)
+ * C - Carriage return
+ * L - Line feed
+ *
+ * Quality codes indicate possible error of
+ * 468-DC GOES Receiver:
+ * GPS-TM/TMD Receiver: (default quality codes for XL-DC)
+ * ? +/- 1 milliseconds # +/- 100 microseconds
+ * * +/- 10 microseconds . +/- 1 microsecond
+ * space less than 1 microsecond
+ * OM-DC OMEGA Receiver: (default quality codes for OMEGA)
+ * WARNING OMEGA navigation system is no longer existent
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ *
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ *
+ * Notes on 468-DC and OMEGA receiver:
+ *
+ * Send the clock a 'R' or 'C' and once per second a timestamp will
+ * appear. Send a 'P' to get the satellite position once (GOES only.)
+ *
+ * Notes on the 468-DC receiver:
+ *
+ * Since the old east/west satellite locations are only historical, you can't
+ * set your clock propagation delay settings correctly and still use
+ * automatic mode. The manual says to use a compromise when setting the
+ * switches. This results in significant errors. The solution; use fudge
+ * time1 and time2 to incorporate corrections. If your clock is set for
+ * 50 and it should be 58 for using the west and 46 for using the east,
+ * use the line
+ *
+ * fudge 127.127.5.0 time1 +0.008 time2 -0.004
+ *
+ * This corrects the 4 milliseconds advance and 8 milliseconds retard
+ * needed. The software will ask the clock which satellite it sees.
+ *
+ * Ntp.conf parameters:
+ * time1 - offset applied to samples when reading WEST satellite (default = 0)
+ * time2 - offset applied to samples when reading EAST satellite (default = 0)
+ * val1 - stratum to assign to this clock (default = 0)
+ * val2 - refid assigned to this clock (default = "TRUE", see below)
+ * flag1 - will silence the clock side of ntpd, just reading the clock
+ * without trying to write to it. (default = 0)
+ * flag2 - generate a debug file /tmp/true%d.
+ * flag3 - enable ppsclock streams module
+ * flag4 - use the PCL-720 (BSD/OS only)
+ */
+
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/true%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "TRUE" /* reference id */
+#define DESCRIPTION "Kinemetrics/TrueTime Receiver"
+
+/*
+ * Tags which station (satellite) we see
+ */
+#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
+#define GOES_EAST 1 /* until you discover otherwise */
+
+/*
+ * used by the state machine
+ */
+enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
+ e_Poll, e_Location, e_TS, e_Max};
+const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
+ "Poll", "Location", "TS"};
+#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
+
+enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
+ s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
+const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
+ "Init", "F18", "F50", "Start", "Auto"};
+#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
+
+enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_Max};
+const char *types[] = {"unknown", "goes", "tm", "tcu", "omega"};
+#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
+
+/*
+ * unit control structure
+ */
+struct true_unit {
+ unsigned int pollcnt; /* poll message counter */
+ unsigned int station; /* which station we are on */
+ unsigned int polled; /* Hand in a time sample? */
+ enum true_state state; /* state machine */
+ enum true_type type; /* what kind of clock is it? */
+ int unit; /* save an extra copy of this */
+ FILE *debug; /* debug logging file */
+#ifdef CLOCK_PPS720
+ int pcl720init; /* init flag for PCL 720 */
+#endif
+};
+
+/*
+ * Function prototypes
+ */
+static int true_start P((int, struct peer *));
+static void true_shutdown P((int, struct peer *));
+static void true_receive P((struct recvbuf *));
+static void true_poll P((int, struct peer *));
+static void true_send P((struct peer *, const char *));
+static void true_doevent P((struct peer *, enum true_event));
+
+#ifdef CLOCK_PPS720
+static u_long true_sample720 P((void));
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_true = {
+ true_start, /* start up driver */
+ true_shutdown, /* shut down driver */
+ true_poll, /* transmit poll message */
+ noentry, /* not used (old true_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old true_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+#if !defined(__STDC__)
+# define true_debug (void)
+#else
+static void
+true_debug(struct peer *peer, const char *fmt, ...)
+{
+ va_list ap;
+ int want_debugging, now_debugging;
+ struct refclockproc *pp;
+ struct true_unit *up;
+
+ va_start(ap, fmt);
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+
+ want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
+ now_debugging = (up->debug != NULL);
+ if (want_debugging != now_debugging)
+ {
+ if (want_debugging) {
+ char filename[40];
+ int fd;
+
+ snprintf(filename, sizeof(filename), "/tmp/true%d.debug", up->unit);
+ fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (fd >= 0 && (up->debug = fdopen(fd, "r+"))) {
+#ifdef HAVE_SETVBUF
+ static char buf[BUFSIZ];
+ setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(up->debug);
+#endif
+ }
+ } else {
+ fclose(up->debug);
+ up->debug = NULL;
+ }
+ }
+
+ if (up->debug) {
+ fprintf(up->debug, "true%d: ", up->unit);
+ vfprintf(up->debug, fmt, ap);
+ }
+}
+#endif /*STDC*/
+
+/*
+ * true_start - open the devices and initialize data for processing
+ */
+static int
+true_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+
+ /*
+ * Open serial port
+ */
+ (void)snprintf(device, sizeof(device), DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct true_unit *)
+ emalloc(sizeof(struct true_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct true_unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = true_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->type = t_unknown;
+ up->state = s_Base;
+ true_doevent(peer, e_Init);
+ return (1);
+}
+
+/*
+ * true_shutdown - shut down the clock
+ */
+static void
+true_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * true_receive - receive data from the serial interface on a clock
+ */
+static void
+true_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ u_short new_station;
+ char synced;
+ int i;
+ int lat, lon, off; /* GOES Satellite position */
+ /* Use these variable to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /*
+ * Get the clock this applies to and pointers to the data.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+ rd_lastcode[rd_lencode] = '\0';
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (rd_lencode == 0)
+ return;
+ pp->lencode = rd_lencode;
+ strcpy(pp->a_lastcode, rd_lastcode);
+ pp->lastrec = rd_tmp;
+ true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode);
+
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code decodes a multitude of different
+ * clock messages. Timecodes are processed if needed. All replies
+ * will be run through the state machine to tweak driver options
+ * and program the clock.
+ */
+
+ /*
+ * Clock misunderstood our last command?
+ */
+ if (pp->a_lastcode[0] == '?' ||
+ strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
+ true_doevent(peer, e_Huh);
+ return;
+ }
+
+ /*
+ * Timecode: "nnnnn+nnn-nnn"
+ * (from GOES clock when asked about satellite position)
+ */
+ if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
+ (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
+ sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
+ ) {
+ const char *label = "Botch!";
+
+ /*
+ * This is less than perfect. Call the (satellite)
+ * either EAST or WEST and adjust slop accodingly
+ * Perfectionists would recalculate the exact delay
+ * and adjust accordingly...
+ */
+ if (lon > 7000 && lon < 14000) {
+ if (lon < 10000) {
+ new_station = GOES_EAST;
+ label = "EAST";
+ } else {
+ new_station = GOES_WEST;
+ label = "WEST";
+ }
+
+ if (new_station != up->station) {
+ double dtemp;
+
+ dtemp = pp->fudgetime1;
+ pp->fudgetime1 = pp->fudgetime2;
+ pp->fudgetime2 = dtemp;
+ up->station = new_station;
+ }
+ }
+ else {
+ /*refclock_report(peer, CEVNT_BADREPLY);*/
+ label = "UNKNOWN";
+ }
+ true_debug(peer, "GOES: station %s\n", label);
+ true_doevent(peer, e_Satellite);
+ return;
+ }
+
+ /*
+ * Timecode: "Fnn"
+ * (from TM/TMD clock when it wants to tell us what it's up to.)
+ */
+ if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
+ switch (i) {
+ case 50:
+ true_doevent(peer, e_F50);
+ break;
+ case 51:
+ true_doevent(peer, e_F51);
+ break;
+ default:
+ true_debug(peer, "got F%02d - ignoring\n", i);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
+ * (from a TM/TMD/XL clock during initialization.)
+ */
+ if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
+ strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
+ true_doevent(peer, e_F18);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
+ }
+ return;
+ }
+
+ /*
+ * Timecode: "N03726428W12209421+000033"
+ * 1 2
+ * 0123456789012345678901234
+ * (from a TCU during initialization)
+ */
+ if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
+ (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
+ pp->a_lastcode[18] == '+') {
+ true_doevent(peer, e_Location);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
+ }
+ return;
+ }
+ /*
+ * Timecode: "ddd:hh:mm:ssQ"
+ * (from all clocks supported by this driver.)
+ */
+ if (pp->a_lastcode[3] == ':' &&
+ pp->a_lastcode[6] == ':' &&
+ pp->a_lastcode[9] == ':' &&
+ sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
+ &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &synced) == 5) {
+
+ /*
+ * Adjust the synchronize indicator according to timecode
+ * say were OK, and then say not if we really are not OK
+ */
+ if (synced == '>' || synced == '#' || synced == '?')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ true_doevent(peer, e_TS);
+
+#ifdef CLOCK_PPS720
+ /* If it's taken more than 65ms to get here, we'll lose. */
+ if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
+ l_fp off;
+
+#ifdef CLOCK_ATOM
+ /*
+ * find out what time it really is. Include
+ * the count from the PCL720
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, pp->lastrec.l_ui,
+ &pp->yearstart, &off.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ off.l_uf = 0;
+#endif
+
+ pp->usec = true_sample720();
+#ifdef CLOCK_ATOM
+ TVUTOTSF(pp->usec, off.l_uf);
+#endif
+
+ /*
+ * Stomp all over the timestamp that was pulled out
+ * of the input stream. It's irrelevant since we've
+ * adjusted the input time to reflect now (via pp->usec)
+ * rather than when the data was collected.
+ */
+ get_systime(&pp->lastrec);
+#ifdef CLOCK_ATOM
+ /*
+ * Create a true offset for feeding to pps_sample()
+ */
+ L_SUB(&off, &pp->lastrec);
+
+ pps_sample(peer, &off);
+#endif
+ true_debug(peer, "true_sample720: %luus\n", pp->usec);
+ }
+#endif
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ true_doevent(peer, e_Poll);
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ /*
+ * If clock is good we send a NOMINAL message so that
+ * any previous BAD messages are nullified
+ */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succedded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+
+ return;
+ }
+
+ /*
+ * No match to known timecodes, report failure and return
+ */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+}
+
+
+/*
+ * true_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+true_send(
+ struct peer *peer,
+ const char *cmd
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ register int len = strlen(cmd);
+
+ true_debug(peer, "Send '%s'\n", cmd);
+ if (write(pp->io.fd, cmd, (unsigned)len) != len)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+}
+
+
+/*
+ * state machine for initializing and controlling a clock
+ */
+static void
+true_doevent(
+ struct peer *peer,
+ enum true_event event
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ if (event != e_TS) {
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
+ typeStr(up->type),
+ stateStr(up->state),
+ eventStr(event));
+ }
+ }
+ true_debug(peer, "clock %s, state %s, event %s\n",
+ typeStr(up->type), stateStr(up->state), eventStr(event));
+ switch (up->type) {
+ case t_goes:
+ switch (event) {
+ case e_Init: /* FALLTHROUGH */
+ case e_Satellite:
+ /*
+ * Switch back to on-second time codes and return.
+ */
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_Poll:
+ /*
+ * After each poll, check the station (satellite).
+ */
+ true_send(peer, "P");
+ /* No state change needed. */
+ break;
+ default:
+ break;
+ }
+ /* FALLTHROUGH */
+ case t_omega:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tm:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "F18\r");
+ up->state = s_Init;
+ break;
+ case e_F18:
+ true_send(peer, "F50\r");
+ up->state = s_F18;
+ break;
+ case e_F50:
+ true_send(peer, "F51\r");
+ up->state = s_F50;
+ break;
+ case e_F51:
+ true_send(peer, "F08\r");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tcu:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */
+ true_send(peer, "TSU\r"); /* UTC, not GPS. */
+ true_send(peer, "AU\r"); /* Auto Timestamps. */
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_unknown:
+ switch (up->state) {
+ case s_Base:
+ if (event != e_Init)
+ abort();
+ true_send(peer, "P\r");
+ up->state = s_InqGOES;
+ break;
+ case s_InqGOES:
+ switch (event) {
+ case e_Satellite:
+ up->type = t_goes;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh: /*FALLTHROUGH*/
+ case e_TS:
+ up->state = s_InqOmega;
+ true_send(peer, "C\r");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqOmega:
+ switch (event) {
+ case e_TS:
+ up->type = t_omega;
+ up->state = s_Auto; /* Inq side-effect. */
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_InqTM;
+ true_send(peer, "F18\r");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTM:
+ switch (event) {
+ case e_F18:
+ up->type = t_tm;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ true_send(peer, "PO\r");
+ up->state = s_InqTCU;
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTCU:
+ switch (event) {
+ case e_Location:
+ up->type = t_tcu;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_Base;
+ sleep(1); /* XXX */
+ break;
+ default:
+ abort();
+ }
+ break;
+ /*
+ * An expedient hack to prevent lint complaints,
+ * these don't actually need to be used here...
+ */
+ case s_Init:
+ case s_F18:
+ case s_F50:
+ case s_Start:
+ case s_Auto:
+ case s_Max:
+ msyslog(LOG_INFO, "TRUE: state %s is unexpected!", stateStr(up->state));
+ }
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+#ifdef CLOCK_PPS720
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
+ /* Make counter trigger on gate0, count down from 65535. */
+ pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
+ /*
+ * (These constants are OK since
+ * they represent hardware maximums.)
+ */
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 initialized");
+ }
+ up->pcl720init++;
+ }
+#endif
+
+
+}
+
+/*
+ * true_poll - called by the transmit procedure
+ */
+static void
+true_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ if (up->pollcnt > 0)
+ up->pollcnt--;
+ else {
+ true_doevent(peer, e_Init);
+ refclock_report(peer, CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask true_receive to hand in a
+ * timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+#ifdef CLOCK_PPS720
+/*
+ * true_sample720 - sample the PCL-720
+ */
+static u_long
+true_sample720(void)
+{
+ unsigned long f;
+
+ /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
+ * If it is not being held low now, we did not get called
+ * within 65535us.
+ */
+ if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 out of synch");
+ }
+ return (0);
+ }
+ f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
+#ifdef PPS720_DEBUG
+ msyslog(LOG_DEBUG, "PCL-720: %luus", f);
+#endif
+ return (f);
+}
+#endif
+
+#else
+int refclock_true_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_tt560.c b/contrib/ntp/ntpd/refclock_tt560.c
new file mode 100644
index 0000000..f3d7bc1
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_tt560.c
@@ -0,0 +1,274 @@
+/*
+ * refclock_tt560 - clock driver for the TrueTime 560 IRIG-B decoder
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TT560)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tt560_api.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the TrueTime 560 IRIG-B decoder for the PCI bus.
+ */
+
+/*
+ * TT560 interface definitions
+ */
+#define DEVICE "/dev/tt560%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "TrueTime 560 IRIG-B PCI Decoder"
+
+/*
+ * Unit control structure
+ */
+struct tt560unit {
+ tt_mem_space_t *tt_mem; /* mapped address of PCI board */
+ time_freeze_reg_t tt560rawt; /* data returned from PCI board */
+};
+
+typedef union byteswap_u
+{
+ unsigned int long_word;
+ unsigned char byte[4];
+} byteswap_t;
+
+/*
+ * Function prototypes
+ */
+static int tt560_start P((int, struct peer *));
+static void tt560_shutdown P((int, struct peer *));
+static void tt560_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tt560 = {
+ tt560_start, /* clock_start */
+ tt560_shutdown, /* clock_shutdown */
+ tt560_poll, /* clock_poll */
+ noentry, /* clock_control (not used) */
+ noentry, /* clock_init (not used) */
+ noentry, /* clock_buginfo (not used) */
+ NOFLAGS /* clock_flags (not used) */
+};
+
+
+/*
+ * tt560_start - open the TT560 device and initialize data for processing
+ */
+static int
+tt560_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+ caddr_t membase;
+
+ /*
+ * Open TT560 device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDWR);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tt560_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Map the device registers into user space.
+ */
+ membase = mmap ((caddr_t) 0, TTIME_MEMORY_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, (off_t)0);
+
+ if (membase == (caddr_t) -1) {
+ msyslog(LOG_ERR, "tt560_start: mapping of %s: %m", device);
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tt560unit *) emalloc(sizeof(struct tt560unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tt560unit));
+ up->tt_mem = (tt_mem_space_t *)membase;
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tt560_shutdown - shut down the clock
+ */
+static void
+tt560_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tt560_poll - called by the transmit procedure
+ */
+static void
+tt560_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ time_freeze_reg_t *tp;
+ tt_mem_space_t *mp;
+
+ int i;
+ unsigned int *p_time_t, *tt_mem_t;
+
+ /*
+ * This is the main routine. It snatches the time from the TT560
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ mp = up->tt_mem;
+ tp = &up->tt560rawt;
+
+ p_time_t = (unsigned int *)tp;
+ tt_mem_t = (unsigned int *)&mp->time_freeze_reg;
+
+ *tt_mem_t = 0; /* update the time freeze register */
+ /* and copy time stamp to memory */
+ for (i=0; i < TIME_FREEZE_REG_LEN; i++) {
+ *p_time_t = byte_swap(*tt_mem_t);
+ p_time_t++;
+ tt_mem_t++;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ */
+ sprintf(pp->a_lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->hun_day, tp->tens_day, tp->unit_day,
+ tp->tens_hour, tp->unit_hour,
+ tp->tens_min, tp->unit_min,
+ tp->tens_sec, tp->unit_sec,
+ tp->hun_ms, tp->tens_ms, tp->unit_ms,
+ tp->hun_us, tp->tens_us, tp->unit_us,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tt560: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if ((tp->status & 0x6) != 0x6)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+/******************************************************************
+ *
+ * byte_swap
+ *
+ * Inputs: 32 bit integer
+ *
+ * Output: byte swapped 32 bit integer.
+ *
+ * This routine is used to compensate for the byte alignment
+ * differences between big-endian and little-endian integers.
+ *
+ ******************************************************************/
+static unsigned int
+byte_swap(unsigned int input_num)
+{
+ byteswap_t byte_swap;
+ unsigned char temp;
+
+ byte_swap.long_word = input_num;
+
+ temp = byte_swap.byte[3];
+ byte_swap.byte[3] = byte_swap.byte[0];
+ byte_swap.byte[0] = temp;
+
+ temp = byte_swap.byte[2];
+ byte_swap.byte[2] = byte_swap.byte[1];
+ byte_swap.byte[1] = temp;
+
+ return (byte_swap.long_word);
+}
+
+#else
+int refclock_tt560_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_ulink.c b/contrib/ntp/ntpd/refclock_ulink.c
new file mode 100644
index 0000000..d7a62fe
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_ulink.c
@@ -0,0 +1,589 @@
+/*
+ * refclock_ulink - clock driver for Ultralink WWVB receiver
+ */
+
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1992-1998 *
+ * *
+ * 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, and that the name *
+ * University of Delaware not be used in advertising or publicity *
+ * pertaining to distribution of the software without specific, *
+ * written prior permission. The University of Delaware makes no *
+ * representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied *
+ * warranty. *
+ **********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ULINK)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
+ *
+ * this driver was based on the refclock_wwvb.c driver
+ * in the ntp distribution.
+ *
+ * Fudge Factors
+ *
+ * fudge flag1 0 don't poll clock
+ * 1 send poll character
+ *
+ * revision history:
+ * 99/9/09 j.c.lang original edit's
+ * 99/9/11 j.c.lang changed timecode parse to
+ * match what the radio actually
+ * sends.
+ * 99/10/11 j.c.lang added support for continous
+ * time code mode (dipsw2)
+ * 99/11/26 j.c.lang added support for 320 decoder
+ * (taken from Dave Strout's
+ * Model 320 driver)
+ * 99/11/29 j.c.lang added fudge flag 1 to control
+ * clock polling
+ * 99/12/15 j.c.lang fixed 320 quality flag
+ * 01/02/21 s.l.smith fixed 33x quality flag
+ * added more debugging stuff
+ * updated 33x time code explanation
+ * 04/01/23 frank migge added support for 325 decoder
+ * (tested with ULM325.F)
+ *
+ * Questions, bugs, ideas send to:
+ * Joseph C. Lang
+ * tcnojl1@earthlink.net
+ *
+ * Dave Strout
+ * dstrout@linuxfoundry.com
+ *
+ * Frank Migge
+ * frank.migge@oracle.com
+ *
+ *
+ * on the Ultralink model 33X decoder Dip switch 2 controls
+ * polled or continous timecode
+ * set fudge flag1 if using polled (needed for model 320 and 325)
+ * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
+*/
+
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 10 ms) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
+
+#define LEN33X 32 /* timecode length Model 33X and 325 */
+#define LEN320 24 /* timecode length Model 320 */
+
+#define SIGLCHAR33x 'S' /* signal strength identifier char 325 */
+#define SIGLCHAR325 'R' /* signal strength identifier char 33x */
+
+/*
+ * unit control structure
+ */
+struct ulinkunit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+};
+
+/*
+ * Function prototypes
+ */
+static int ulink_start P((int, struct peer *));
+static void ulink_shutdown P((int, struct peer *));
+static void ulink_receive P((struct recvbuf *));
+static void ulink_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_ulink = {
+ ulink_start, /* start up driver */
+ ulink_shutdown, /* shut down driver */
+ ulink_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS
+};
+
+
+/*
+ * ulink_start - open the devices and initialize data for processing
+ */
+static int
+ulink_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct ulinkunit *)
+ emalloc(sizeof(struct ulinkunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct ulinkunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = ulink_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * ulink_shutdown - shut down the clock
+ */
+static void
+ulink_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ulinkunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * ulink_receive - receive data from the serial interface
+ */
+static void
+ulink_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct ulinkunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int quality; /* quality indicator */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char leapchar; /* leap indicator */
+ char modechar; /* model 320 mode flag */
+ char siglchar; /* model difference between 33x/325 */
+ char char_quality[2]; /* temp quality flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct ulinkunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+#ifdef DEBUG
+ if (debug)
+ printf("ulink: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ syncchar = leapchar = modechar = siglchar = ' ';
+ switch (pp->lencode ) {
+ case LEN33X:
+
+ /*
+ * First we check if the format is 33x or 325:
+ * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
+ * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
+ * simply by comparing if the signal level is 'S' or 'R'
+ */
+
+ if (sscanf(pp->a_lastcode, "%c%*31c",
+ &siglchar) == 1) {
+
+ if(siglchar == SIGLCHAR325) {
+
+ /*
+ * decode for a Model 325 decoder.
+ * Timecode format from January 23, 2004 datasheet is:
+ *
+ * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
+ *
+ * R WWVB decodersignal readability R1 - R5
+ * 5 R1 is unreadable, R5 is best
+ * space a space (0x20)
+ * 1 Data bit 0, 1, M (pos mark), or ? (unknown).
+ * C Reception from either (C)olorado or (H)awaii
+ * 00 Hours since last good WWVB frame sync. Will
+ * be 00-99
+ * space Space char (0x20) or (0xa5) if locked to wwvb
+ * YYYY Current year, 2000-2099
+ * + Leap year indicator. '+' if a leap year,
+ * a space (0x20) if not.
+ * DDD Day of year, 000 - 365.
+ * UTC Timezone (always 'UTC').
+ * S Daylight savings indicator
+ * S - standard time (STD) in effect
+ * O - during STD to DST day 0000-2400
+ * D - daylight savings time (DST) in effect
+ * I - during DST to STD day 0000-2400
+ * space Space character (0x20)
+ * HH Hours 00-23
+ * : This is the REAL in sync indicator (: = insync)
+ * MM Minutes 00-59
+ * : : = in sync ? = NOT in sync
+ * SS Seconds 00-59
+ * L Leap second flag. Changes from space (0x20)
+ * to 'I' or 'D' during month preceding leap
+ * second adjustment. (I)nsert or (D)elete
+ * +5 UT1 correction (sign + digit ))
+ */
+
+ if (sscanf(pp->a_lastcode,
+ "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
+ char_quality, &pp->year, &pp->day,
+ &pp->hour, &syncchar, &pp->minute, &pp->second,
+ &leapchar) == 8) {
+
+ if (char_quality[0] == '0') {
+ quality = 0;
+ } else if (char_quality[0] == '0') {
+ quality = (char_quality[1] & 0x0f);
+ } else {
+ quality = 99;
+ }
+
+ if (leapchar == 'I' ) leapchar = '+';
+ if (leapchar == 'D' ) leapchar = '-';
+
+ /*
+ #ifdef DEBUG
+ if (debug) {
+ printf("ulink: char_quality %c %c\n",
+ char_quality[0], char_quality[1]);
+ printf("ulink: quality %d\n", quality);
+ printf("ulink: syncchar %x\n", syncchar);
+ printf("ulink: leapchar %x\n", leapchar);
+ }
+ #endif
+ */
+
+ }
+
+ }
+ if(siglchar == SIGLCHAR33x) {
+
+ /*
+ * We got a Model 33X decoder.
+ * Timecode format from January 29, 2001 datasheet is:
+ * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
+ * S WWVB decoder sync indicator. S for in-sync(?)
+ * or N for noisy signal.
+ * 9+ RF signal level in S-units, 0-9 followed by
+ * a space (0x20). The space turns to '+' if the
+ * level is over 9.
+ * D Data bit 0, 1, 2 (position mark), or
+ * 3 (unknown).
+ * space Space character (0x20)
+ * 00 Hours since last good WWVB frame sync. Will
+ * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
+ * if currently in sync.
+ * space Space character (0x20)
+ * YYYY Current year, 1990-2089
+ * + Leap year indicator. '+' if a leap year,
+ * a space (0x20) if not.
+ * DDD Day of year, 001 - 366.
+ * UTC Timezone (always 'UTC').
+ * S Daylight savings indicator
+ * S - standard time (STD) in effect
+ * O - during STD to DST day 0000-2400
+ * D - daylight savings time (DST) in effect
+ * I - during DST to STD day 0000-2400
+ * space Space character (0x20)
+ * HH Hours 00-23
+ * : This is the REAL in sync indicator (: = insync)
+ * MM Minutes 00-59
+ * : : = in sync ? = NOT in sync
+ * SS Seconds 00-59
+ * L Leap second flag. Changes from space (0x20)
+ * to '+' or '-' during month preceding leap
+ * second adjustment.
+ * +5 UT1 correction (sign + digit ))
+ */
+
+ if (sscanf(pp->a_lastcode,
+ "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
+ char_quality, &pp->year, &pp->day,
+ &pp->hour, &syncchar, &pp->minute, &pp->second,
+ &leapchar) == 8) {
+
+ if (char_quality[0] == 'L') {
+ quality = 0;
+ } else if (char_quality[0] == '0') {
+ quality = (char_quality[1] & 0x0f);
+ } else {
+ quality = 99;
+ }
+
+ /*
+ #ifdef DEBUG
+ if (debug) {
+ printf("ulink: char_quality %c %c\n",
+ char_quality[0], char_quality[1]);
+ printf("ulink: quality %d\n", quality);
+ printf("ulink: syncchar %x\n", syncchar);
+ printf("ulink: leapchar %x\n", leapchar);
+ }
+ #endif
+ */
+
+ }
+ }
+ break;
+ }
+
+ case LEN320:
+
+ /*
+ * Model 320 Decoder
+ * The timecode format is:
+ *
+ * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
+ *
+ * where:
+ *
+ * S = 'S' -- sync'd in last hour,
+ * '0'-'9' - hours x 10 since last update,
+ * '?' -- not in sync
+ * Q = Number of correlating time-frames, from 0 to 5
+ * R = 'R' -- reception in progress,
+ * 'N' -- Noisy reception,
+ * ' ' -- standby mode
+ * YYYY = year from 1990 to 2089
+ * DDD = current day from 1 to 366
+ * + = '+' if current year is a leap year, else ' '
+ * HH = UTC hour 0 to 23
+ * MM = Minutes of current hour from 0 to 59
+ * SS = Seconds of current minute from 0 to 59
+ * mm = 10's milliseconds of the current second from 00 to 99
+ * L = Leap second pending at end of month
+ * 'I' = insert, 'D'= delete
+ * T = DST <-> STD transition indicators
+ *
+ */
+
+ if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
+ &syncchar, &quality, &modechar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second,
+ &pp->nsec, &leapchar) == 10) {
+ pp->nsec *= 10000000; /* M320 returns 10's of msecs */
+ if (leapchar == 'I' ) leapchar = '+';
+ if (leapchar == 'D' ) leapchar = '-';
+ if (syncchar != '?' ) syncchar = ':';
+
+ break;
+ }
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality indicator
+ * For the 325 & 33x series, the lower the number the "better"
+ * the time is. I used the dispersion as the measure of time
+ * quality. The quality indicator in the 320 is the number of
+ * correlating time frames (the more the better)
+ */
+
+ /*
+ * The spec sheet for the 325 & 33x series states the clock will
+ * maintain +/-0.002 seconds accuracy when locked to WWVB. This
+ * is indicated by 'Lk' in the quality portion of the incoming
+ * string. When not in lock, a drift of +/-0.015 seconds should
+ * be allowed for.
+ * With the quality indicator decoding scheme above, the 'Lk'
+ * condition will produce a quality value of 0. If the quality
+ * indicator starts with '0' then the second character is the
+ * number of hours since we were last locked. If the first
+ * character is anything other than 'L' or '0' then we have been
+ * out of lock for more than 9 hours so we assume the worst and
+ * force a quality value that selects the 'default' maximum
+ * dispersion. The dispersion values below are what came with the
+ * driver. They're not unreasonable so they've not been changed.
+ */
+
+ if (pp->lencode == LEN33X) {
+ switch (quality) {
+ case 0 :
+ pp->disp=.002;
+ break;
+ case 1 :
+ pp->disp=.02;
+ break;
+ case 2 :
+ pp->disp=.04;
+ break;
+ case 3 :
+ pp->disp=.08;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+ } else {
+ switch (quality) {
+ case 5 :
+ pp->disp=.002;
+ break;
+ case 4 :
+ pp->disp=.02;
+ break;
+ case 3 :
+ pp->disp=.04;
+ break;
+ case 2 :
+ pp->disp=.08;
+ break;
+ case 1 :
+ pp->disp=.16;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+
+ }
+
+ /*
+ * Decode synchronization, and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ */
+
+ if (syncchar != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapchar == '-')
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ }
+
+}
+
+/*
+ * ulink_poll - called by the transmit procedure
+ */
+
+static void
+ulink_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ pollchar = 'T';
+ if (pp->sloppyclockflag & CLK_FLAG1) {
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+ else
+ pp->polls++;
+
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ peer->burst = NSTAGE;
+
+}
+
+#else
+int refclock_ulink_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_wwv.c b/contrib/ntp/ntpd/refclock_wwv.c
new file mode 100644
index 0000000..b7e0d9a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_wwv.c
@@ -0,0 +1,2709 @@
+/*
+ * refclock_wwv - clock driver for NIST WWV/H time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_WWV)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+#include "audio.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#define ICOM 1
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+
+/*
+ * Audio WWV/H demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from NIST time/frequency stations WWV in Boulder,
+ * CO, and WWVH in Kauai, HI. Transmissions are made continuously on
+ * 2.5, 5, 10 and 15 MHz from WWV and WWVH, and 20 MHz from WWV. An
+ * ordinary AM shortwave receiver can be tuned manually to one of these
+ * frequencies or, in the case of ICOM receivers, the receiver can be
+ * tuned automatically using this program as propagation conditions
+ * change throughout the weasons, both day and night.
+ *
+ * The driver receives, demodulates and decodes the radio signals when
+ * connected to the audio codec of a workstation running Solaris, SunOS
+ * FreeBSD or Linux, and with a little help, other workstations with
+ * similar codecs or sound cards. In this implementation, only one audio
+ * driver and codec can be supported on a single machine.
+ *
+ * The demodulation and decoding algorithms used in this driver are
+ * based on those developed for the TAPR DSP93 development board and the
+ * TI 320C25 digital signal processor described in: Mills, D.L. A
+ * precision radio clock for WWV transmissions. Electrical Engineering
+ * Report 97-8-1, University of Delaware, August 1997, 25 pp., available
+ * from www.eecis.udel.edu/~mills/reports.html. The algorithms described
+ * in this report have been modified somewhat to improve performance
+ * under weak signal conditions and to provide an automatic station
+ * identification feature.
+ *
+ * The ICOM code is normally compiled in the driver. It isn't used,
+ * unless the mode keyword on the server configuration command specifies
+ * a nonzero ICOM ID select code. The C-IV trace is turned on if the
+ * debug level is greater than one.
+ *
+ * Fudge factors
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. Fudge flag2 selects the audio input
+ * port, where 0 is the mike port (default) and 1 is the line-in port.
+ * It does not seem useful to select the compact disc player port. Fudge
+ * flag3 enables audio monitoring of the input signal. For this purpose,
+ * the monitor gain is set to a default value.
+ */
+/*
+ * General definitions. These ordinarily do not need to be changed.
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (50 ms) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define DESCRIPTION "WWV/H Audio Demodulator/Decoder" /* WRU */
+#define SECOND 8000 /* second epoch (sample rate) (Hz) */
+#define MINUTE (SECOND * 60) /* minute epoch */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXAMP 6000. /* max signal level reference */
+#define MAXCLP 100 /* max clips above reference per s */
+#define MAXSNR 40. /* max SNR reference */
+#define MAXFREQ 1.5 /* max frequency tolerance (187 PPM) */
+#define DATCYC 170 /* data filter cycles */
+#define DATSIZ (DATCYC * MS) /* data filter size */
+#define SYNCYC 800 /* minute filter cycles */
+#define SYNSIZ (SYNCYC * MS) /* minute filter size */
+#define TCKCYC 5 /* tick filter cycles */
+#define TCKSIZ (TCKCYC * MS) /* tick filter size */
+#define NCHAN 5 /* number of radio channels */
+#define AUDIO_PHI 5e-6 /* dispersion growth factor */
+
+/*
+ * Tunable parameters. The DGAIN parameter can be changed to fit the
+ * audio response of the radio at 100 Hz. The WWV/WWVH data subcarrier
+ * is transmitted at about 20 percent percent modulation; the matched
+ * filter boosts it by a factor of 17 and the receiver response does
+ * what it does. The compromise value works for ICOM radios. If the
+ * radio is not tunable, the DCHAN parameter can be changed to fit the
+ * expected best propagation frequency: higher if further from the
+ * transmitter, lower if nearer. The compromise value works for the US
+ * right coast. The FREQ_OFFSET parameter can be used as a frequency
+ * vernier to correct codec requency if greater than MAXFREQ.
+ */
+#define DCHAN 3 /* default radio channel (15 Mhz) */
+#define DGAIN 5. /* subcarrier gain */
+#define FREQ_OFFSET 0. /* codec frequency correction (PPM) */
+
+/*
+ * General purpose status bits (status)
+ *
+ * SELV and/or SELH are set when WWV or WWVH have been heard and cleared
+ * on signal loss. SSYNC is set when the second sync pulse has been
+ * acquired and cleared by signal loss. MSYNC is set when the minute
+ * sync pulse has been acquired. DSYNC is set when the units digit has
+ * has reached the threshold and INSYNC is set when all nine digits have
+ * reached the threshold. The MSYNC, DSYNC and INSYNC bits are cleared
+ * only by timeout, upon which the driver starts over from scratch.
+ *
+ * DGATE is lit if the data bit amplitude or SNR is below thresholds and
+ * BGATE is lit if the pulse width amplitude or SNR is below thresolds.
+ * LEPSEC is set during the last minute of the leap day. At the end of
+ * this minute the driver inserts second 60 in the seconds state machine
+ * and the minute sync slips a second.
+ */
+#define MSYNC 0x0001 /* minute epoch sync */
+#define SSYNC 0x0002 /* second epoch sync */
+#define DSYNC 0x0004 /* minute units sync */
+#define INSYNC 0x0008 /* clock synchronized */
+#define FGATE 0x0010 /* frequency gate */
+#define DGATE 0x0020 /* data pulse amplitude error */
+#define BGATE 0x0040 /* data pulse width error */
+#define LEPSEC 0x1000 /* leap minute */
+
+/*
+ * Station scoreboard bits
+ *
+ * These are used to establish the signal quality for each of the five
+ * frequencies and two stations.
+ */
+#define SELV 0x0100 /* WWV station select */
+#define SELH 0x0200 /* WWVH station select */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These bits indicate various alarm conditions, which are decoded to
+ * form the quality character included in the timecode.
+ */
+#define CMPERR 1 /* digit or misc bit compare error */
+#define LOWERR 2 /* low bit or digit amplitude or SNR */
+#define NINERR 4 /* less than nine digits in minute */
+#define SYNERR 8 /* not tracking second sync */
+
+/*
+ * Watchcat timeouts (watch)
+ *
+ * If these timeouts expire, the status bits are mashed to zero and the
+ * driver starts from scratch. Suitably more refined procedures may be
+ * developed in future. All these are in minutes.
+ */
+#define ACQSN 6 /* station acquisition timeout */
+#define DATA 15 /* unit minutes timeout */
+#define SYNCH 40 /* station sync timeout */
+#define PANIC (2 * 1440) /* panic timeout */
+
+/*
+ * Thresholds. These establish the minimum signal level, minimum SNR and
+ * maximum jitter thresholds which establish the error and false alarm
+ * rates of the driver. The values defined here may be on the
+ * adventurous side in the interest of the highest sensitivity.
+ */
+#define MTHR 13. /* minute sync gate (percent) */
+#define TTHR 50. /* minute sync threshold (percent) */
+#define AWND 20 /* minute sync jitter threshold (ms) */
+#define ATHR 2500. /* QRZ minute sync threshold */
+#define ASNR 20. /* QRZ minute sync SNR threshold (dB) */
+#define QTHR 2500. /* QSY minute sync threshold */
+#define QSNR 20. /* QSY minute sync SNR threshold (dB) */
+#define STHR 2500. /* second sync threshold */
+#define SSNR 15. /* second sync SNR threshold (dB) */
+#define SCMP 10 /* second sync compare threshold */
+#define DTHR 1000. /* bit threshold */
+#define DSNR 10. /* bit SNR threshold (dB) */
+#define AMIN 3 /* min bit count */
+#define AMAX 6 /* max bit count */
+#define BTHR 1000. /* digit threshold */
+#define BSNR 3. /* digit likelihood threshold (dB) */
+#define BCMP 3 /* digit compare threshold */
+#define MAXERR 40 /* maximum error alarm */
+
+/*
+ * Tone frequency definitions. The increments are for 4.5-deg sine
+ * table.
+ */
+#define MS (SECOND / 1000) /* samples per millisecond */
+#define IN100 ((100 * 80) / SECOND) /* 100 Hz increment */
+#define IN1000 ((1000 * 80) / SECOND) /* 1000 Hz increment */
+#define IN1200 ((1200 * 80) / SECOND) /* 1200 Hz increment */
+
+/*
+ * Acquisition and tracking time constants
+ */
+#define MINAVG 8 /* min averaging time */
+#define MAXAVG 1024 /* max averaging time */
+#define FCONST 3 /* frequency time constant */
+#define TCONST 16 /* data bit/digit time constant */
+
+/*
+ * Miscellaneous status bits (misc)
+ *
+ * These bits correspond to designated bits in the WWV/H timecode. The
+ * bit probabilities are exponentially averaged over several minutes and
+ * processed by a integrator and threshold.
+ */
+#define DUT1 0x01 /* 56 DUT .1 */
+#define DUT2 0x02 /* 57 DUT .2 */
+#define DUT4 0x04 /* 58 DUT .4 */
+#define DUTS 0x08 /* 50 DUT sign */
+#define DST1 0x10 /* 55 DST1 leap warning */
+#define DST2 0x20 /* 2 DST2 DST1 delayed one day */
+#define SECWAR 0x40 /* 3 leap second warning */
+
+/*
+ * The on-time synchronization point for the driver is the second epoch
+ * sync pulse produced by the FIR matched filters. As the 5-ms delay of
+ * these filters is compensated, the program delay is 1.1 ms due to the
+ * 600-Hz IIR bandpass filter. The measured receiver delay is 4.7 ms and
+ * the codec delay less than 0.2 ms. The additional propagation delay
+ * specific to each receiver location can be programmed in the fudge
+ * time1 and time2 values for WWV and WWVH, respectively.
+ */
+#define PDELAY (.0011 + .0047 + .0002) /* net system delay (s) */
+
+/*
+ * Table of sine values at 4.5-degree increments. This is used by the
+ * synchronous matched filter demodulators.
+ */
+double sintab[] = {
+ 0.000000e+00, 7.845910e-02, 1.564345e-01, 2.334454e-01, /* 0-3 */
+ 3.090170e-01, 3.826834e-01, 4.539905e-01, 5.224986e-01, /* 4-7 */
+ 5.877853e-01, 6.494480e-01, 7.071068e-01, 7.604060e-01, /* 8-11 */
+ 8.090170e-01, 8.526402e-01, 8.910065e-01, 9.238795e-01, /* 12-15 */
+ 9.510565e-01, 9.723699e-01, 9.876883e-01, 9.969173e-01, /* 16-19 */
+ 1.000000e+00, 9.969173e-01, 9.876883e-01, 9.723699e-01, /* 20-23 */
+ 9.510565e-01, 9.238795e-01, 8.910065e-01, 8.526402e-01, /* 24-27 */
+ 8.090170e-01, 7.604060e-01, 7.071068e-01, 6.494480e-01, /* 28-31 */
+ 5.877853e-01, 5.224986e-01, 4.539905e-01, 3.826834e-01, /* 32-35 */
+ 3.090170e-01, 2.334454e-01, 1.564345e-01, 7.845910e-02, /* 36-39 */
+-0.000000e+00, -7.845910e-02, -1.564345e-01, -2.334454e-01, /* 40-43 */
+-3.090170e-01, -3.826834e-01, -4.539905e-01, -5.224986e-01, /* 44-47 */
+-5.877853e-01, -6.494480e-01, -7.071068e-01, -7.604060e-01, /* 48-51 */
+-8.090170e-01, -8.526402e-01, -8.910065e-01, -9.238795e-01, /* 52-55 */
+-9.510565e-01, -9.723699e-01, -9.876883e-01, -9.969173e-01, /* 56-59 */
+-1.000000e+00, -9.969173e-01, -9.876883e-01, -9.723699e-01, /* 60-63 */
+-9.510565e-01, -9.238795e-01, -8.910065e-01, -8.526402e-01, /* 64-67 */
+-8.090170e-01, -7.604060e-01, -7.071068e-01, -6.494480e-01, /* 68-71 */
+-5.877853e-01, -5.224986e-01, -4.539905e-01, -3.826834e-01, /* 72-75 */
+-3.090170e-01, -2.334454e-01, -1.564345e-01, -7.845910e-02, /* 76-79 */
+ 0.000000e+00}; /* 80 */
+
+/*
+ * Decoder operations at the end of each second are driven by a state
+ * machine. The transition matrix consists of a dispatch table indexed
+ * by second number. Each entry in the table contains a case switch
+ * number and argument.
+ */
+struct progx {
+ int sw; /* case switch number */
+ int arg; /* argument */
+};
+
+/*
+ * Case switch numbers
+ */
+#define IDLE 0 /* no operation */
+#define COEF 1 /* BCD bit */
+#define COEF1 2 /* BCD bit for minute unit */
+#define COEF2 3 /* BCD bit not used */
+#define DECIM9 4 /* BCD digit 0-9 */
+#define DECIM6 5 /* BCD digit 0-6 */
+#define DECIM3 6 /* BCD digit 0-3 */
+#define DECIM2 7 /* BCD digit 0-2 */
+#define MSCBIT 8 /* miscellaneous bit */
+#define MSC20 9 /* miscellaneous bit */
+#define MSC21 10 /* QSY probe channel */
+#define MIN1 11 /* latch time */
+#define MIN2 12 /* leap second */
+#define SYNC2 13 /* latch minute sync pulse */
+#define SYNC3 14 /* latch data pulse */
+
+/*
+ * Offsets in decoding matrix
+ */
+#define MN 0 /* minute digits (2) */
+#define HR 2 /* hour digits (2) */
+#define DA 4 /* day digits (3) */
+#define YR 7 /* year digits (2) */
+
+struct progx progx[] = {
+ {SYNC2, 0}, /* 0 latch minute sync pulse */
+ {SYNC3, 0}, /* 1 latch data pulse */
+ {MSCBIT, DST2}, /* 2 dst2 */
+ {MSCBIT, SECWAR}, /* 3 lw */
+ {COEF, 0}, /* 4 1 year units */
+ {COEF, 1}, /* 5 2 */
+ {COEF, 2}, /* 6 4 */
+ {COEF, 3}, /* 7 8 */
+ {DECIM9, YR}, /* 8 */
+ {IDLE, 0}, /* 9 p1 */
+ {COEF1, 0}, /* 10 1 minute units */
+ {COEF1, 1}, /* 11 2 */
+ {COEF1, 2}, /* 12 4 */
+ {COEF1, 3}, /* 13 8 */
+ {DECIM9, MN}, /* 14 */
+ {COEF, 0}, /* 15 10 minute tens */
+ {COEF, 1}, /* 16 20 */
+ {COEF, 2}, /* 17 40 */
+ {COEF2, 3}, /* 18 80 (not used) */
+ {DECIM6, MN + 1}, /* 19 p2 */
+ {COEF, 0}, /* 20 1 hour units */
+ {COEF, 1}, /* 21 2 */
+ {COEF, 2}, /* 22 4 */
+ {COEF, 3}, /* 23 8 */
+ {DECIM9, HR}, /* 24 */
+ {COEF, 0}, /* 25 10 hour tens */
+ {COEF, 1}, /* 26 20 */
+ {COEF2, 2}, /* 27 40 (not used) */
+ {COEF2, 3}, /* 28 80 (not used) */
+ {DECIM2, HR + 1}, /* 29 p3 */
+ {COEF, 0}, /* 30 1 day units */
+ {COEF, 1}, /* 31 2 */
+ {COEF, 2}, /* 32 4 */
+ {COEF, 3}, /* 33 8 */
+ {DECIM9, DA}, /* 34 */
+ {COEF, 0}, /* 35 10 day tens */
+ {COEF, 1}, /* 36 20 */
+ {COEF, 2}, /* 37 40 */
+ {COEF, 3}, /* 38 80 */
+ {DECIM9, DA + 1}, /* 39 p4 */
+ {COEF, 0}, /* 40 100 day hundreds */
+ {COEF, 1}, /* 41 200 */
+ {COEF2, 2}, /* 42 400 (not used) */
+ {COEF2, 3}, /* 43 800 (not used) */
+ {DECIM3, DA + 2}, /* 44 */
+ {IDLE, 0}, /* 45 */
+ {IDLE, 0}, /* 46 */
+ {IDLE, 0}, /* 47 */
+ {IDLE, 0}, /* 48 */
+ {IDLE, 0}, /* 49 p5 */
+ {MSCBIT, DUTS}, /* 50 dut+- */
+ {COEF, 0}, /* 51 10 year tens */
+ {COEF, 1}, /* 52 20 */
+ {COEF, 2}, /* 53 40 */
+ {COEF, 3}, /* 54 80 */
+ {MSC20, DST1}, /* 55 dst1 */
+ {MSCBIT, DUT1}, /* 56 0.1 dut */
+ {MSCBIT, DUT2}, /* 57 0.2 */
+ {MSC21, DUT4}, /* 58 0.4 QSY probe channel */
+ {MIN1, 0}, /* 59 p6 latch time */
+ {MIN2, 0} /* 60 leap second */
+};
+
+/*
+ * BCD coefficients for maximum likelihood digit decode
+ */
+#define P15 1. /* max positive number */
+#define N15 -1. /* max negative number */
+
+/*
+ * Digits 0-9
+ */
+#define P9 (P15 / 4) /* mark (+1) */
+#define N9 (N15 / 4) /* space (-1) */
+
+double bcd9[][4] = {
+ {N9, N9, N9, N9}, /* 0 */
+ {P9, N9, N9, N9}, /* 1 */
+ {N9, P9, N9, N9}, /* 2 */
+ {P9, P9, N9, N9}, /* 3 */
+ {N9, N9, P9, N9}, /* 4 */
+ {P9, N9, P9, N9}, /* 5 */
+ {N9, P9, P9, N9}, /* 6 */
+ {P9, P9, P9, N9}, /* 7 */
+ {N9, N9, N9, P9}, /* 8 */
+ {P9, N9, N9, P9}, /* 9 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-6 (minute tens)
+ */
+#define P6 (P15 / 3) /* mark (+1) */
+#define N6 (N15 / 3) /* space (-1) */
+
+double bcd6[][4] = {
+ {N6, N6, N6, 0}, /* 0 */
+ {P6, N6, N6, 0}, /* 1 */
+ {N6, P6, N6, 0}, /* 2 */
+ {P6, P6, N6, 0}, /* 3 */
+ {N6, N6, P6, 0}, /* 4 */
+ {P6, N6, P6, 0}, /* 5 */
+ {N6, P6, P6, 0}, /* 6 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-3 (day hundreds)
+ */
+#define P3 (P15 / 2) /* mark (+1) */
+#define N3 (N15 / 2) /* space (-1) */
+
+double bcd3[][4] = {
+ {N3, N3, 0, 0}, /* 0 */
+ {P3, N3, 0, 0}, /* 1 */
+ {N3, P3, 0, 0}, /* 2 */
+ {P3, P3, 0, 0}, /* 3 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-2 (hour tens)
+ */
+#define P2 (P15 / 2) /* mark (+1) */
+#define N2 (N15 / 2) /* space (-1) */
+
+double bcd2[][4] = {
+ {N2, N2, 0, 0}, /* 0 */
+ {P2, N2, 0, 0}, /* 1 */
+ {N2, P2, 0, 0}, /* 2 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * DST decode (DST2 DST1) for prettyprint
+ */
+char dstcod[] = {
+ 'S', /* 00 standard time */
+ 'I', /* 01 set clock ahead at 0200 local */
+ 'O', /* 10 set clock back at 0200 local */
+ 'D' /* 11 daylight time */
+};
+
+/*
+ * The decoding matrix consists of nine row vectors, one for each digit
+ * of the timecode. The digits are stored from least to most significant
+ * order. The maximum likelihood timecode is formed from the digits
+ * corresponding to the maximum likelihood values reading in the
+ * opposite order: yy ddd hh:mm.
+ */
+struct decvec {
+ int radix; /* radix (3, 4, 6, 10) */
+ int digit; /* current clock digit */
+ int mldigit; /* maximum likelihood digit */
+ int count; /* match count */
+ double digprb; /* max digit probability */
+ double digsnr; /* likelihood function (dB) */
+ double like[10]; /* likelihood integrator 0-9 */
+};
+
+/*
+ * The station structure (sp) is used to acquire the minute pulse from
+ * WWV and/or WWVH. These stations are distinguished by the frequency
+ * used for the second and minute sync pulses, 1000 Hz for WWV and 1200
+ * Hz for WWVH. Other than frequency, the format is the same.
+ */
+struct sync {
+ double epoch; /* accumulated epoch differences */
+ double maxeng; /* sync max energy */
+ double noieng; /* sync noise energy */
+ long pos; /* max amplitude position */
+ long lastpos; /* last max position */
+ long mepoch; /* minute synch epoch */
+
+ double amp; /* sync signal */
+ double syneng; /* sync signal max */
+ double synmax; /* sync signal max latched at 0 s */
+ double synsnr; /* sync signal SNR */
+ double metric; /* signal quality metric */
+ int reach; /* reachability register */
+ int count; /* bit counter */
+ int select; /* select bits */
+ char refid[5]; /* reference identifier */
+};
+
+/*
+ * The channel structure (cp) is used to mitigate between channels.
+ */
+struct chan {
+ int gain; /* audio gain */
+ struct sync wwv; /* wwv station */
+ struct sync wwvh; /* wwvh station */
+};
+
+/*
+ * WWV unit control structure (up)
+ */
+struct wwvunit {
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ double phase, freq; /* logical clock phase and frequency */
+ double monitor; /* audio monitor point */
+#ifdef ICOM
+ int fd_icom; /* ICOM file descriptor */
+#endif /* ICOM */
+ int errflg; /* error flags */
+ int watch; /* watchcat */
+
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clipped count */
+
+ /*
+ * Variables used to establish basic system timing
+ */
+ int avgint; /* master time constant */
+ int yepoch; /* sync epoch */
+ int repoch; /* buffered sync epoch */
+ double epomax; /* second sync amplitude */
+ double eposnr; /* second sync SNR */
+ double irig; /* data I channel amplitude */
+ double qrig; /* data Q channel amplitude */
+ int datapt; /* 100 Hz ramp */
+ double datpha; /* 100 Hz VFO control */
+ int rphase; /* second sample counter */
+ long mphase; /* minute sample counter */
+
+ /*
+ * Variables used to mitigate which channel to use
+ */
+ struct chan mitig[NCHAN]; /* channel data */
+ struct sync *sptr; /* station pointer */
+ int dchan; /* data channel */
+ int schan; /* probe channel */
+ int achan; /* active channel */
+
+ /*
+ * Variables used by the clock state machine
+ */
+ struct decvec decvec[9]; /* decoding matrix */
+ int rsec; /* seconds counter */
+ int digcnt; /* count of digits synchronized */
+
+ /*
+ * Variables used to estimate signal levels and bit/digit
+ * probabilities
+ */
+ double datsig; /* data signal max */
+ double datsnr; /* data signal SNR (dB) */
+
+ /*
+ * Variables used to establish status and alarm conditions
+ */
+ int status; /* status bits */
+ int alarm; /* alarm flashers */
+ int misc; /* miscellaneous timecode bits */
+ int errcnt; /* data bit error counter */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwv_start P((int, struct peer *));
+static void wwv_shutdown P((int, struct peer *));
+static void wwv_receive P((struct recvbuf *));
+static void wwv_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void wwv_epoch P((struct peer *));
+static void wwv_rf P((struct peer *, double));
+static void wwv_endpoc P((struct peer *, int));
+static void wwv_rsec P((struct peer *, double));
+static void wwv_qrz P((struct peer *, struct sync *, int));
+static void wwv_corr4 P((struct peer *, struct decvec *,
+ double [], double [][4]));
+static void wwv_gain P((struct peer *));
+static void wwv_tsec P((struct peer *));
+static int timecode P((struct wwvunit *, char *));
+static double wwv_snr P((double, double));
+static int carry P((struct decvec *));
+static int wwv_newchan P((struct peer *));
+static void wwv_newgame P((struct peer *));
+static double wwv_metric P((struct sync *));
+static void wwv_clock P((struct peer *));
+#ifdef ICOM
+static int wwv_qsy P((struct peer *, int));
+#endif /* ICOM */
+
+static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwv = {
+ wwv_start, /* start up driver */
+ wwv_shutdown, /* shut down driver */
+ wwv_poll, /* transmit poll message */
+ noentry, /* not used (old wwv_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwv_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwv_start - open the devices and initialize data for processing
+ */
+static int
+wwv_start(
+ int unit, /* instance number (used by PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif /* DEBUG */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvunit *)emalloc(sizeof(struct wwvunit)))) {
+ close(fd);
+ return (0);
+ }
+ memset(up, 0, sizeof(struct wwvunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = wwv_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1.; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3.; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+
+ /*
+ * Initialize the decoding matrix with the radix for each digit
+ * position.
+ */
+ up->decvec[MN].radix = 10; /* minutes */
+ up->decvec[MN + 1].radix = 6;
+ up->decvec[HR].radix = 10; /* hours */
+ up->decvec[HR + 1].radix = 3;
+ up->decvec[DA].radix = 10; /* days */
+ up->decvec[DA + 1].radix = 10;
+ up->decvec[DA + 2].radix = 4;
+ up->decvec[YR].radix = 10; /* years */
+ up->decvec[YR + 1].radix = 10;
+
+#ifdef ICOM
+ /*
+ * Initialize autotune if available. Note that the ICOM select
+ * code must be less than 128, so the high order bit can be used
+ * to select the line speed 0 (9600 bps) or 1 (1200 bps).
+ */
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif /* DEBUG */
+ if (peer->ttl != 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ if (up->fd_icom < 0) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: %m");
+ up->errflg = CEVNT_FAULT;
+ }
+ }
+ if (up->fd_icom > 0) {
+ if (wwv_qsy(peer, DCHAN) != 0) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: radio not found");
+ up->errflg = CEVNT_FAULT;
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+
+ /*
+ * Let the games begin.
+ */
+ wwv_newgame(peer);
+ return (1);
+}
+
+
+/*
+ * wwv_shutdown - shut down the clock
+ */
+static void
+wwv_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (up == NULL)
+ return;
+
+ io_closeclock(&pp->io);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+#endif /* ICOM */
+ free(up);
+}
+
+
+/*
+ * wwv_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the A/D sample clock by dropping or duplicating codec samples.
+ * It also controls the A/D signal level with an AGC loop to mimimize
+ * quantization noise and avoid overload.
+ */
+static void
+wwv_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXAMP (6000) and
+ * record the number of clips to be used later by the
+ * AGC.
+ */
+ if (sample > MAXAMP) {
+ sample = MAXAMP;
+ up->clipcnt++;
+ } else if (sample < -MAXAMP) {
+ sample = -MAXAMP;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += up->freq / SECOND;
+ up->phase += FREQ_OFFSET / 1e6;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ wwv_rf(peer, sample);
+ wwv_rf(peer, sample);
+ } else {
+ wwv_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * wwv_poll - called by the transmit procedure
+ *
+ * This routine keeps track of status. If no offset samples have been
+ * processed during a poll interval, a timeout event is declared. If
+ * errors have have occurred during the interval, they are reported as
+ * well.
+ */
+static void
+wwv_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (pp->coderecv == pp->codeproc)
+ up->errflg = CEVNT_TIMEOUT;
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+ pp->polls++;
+}
+
+
+/*
+ * wwv_rf - process signals and demodulate to baseband
+ *
+ * This routine grooms and filters decompanded raw audio samples. The
+ * output signal is the 100-Hz filtered baseband data signal in
+ * quadrature phase. The routine also determines the minute synch epoch,
+ * as well as certain signal maxima, minima and related values.
+ *
+ * There are two 1-s ramps used by this program. Both count the 8000
+ * logical clock samples spanning exactly one second. The epoch ramp
+ * counts the samples starting at an arbitrary time. The rphase ramp
+ * counts the samples starting at the 5-ms second sync pulse found
+ * during the epoch ramp.
+ *
+ * There are two 1-m ramps used by this program. The mphase ramp counts
+ * the 480,000 logical clock samples spanning exactly one minute and
+ * starting at an arbitrary time. The rsec ramp counts the 60 seconds of
+ * the minute starting at the 800-ms minute sync pulse found during the
+ * mphase ramp. The rsec ramp drives the seconds state machine to
+ * determine the bits and digits of the timecode.
+ *
+ * Demodulation operations are based on three synthesized quadrature
+ * sinusoids: 100 Hz for the data signal, 1000 Hz for the WWV sync
+ * signal and 1200 Hz for the WWVH sync signal. These drive synchronous
+ * matched filters for the data signal (170 ms at 100 Hz), WWV minute
+ * sync signal (800 ms at 1000 Hz) and WWVH minute sync signal (800 ms
+ * at 1200 Hz). Two additional matched filters are switched in
+ * as required for the WWV second sync signal (5 cycles at 1000 Hz) and
+ * WWVH second sync signal (6 cycles at 1200 Hz).
+ */
+static void
+wwv_rf(
+ struct peer *peer, /* peerstructure pointer */
+ double isig /* input signal */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp, *rp;
+
+ static double lpf[5]; /* 150-Hz lpf delay line */
+ double data; /* lpf output */
+ static double bpf[9]; /* 1000/1200-Hz bpf delay line */
+ double syncx; /* bpf output */
+ static double mf[41]; /* 1000/1200-Hz mf delay line */
+ double mfsync; /* mf output */
+
+ static int iptr; /* data channel pointer */
+ static double ibuf[DATSIZ]; /* data I channel delay line */
+ static double qbuf[DATSIZ]; /* data Q channel delay line */
+
+ static int jptr; /* sync channel pointer */
+ static int kptr; /* tick channel pointer */
+
+ static int csinptr; /* wwv channel phase */
+ static double cibuf[SYNSIZ]; /* wwv I channel delay line */
+ static double cqbuf[SYNSIZ]; /* wwv Q channel delay line */
+ static double ciamp; /* wwv I channel amplitude */
+ static double cqamp; /* wwv Q channel amplitude */
+
+ static double csibuf[TCKSIZ]; /* wwv I tick delay line */
+ static double csqbuf[TCKSIZ]; /* wwv Q tick delay line */
+ static double csiamp; /* wwv I tick amplitude */
+ static double csqamp; /* wwv Q tick amplitude */
+
+ static int hsinptr; /* wwvh channel phase */
+ static double hibuf[SYNSIZ]; /* wwvh I channel delay line */
+ static double hqbuf[SYNSIZ]; /* wwvh Q channel delay line */
+ static double hiamp; /* wwvh I channel amplitude */
+ static double hqamp; /* wwvh Q channel amplitude */
+
+ static double hsibuf[TCKSIZ]; /* wwvh I tick delay line */
+ static double hsqbuf[TCKSIZ]; /* wwvh Q tick delay line */
+ static double hsiamp; /* wwvh I tick amplitude */
+ static double hsqamp; /* wwvh Q tick amplitude */
+
+ static double epobuf[SECOND]; /* second sync comb filter */
+ static double epomax, nxtmax; /* second sync amplitude buffer */
+ static int epopos; /* epoch second sync position buffer */
+
+ static int iniflg; /* initialization flag */
+ int pdelay; /* propagation delay (samples) */
+ int epoch; /* comb filter index */
+ double dtemp;
+ int i;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)lpf, 0, sizeof(lpf));
+ memset((char *)bpf, 0, sizeof(bpf));
+ memset((char *)mf, 0, sizeof(mf));
+ memset((char *)ibuf, 0, sizeof(ibuf));
+ memset((char *)qbuf, 0, sizeof(qbuf));
+ memset((char *)cibuf, 0, sizeof(cibuf));
+ memset((char *)cqbuf, 0, sizeof(cqbuf));
+ memset((char *)csibuf, 0, sizeof(csibuf));
+ memset((char *)csqbuf, 0, sizeof(csqbuf));
+ memset((char *)hibuf, 0, sizeof(hibuf));
+ memset((char *)hqbuf, 0, sizeof(hqbuf));
+ memset((char *)hsibuf, 0, sizeof(hsibuf));
+ memset((char *)hsqbuf, 0, sizeof(hsqbuf));
+ memset((char *)epobuf, 0, sizeof(epobuf));
+ }
+
+ /*
+ * Baseband data demodulation. The 100-Hz subcarrier is
+ * extracted using a 150-Hz IIR lowpass filter. This attenuates
+ * the 1000/1200-Hz sync signals, as well as the 440-Hz and
+ * 600-Hz tones and most of the noise and voice modulation
+ * components.
+ *
+ * The subcarrier is transmitted 10 dB down from the carrier.
+ * The DGAIN parameter can be adjusted for this and to
+ * compensate for the radio audio response at 100 Hz.
+ *
+ * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple.
+ */
+ data = (lpf[4] = lpf[3]) * 8.360961e-01;
+ data += (lpf[3] = lpf[2]) * -3.481740e+00;
+ data += (lpf[2] = lpf[1]) * 5.452988e+00;
+ data += (lpf[1] = lpf[0]) * -3.807229e+00;
+ lpf[0] = isig * DGAIN - data;
+ data = lpf[0] * 3.281435e-03
+ + lpf[1] * -1.149947e-02
+ + lpf[2] * 1.654858e-02
+ + lpf[3] * -1.149947e-02
+ + lpf[4] * 3.281435e-03;
+
+ /*
+ * The 100-Hz data signal is demodulated using a pair of
+ * quadrature multipliers, matched filters and a phase lock
+ * loop. The I and Q quadrature data signals are produced by
+ * multiplying the filtered signal by 100-Hz sine and cosine
+ * signals, respectively. The signals are processed by 170-ms
+ * synchronous matched filters to produce the amplitude and
+ * phase signals used by the demodulator. The signals are scaled
+ * to produce unit energy at the maximum value.
+ */
+ i = up->datapt;
+ up->datapt = (up->datapt + IN100) % 80;
+ dtemp = sintab[i] * data / (MS / 2. * DATCYC);
+ up->irig -= ibuf[iptr];
+ ibuf[iptr] = dtemp;
+ up->irig += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * data / (MS / 2. * DATCYC);
+ up->qrig -= qbuf[iptr];
+ qbuf[iptr] = dtemp;
+ up->qrig += dtemp;
+ iptr = (iptr + 1) % DATSIZ;
+
+ /*
+ * Baseband sync demodulation. The 1000/1200 sync signals are
+ * extracted using a 600-Hz IIR bandpass filter. This removes
+ * the 100-Hz data subcarrier, as well as the 440-Hz and 600-Hz
+ * tones and most of the noise and voice modulation components.
+ *
+ * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple.
+ */
+ syncx = (bpf[8] = bpf[7]) * 4.897278e-01;
+ syncx += (bpf[7] = bpf[6]) * -2.765914e+00;
+ syncx += (bpf[6] = bpf[5]) * 8.110921e+00;
+ syncx += (bpf[5] = bpf[4]) * -1.517732e+01;
+ syncx += (bpf[4] = bpf[3]) * 1.975197e+01;
+ syncx += (bpf[3] = bpf[2]) * -1.814365e+01;
+ syncx += (bpf[2] = bpf[1]) * 1.159783e+01;
+ syncx += (bpf[1] = bpf[0]) * -4.735040e+00;
+ bpf[0] = isig - syncx;
+ syncx = bpf[0] * 8.203628e-03
+ + bpf[1] * -2.375732e-02
+ + bpf[2] * 3.353214e-02
+ + bpf[3] * -4.080258e-02
+ + bpf[4] * 4.605479e-02
+ + bpf[5] * -4.080258e-02
+ + bpf[6] * 3.353214e-02
+ + bpf[7] * -2.375732e-02
+ + bpf[8] * 8.203628e-03;
+
+ /*
+ * The 1000/1200 sync signals are demodulated using a pair of
+ * quadrature multipliers and matched filters. However,
+ * synchronous demodulation at these frequencies is impractical,
+ * so only the signal amplitude is used. The I and Q quadrature
+ * sync signals are produced by multiplying the filtered signal
+ * by 1000-Hz (WWV) and 1200-Hz (WWVH) sine and cosine signals,
+ * respectively. The WWV and WWVH signals are processed by 800-
+ * ms synchronous matched filters and combined to produce the
+ * minute sync signal and detect which one (or both) the WWV or
+ * WWVH signal is present. The WWV and WWVH signals are also
+ * processed by 5-ms synchronous matched filters and combined to
+ * produce the second sync signal. The signals are scaled to
+ * produce unit energy at the maximum value.
+ *
+ * Note the master timing ramps, which run continuously. The
+ * minute counter (mphase) counts the samples in the minute,
+ * while the second counter (epoch) counts the samples in the
+ * second.
+ */
+ up->mphase = (up->mphase + 1) % MINUTE;
+ epoch = up->mphase % SECOND;
+
+ /*
+ * WWV
+ */
+ i = csinptr;
+ csinptr = (csinptr + IN1000) % 80;
+
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ ciamp -= cibuf[jptr];
+ cibuf[jptr] = dtemp;
+ ciamp += dtemp;
+ csiamp -= csibuf[kptr];
+ csibuf[kptr] = dtemp;
+ csiamp += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ cqamp -= cqbuf[jptr];
+ cqbuf[jptr] = dtemp;
+ cqamp += dtemp;
+ csqamp -= csqbuf[kptr];
+ csqbuf[kptr] = dtemp;
+ csqamp += dtemp;
+
+ sp = &up->mitig[up->achan].wwv;
+ sp->amp = sqrt(ciamp * ciamp + cqamp * cqamp) / SYNCYC;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, sp, (int)(pp->fudgetime1 * SECOND));
+
+ /*
+ * WWVH
+ */
+ i = hsinptr;
+ hsinptr = (hsinptr + IN1200) % 80;
+
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ hiamp -= hibuf[jptr];
+ hibuf[jptr] = dtemp;
+ hiamp += dtemp;
+ hsiamp -= hsibuf[kptr];
+ hsibuf[kptr] = dtemp;
+ hsiamp += dtemp;
+
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / (MS / 2.);
+ hqamp -= hqbuf[jptr];
+ hqbuf[jptr] = dtemp;
+ hqamp += dtemp;
+ hsqamp -= hsqbuf[kptr];
+ hsqbuf[kptr] = dtemp;
+ hsqamp += dtemp;
+
+ rp = &up->mitig[up->achan].wwvh;
+ rp->amp = sqrt(hiamp * hiamp + hqamp * hqamp) / SYNCYC;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, rp, (int)(pp->fudgetime2 * SECOND));
+ jptr = (jptr + 1) % SYNSIZ;
+ kptr = (kptr + 1) % TCKSIZ;
+
+ /*
+ * The following section is called once per minute. It does
+ * housekeeping and timeout functions and empties the dustbins.
+ */
+ if (up->mphase == 0) {
+ up->watch++;
+ if (!(up->status & MSYNC)) {
+
+ /*
+ * If minute sync has not been acquired before
+ * ACQSN timeout (6 min), or if no signal is
+ * heard, the program cycles to the next
+ * frequency and tries again.
+ */
+ if (!wwv_newchan(peer))
+ up->watch = 0;
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ wwv_qsy(peer, up->dchan);
+#endif /* ICOM */
+ } else {
+
+ /*
+ * If the leap bit is set, set the minute epoch
+ * back one second so the station processes
+ * don't miss a beat.
+ */
+ if (up->status & LEPSEC) {
+ up->mphase -= SECOND;
+ if (up->mphase < 0)
+ up->mphase += MINUTE;
+ }
+ }
+ }
+
+ /*
+ * When the channel metric reaches threshold and the second
+ * counter matches the minute epoch within the second, the
+ * driver has synchronized to the station. The second number is
+ * the remaining seconds until the next minute epoch, while the
+ * sync epoch is zero. Watch out for the first second; if
+ * already synchronized to the second, the buffered sync epoch
+ * must be set.
+ *
+ * Note the guard interval is 200 ms; if for some reason the
+ * clock drifts more than that, it might wind up in the wrong
+ * second. If the maximum frequency error is not more than about
+ * 1 PPM, the clock can go as much as two days while still in
+ * the same second.
+ */
+ if (up->status & MSYNC) {
+ wwv_epoch(peer);
+ } else if (up->sptr != NULL) {
+ sp = up->sptr;
+ if (sp->metric >= TTHR && epoch == sp->mepoch % SECOND) {
+ up->rsec = (60 - sp->mepoch / SECOND) % 60;
+ up->rphase = 0;
+ up->status |= MSYNC;
+ up->watch = 0;
+ if (!(up->status & SSYNC))
+ up->repoch = up->yepoch = epoch;
+ else
+ up->repoch = up->yepoch;
+
+ }
+ }
+
+ /*
+ * The second sync pulse is extracted using 5-ms (40 sample) FIR
+ * matched filters at 1000 Hz for WWV or 1200 Hz for WWVH. This
+ * pulse is used for the most precise synchronization, since if
+ * provides a resolution of one sample (125 us). The filters run
+ * only if the station has been reliably determined.
+ */
+ if (up->status & SELV) {
+ pdelay = (int)(pp->fudgetime1 * SECOND);
+ mfsync = sqrt(csiamp * csiamp + csqamp * csqamp) /
+ TCKCYC;
+ } else if (up->status & SELH) {
+ pdelay = (int)(pp->fudgetime2 * SECOND);
+ mfsync = sqrt(hsiamp * hsiamp + hsqamp * hsqamp) /
+ TCKCYC;
+ } else {
+ pdelay = 0;
+ mfsync = 0;
+ }
+
+ /*
+ * Enhance the seconds sync pulse using a 1-s (8000-sample) comb
+ * filter. Correct for the FIR matched filter delay, which is 5
+ * ms for both the WWV and WWVH filters, and also for the
+ * propagation delay. Once each second look for second sync. If
+ * not in minute sync, fiddle the codec gain. Note the SNR is
+ * computed from the maximum sample and the envelope of the
+ * sample 6 ms before it, so if we slip more than a cycle the
+ * SNR should plummet. The signal is scaled to produce unit
+ * energy at the maximum value.
+ */
+ dtemp = (epobuf[epoch] += (mfsync - epobuf[epoch]) /
+ up->avgint);
+ if (dtemp > epomax) {
+ int j;
+
+ epomax = dtemp;
+ epopos = epoch;
+ j = epoch - 6 * MS;
+ if (j < 0)
+ j += SECOND;
+ nxtmax = fabs(epobuf[j]);
+ }
+ if (epoch == 0) {
+ up->epomax = epomax;
+ up->eposnr = wwv_snr(epomax, nxtmax);
+ epopos -= pdelay + TCKCYC * MS;
+ if (epopos < 0)
+ epopos += SECOND;
+ wwv_endpoc(peer, epopos);
+ if (!(up->status & SSYNC))
+ up->alarm |= SYNERR;
+ epomax = 0;
+ if (!(up->status & MSYNC))
+ wwv_gain(peer);
+ }
+}
+
+
+/*
+ * wwv_qrz - identify and acquire WWV/WWVH minute sync pulse
+ *
+ * This routine implements a virtual station process used to acquire
+ * minute sync and to mitigate among the ten frequency and station
+ * combinations. During minute sync acquisition the process probes each
+ * frequency and station in turn for the minute pulse, which
+ * involves searching through the entire 480,000-sample minute. The
+ * process finds the maximum signal and RMS noise plus signal. Then, the
+ * actual noise is determined by subtracting the energy of the matched
+ * filter.
+ *
+ * Students of radar receiver technology will discover this algorithm
+ * amounts to a range-gate discriminator. A valid pulse must have peak
+ * amplitude at least QTHR (2500) and SNR at least QSNR (20) dB and the
+ * difference between the current and previous epoch must be less than
+ * AWND (20 ms). Note that the discriminator peak occurs about 800 ms
+ * into the second, so the timing is retarded to the previous second
+ * epoch.
+ */
+static void
+wwv_qrz(
+ struct peer *peer, /* peer structure pointer */
+ struct sync *sp, /* sync channel structure */
+ int pdelay /* propagation delay (samples) */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ char tbuf[80]; /* monitor buffer */
+ long epoch;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Find the sample with peak amplitude, which defines the minute
+ * epoch. Accumulate all samples to determine the total noise
+ * energy.
+ */
+ epoch = up->mphase - pdelay - SYNSIZ;
+ if (epoch < 0)
+ epoch += MINUTE;
+ if (sp->amp > sp->maxeng) {
+ sp->maxeng = sp->amp;
+ sp->pos = epoch;
+ }
+ sp->noieng += sp->amp;
+
+ /*
+ * At the end of the minute, determine the epoch of the minute
+ * sync pulse, as well as the difference between the current and
+ * previous epoches due to the intrinsic frequency error plus
+ * jitter. When calculating the SNR, subtract the pulse energy
+ * from the total noise energy and then normalize.
+ */
+ if (up->mphase == 0) {
+ sp->synmax = sp->maxeng;
+ sp->synsnr = wwv_snr(sp->synmax, (sp->noieng -
+ sp->synmax) / MINUTE);
+ if (sp->count == 0)
+ sp->lastpos = sp->pos;
+ epoch = (sp->pos - sp->lastpos) % MINUTE;
+ sp->reach <<= 1;
+ if (sp->reach & (1 << AMAX))
+ sp->count--;
+ if (sp->synmax > ATHR && sp->synsnr > ASNR) {
+ if (abs(epoch) < AWND * MS) {
+ sp->reach |= 1;
+ sp->count++;
+ sp->mepoch = sp->lastpos = sp->pos;
+ } else if (sp->count == 1) {
+ sp->lastpos = sp->pos;
+ }
+ }
+ if (up->watch > ACQSN)
+ sp->metric = 0;
+ else
+ sp->metric = wwv_metric(sp);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %4ld %4ld",
+ up->status, up->gain, sp->refid,
+ sp->reach & 0xffff, sp->metric, sp->synmax,
+ sp->synsnr, sp->pos % SECOND, epoch);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ sp->maxeng = sp->noieng = 0;
+ }
+}
+
+
+/*
+ * wwv_endpoc - identify and acquire second sync pulse
+ *
+ * This routine is called at the end of the second sync interval. It
+ * determines the second sync epoch position within the second and
+ * disciplines the sample clock using a frequency-lock loop (FLL).
+ *
+ * Second sync is determined in the RF input routine as the maximum
+ * over all 8000 samples in the second comb filter. To assure accurate
+ * and reliable time and frequency discipline, this routine performs a
+ * great deal of heavy-handed heuristic data filtering and grooming.
+ */
+static void
+wwv_endpoc(
+ struct peer *peer, /* peer structure pointer */
+ int epopos /* epoch max position */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ static int epoch_mf[3]; /* epoch median filter */
+ static int tepoch; /* current second epoch */
+ static int xepoch; /* last second epoch */
+ static int zepoch; /* last run epoch */
+ static int zcount; /* last run end time */
+ static int scount; /* seconds counter */
+ static int syncnt; /* run length counter */
+ static int maxrun; /* longest run length */
+ static int mepoch; /* longest run end epoch */
+ static int mcount; /* longest run end time */
+ static int avgcnt; /* averaging interval counter */
+ static int avginc; /* averaging ratchet */
+ static int iniflg; /* initialization flag */
+ char tbuf[80]; /* monitor buffer */
+ double dtemp;
+ int tmp2;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)epoch_mf, 0, sizeof(epoch_mf));
+ }
+
+ /*
+ * If the signal amplitude or SNR fall below thresholds, dim the
+ * second sync lamp and wait for hotter ions. If no stations are
+ * heard, we are either in a probe cycle or the ions are really
+ * cold.
+ */
+ scount++;
+ if (up->epomax < STHR || up->eposnr < SSNR) {
+ up->status &= ~(SSYNC | FGATE);
+ avgcnt = syncnt = maxrun = 0;
+ return;
+ }
+ if (!(up->status & (SELV | SELH)))
+ return;
+
+ /*
+ * A three-stage median filter is used to help denoise the
+ * second sync pulse. The median sample becomes the candidate
+ * epoch.
+ */
+ epoch_mf[2] = epoch_mf[1];
+ epoch_mf[1] = epoch_mf[0];
+ epoch_mf[0] = epopos;
+ if (epoch_mf[0] > epoch_mf[1]) {
+ if (epoch_mf[1] > epoch_mf[2])
+ tepoch = epoch_mf[1]; /* 0 1 2 */
+ else if (epoch_mf[2] > epoch_mf[0])
+ tepoch = epoch_mf[0]; /* 2 0 1 */
+ else
+ tepoch = epoch_mf[2]; /* 0 2 1 */
+ } else {
+ if (epoch_mf[1] < epoch_mf[2])
+ tepoch = epoch_mf[1]; /* 2 1 0 */
+ else if (epoch_mf[2] < epoch_mf[0])
+ tepoch = epoch_mf[0]; /* 1 0 2 */
+ else
+ tepoch = epoch_mf[2]; /* 1 2 0 */
+ }
+
+
+ /*
+ * If the epoch candidate is the same as the last one, increment
+ * the run counter. If not, save the length, epoch and end
+ * time of the current run for use later and reset the counter.
+ * The epoch is considered valid if the run is at least SCMP
+ * (10) s, the minute is synchronized and the interval since the
+ * last epoch is not greater than the averaging interval. Thus,
+ * after a long absence, the program will wait a full averaging
+ * interval while the comb filter charges up and noise
+ * dissapates..
+ */
+ tmp2 = (tepoch - xepoch) % SECOND;
+ if (tmp2 == 0) {
+ syncnt++;
+ if (syncnt > SCMP && up->status & MSYNC && (up->status &
+ FGATE || scount - zcount <= up->avgint)) {
+ up->status |= SSYNC;
+ up->yepoch = tepoch;
+ }
+ } else if (syncnt >= maxrun) {
+ maxrun = syncnt;
+ mcount = scount;
+ mepoch = xepoch;
+ syncnt = 0;
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & MSYNC))
+ {
+ sprintf(tbuf,
+ "wwv1 %04x %3d %4d %5.0f %5.1f %5d %4d %4d %4d",
+ up->status, up->gain, tepoch, up->epomax,
+ up->eposnr, tmp2, avgcnt, syncnt,
+ maxrun);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ avgcnt++;
+ if (avgcnt < up->avgint) {
+ xepoch = tepoch;
+ return;
+ }
+
+ /*
+ * The sample clock frequency is disciplined using a first-order
+ * feedback loop with time constant consistent with the Allan
+ * intercept of typical computer clocks. During each averaging
+ * interval the candidate epoch at the end of the longest run is
+ * determined. If the longest run is zero, all epoches in the
+ * interval are different, so the candidate epoch is the current
+ * epoch. The frequency update is computed from the candidate
+ * epoch difference (125-us units) and time difference (seconds)
+ * between updates.
+ */
+ if (syncnt >= maxrun) {
+ maxrun = syncnt;
+ mcount = scount;
+ mepoch = xepoch;
+ }
+ xepoch = tepoch;
+ if (maxrun == 0) {
+ mepoch = tepoch;
+ mcount = scount;
+ }
+
+ /*
+ * The master clock runs at the codec sample frequency of 8000
+ * Hz, so the intrinsic time resolution is 125 us. The frequency
+ * resolution ranges from 18 PPM at the minimum averaging
+ * interval of 8 s to 0.12 PPM at the maximum interval of 1024
+ * s. An offset update is determined at the end of the longest
+ * run in each averaging interval. The frequency adjustment is
+ * computed from the difference between offset updates and the
+ * interval between them.
+ *
+ * The maximum frequency adjustment ranges from 187 PPM at the
+ * minimum interval to 1.5 PPM at the maximum. If the adjustment
+ * exceeds the maximum, the update is discarded and the
+ * hysteresis counter is decremented. Otherwise, the frequency
+ * is incremented by the adjustment, but clamped to the maximum
+ * 187.5 PPM. If the update is less than half the maximum, the
+ * hysteresis counter is incremented. If the counter increments
+ * to +3, the averaging interval is doubled and the counter set
+ * to zero; if it decrements to -3, the interval is halved and
+ * the counter set to zero.
+ */
+ dtemp = (mepoch - zepoch) % SECOND;
+ if (up->status & FGATE) {
+ if (abs(dtemp) < MAXFREQ * MINAVG) {
+ up->freq += (dtemp / 2.) / ((mcount - zcount) *
+ FCONST);
+ if (up->freq > MAXFREQ)
+ up->freq = MAXFREQ;
+ else if (up->freq < -MAXFREQ)
+ up->freq = -MAXFREQ;
+ if (abs(dtemp) < MAXFREQ * MINAVG / 2.) {
+ if (avginc < 3) {
+ avginc++;
+ } else {
+ if (up->avgint < MAXAVG) {
+ up->avgint <<= 1;
+ avginc = 0;
+ }
+ }
+ }
+ } else {
+ if (avginc > -3) {
+ avginc--;
+ } else {
+ if (up->avgint > MINAVG) {
+ up->avgint >>= 1;
+ avginc = 0;
+ }
+ }
+ }
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv2 %04x %5.0f %5.1f %5d %4d %4d %4d %4.0f %7.2f",
+ up->status, up->epomax, up->eposnr, mepoch,
+ up->avgint, maxrun, mcount - zcount, dtemp,
+ up->freq * 1e6 / SECOND);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+
+ /*
+ * This is a valid update; set up for the next interval.
+ */
+ up->status |= FGATE;
+ zepoch = mepoch;
+ zcount = mcount;
+ avgcnt = syncnt = maxrun = 0;
+}
+
+
+/*
+ * wwv_epoch - epoch scanner
+ *
+ * This routine extracts data signals from the 100-Hz subcarrier. It
+ * scans the receiver second epoch to determine the signal amplitudes
+ * and pulse timings. Receiver synchronization is determined by the
+ * minute sync pulse detected in the wwv_rf() routine and the second
+ * sync pulse detected in the wwv_epoch() routine. The transmitted
+ * signals are delayed by the propagation delay, receiver delay and
+ * filter delay of this program. Delay corrections are introduced
+ * separately for WWV and WWVH.
+ *
+ * Most communications radios use a highpass filter in the audio stages,
+ * which can do nasty things to the subcarrier phase relative to the
+ * sync pulses. Therefore, the data subcarrier reference phase is
+ * disciplined using the hardlimited quadrature-phase signal sampled at
+ * the same time as the in-phase signal. The phase tracking loop uses
+ * phase adjustments of plus-minus one sample (125 us).
+ */
+static void
+wwv_epoch(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ static double sigmin, sigzer, sigone, engmax, engmin;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Find the maximum minute sync pulse energy for both the
+ * WWV and WWVH stations. This will be used later for channel
+ * and station mitigation. Also set the seconds epoch at 800 ms
+ * well before the end of the second to make sure we never set
+ * the epoch backwards.
+ */
+ cp = &up->mitig[up->achan];
+ if (cp->wwv.amp > cp->wwv.syneng)
+ cp->wwv.syneng = cp->wwv.amp;
+ if (cp->wwvh.amp > cp->wwvh.syneng)
+ cp->wwvh.syneng = cp->wwvh.amp;
+ if (up->rphase == 800 * MS)
+ up->repoch = up->yepoch;
+
+ /*
+ * Use the signal amplitude at epoch 15 ms as the noise floor.
+ * This gives a guard time of +-15 ms from the beginning of the
+ * second until the second pulse rises at 30 ms. There is a
+ * compromise here; we want to delay the sample as long as
+ * possible to give the radio time to change frequency and the
+ * AGC to stabilize, but as early as possible if the second
+ * epoch is not exact.
+ */
+ if (up->rphase == 15 * MS)
+ sigmin = sigzer = sigone = up->irig;
+
+ /*
+ * Latch the data signal at 200 ms. Keep this around until the
+ * end of the second. Use the signal energy as the peak to
+ * compute the SNR. Use the Q sample to adjust the 100-Hz
+ * reference oscillator phase.
+ */
+ if (up->rphase == 200 * MS) {
+ sigzer = up->irig;
+ engmax = sqrt(up->irig * up->irig + up->qrig *
+ up->qrig);
+ up->datpha = up->qrig / up->avgint;
+ if (up->datpha >= 0) {
+ up->datapt++;
+ if (up->datapt >= 80)
+ up->datapt -= 80;
+ } else {
+ up->datapt--;
+ if (up->datapt < 0)
+ up->datapt += 80;
+ }
+ }
+
+
+ /*
+ * Latch the data signal at 500 ms. Keep this around until the
+ * end of the second.
+ */
+ else if (up->rphase == 500 * MS)
+ sigone = up->irig;
+
+ /*
+ * At the end of the second crank the clock state machine and
+ * adjust the codec gain. Note the epoch is buffered from the
+ * center of the second in order to avoid jitter while the
+ * seconds synch is diddling the epoch. Then, determine the true
+ * offset and update the median filter in the driver interface.
+ *
+ * Use the energy at the end of the second as the noise to
+ * compute the SNR for the data pulse. This gives a better
+ * measurement than the beginning of the second, especially when
+ * returning from the probe channel. This gives a guard time of
+ * 30 ms from the decay of the longest pulse to the rise of the
+ * next pulse.
+ */
+ up->rphase++;
+ if (up->mphase % SECOND == up->repoch) {
+ up->status &= ~(DGATE | BGATE);
+ engmin = sqrt(up->irig * up->irig + up->qrig *
+ up->qrig);
+ up->datsig = engmax;
+ up->datsnr = wwv_snr(engmax, engmin);
+
+ /*
+ * If the amplitude or SNR is below threshold, average a
+ * 0 in the the integrators; otherwise, average the
+ * bipolar signal. This is done to avoid noise polution.
+ */
+ if (engmax < DTHR || up->datsnr < DSNR) {
+ up->status |= DGATE;
+ wwv_rsec(peer, 0);
+ } else {
+ sigzer -= sigone;
+ sigone -= sigmin;
+ wwv_rsec(peer, sigone - sigzer);
+ }
+ if (up->status & (DGATE | BGATE))
+ up->errcnt++;
+ if (up->errcnt > MAXERR)
+ up->alarm |= LOWERR;
+ wwv_gain(peer);
+ cp = &up->mitig[up->achan];
+ cp->wwv.syneng = 0;
+ cp->wwvh.syneng = 0;
+ up->rphase = 0;
+ }
+}
+
+
+/*
+ * wwv_rsec - process receiver second
+ *
+ * This routine is called at the end of each receiver second to
+ * implement the per-second state machine. The machine assembles BCD
+ * digit bits, decodes miscellaneous bits and dances the leap seconds.
+ *
+ * Normally, the minute has 60 seconds numbered 0-59. If the leap
+ * warning bit is set, the last minute (1439) of 30 June (day 181 or 182
+ * for leap years) or 31 December (day 365 or 366 for leap years) is
+ * augmented by one second numbered 60. This is accomplished by
+ * extending the minute interval by one second and teaching the state
+ * machine to ignore it.
+ */
+static void
+wwv_rsec(
+ struct peer *peer, /* peer structure pointer */
+ double bit
+ )
+{
+ static int iniflg; /* initialization flag */
+ static double bcddld[4]; /* BCD data bits */
+ static double bitvec[61]; /* bit integrator for misc bits */
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ struct sync *sp, *rp;
+ char tbuf[80]; /* monitor buffer */
+ int sw, arg, nsec;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)bitvec, 0, sizeof(bitvec));
+ }
+
+ /*
+ * The bit represents the probability of a hit on zero (negative
+ * values), a hit on one (positive values) or a miss (zero
+ * value). The likelihood vector is the exponential average of
+ * these probabilities. Only the bits of this vector
+ * corresponding to the miscellaneous bits of the timecode are
+ * used, but it's easier to do them all. After that, crank the
+ * seconds state machine.
+ */
+ nsec = up->rsec;
+ up->rsec++;
+ bitvec[nsec] += (bit - bitvec[nsec]) / TCONST;
+ sw = progx[nsec].sw;
+ arg = progx[nsec].arg;
+
+ /*
+ * The minute state machine. Fly off to a particular section as
+ * directed by the transition matrix and second number.
+ */
+ switch (sw) {
+
+ /*
+ * Ignore this second.
+ */
+ case IDLE: /* 9, 45-49 */
+ break;
+
+ /*
+ * Probe channel stuff
+ *
+ * The WWV/H format contains data pulses in second 59 (position
+ * identifier) and second 1, but not in second 0. The minute
+ * sync pulse is contained in second 0. At the end of second 58
+ * QSY to the probe channel, which rotates in turn over all
+ * WWV/H frequencies. At the end of second 0 measure the minute
+ * sync pulse. At the end of second 1 measure the data pulse and
+ * QSY back to the data channel. Note that the actions commented
+ * here happen at the end of the second numbered as shown.
+ *
+ * At the end of second 0 save the minute sync amplitude latched
+ * at 800 ms as the signal later used to calculate the SNR.
+ */
+ case SYNC2: /* 0 */
+ cp = &up->mitig[up->achan];
+ cp->wwv.synmax = cp->wwv.syneng;
+ cp->wwvh.synmax = cp->wwvh.syneng;
+ break;
+
+ /*
+ * At the end of second 1 use the minute sync amplitude latched
+ * at 800 ms as the noise to calculate the SNR. If the minute
+ * sync pulse and SNR are above thresholds and the data pulse
+ * amplitude and SNR are above thresolds, shift a 1 into the
+ * station reachability register; otherwise, shift a 0. The
+ * number of 1 bits in the last six intervals is a component of
+ * the channel metric computed by the wwv_metric() routine.
+ * Finally, QSY back to the data channel.
+ */
+ case SYNC3: /* 1 */
+ cp = &up->mitig[up->achan];
+
+ /*
+ * WWV station
+ */
+ sp = &cp->wwv;
+ sp->synsnr = wwv_snr(sp->synmax, sp->amp);
+ sp->reach <<= 1;
+ if (sp->reach & (1 << AMAX))
+ sp->count--;
+ if (sp->synmax >= QTHR && sp->synsnr >= QSNR &&
+ !(up->status & (DGATE | BGATE))) {
+ sp->reach |= 1;
+ sp->count++;
+ }
+ sp->metric = wwv_metric(sp);
+
+ /*
+ * WWVH station
+ */
+ rp = &cp->wwvh;
+ rp->synsnr = wwv_snr(rp->synmax, rp->amp);
+ rp->reach <<= 1;
+ if (rp->reach & (1 << AMAX))
+ rp->count--;
+ if (rp->synmax >= QTHR && rp->synsnr >= QSNR &&
+ !(up->status & (DGATE | BGATE))) {
+ rp->reach |= 1;
+ rp->count++;
+ }
+ rp->metric = wwv_metric(rp);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv5 %04x %3d %4d %.0f/%.1f %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f",
+ up->status, up->gain, up->yepoch,
+ up->epomax, up->eposnr, up->datsig,
+ up->datsnr,
+ sp->refid, sp->reach & 0xffff,
+ sp->metric, sp->synmax, sp->synsnr,
+ rp->refid, rp->reach & 0xffff,
+ rp->metric, rp->synmax, rp->synsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ up->errcnt = up->digcnt = up->alarm = 0;
+
+ /*
+ * We now begin the minute scan. If not yet synchronized
+ * to a station, restart if the units digit has not been
+ * found within the DATA timeout (15 m) or if not
+ * synchronized within the SYNCH timeout (40 m). After
+ * synchronizing to a station, restart if no stations
+ * are found within the PANIC timeout (2 days).
+ */
+ if (up->status & INSYNC) {
+ if (up->watch > PANIC) {
+ wwv_newgame(peer);
+ return;
+ }
+ } else {
+ if (!(up->status & DSYNC)) {
+ if (up->watch > DATA) {
+ wwv_newgame(peer);
+ return;
+ }
+ }
+ if (up->watch > SYNCH) {
+ wwv_newgame(peer);
+ return;
+ }
+ }
+ wwv_newchan(peer);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ wwv_qsy(peer, up->dchan);
+#endif /* ICOM */
+ break;
+
+ /*
+ * Save the bit probability in the BCD data vector at the index
+ * given by the argument. Bits not used in the digit are forced
+ * to zero.
+ */
+ case COEF1: /* 4-7 */
+ bcddld[arg] = bit;
+ break;
+
+ case COEF: /* 10-13, 15-17, 20-23, 25-26,
+ 30-33, 35-38, 40-41, 51-54 */
+ if (up->status & DSYNC)
+ bcddld[arg] = bit;
+ else
+ bcddld[arg] = 0;
+ break;
+
+ case COEF2: /* 18, 27-28, 42-43 */
+ bcddld[arg] = 0;
+ break;
+
+ /*
+ * Correlate coefficient vector with each valid digit vector and
+ * save in decoding matrix. We step through the decoding matrix
+ * digits correlating each with the coefficients and saving the
+ * greatest and the next lower for later SNR calculation.
+ */
+ case DECIM2: /* 29 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2);
+ break;
+
+ case DECIM3: /* 44 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3);
+ break;
+
+ case DECIM6: /* 19 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6);
+ break;
+
+ case DECIM9: /* 8, 14, 24, 34, 39 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9);
+ break;
+
+ /*
+ * Miscellaneous bits. If above the positive threshold, declare
+ * 1; if below the negative threshold, declare 0; otherwise
+ * raise the BGATE bit. The design is intended to avoid
+ * integrating noise under low SNR conditions.
+ */
+ case MSC20: /* 55 */
+ wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9);
+ /* fall through */
+
+ case MSCBIT: /* 2-3, 50, 56-57 */
+ if (bitvec[nsec] > BTHR) {
+ if (!(up->misc & arg))
+ up->alarm |= CMPERR;
+ up->misc |= arg;
+ } else if (bitvec[nsec] < -BTHR) {
+ if (up->misc & arg)
+ up->alarm |= CMPERR;
+ up->misc &= ~arg;
+ } else {
+ up->status |= BGATE;
+ }
+ break;
+
+ /*
+ * Save the data channel gain, then QSY to the probe channel and
+ * dim the seconds comb filters. The newchan() routine will
+ * light them back up.
+ */
+ case MSC21: /* 58 */
+ if (bitvec[nsec] > BTHR) {
+ if (!(up->misc & arg))
+ up->alarm |= CMPERR;
+ up->misc |= arg;
+ } else if (bitvec[nsec] < -BTHR) {
+ if (up->misc & arg)
+ up->alarm |= CMPERR;
+ up->misc &= ~arg;
+ } else {
+ up->status |= BGATE;
+ }
+ up->status &= ~(SELV | SELH);
+#ifdef ICOM
+ if (up->fd_icom > 0) {
+ up->schan = (up->schan + 1) % NCHAN;
+ wwv_qsy(peer, up->schan);
+ } else {
+ up->mitig[up->achan].gain = up->gain;
+ }
+#else
+ up->mitig[up->achan].gain = up->gain;
+#endif /* ICOM */
+ break;
+
+ /*
+ * The endgames
+ *
+ * During second 59 the receiver and codec AGC are settling
+ * down, so the data pulse is unusable as quality metric. If
+ * LEPSEC is set on the last minute of 30 June or 31 December,
+ * the transmitter and receiver insert an extra second (60) in
+ * the timescale and the minute sync repeats the second. Once
+ * leaps occurred at intervals of about 18 months, but the last
+ * leap before the most recent leap in 1995 was in 1998.
+ */
+ case MIN1: /* 59 */
+ if (up->status & LEPSEC)
+ break;
+
+ /* fall through */
+
+ case MIN2: /* 60 */
+ up->status &= ~LEPSEC;
+ wwv_tsec(peer);
+ up->rsec = 0;
+ wwv_clock(peer);
+ break;
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ DSYNC)) {
+ sprintf(tbuf,
+ "wwv3 %2d %04x %3d %4d %5.0f %5.1f %5.0f %5.1f %5.0f",
+ nsec, up->status, up->gain, up->yepoch, up->epomax,
+ up->eposnr, up->datsig, up->datsnr, bit);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ pp->disp += AUDIO_PHI;
+}
+
+/*
+ * The radio clock is set if the alarm bits are all zero. After that,
+ * the time is considered valid if the second sync bit is lit. It should
+ * not be a surprise, especially if the radio is not tunable, that
+ * sometimes no stations are above the noise and the integrators
+ * discharge below the thresholds. We assume that, after a day of signal
+ * loss, the minute sync epoch will be in the same second. This requires
+ * the codec frequency be accurate within 6 PPM. Practical experience
+ * shows the frequency typically within 0.1 PPM, so after a day of
+ * signal loss, the time should be within 8.6 ms..
+ */
+static void
+wwv_clock(
+ struct peer *peer /* peer unit pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ l_fp offset; /* offset in NTP seconds */
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (!(up->status & SSYNC))
+ up->alarm |= SYNERR;
+ if (up->digcnt < 9)
+ up->alarm |= NINERR;
+ if (!(up->alarm))
+ up->status |= INSYNC;
+ if (up->status & INSYNC && up->status & SSYNC) {
+ if (up->misc & SECWAR)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->second = up->rsec;
+ pp->minute = up->decvec[MN].digit + up->decvec[MN +
+ 1].digit * 10;
+ pp->hour = up->decvec[HR].digit + up->decvec[HR +
+ 1].digit * 10;
+ pp->day = up->decvec[DA].digit + up->decvec[DA +
+ 1].digit * 10 + up->decvec[DA + 2].digit * 100;
+ pp->year = up->decvec[YR].digit + up->decvec[YR +
+ 1].digit * 10;
+ pp->year += 2000;
+ L_CLR(&offset);
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, up->timestamp.l_ui,
+ &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ up->watch = 0;
+ pp->disp = 0;
+ pp->lastref = up->timestamp;
+ refclock_process_offset(pp, offset,
+ up->timestamp, PDELAY);
+ refclock_receive(peer);
+ }
+ }
+ pp->lencode = timecode(up, pp->a_lastcode);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwv: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif /* DEBUG */
+}
+
+
+/*
+ * wwv_corr4 - determine maximum likelihood digit
+ *
+ * This routine correlates the received digit vector with the BCD
+ * coefficient vectors corresponding to all valid digits at the given
+ * position in the decoding matrix. The maximum value corresponds to the
+ * maximum likelihood digit, while the ratio of this value to the next
+ * lower value determines the likelihood function. Note that, if the
+ * digit is invalid, the likelihood vector is averaged toward a miss.
+ */
+static void
+wwv_corr4(
+ struct peer *peer, /* peer unit pointer */
+ struct decvec *vp, /* decoding table pointer */
+ double data[], /* received data vector */
+ double tab[][4] /* correlation vector array */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ double topmax, nxtmax; /* metrics */
+ double acc; /* accumulator */
+ char tbuf[80]; /* monitor buffer */
+ int mldigit; /* max likelihood digit */
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Correlate digit vector with each BCD coefficient vector. If
+ * any BCD digit bit is bad, consider all bits a miss. Until the
+ * minute units digit has been resolved, don't to anything else.
+ * Note the SNR is calculated as the ratio of the largest
+ * likelihood value to the next largest likelihood value.
+ */
+ mldigit = 0;
+ topmax = nxtmax = -MAXAMP;
+ for (i = 0; tab[i][0] != 0; i++) {
+ acc = 0;
+ for (j = 0; j < 4; j++)
+ acc += data[j] * tab[i][j];
+ acc = (vp->like[i] += (acc - vp->like[i]) / TCONST);
+ if (acc > topmax) {
+ nxtmax = topmax;
+ topmax = acc;
+ mldigit = i;
+ } else if (acc > nxtmax) {
+ nxtmax = acc;
+ }
+ }
+ vp->digprb = topmax;
+ vp->digsnr = wwv_snr(topmax, nxtmax);
+
+ /*
+ * The current maximum likelihood digit is compared to the last
+ * maximum likelihood digit. If different, the compare counter
+ * and maximum likelihood digit are reset. When the compare
+ * counter reaches the BCMP threshold (3), the digit is assumed
+ * correct. When the compare counter of all nine digits have
+ * reached threshold, the clock is assumed correct.
+ *
+ * Note that the clock display digit is set before the compare
+ * counter has reached threshold; however, the clock display is
+ * not considered correct until all nine clock digits have
+ * reached threshold. This is intended as eye candy, but avoids
+ * mistakes when the signal is low and the SNR is very marginal.
+ * once correctly set, the maximum likelihood digit is ignored
+ * on the assumption the clock will always be correct unless for
+ * some reason it drifts to a different second.
+ */
+ vp->mldigit = mldigit;
+ if (vp->digprb < BTHR || vp->digsnr < BSNR) {
+ vp->count = 0;
+ up->status |= BGATE;
+ } else {
+ up->status |= DSYNC;
+ if (vp->digit != mldigit) {
+ vp->count = 0;
+ up->alarm |= CMPERR;
+ if (!(up->status & INSYNC))
+ vp->digit = mldigit;
+ } else {
+ if (vp->count < BCMP)
+ vp->count++;
+ else
+ up->digcnt++;
+ }
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ INSYNC)) {
+ sprintf(tbuf,
+ "wwv4 %2d %04x %3d %4d %5.0f %2d %d %d %d %5.0f %5.1f",
+ up->rsec - 1, up->status, up->gain, up->yepoch,
+ up->epomax, vp->radix, vp->digit, vp->mldigit,
+ vp->count, vp->digprb, vp->digsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * wwv_tsec - transmitter minute processing
+ *
+ * This routine is called at the end of the transmitter minute. It
+ * implements a state machine that advances the logical clock subject to
+ * the funny rules that govern the conventional clock and calendar.
+ */
+static void
+wwv_tsec(
+ struct peer *peer /* driver structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ int minute, day, isleap;
+ int temp;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Advance minute unit of the day. Don't propagate carries until
+ * the unit minute digit has been found.
+ */
+ temp = carry(&up->decvec[MN]); /* minute units */
+ if (!(up->status & DSYNC))
+ return;
+
+ /*
+ * Propagate carries through the day.
+ */
+ if (temp == 0) /* carry minutes */
+ temp = carry(&up->decvec[MN + 1]);
+ if (temp == 0) /* carry hours */
+ temp = carry(&up->decvec[HR]);
+ if (temp == 0)
+ temp = carry(&up->decvec[HR + 1]);
+
+ /*
+ * Decode the current minute and day. Set leap day if the
+ * timecode leap bit is set on 30 June or 31 December. Set leap
+ * minute if the last minute on leap day, but only if the clock
+ * is syncrhronized. This code fails in 2400 AD.
+ */
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit *
+ 10 + up->decvec[HR].digit * 60 + up->decvec[HR +
+ 1].digit * 600;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+
+ /*
+ * Set the leap bit on the last minute of the leap day.
+ */
+ isleap = up->decvec[YR].digit & 0x3;
+ if (up->misc & SECWAR && up->status & INSYNC) {
+ if ((day == (isleap ? 182 : 183) || day == (isleap ?
+ 365 : 366)) && minute == 1439)
+ up->status |= LEPSEC;
+ }
+
+ /*
+ * Roll the day if this the first minute and propagate carries
+ * through the year.
+ */
+ if (minute != 1440)
+ return;
+
+ minute = 0;
+ while (carry(&up->decvec[HR]) != 0); /* advance to minute 0 */
+ while (carry(&up->decvec[HR + 1]) != 0);
+ day++;
+ temp = carry(&up->decvec[DA]); /* carry days */
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 1]);
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 2]);
+
+ /*
+ * Roll the year if this the first day and propagate carries
+ * through the century.
+ */
+ if (day != (isleap ? 365 : 366))
+ return;
+
+ day = 1;
+ while (carry(&up->decvec[DA]) != 1); /* advance to day 1 */
+ while (carry(&up->decvec[DA + 1]) != 0);
+ while (carry(&up->decvec[DA + 2]) != 0);
+ temp = carry(&up->decvec[YR]); /* carry years */
+ if (temp == 0)
+ carry(&up->decvec[YR + 1]);
+}
+
+
+/*
+ * carry - process digit
+ *
+ * This routine rotates a likelihood vector one position and increments
+ * the clock digit modulo the radix. It returns the new clock digit or
+ * zero if a carry occurred. Once synchronized, the clock digit will
+ * match the maximum likelihood digit corresponding to that position.
+ */
+static int
+carry(
+ struct decvec *dp /* decoding table pointer */
+ )
+{
+ int temp;
+ int j;
+
+ dp->digit++;
+ if (dp->digit == dp->radix)
+ dp->digit = 0;
+ temp = dp->like[dp->radix - 1];
+ for (j = dp->radix - 1; j > 0; j--)
+ dp->like[j] = dp->like[j - 1];
+ dp->like[0] = temp;
+ return (dp->digit);
+}
+
+
+/*
+ * wwv_snr - compute SNR or likelihood function
+ */
+static double
+wwv_snr(
+ double signal, /* signal */
+ double noise /* noise */
+ )
+{
+ double rval;
+
+ /*
+ * This is a little tricky. Due to the way things are measured,
+ * either or both the signal or noise amplitude can be negative
+ * or zero. The intent is that, if the signal is negative or
+ * zero, the SNR must always be zero. This can happen with the
+ * subcarrier SNR before the phase has been aligned. On the
+ * other hand, in the likelihood function the "noise" is the
+ * next maximum down from the peak and this could be negative.
+ * However, in this case the SNR is truly stupendous, so we
+ * simply cap at MAXSNR dB (40).
+ */
+ if (signal <= 0) {
+ rval = 0;
+ } else if (noise <= 0) {
+ rval = MAXSNR;
+ } else {
+ rval = 20. * log10(signal / noise);
+ if (rval > MAXSNR)
+ rval = MAXSNR;
+ }
+ return (rval);
+}
+
+
+/*
+ * wwv_newchan - change to new data channel
+ *
+ * The radio actually appears to have ten channels, one channel for each
+ * of five frequencies and each of two stations (WWV and WWVH), although
+ * if not tunable only the DCHAN channel appears live. While the radio
+ * is tuned to the working data channel frequency and station for most
+ * of the minute, during seconds 59, 0 and 1 the radio is tuned to a
+ * probe frequency in order to search for minute sync pulse and data
+ * subcarrier from other transmitters.
+ *
+ * The search for WWV and WWVH operates simultaneously, with WWV minute
+ * sync pulse at 1000 Hz and WWVH at 1200 Hz. The probe frequency
+ * rotates each minute over 2.5, 5, 10, 15 and 20 MHz in order and yes,
+ * we all know WWVH is dark on 20 MHz, but few remember when WWV was lit
+ * on 25 MHz.
+ *
+ * This routine selects the best channel using a metric computed from
+ * the reachability register and minute pulse amplitude. Normally, the
+ * award goes to the the channel with the highest metric; but, in case
+ * of ties, the award goes to the channel with the highest minute sync
+ * pulse amplitude and then to the highest frequency.
+ *
+ * The routine performs an important squelch function to keep dirty data
+ * from polluting the integrators. In order to consider a station valid,
+ * the metric must be at least MTHR (13); otherwise, the station select
+ * bits are cleared so the second sync is disabled and the data bit
+ * integrators averaged to a miss.
+ */
+static int
+wwv_newchan(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp, *rp;
+ double rank, dtemp;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Search all five station pairs looking for the channel with
+ * maximum metric. If no station is found above thresholds, tune
+ * to WWV on 15 MHz, set the reference ID to NONE and wait for
+ * hotter ions.
+ */
+ sp = NULL;
+ j = 0;
+ rank = 0;
+ for (i = 0; i < NCHAN; i++) {
+ rp = &up->mitig[i].wwvh;
+ dtemp = rp->metric;
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ rp = &up->mitig[i].wwv;
+ dtemp = rp->metric;
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ }
+
+ /*
+ * If the strongest signal is less than the MTHR threshold (13),
+ * we are beneath the waves, so squelch the second sync. If the
+ * strongest signal is greater than the threshold, tune to that
+ * frequency and transmitter QTH.
+ */
+ if (rank < MTHR) {
+ up->dchan = (up->dchan + 1) % NCHAN;
+ up->status &= ~(SELV | SELH);
+ return (FALSE);
+ }
+ up->dchan = j;
+ up->status |= SELV | SELH;
+ up->sptr = sp;
+ memcpy(&pp->refid, sp->refid, 4);
+ peer->refid = pp->refid;
+ return (TRUE);
+}
+
+
+/*
+ * wwv_newgame - reset and start over
+ *
+ * There are four conditions resulting in a new game:
+ *
+ * 1 During initial acquisition (MSYNC dark) going 6 minutes (ACQSN)
+ * without reliably finding the minute pulse (MSYNC lit).
+ *
+ * 2 After finding the minute pulse (MSYNC lit), going 15 minutes
+ * (DATA) without finding the unit seconds digit.
+ *
+ * 3 After finding good data (DATA lit), going more than 40 minutes
+ * (SYNCH) without finding station sync (INSYNC lit).
+ *
+ * 4 After finding station sync (INSYNC lit), going more than 2 days
+ * (PANIC) without finding any station.
+ */
+static void
+wwv_newgame(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ int i;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Initialize strategic values. Note we set the leap bits
+ * NOTINSYNC and the refid "NONE".
+ */
+ peer->leap = LEAP_NOTINSYNC;
+ up->watch = up->status = up->alarm = 0;
+ up->avgint = MINAVG;
+ up->freq = 0;
+ up->gain = MAXGAIN / 2;
+
+ /*
+ * Initialize the station processes for audio gain, select bit,
+ * station/frequency identifier and reference identifier. Start
+ * probing at the next channel after the data channel.
+ */
+ memset(up->mitig, 0, sizeof(up->mitig));
+ for (i = 0; i < NCHAN; i++) {
+ cp = &up->mitig[i];
+ cp->gain = up->gain;
+ cp->wwv.select = SELV;
+ sprintf(cp->wwv.refid, "WV%.0f", floor(qsy[i]));
+ cp->wwvh.select = SELH;
+ sprintf(cp->wwvh.refid, "WH%.0f", floor(qsy[i]));
+ }
+ up->dchan = (DCHAN + NCHAN - 1) % NCHAN;;
+ wwv_newchan(peer);
+ up->achan = up->schan = up->dchan;
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ wwv_qsy(peer, up->dchan);
+#endif /* ICOM */
+}
+
+/*
+ * wwv_metric - compute station metric
+ *
+ * The most significant bits represent the number of ones in the
+ * station reachability register. The least significant bits represent
+ * the minute sync pulse amplitude. The combined value is scaled 0-100.
+ */
+double
+wwv_metric(
+ struct sync *sp /* station pointer */
+ )
+{
+ double dtemp;
+
+ dtemp = sp->count * MAXAMP;
+ if (sp->synmax < MAXAMP)
+ dtemp += sp->synmax;
+ else
+ dtemp += MAXAMP - 1;
+ dtemp /= (AMAX + 1) * MAXAMP;
+ return (dtemp * 100.);
+}
+
+
+#ifdef ICOM
+/*
+ * wwv_qsy - Tune ICOM receiver
+ *
+ * This routine saves the AGC for the current channel, switches to a new
+ * channel and restores the AGC for that channel. If a tunable receiver
+ * is not available, just fake it.
+ */
+static int
+wwv_qsy(
+ struct peer *peer, /* peer structure pointer */
+ int chan /* channel */
+ )
+{
+ int rval = 0;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (up->fd_icom > 0) {
+ up->mitig[up->achan].gain = up->gain;
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[chan]);
+ up->achan = chan;
+ up->gain = up->mitig[up->achan].gain;
+ }
+ return (rval);
+}
+#endif /* ICOM */
+
+
+/*
+ * timecode - assemble timecode string and length
+ *
+ * Prettytime format - similar to Spectracom
+ *
+ * sq yy ddd hh:mm:ss ld dut lset agc iden sig errs freq avgt
+ *
+ * s sync indicator ('?' or ' ')
+ * q error bits (hex 0-F)
+ * yyyy year of century
+ * ddd day of year
+ * hh hour of day
+ * mm minute of hour
+ * ss second of minute)
+ * l leap second warning (' ' or 'L')
+ * d DST state ('S', 'D', 'I', or 'O')
+ * dut DUT sign and magnitude (0.1 s)
+ * lset minutes since last clock update
+ * agc audio gain (0-255)
+ * iden reference identifier (station and frequency)
+ * sig signal quality (0-100)
+ * errs bit errors in last minute
+ * freq frequency offset (PPM)
+ * avgt averaging time (s)
+ */
+static int
+timecode(
+ struct wwvunit *up, /* driver structure pointer */
+ char *ptr /* target string */
+ )
+{
+ struct sync *sp;
+ int year, day, hour, minute, second, dut;
+ char synchar, leapchar, dst;
+ char cptr[50];
+
+
+ /*
+ * Common fixed-format fields
+ */
+ synchar = (up->status & INSYNC) ? ' ' : '?';
+ year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10 +
+ 2000;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+ hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10;
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10;
+ second = 0;
+ leapchar = (up->misc & SECWAR) ? 'L' : ' ';
+ dst = dstcod[(up->misc >> 4) & 0x3];
+ dut = up->misc & 0x7;
+ if (!(up->misc & DUTS))
+ dut = -dut;
+ sprintf(ptr, "%c%1X", synchar, up->alarm);
+ sprintf(cptr, " %4d %03d %02d:%02d:%02d %c%c %+d",
+ year, day, hour, minute, second, leapchar, dst, dut);
+ strcat(ptr, cptr);
+
+ /*
+ * Specific variable-format fields
+ */
+ sp = up->sptr;
+ sprintf(cptr, " %d %d %s %.0f %d %.1f %d", up->watch,
+ up->mitig[up->dchan].gain, sp->refid, sp->metric,
+ up->errcnt, up->freq / SECOND * 1e6, up->avgint);
+ strcat(ptr, cptr);
+ return (strlen(ptr));
+}
+
+
+/*
+ * wwv_gain - adjust codec gain
+ *
+ * This routine is called at the end of each second. During the second
+ * the number of signal clips above the MAXAMP threshold (6000). If
+ * there are no clips, the gain is bumped up; if there are more than
+ * MAXCLP clips (100), it is bumped down. The decoder is relatively
+ * insensitive to amplitude, so this crudity works just peachy. The
+ * input port is set and the error flag is cleared, mostly to be ornery.
+ */
+static void
+wwv_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+#if DEBUG
+ if (debug > 1)
+ audio_show();
+#endif
+}
+
+
+#else
+int refclock_wwv_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_wwvb.c b/contrib/ntp/ntpd/refclock_wwvb.c
new file mode 100644
index 0000000..7bddd3a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_wwvb.c
@@ -0,0 +1,459 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB
+ * and GPS clocks have proven reliable sources of time; however, the
+ * WWVB clocks have proven vulnerable to high ambient conductive RF
+ * interference. The claimed accuracy of the WWVB clocks is 100 us
+ * relative to the broadcast signal, while the claimed accuracy of the
+ * GPS clock is 50 ns; however, in most cases the actual accuracy is
+ * limited by the resolution of the timecode and the latencies of the
+ * serial interface and operating system.
+ *
+ * The WWVB and GPS clocks should be configured for 24-hour display,
+ * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
+ * baud rate 9600. If the clock is to used as the source for the IRIG
+ * Audio Decoder (refclock_irig.c in this distribution), it should be
+ * configured for AM IRIG output and IRIG format 1 (IRIG B with
+ * signature control). The GPS clock can be configured either to respond
+ * to a 'T' poll character or left running continuously.
+ *
+ * There are two timecode formats used by these clocks. Format 0, which
+ * is available with both the Netclock/2 and 8170, and format 2, which
+ * is available only with the Netclock/2, specially modified 8170 and
+ * GPS.
+ *
+ * Format 0 (22 ASCII printing characters):
+ *
+ * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss = hours, minutes, seconds
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ *
+ * The alarm condition is indicated by other than ' ' at a, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours.
+ *
+ * Format 2 (24 ASCII printing characters):
+ *
+ * <cr><lf>iqyy ddd hh:mm:ss.fff ld
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ * yy = year (as broadcast)
+ * ddd = day of year
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ *
+ * The alarm condition is indicated by other than ' ' at a, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours. The unlock condition is indicated by other than ' '
+ * at q.
+ *
+ * The q is normally ' ' when the time error is less than 1 ms and a
+ * character in the set 'A'...'D' when the time error is less than 10,
+ * 100, 500 and greater than 500 ms respectively. The l is normally ' ',
+ * but is set to 'L' early in the month of an upcoming UTC leap second
+ * and reset to ' ' on the first day of the following month. The d is
+ * set to 'S' for standard time 'I' on the day preceding a switch to
+ * daylight time, 'D' for daylight time and 'O' on the day preceding a
+ * switch to standard time. The start bit of the first <cr> is
+ * synchronized to the indicated time as returned.
+ *
+ * This driver does not need to be told which format is in use - it
+ * figures out which one from the length of the message. The driver
+ * makes no attempt to correct for the intrinsic jitter of the radio
+ * itself, which is a known problem with the older radios.
+ *
+ * Fudge Factors
+ *
+ * This driver can retrieve a table of quality data maintained
+ * internally by the Netclock/2 clock. If flag4 of the fudge
+ * configuration command is set to 1, the driver will retrieve this
+ * table and write it to the clockstats file when the first timecode
+ * message of a new day is received.
+ *
+ * PPS calibration fudge time 1: format 0 .003134, format 2 .004034
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */
+
+#define LENWWVB0 22 /* format 0 timecode length */
+#define LENWWVB1 22 /* format 1 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define LENWWVB3 29 /* format 3 timecode length */
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * WWVB unit control structure
+ */
+struct wwvbunit {
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwvb_start P((int, struct peer *));
+static void wwvb_shutdown P((int, struct peer *));
+static void wwvb_receive P((struct recvbuf *));
+static void wwvb_poll P((int, struct peer *));
+static void wwvb_timer P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwvb = {
+ wwvb_start, /* start up driver */
+ wwvb_shutdown, /* shut down driver */
+ wwvb_poll, /* transmit poll message */
+ noentry, /* not used (old wwvb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ wwvb_timer /* called once per second */
+};
+
+
+/*
+ * wwvb_start - open the devices and initialize data for processing
+ */
+static int
+wwvb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = wwvb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * wwvb_shutdown - shut down the clock
+ */
+static void
+wwvb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * wwvb_receive - receive data from the serial interface
+ */
+static void
+wwvb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int tz; /* time zone */
+ int day, month; /* ddd conversion */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char leapchar; /* leap indicator */
+ char dstchar; /* daylight/standard indicator */
+ char tmpchar; /* trashbin */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. Note: in format 0 on
+ * a Netclock/2 or upgraded 8170 the start bit is delayed 100
+ * +-50 us relative to the pps; however, on an unmodified 8170
+ * the start bit can be delayed up to 10 ms. In format 2 the
+ * reading precision is only to the millisecond. Thus, unless
+ * you have a PPS gadget and don't have to have the year, format
+ * 0 provides the lowest jitter.
+ */
+ if (temp == 0) {
+ up->laststamp = trtmp;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code uses the timecode length to determine
+ * format 0, 2 or 3. If the timecode has invalid length or is
+ * not in proper format, we declare bad format and exit.
+ */
+ syncchar = qualchar = leapchar = dstchar = ' ';
+ tz = 0;
+ switch (pp->lencode) {
+
+ case LENWWVB0:
+
+ /*
+ * Timecode format 0: "I ddd hh:mm:ss DTZ=nn"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &tmpchar, &dstchar, &tz) == 8)
+ pp->nsec = 0;
+ break;
+
+ case LENWWVB2:
+
+ /*
+ * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
+ if (sscanf(pp->a_lastcode,
+ "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &leapchar) == 9)
+ pp->nsec *= 1000000;
+ break;
+
+ case LENWWVB3:
+
+ /*
+ * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
+ */
+ if (sscanf(pp->a_lastcode,
+ "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
+ &syncchar, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
+ {
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->nsec = 0;
+ break;
+ }
+
+ default:
+
+ /*
+ * Unknown format: If dumping internal table, record
+ * stats; otherwise, declare bad format.
+ */
+ if (up->linect > 0) {
+ up->linect--;
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+ } else {
+ refclock_report(peer, CEVNT_BADREPLY);
+ }
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ * Once synchronized, the dispersion depends only on the
+ * quality character.
+ */
+ switch (qualchar) {
+
+ case ' ':
+ pp->disp = .001;
+ pp->lastref = pp->lastrec;
+ break;
+
+ case 'A':
+ pp->disp = .01;
+ break;
+
+ case 'B':
+ pp->disp = .1;
+ break;
+
+ case 'C':
+ pp->disp = .5;
+ break;
+
+ case 'D':
+ pp->disp = MAXDISPERSE;
+ break;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ if (peer->disp > MAXDISTANCE)
+ refclock_receive(peer);
+}
+
+
+/*
+ * wwvb_timer - called once per second by the transmit procedure
+ */
+static void
+wwvb_timer(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ char pollchar; /* character sent to clock */
+
+ /*
+ * Time to poll the clock. The Spectracom clock responds to a
+ * 'T' by returning a timecode in the format(s) specified above.
+ * Note there is no checking on state, since this may not be the
+ * only customer reading the clock. Only one customer need poll
+ * the clock; all others just listen in.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+}
+
+
+/*
+ * wwvb_poll - called by the transmit procedure
+ */
+static void
+wwvb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Sweep up the samples received since the last poll. If none
+ * are received, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ pp->polls++;
+
+ /*
+ * If the monitor flag is set (flag4), we dump the internal
+ * quality table at the first timecode beginning the day.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
+ (int)up->lasthour)
+ up->linect = MONLIN;
+ up->lasthour = pp->hour;
+
+ /*
+ * Process median filter samples. If none received, declare a
+ * timeout and keep going.
+ */
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+}
+
+#else
+int refclock_wwvb_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_zyfer.c b/contrib/ntp/ntpd/refclock_zyfer.c
new file mode 100644
index 0000000..44f2c4d
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_zyfer.c
@@ -0,0 +1,346 @@
+/*
+ * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
+ *
+ * Harlan Stenn, Jan 2002
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+/*
+ * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
+ * This clock also provides PPS as well as IRIG outputs.
+ * Precision is limited by the serial driver, etc.
+ *
+ * If I was really brave I'd hack/generalize the serial driver to deal
+ * with arbitrary on-time characters. This clock *begins* the stream with
+ * `!`, the on-time character, and the string is *not* EOL-terminated.
+ *
+ * Configure the beast for 9600, 8N1. While I see leap-second stuff
+ * in the documentation, the published specs on the TOD format only show
+ * the seconds going to '59'. I see no leap warning in the TOD format.
+ *
+ * The clock sends the following message once per second:
+ *
+ * !TIME,2002,017,07,59,32,2,4,1
+ * YYYY DDD HH MM SS m T O
+ *
+ * ! On-time character
+ * YYYY Year
+ * DDD 001-366 Day of Year
+ * HH 00-23 Hour
+ * MM 00-59 Minute
+ * SS 00-59 Second (probably 00-60)
+ * m 1-5 Time Mode:
+ * 1 = GPS time
+ * 2 = UTC time
+ * 3 = LGPS time (Local GPS)
+ * 4 = LUTC time (Local UTC)
+ * 5 = Manual time
+ * T 4-9 Time Figure Of Merit:
+ * 4 x <= 1us
+ * 5 1us < x <= 10 us
+ * 6 10us < x <= 100us
+ * 7 100us < x <= 1ms
+ * 8 1ms < x <= 10ms
+ * 9 10ms < x
+ * O 0-4 Operation Mode:
+ * 0 Warm-up
+ * 1 Time Locked
+ * 2 Coasting
+ * 3 Recovering
+ * 4 Manual
+ *
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/zyfer%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Zyfer GPStarplus" /* WRU */
+
+#define LENZYFER 29 /* timecode length */
+
+/*
+ * Unit control structure
+ */
+struct zyferunit {
+ u_char Rcvbuf[LENZYFER + 1];
+ u_char polled; /* poll message flag */
+ int pollcnt;
+ l_fp tstamp; /* timestamp of last poll */
+ int Rcvptr;
+};
+
+/*
+ * Function prototypes
+ */
+static int zyfer_start P((int, struct peer *));
+static void zyfer_shutdown P((int, struct peer *));
+static void zyfer_receive P((struct recvbuf *));
+static void zyfer_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_zyfer = {
+ zyfer_start, /* start up driver */
+ zyfer_shutdown, /* shut down driver */
+ zyfer_poll, /* transmit poll message */
+ noentry, /* not used (old zyfer_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old zyfer_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * zyfer_start - open the devices and initialize data for processing
+ */
+static int
+zyfer_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port.
+ * Something like LDISC_ACTS that looked for ! would be nice...
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
+ return (0);
+
+ msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct zyferunit *)
+ emalloc(sizeof(struct zyferunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct zyferunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = zyfer_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->polled = 0; /* May not be needed... */
+
+ return (1);
+}
+
+
+/*
+ * zyfer_shutdown - shut down the clock
+ */
+static void
+zyfer_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * zyfer_receive - receive data from the serial interface
+ */
+static void
+zyfer_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int tmode; /* Time mode */
+ int tfom; /* Time Figure Of Merit */
+ int omode; /* Operation mode */
+ u_char *p;
+#ifdef PPS
+ struct ppsclockev ppsev;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+#endif /* PPS */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+ /*
+ * If lencode is 0:
+ * - if *rbufp->recv_space is !
+ * - - call refclock_gtlin to get things going
+ * - else flush
+ * else stuff it on the end of lastcode
+ * If we don't have LENZYFER bytes
+ * - wait for more data
+ * Crack the beast, and if it's OK, process it.
+ *
+ * We use refclock_gtlin() because we might use LDISC_CLK.
+ *
+ * Under FreeBSD, we get the ! followed by two 14-byte packets.
+ */
+
+ if (pp->lencode >= LENZYFER)
+ pp->lencode = 0;
+
+ if (!pp->lencode) {
+ if (*p == '!')
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
+ BMAX, &pp->lastrec);
+ else
+ return;
+ } else {
+ memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
+ pp->lencode += rbufp->recv_length;
+ pp->a_lastcode[pp->lencode] = '\0';
+ }
+
+ if (pp->lencode < LENZYFER)
+ return;
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+
+ if (pp->lencode != LENZYFER) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
+ */
+ if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
+ &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
+ &tmode, &tfom, &omode) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ if (tmode != 2) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /* Should we make sure tfom is 4? */
+
+ if (omode != 1) {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#ifdef PPS
+ if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
+ ppsev.tv.tv_sec += (u_int32) JAN_1970;
+ TVTOTS(&ppsev.tv,&up->tstamp);
+ }
+ /* record the last ppsclock event time stamp */
+ pp->lastrec = up->tstamp;
+#endif /* PPS */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Good place for record_clock_stats()
+ */
+ up->pollcnt = 2;
+
+ if (up->polled) {
+ up->polled = 0;
+ refclock_receive(peer);
+ }
+}
+
+
+/*
+ * zyfer_poll - called by the transmit procedure
+ */
+static void
+zyfer_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ if (!up->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+ up->polled = 1;
+}
+
+#else
+int refclock_zyfer_bs;
+#endif /* REFCLOCK */
OpenPOWER on IntegriCloud