diff options
author | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
commit | ef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch) | |
tree | fc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd | |
download | FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz |
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd')
54 files changed, 48161 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/Makefile.am b/contrib/ntp/ntpd/Makefile.am new file mode 100644 index 0000000..9926684 --- /dev/null +++ b/contrib/ntp/ntpd/Makefile.am @@ -0,0 +1,45 @@ +#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies +AUTOMAKE_OPTIONS = ../util/ansi2knr +bin_PROGRAMS = ntpd +INCLUDES = -I$(top_srcdir)/include +# LDADD might need RESLIB and ADJLIB +LDADD = version.o @LIBPARSE@ ../libntp/libntp.a @LIBRSAREF@ +ntpd_LDADD = $(LDADD) -lm +DISTCLEANFILES = .version version.c +#EXTRA_DIST = ntpd.mak +ETAGS_ARGS = Makefile.am +### Y2Kfixes +check_PROGRAMS = @MAKE_CHECK_Y2K@ +EXTRA_PROGRAMS = check_y2k + +check-local: @MAKE_CHECK_Y2K@ + [ -z "@MAKE_CHECK_Y2K@" ] || ./@MAKE_CHECK_Y2K@ + +ntpd_SOURCES = map_vme.c ntp_config.c ntp_control.c ntp_io.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 ntp_intres.c ntp_filegen.c ntpd.c \ + refclock_conf.c refclock_chu.c refclock_local.c \ + refclock_pst.c refclock_wwvb.c refclock_mx4200.c \ + refclock_parse.c refclock_as2201.c refclock_bancomm.c \ + refclock_tpro.c refclock_leitch.c refclock_irig.c \ + refclock_msfees.c refclock_trak.c refclock_datum.c \ + refclock_acts.c refclock_heath.c refclock_nmea.c \ + refclock_atom.c refclock_ptbacts.c refclock_jupiter.c \ + refclock_usno.c refclock_true.c refclock_hpgps.c \ + refclock_shm.c refclock_gpsvme.c refclock_arbiter.c \ + refclock_arc.c refclock_palisade.c refclock_palisade.h \ + refclock_oncore.c refclock_chronolog.c refclock_dumbclock.c \ + refclock_ulink.c jupiter.h + +$(PROGRAMS): $(LDADD) + +../libntp/libntp.a: + cd ../libntp && $(MAKE) + +../libparse/libparse.a: + cd ../libparse && $(MAKE) + +version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ @LIBRSAREF@ Makefile + $(top_builddir)/scripts/mkver ntpd + $(COMPILE) -c version.c diff --git a/contrib/ntp/ntpd/Makefile.in b/contrib/ntp/ntpd/Makefile.in new file mode 100644 index 0000000..ac5e591 --- /dev/null +++ b/contrib/ntp/ntpd/Makefile.in @@ -0,0 +1,907 @@ +# Makefile.in generated automatically by automake 1.4a from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_FLAG = +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_alias = @build_alias@ +build_triplet = @build@ +host_alias = @host_alias@ +host_triplet = @host@ +target_alias = @target_alias@ +target_triplet = @target@ +AMTAR = @AMTAR@ +AMTARFLAGS = @AMTARFLAGS@ +AWK = @AWK@ +CC = @CC@ +CFLAGS = @CFLAGS@ +CHUTEST = @CHUTEST@ +CLKTEST = @CLKTEST@ +CPP = @CPP@ +DCFD = @DCFD@ +LDFLAGS = @LDFLAGS@ +LIBPARSE = @LIBPARSE@ +LIBRSAREF = @LIBRSAREF@ +LN_S = @LN_S@ +MAKEINFO = @MAKEINFO@ +MAKE_ADJTIMED = @MAKE_ADJTIMED@ +MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@ +MAKE_LIBPARSE = @MAKE_LIBPARSE@ +MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@ +MAKE_LIBRSAREF = @MAKE_LIBRSAREF@ +MAKE_NTPTIME = @MAKE_NTPTIME@ +MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@ +MAKE_TICKADJ = @MAKE_TICKADJ@ +PACKAGE = @PACKAGE@ +PATH_SH = @PATH_SH@ +PROPDELAY = @PROPDELAY@ +RANLIB = @RANLIB@ +RSAREF = @RSAREF@ +TESTDCF = @TESTDCF@ +U = @U@ +VERSION = @VERSION@ + +#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies + + +AUTOMAKE_OPTIONS = ../util/ansi2knr +bin_PROGRAMS = ntpd +INCLUDES = -I$(top_srcdir)/include +# LDADD might need RESLIB and ADJLIB +LDADD = version.o @LIBPARSE@ ../libntp/libntp.a @LIBRSAREF@ +ntpd_LDADD = $(LDADD) -lm +DISTCLEANFILES = .version version.c +#EXTRA_DIST = ntpd.mak +ETAGS_ARGS = Makefile.am +### Y2Kfixes +check_PROGRAMS = @MAKE_CHECK_Y2K@ +EXTRA_PROGRAMS = check_y2k + +ntpd_SOURCES = map_vme.c ntp_config.c ntp_control.c ntp_io.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 ntp_intres.c ntp_filegen.c ntpd.c \ + refclock_conf.c refclock_chu.c refclock_local.c \ + refclock_pst.c refclock_wwvb.c refclock_mx4200.c \ + refclock_parse.c refclock_as2201.c refclock_bancomm.c \ + refclock_tpro.c refclock_leitch.c refclock_irig.c \ + refclock_msfees.c refclock_trak.c refclock_datum.c \ + refclock_acts.c refclock_heath.c refclock_nmea.c \ + refclock_atom.c refclock_ptbacts.c refclock_jupiter.c \ + refclock_usno.c refclock_true.c refclock_hpgps.c \ + refclock_shm.c refclock_gpsvme.c refclock_arbiter.c \ + refclock_arc.c refclock_palisade.c refclock_palisade.h \ + refclock_oncore.c refclock_chronolog.c refclock_dumbclock.c \ + refclock_ulink.c jupiter.h + +subdir = ntpd +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +PROGRAMS = $(bin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I.. +CPPFLAGS = @CPPFLAGS@ +LIBS = @LIBS@ +ANSI2KNR = ../util/ansi2knr +check_y2k_SOURCES = check_y2k.c +check_y2k_OBJECTS = check_y2k$U.o +check_y2k_LDADD = $(LDADD) +check_y2k_DEPENDENCIES = version.o ../libntp/libntp.a +check_y2k_LDFLAGS = +am_ntpd_OBJECTS = map_vme$U.o ntp_config$U.o ntp_control$U.o ntp_io$U.o \ +ntp_loopfilter$U.o ntp_monitor$U.o ntp_peer$U.o ntp_proto$U.o \ +ntp_refclock$U.o ntp_request$U.o ntp_restrict$U.o ntp_timer$U.o \ +ntp_util$U.o ntp_intres$U.o ntp_filegen$U.o ntpd$U.o refclock_conf$U.o \ +refclock_chu$U.o refclock_local$U.o refclock_pst$U.o refclock_wwvb$U.o \ +refclock_mx4200$U.o refclock_parse$U.o refclock_as2201$U.o \ +refclock_bancomm$U.o refclock_tpro$U.o refclock_leitch$U.o \ +refclock_irig$U.o refclock_msfees$U.o refclock_trak$U.o \ +refclock_datum$U.o refclock_acts$U.o refclock_heath$U.o \ +refclock_nmea$U.o refclock_atom$U.o refclock_ptbacts$U.o \ +refclock_jupiter$U.o refclock_usno$U.o refclock_true$U.o \ +refclock_hpgps$U.o refclock_shm$U.o refclock_gpsvme$U.o \ +refclock_arbiter$U.o refclock_arc$U.o refclock_palisade$U.o \ +refclock_oncore$U.o refclock_chronolog$U.o refclock_dumbclock$U.o \ +refclock_ulink$U.o +ntpd_OBJECTS = $(am_ntpd_OBJECTS) +ntpd_DEPENDENCIES = version.o ../libntp/libntp.a +ntpd_LDFLAGS = +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = check_y2k.c $(ntpd_SOURCES) +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +GZIP_ENV = --best +SOURCES = check_y2k.c $(ntpd_SOURCES) +OBJECTS = check_y2k$U.o $(am_ntpd_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .c .o +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps ntpd/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-binPROGRAMS: + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +distclean-binPROGRAMS: + +maintainer-clean-binPROGRAMS: + +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + f="`echo $$p|sed -e 's/$(EXEEXT)$$//' -e '$(transform)' -e 's/$$/$(EXEEXT)/'`"; \ + echo " $(INSTALL_PROGRAM) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f="`echo $$p|sed -e 's/$(EXEEXT)$$//' -e '$(transform)' -e 's/$$/$(EXEEXT)/'`"; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +mostlyclean-checkPROGRAMS: + +clean-checkPROGRAMS: + -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) + +distclean-checkPROGRAMS: + +maintainer-clean-checkPROGRAMS: + +.c.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: +../util/ansi2knr: ../util/ansi2knr.o + cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr + +../util/ansi2knr.o: + cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr.o + + +mostlyclean-kr: + -rm -f *_.c + +clean-kr: + +distclean-kr: + +maintainer-clean-kr: +check_y2k$U.o: + +check_y2k: $(check_y2k_OBJECTS) $(check_y2k_DEPENDENCIES) + @rm -f check_y2k + $(LINK) $(check_y2k_LDFLAGS) $(check_y2k_OBJECTS) $(check_y2k_LDADD) $(LIBS) +map_vme$U.o: +ntp_config$U.o: +ntp_control$U.o: +ntp_io$U.o: +ntp_loopfilter$U.o: +ntp_monitor$U.o: +ntp_peer$U.o: +ntp_proto$U.o: +ntp_refclock$U.o: +ntp_request$U.o: +ntp_restrict$U.o: +ntp_timer$U.o: +ntp_util$U.o: +ntp_intres$U.o: +ntp_filegen$U.o: +ntpd$U.o: +refclock_conf$U.o: +refclock_chu$U.o: +refclock_local$U.o: +refclock_pst$U.o: +refclock_wwvb$U.o: +refclock_mx4200$U.o: +refclock_parse$U.o: +refclock_as2201$U.o: +refclock_bancomm$U.o: +refclock_tpro$U.o: +refclock_leitch$U.o: +refclock_irig$U.o: +refclock_msfees$U.o: +refclock_trak$U.o: +refclock_datum$U.o: +refclock_acts$U.o: +refclock_heath$U.o: +refclock_nmea$U.o: +refclock_atom$U.o: +refclock_ptbacts$U.o: +refclock_jupiter$U.o: +refclock_usno$U.o: +refclock_true$U.o: +refclock_hpgps$U.o: +refclock_shm$U.o: +refclock_gpsvme$U.o: +refclock_arbiter$U.o: +refclock_arc$U.o: +refclock_palisade$U.o: +refclock_oncore$U.o: +refclock_chronolog$U.o: +refclock_dumbclock$U.o: +refclock_ulink$U.o: + +ntpd: $(ntpd_OBJECTS) $(ntpd_DEPENDENCIES) + @rm -f ntpd + $(LINK) $(ntpd_LDFLAGS) $(ntpd_OBJECTS) $(ntpd_LDADD) $(LIBS) +check_y2k_.c: check_y2k.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > check_y2k_.c +map_vme_.c: map_vme.c $(ANSI2KNR) + $(CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/map_vme.c; then echo $(srcdir)/map_vme.c; else echo map_vme.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > map_vme_.c +ntp_config_.c: ntp_config.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_config_.c +ntp_control_.c: ntp_control.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_control_.c +ntp_filegen_.c: ntp_filegen.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_filegen_.c +ntp_intres_.c: ntp_intres.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_intres_.c +ntp_io_.c: ntp_io.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_io_.c +ntp_loopfilter_.c: ntp_loopfilter.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_loopfilter_.c +ntp_monitor_.c: ntp_monitor.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_monitor_.c +ntp_peer_.c: ntp_peer.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_peer_.c +ntp_proto_.c: ntp_proto.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_proto_.c +ntp_refclock_.c: ntp_refclock.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_refclock_.c +ntp_request_.c: ntp_request.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_request_.c +ntp_restrict_.c: ntp_restrict.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_restrict_.c +ntp_timer_.c: ntp_timer.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_timer_.c +ntp_util_.c: ntp_util.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntp_util_.c +ntpd_.c: ntpd.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > ntpd_.c +refclock_acts_.c: refclock_acts.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_acts_.c +refclock_arbiter_.c: refclock_arbiter.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_arbiter_.c +refclock_arc_.c: refclock_arc.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_arc_.c +refclock_as2201_.c: refclock_as2201.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_as2201_.c +refclock_atom_.c: refclock_atom.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_atom_.c +refclock_bancomm_.c: refclock_bancomm.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_bancomm_.c +refclock_chronolog_.c: refclock_chronolog.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_chronolog_.c +refclock_chu_.c: refclock_chu.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_chu_.c +refclock_conf_.c: refclock_conf.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_conf_.c +refclock_datum_.c: refclock_datum.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_datum_.c +refclock_dumbclock_.c: refclock_dumbclock.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_dumbclock_.c +refclock_gpsvme_.c: refclock_gpsvme.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_gpsvme_.c +refclock_heath_.c: refclock_heath.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_heath_.c +refclock_hpgps_.c: refclock_hpgps.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_hpgps_.c +refclock_irig_.c: refclock_irig.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_irig_.c +refclock_jupiter_.c: refclock_jupiter.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_jupiter_.c +refclock_leitch_.c: refclock_leitch.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_leitch_.c +refclock_local_.c: refclock_local.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_local_.c +refclock_msfees_.c: refclock_msfees.c $(ANSI2KNR) + $(CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_msfees.c; then echo $(srcdir)/refclock_msfees.c; else echo refclock_msfees.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > refclock_msfees_.c +refclock_mx4200_.c: refclock_mx4200.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_mx4200_.c +refclock_nmea_.c: refclock_nmea.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_nmea_.c +refclock_oncore_.c: refclock_oncore.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_oncore_.c +refclock_palisade_.c: refclock_palisade.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_palisade_.c +refclock_parse_.c: refclock_parse.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_parse_.c +refclock_pst_.c: refclock_pst.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_pst_.c +refclock_ptbacts_.c: refclock_ptbacts.c $(ANSI2KNR) + $(CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ptbacts.c; then echo $(srcdir)/refclock_ptbacts.c; else echo refclock_ptbacts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > refclock_ptbacts_.c +refclock_shm_.c: refclock_shm.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_shm_.c +refclock_tpro_.c: refclock_tpro.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_tpro_.c +refclock_trak_.c: refclock_trak.c $(ANSI2KNR) + $(CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_trak.c; then echo $(srcdir)/refclock_trak.c; else echo refclock_trak.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > refclock_trak_.c +refclock_true_.c: refclock_true.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_true_.c +refclock_ulink_.c: refclock_ulink.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_ulink_.c +refclock_usno_.c: refclock_usno.c $(ANSI2KNR) + $(CPP) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_usno.c; then echo $(srcdir)/refclock_usno.c; else echo refclock_usno.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > refclock_usno_.c +refclock_wwvb_.c: refclock_wwvb.c $(ANSI2KNR) + $(CPP) $(DEFS) $(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) > refclock_wwvb_.c +check_y2k_.o map_vme_.o ntp_config_.o ntp_control_.o ntp_filegen_.o \ +ntp_intres_.o ntp_io_.o ntp_loopfilter_.o ntp_monitor_.o ntp_peer_.o \ +ntp_proto_.o ntp_refclock_.o ntp_request_.o ntp_restrict_.o \ +ntp_timer_.o ntp_util_.o ntpd_.o refclock_acts_.o refclock_arbiter_.o \ +refclock_arc_.o refclock_as2201_.o refclock_atom_.o refclock_bancomm_.o \ +refclock_chronolog_.o refclock_chu_.o refclock_conf_.o \ +refclock_datum_.o refclock_dumbclock_.o refclock_gpsvme_.o \ +refclock_heath_.o refclock_hpgps_.o refclock_irig_.o \ +refclock_jupiter_.o refclock_leitch_.o refclock_local_.o \ +refclock_msfees_.o refclock_mx4200_.o refclock_nmea_.o \ +refclock_oncore_.o refclock_palisade_.o refclock_parse_.o \ +refclock_pst_.o refclock_ptbacts_.o refclock_shm_.o refclock_tpro_.o \ +refclock_trak_.o refclock_true_.o refclock_ulink_.o refclock_usno_.o \ +refclock_wwvb_.o : $(ANSI2KNR) + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + ${AWK:-awk} ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + ${AWK:-awk} ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +check_y2k.o: check_y2k.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_malloc.h \ + ../include/ntp_refclock.h ../include/recvbuff.h \ + ../include/ntp_calendar.h ../include/parse.h \ + ../include/parse_conf.h +map_vme.o: map_vme.c ../config.h +ntp_config.o: ntp_config.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_filegen.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +ntp_control.o: ntp_control.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_control.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_filegen.o: ntp_filegen.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_string.h ../include/ntp_calendar.h \ + ../include/ntp_filegen.h ../include/ntp_stdlib.h \ + ../include/l_stdlib.h +ntp_intres.o: ntp_intres.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_request.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_io.o: ntp_io.c ../config.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h ../include/iosignal.h \ + ../include/ntp_if.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_loopfilter.o: ntp_loopfilter.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_syscall.h +ntp_monitor.o: ntp_monitor.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h ../include/ntp_if.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +ntp_peer.o: ntp_peer.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_proto.o: ntp_proto.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_unixtime.h ../include/ntp_control.h +ntp_refclock.o: ntp_refclock.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_request.o: ntp_request.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_request.h ../include/ntp_control.h \ + ../include/ntp_if.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_syscall.h +ntp_restrict.o: ntp_restrict.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_if.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +ntp_timer.o: ntp_timer.c ../config.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntp_util.o: ntp_util.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_filegen.h \ + ../include/ntp_if.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +ntpd.o: ntpd.c ../config.h ../include/ntpd.h ../include/ntp_syslog.h \ + ../include/ntp_fp.h ../include/ntp_types.h \ + ../include/ntp_machine.h ../include/ntp_proto.h \ + ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_acts.o: refclock_acts.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_control.h +refclock_arbiter.o: refclock_arbiter.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_arc.o: refclock_arc.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_as2201.o: refclock_as2201.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_atom.o: refclock_atom.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_bancomm.o: refclock_bancomm.c ../config.h +refclock_chronolog.o: refclock_chronolog.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_chu.o: refclock_chu.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_conf.o: refclock_conf.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_datum.o: refclock_datum.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_dumbclock.o: refclock_dumbclock.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_gpsvme.o: refclock_gpsvme.c ../config.h +refclock_heath.o: refclock_heath.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_hpgps.o: refclock_hpgps.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_irig.o: refclock_irig.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_jupiter.o: refclock_jupiter.c ../config.h +refclock_leitch.o: refclock_leitch.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_local.o: refclock_local.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_syscall.h +refclock_msfees.o: refclock_msfees.c ../config.h +refclock_mx4200.o: refclock_mx4200.c ../config.h +refclock_nmea.o: refclock_nmea.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_oncore.o: refclock_oncore.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_palisade.o: refclock_palisade.c ../config.h refclock_palisade.h \ + ../include/ntpd.h ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_control.h ../include/ntp_unixtime.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_parse.o: refclock_parse.c ../config.h +refclock_pst.o: refclock_pst.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h +refclock_ptbacts.o: refclock_ptbacts.c ../config.h refclock_acts.c \ + ../include/ntpd.h ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_control.h +refclock_shm.o: refclock_shm.c ../config.h +refclock_tpro.o: refclock_tpro.c ../config.h +refclock_trak.o: refclock_trak.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_stdlib.h ../include/ntp_string.h \ + ../include/l_stdlib.h ../include/ntp_unixtime.h +refclock_true.o: refclock_true.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_ulink.o: refclock_ulink.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h +refclock_usno.o: refclock_usno.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_unixtime.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h \ + ../include/ntp_control.h +refclock_wwvb.o: refclock_wwvb.c ../config.h ../include/ntpd.h \ + ../include/ntp_syslog.h ../include/ntp_fp.h \ + ../include/ntp_types.h ../include/ntp_machine.h \ + ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \ + ../include/ntp_malloc.h ../include/ntp_refclock.h \ + ../include/recvbuff.h ../include/ntp_io.h \ + ../include/ntp_calendar.h ../include/ntp_stdlib.h \ + ../include/ntp_string.h ../include/l_stdlib.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-binPROGRAMS +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-binPROGRAMS +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_STRIP_FLAG=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-checkPROGRAMS \ + mostlyclean-compile mostlyclean-kr mostlyclean-tags \ + mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-compile clean-kr \ + clean-tags clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-binPROGRAMS distclean-checkPROGRAMS \ + distclean-compile distclean-kr distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-binPROGRAMS \ + maintainer-clean-checkPROGRAMS maintainer-clean-compile \ + maintainer-clean-kr maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ +maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ +mostlyclean-checkPROGRAMS distclean-checkPROGRAMS clean-checkPROGRAMS \ +maintainer-clean-checkPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile mostlyclean-kr distclean-kr \ +clean-kr maintainer-clean-kr tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi \ +check-local check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all install-strip \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +check-local: @MAKE_CHECK_Y2K@ + [ -z "@MAKE_CHECK_Y2K@" ] || ./@MAKE_CHECK_Y2K@ + +$(PROGRAMS): $(LDADD) + +../libntp/libntp.a: + cd ../libntp && $(MAKE) + +../libparse/libparse.a: + cd ../libparse && $(MAKE) + +version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ @LIBRSAREF@ Makefile + $(top_builddir)/scripts/mkver ntpd + $(COMPILE) -c version.c + +# 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..3cc05fc --- /dev/null +++ b/contrib/ntp/ntpd/check_y2k.c @@ -0,0 +1,624 @@ +/* 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 <sys/types.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 */ +# include <sys/signal.h> +# ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif /* HAVE_SYS_IOCTL_H */ +# include <sys/time.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 */ + +#include "ntpd.h" + +/* } end definitions lifted from ntpd.c */ + +#include "ntp_calendar.h" +#include "parse.h" + +#define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 ) + +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/jupiter.h b/contrib/ntp/ntpd/jupiter.h new file mode 100644 index 0000000..84b9a59 --- /dev/null +++ b/contrib/ntp/ntpd/jupiter.h @@ -0,0 +1,255 @@ +/* @(#) $Header: /cvs/ntp/ntpd/jupiter.h,v 1.1.1.1 1999/05/26 00:48:19 stenn Exp $ (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/map_vme.c b/contrib/ntp/ntpd/map_vme.c new file mode 100644 index 0000000..3c161cc --- /dev/null +++ b/contrib/ntp/ntpd/map_vme.c @@ -0,0 +1,135 @@ +/********************************************************/ +/* map_vme.c */ +/* VME control of TrueTime VME-SG sync gen card */ +/* and TrueTime GPS-VME receiver card */ +/* Version for 700 series HPUX 9.0 */ +/* Richard E.Schmidt, US Naval Observatory, Washington */ +/* 27 March 94 */ +/********************************************************/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_GPSVME) +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/rtprio.h> /* for rtprio */ +#include <sys/lock.h> /* for plock */ +#include "/etc/conf/machine/vme2.h" +#include "/etc/conf/h/io.h" +#include "gps.h" + +/* GLOBALS */ +void *gps_base; +unsigned short *greg[NREGS]; +struct vme2_map_addr ma; /* memory mapped structure */ +int fd; /* file descriptor for VME */ + +void unmap_vme (); + +caddr_t +map_vme ( + char *filename + ) +{ + int ret; + caddr_t base; + struct vme2_io_testx tx; + caddr_t cp; + +#define VME_START_ADDR 0x00000 /* Starting address in A16N VME Space */ +#define VMESIZE 0xFF /* 256 bytes of A16N space length */ + + /* + To create the HP9000/700 series device file, /dev/vme2: + mknod /dev/vme2 c 44 0x0; chmod 600 /dev/vme2 + + Then must create /etc/vme.CFG and run /etc/vme_config and reboot. + */ + if ((fd = open (filename, O_RDWR)) < 0) { + printf("ERROR: VME bus adapter open failed. errno:%d\n", + errno); + if(errno == ENODEV) { + printf("ENODEV. Is driver in kernel? vme2 in dfile?\n"); + } + exit(errno); + } + tx.card_type = VME_A16; + tx.vme_addr = VME_START_ADDR; + tx.width = SHORT_WIDE; + + if(ioctl(fd, VME2_IO_TESTR, &tx)) { + printf("ioctl to test VME space failed. Errno: %d\n", + errno); + exit(errno); + } + if(tx.error) + printf("io_testr failed internal error %d\n",tx.error); + if(tx.access_result <= 0) { + printf("io_testr failed\n"); + exit(2); + } + + /* If successful mmap the device */ + /* NOW MAP THE CARD */ + ma.card_type = VME_A16; + ma.vme_addr = VME_START_ADDR; + ma.size = VMESIZE; + + if(ioctl(fd, VME2_MAP_ADDR, &ma)) { + printf("ioctl to map VME space failed. Errno: %d\n", + errno); + exit(errno); + } + if(ma.error) { + printf("ioctl to map VME failed\n"); + exit(ENOMEM); + } + base = ma.user_addr; + return(base); +} + + +void +unmap_vme(void) +{ + if(ioctl(fd, VME2_UNMAP_ADDR, &ma)) + printf("ioctl to unmap VME space failed. Errno: %d\n", + errno); + close(fd); + return; +} + + +int +init_vme(boid) +{ + /* set up address offsets */ + + gps_base = map_vme (GPS_VME); + +/* offsets from base address: */ + + greg[0] = (unsigned short *)gps_base + GFRZ1; + greg[1] = (unsigned short *)gps_base + GUFRZ1; + greg[2] = (unsigned short *)gps_base + GREG1A; + greg[3] = (unsigned short *)gps_base + GREG1B; + greg[4] = (unsigned short *)gps_base + GREG1C; + greg[5] = (unsigned short *)gps_base + GREG1D; + greg[6] = (unsigned short *)gps_base + GREG1E; + + return (0); +} + +#else /* not (REFCLOCK && CLOCK_GPSVME) */ +int map_vme_bs; +#endif /* not (REFCLOCK && CLOCK_GPSVME) */ diff --git a/contrib/ntp/ntpd/ntp_config.c b/contrib/ntp/ntpd/ntp_config.c new file mode 100644 index 0000000..e21f5b0 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_config.c @@ -0,0 +1,2405 @@ +/* + * ntp_config.c - read and apply configuration information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/param.h> +#include <sys/types.h> +#include <signal.h> +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif +#if !defined(VMS) +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#endif /* VMS */ +#include <sys/time.h> + +#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" + +#ifdef SYS_WINNT +#include <io.h> +extern HANDLE ResolverThreadHandle; +#endif /* SYS_WINNT */ + +/* + * 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. + */ + +/* + * Configuration file name + */ +#ifndef CONFIG_FILE +# ifndef SYS_WINNT +# define CONFIG_FILE "/etc/ntp.conf" +# else /* SYS_WINNT */ +# define CONFIG_FILE "%windir%\\system32\\drivers\\etc\\ntp.conf" +# define ALT_CONFIG_FILE "%windir%\\ntp.conf" +# endif /* SYS_WINNT */ +#endif /* not CONFIG_FILE */ + +/* + * We understand the following configuration entries and defaults. + * + * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ] + * broadcastclient + * multicastclient [ 224.0.1.1 ] + * manycastclient [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * manycastserver [ 224.0.1.1 ] + * broadcastdelay 0.0102 + * restrict [ addr ] [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery + * driftfile file_name + * keys file_name + * statsdir /var/NTP/ + * filegen peerstats [ file peerstats ] [ type day ] [ link ] + * clientlimit [ n ] + * clientperiod [ 3600 ] + * trustedkey [ key ] + * requestkey [ key] + * controlkey [ key ] + * trap [ addr ] + * fudge [ addr ] [ stratum ] [ refid ] ... + * pidfile [ ] + * setvar [ ] + * logfile logfile + * logconfig [+|-|=][{sync|sys|peer|clock}{{,all}{info|statistics|events|status}}]... + * enable auth|bclient|pll|kernel|monitor|stats + * disable auth|bclient|pll|kernel|monitor|stats + * phone ... + * pps device [assert|clear] [hardpps] + */ + +/* + * Types of entries we understand. + */ +#define CONFIG_UNKNOWN 0 + +#define CONFIG_PEER 1 +#define CONFIG_SERVER 2 +#define CONFIG_AUTOMAX 3 +#define CONFIG_DRIFTFILE 4 +#define CONFIG_BROADCAST 5 +#define CONFIG_BROADCASTCLIENT 6 +#define CONFIG_AUTHENTICATE 7 +#define CONFIG_KEYS 8 +#define CONFIG_REVOKE 9 +#define CONFIG_PPS 10 +#define CONFIG_RESTRICT 11 +#define CONFIG_BDELAY 12 +#define CONFIG_TRUSTEDKEY 13 +#define CONFIG_REQUESTKEY 14 +#define CONFIG_CONTROLKEY 15 +#define CONFIG_TRAP 16 +#define CONFIG_FUDGE 17 +#define CONFIG_18 18 /* unused */ +#define CONFIG_STATSDIR 19 +#define CONFIG_FILEGEN 20 +#define CONFIG_STATISTICS 21 +#define CONFIG_PIDFILE 22 +#define CONFIG_SETVAR 23 +#define CONFIG_CLIENTLIMIT 24 +#define CONFIG_CLIENTPERIOD 25 +#define CONFIG_MULTICASTCLIENT 26 +#define CONFIG_ENABLE 27 +#define CONFIG_DISABLE 28 +#define CONFIG_PHONE 29 +#define CONFIG_LOGFILE 30 +#define CONFIG_LOGCONFIG 31 +#define CONFIG_MANYCASTCLIENT 32 +#define CONFIG_MANYCASTSERVER 33 + +#define CONF_MOD_VERSION 1 +#define CONF_MOD_KEY 2 +#define CONF_MOD_MINPOLL 3 +#define CONF_MOD_MAXPOLL 4 +#define CONF_MOD_PREFER 5 +#define CONF_MOD_BURST 6 +#define CONF_MOD_SKEY 7 +#define CONF_MOD_TTL 8 +#define CONF_MOD_MODE 9 +#define CONF_MOD_NOSELECT 10 + +#define CONF_RES_MASK 1 +#define CONF_RES_IGNORE 2 +#define CONF_RES_NOSERVE 3 +#define CONF_RES_NOTRUST 4 +#define CONF_RES_NOQUERY 5 +#define CONF_RES_NOMODIFY 6 +#define CONF_RES_NOPEER 7 +#define CONF_RES_NOTRAP 8 +#define CONF_RES_LPTRAP 9 +#define CONF_RES_NTPPORT 10 +#define CONF_RES_LIMITED 11 + +#define CONF_TRAP_PORT 1 +#define CONF_TRAP_INTERFACE 2 + +#define CONF_FDG_TIME1 1 +#define CONF_FDG_TIME2 2 +#define CONF_FDG_STRATUM 3 +#define CONF_FDG_REFID 4 +#define CONF_FDG_FLAG1 5 +#define CONF_FDG_FLAG2 6 +#define CONF_FDG_FLAG3 7 +#define CONF_FDG_FLAG4 8 + +#define CONF_FGEN_FILE 1 +#define CONF_FGEN_TYPE 2 +#define CONF_FGEN_FLAG_LINK 3 +#define CONF_FGEN_FLAG_NOLINK 4 +#define CONF_FGEN_FLAG_ENABLE 5 +#define CONF_FGEN_FLAG_DISABLE 6 + +#define CONF_PPS_ASSERT 1 +#define CONF_PPS_CLEAR 2 +#define CONF_PPS_HARDPPS 3 + +/* + * Translation table - keywords to function index + */ +struct keyword { + const char *text; + int keytype; +}; + +/* + * Command keywords + */ +static struct keyword keywords[] = { + { "peer", CONFIG_PEER }, + { "server", CONFIG_SERVER }, + { "driftfile", CONFIG_DRIFTFILE }, + { "broadcast", CONFIG_BROADCAST }, + { "broadcastclient", CONFIG_BROADCASTCLIENT }, + { "multicastclient", CONFIG_MULTICASTCLIENT }, + { "manycastclient", CONFIG_MANYCASTCLIENT }, + { "manycastserver", CONFIG_MANYCASTSERVER }, + { "authenticate", CONFIG_AUTHENTICATE }, + { "keys", CONFIG_KEYS }, + { "revoke", CONFIG_REVOKE }, + { "pps", CONFIG_PPS }, + { "automax", CONFIG_AUTOMAX }, + { "restrict", CONFIG_RESTRICT }, + { "broadcastdelay", CONFIG_BDELAY }, + { "trustedkey", CONFIG_TRUSTEDKEY }, + { "requestkey", CONFIG_REQUESTKEY }, + { "controlkey", CONFIG_CONTROLKEY }, + { "trap", CONFIG_TRAP }, + { "fudge", CONFIG_FUDGE }, + { "statsdir", CONFIG_STATSDIR }, + { "filegen", CONFIG_FILEGEN }, + { "statistics", CONFIG_STATISTICS }, + { "pidfile", CONFIG_PIDFILE }, + { "setvar", CONFIG_SETVAR }, + { "clientlimit", CONFIG_CLIENTLIMIT }, + { "clientperiod", CONFIG_CLIENTPERIOD }, + { "enable", CONFIG_ENABLE }, + { "disable", CONFIG_DISABLE }, + { "phone", CONFIG_PHONE }, + { "logfile", CONFIG_LOGFILE }, + { "logconfig", CONFIG_LOGCONFIG }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "peer", "server", "broadcast" modifier keywords + */ +static struct keyword mod_keywords[] = { + { "version", CONF_MOD_VERSION }, + { "key", CONF_MOD_KEY }, + { "minpoll", CONF_MOD_MINPOLL }, + { "maxpoll", CONF_MOD_MAXPOLL }, + { "prefer", CONF_MOD_PREFER }, + { "noselect", CONF_MOD_NOSELECT }, + { "burst", CONF_MOD_BURST }, + { "autokey", CONF_MOD_SKEY }, + { "mode", CONF_MOD_MODE }, /* reference clocks */ + { "ttl", CONF_MOD_TTL }, /* NTP peers */ + { "", CONFIG_UNKNOWN } +}; + +/* + * "restrict" modifier keywords + */ +static struct keyword res_keywords[] = { + { "mask", CONF_RES_MASK }, + { "ignore", CONF_RES_IGNORE }, + { "noserve", CONF_RES_NOSERVE }, + { "notrust", CONF_RES_NOTRUST }, + { "noquery", CONF_RES_NOQUERY }, + { "nomodify", CONF_RES_NOMODIFY }, + { "nopeer", CONF_RES_NOPEER }, + { "notrap", CONF_RES_NOTRAP }, + { "lowpriotrap", CONF_RES_LPTRAP }, + { "ntpport", CONF_RES_NTPPORT }, + { "limited", CONF_RES_LIMITED }, + { "", 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[] = { + { "time1", CONF_FDG_TIME1 }, + { "time2", CONF_FDG_TIME2 }, + { "stratum", CONF_FDG_STRATUM }, + { "refid", CONF_FDG_REFID }, + { "flag1", CONF_FDG_FLAG1 }, + { "flag2", CONF_FDG_FLAG2 }, + { "flag3", CONF_FDG_FLAG3 }, + { "flag4", CONF_FDG_FLAG4 }, + { "", CONFIG_UNKNOWN } +}; + + +/* + * "filegen" modifier keywords + */ +static struct keyword filegen_keywords[] = { + { "file", CONF_FGEN_FILE }, + { "type", CONF_FGEN_TYPE }, + { "link", CONF_FGEN_FLAG_LINK }, + { "nolink", CONF_FGEN_FLAG_NOLINK }, + { "enable", CONF_FGEN_FLAG_ENABLE }, + { "disable", CONF_FGEN_FLAG_DISABLE }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "type" modifier keywords + */ +static struct keyword fgen_types[] = { + { "none", FILEGEN_NONE }, + { "pid", FILEGEN_PID }, + { "day", FILEGEN_DAY }, + { "week", FILEGEN_WEEK }, + { "month", FILEGEN_MONTH }, + { "year", FILEGEN_YEAR }, + { "age", FILEGEN_AGE }, + { "", CONFIG_UNKNOWN} +}; + +/* + * "enable", "disable" modifier keywords + */ +static struct keyword flags_keywords[] = { + { "auth", PROTO_AUTHENTICATE }, + { "bclient", PROTO_BROADCLIENT }, + { "ntp", PROTO_NTP }, + { "kernel", PROTO_KERNEL }, + { "monitor", PROTO_MONITOR }, + { "stats", PROTO_FILEGEN }, + { "", CONFIG_UNKNOWN } +}; + +/* + * pps modifier keywords + */ +static struct keyword pps_keywords[] = { + { "assert", CONF_PPS_ASSERT }, + { "clear", CONF_PPS_CLEAR }, + { "hardpps", CONF_PPS_HARDPPS }, + { "", CONFIG_UNKNOWN } +}; + +/* + * "logconfig" building blocks + */ +struct masks { + const char *name; + unsigned long mask; +}; + +static struct masks logcfg_class[] = { + { "sys", NLOG_OSYS }, + { "peer", NLOG_OPEER }, + { "clock", NLOG_OCLOCK }, + { "sync", NLOG_OSYNC }, + { (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 5 /* maximum number of phone strings */ +#define MAXPPS 20 /* maximum length of PPS device string */ +#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */ + + +/* + * 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) + +/* + * File descriptor used by the resolver save routines, and temporary file + * name. + */ +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 + */ +char const *progname; +char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */ +char pps_device[MAXPPS + 1]; /* PPS device name */ +int pps_assert = 1; +int pps_hardpps; +int listen_to_virtual_ips = 0; +#if defined(HAVE_SCHED_SETSCHEDULER) +int config_priority_override = 0; +int config_priority; +#endif + +static const char *ntp_options = "aAbc:dD:f:gk:l:Lmnp:P:r:s:t:v:V:x"; + +#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 *)); +static int getnetnum P((const char *, struct sockaddr_in *, int)); +static void save_resolve P((char *, int, int, int, int, int, int, u_long)); +static void do_resolve_internal P((void)); +static void abort_resolve P((void)); +#if !defined(VMS) +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; +} + +/* + * getstartup - search through the options looking for a debugging flag + */ +void +getstartup( + int argc, + char *argv[] + ) +{ + int errflg; + int c; + +#ifdef DEBUG + debug = 0; /* no debugging by default */ +#endif + + /* + * This is a big hack. We don't really want to read command line + * configuration until everything else is initialized, since + * the ability to configure the system may depend on storage + * and the like having been initialized. Except that we also + * don't want to initialize anything until after detaching from + * the terminal, but we won't know to do that until we've + * parsed the command line. Do that now, crudely, and do it + * again later. Our ntp_getopt() is explicitly reusable, by the + * way. Your own mileage may vary. + * + * This hack is even called twice (to allow complete logging to file) + */ + errflg = 0; + progname = argv[0]; + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) + switch (c) { +#ifdef DEBUG + case 'd': + ++debug; + break; + case 'D': + debug = (int)atol(ntp_optarg); + printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug); + break; +#else + case 'd': + case 'D': + msyslog(LOG_ERR, "ntpd not compiled with -DDEBUG option - no DEBUG support"); + fprintf(stderr, "ntpd not compiled with -DDEBUG option - no DEBUG support"); + ++errflg; + break; +#endif + case 'L': + listen_to_virtual_ips = 1; + break; + case 'l': + { + FILE *new_file; + + new_file = fopen(ntp_optarg, "a"); + if (new_file != NULL) { + NLOG(NLOG_SYSINFO) + msyslog(LOG_NOTICE, "logging to file %s", ntp_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", + ntp_optarg); + } + break; + + case 'n': + ++nofork; + break; + + case '?': + ++errflg; + break; + + default: + break; + } + + if (errflg || ntp_optind != argc) { + (void) fprintf(stderr, "usage: %s [ -abdgmnx ] [ -c config_file ] [ -e e_delay ]\n", progname); + (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); + (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); + (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); +#if defined(HAVE_SCHED_SETSCHEDULER) + (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); +#endif + exit(2); + } + ntp_optind = 0; /* reset ntp_optind to restart ntp_getopt */ + +#ifdef DEBUG + if (debug) { +#ifdef HAVE_SETVBUF + static char buf[BUFSIZ]; + setvbuf(stdout, buf, _IOLBF, BUFSIZ); +#else + setlinebuf(stdout); +#endif + } +#endif +} + +/* + * getconfig - get command line options and read the configuration file + */ +void +getconfig( + int argc, + char *argv[] + ) +{ + register int i; + int c; + int errflg; + int peerversion; + int minpoll; + int maxpoll; + int ttl; + u_long peerkey; + u_long lpeerkey; + int peerflags; + int hmode; + struct sockaddr_in peeraddr; + struct sockaddr_in maskaddr; + FILE *fp; + char line[MAXLINE]; + char *(tokens[MAXTOKENS]); + int ntokens; + int tok = CONFIG_UNKNOWN; + struct interface *localaddr; + 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 */ + struct refclockstat clock_stat; + FILEGEN *filegen; + + /* + * Initialize, initialize + */ + errflg = 0; +#ifdef DEBUG + debug = 0; +#endif /* DEBUG */ +#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 */ + progname = argv[0]; + res_fp = NULL; + memset((char *)sys_phone, 0, sizeof(sys_phone)); + 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.); + + /* + * Decode argument list + */ + while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) { + switch (c) { + case 'a': + proto_config(PROTO_AUTHENTICATE, 1, 0.); + break; + + case 'A': + proto_config(PROTO_AUTHENTICATE, 0, 0.); + break; + + case 'b': + proto_config(PROTO_BROADCLIENT, 1, 0.); + break; + + case 'c': + config_file = ntp_optarg; +#ifdef HAVE_NETINFO + check_netinfo = 0; +#endif + break; + + case 'd': +#ifdef DEBUG + debug++; +#else + errflg++; +#endif /* DEBUG */ + break; + + case 'D': +#ifdef DEBUG + debug = (int)atol(ntp_optarg); + printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug); +#else + errflg++; +#endif /* DEBUG */ + break; + + case 'f': + stats_config(STATS_FREQ_FILE, ntp_optarg); + break; + + case 'g': + correct_any = TRUE; + break; + + case 'k': + getauthkeys(ntp_optarg); + break; + + case 'L': /* already done at pre-scan */ + case 'l': /* already done at pre-scan */ + break; + + case 'm': + proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP), 0.); + sys_bclient = 1; + break; + + case 'n': /* already done at pre-scan */ + break; + + case 'p': + stats_config(STATS_PID_FILE, ntp_optarg); + break; + + case 'P': +#if defined(HAVE_SCHED_SETSCHEDULER) + config_priority = (int)atol(ntp_optarg); + config_priority_override = 1; +#else + errflg++; +#endif + break; + + case 'r': + do { + double tmp; + + if (sscanf(ntp_optarg, "%lf", &tmp) != 1) { + msyslog(LOG_ERR, + "command line broadcast delay value %s undecodable", + ntp_optarg); + } else { + proto_config(PROTO_BROADDELAY, 0, tmp); + } + } while (0); + break; + + case 's': + stats_config(STATS_STATSDIR, ntp_optarg); + break; + + case 't': + do { + u_long tkey; + + tkey = (int)atol(ntp_optarg); + if (tkey <= 0 || tkey > NTP_MAXKEY) { + msyslog(LOG_ERR, + "command line trusted key %s is invalid", + ntp_optarg); + } else { + authtrust(tkey, 1); + } + } while (0); + break; + + case 'v': + case 'V': + set_sys_var(ntp_optarg, strlen(ntp_optarg)+1, + RW | ((c == 'V') ? DEF : 0)); + break; + + case 'x': + allow_set_backward = FALSE; + break; + + default: + errflg++; + break; + } + } + + if (errflg || ntp_optind != argc) { + (void) fprintf(stderr, "usage: %s [ -abdgmnx ] [ -c config_file ] [ -e e_delay ]\n", progname); + (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); + (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); + (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); +#if defined(HAVE_SCHED_SETSCHEDULER) + (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); +#endif + exit(2); + } + + if ( + (fp = 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 = 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 (fp) + tok = gettokens(fp, line, tokens, &ntokens); +#ifdef HAVE_NETINFO + else + tok = gettokens_netinfo(config_netinfo, tokens, &ntokens); +#endif /* HAVE_NETINFO */ + + if (tok == CONFIG_UNKNOWN) 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; + } + + if (!getnetnum(tokens[1], &peeraddr, 0)) { + errflg = -1; + } else { + errflg = 0; + + if ( +#ifdef REFCLOCK + !ISREFCLOCKADR(&peeraddr) && +#endif + ISBADADR(&peeraddr)) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + /* + * Shouldn't be able to specify multicast + * address for server/peer! + * and unicast address for manycastclient! + */ + if (((tok == CONFIG_SERVER) || + (tok == CONFIG_PEER)) && +#ifdef REFCLOCK + !ISREFCLOCKADR(&peeraddr) && +#endif + IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + if ((tok == CONFIG_MANYCASTCLIENT) && + !IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) { + msyslog(LOG_ERR, + "attempt to configure invalid address %s", + ntoa(&peeraddr)); + break; + } + } + + peerversion = NTP_VERSION; + minpoll = NTP_MINDPOLL; + maxpoll = NTP_MAXDPOLL; + peerkey = 0; + peerflags = 0; + ttl = 0; + for (i = 2; i < ntokens; i++) + switch (matchkey(tokens[i], mod_keywords)) { + 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) + 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) + maxpoll = NTP_MAXPOLL; + break; + + case CONF_MOD_PREFER: + peerflags |= FLAG_PREFER; + break; + + case CONF_MOD_NOSELECT: + peerflags |= FLAG_NOSELECT; + break; + + case CONF_MOD_BURST: + peerflags |= FLAG_BURST; + break; + + case CONF_MOD_SKEY: + peerflags |= FLAG_SKEY | FLAG_AUTHENABLE; + break; + + case CONF_MOD_TTL: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "ttl: argument required"); + errflg = 1; + break; + } + ttl = atoi(tokens[++i]); + 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, + (struct interface *)0, hmode, + peerversion, minpoll, maxpoll, + peerflags, ttl, peerkey) + == 0) { + msyslog(LOG_ERR, + "configuration of %s failed", + ntoa(&peeraddr)); + } + } else if (errflg == -1) { + save_resolve(tokens[1], hmode, peerversion, + minpoll, maxpoll, peerflags, ttl, + peerkey); + } + break; + + case CONFIG_DRIFTFILE: + if (ntokens >= 2) + stats_config(STATS_FREQ_FILE, tokens[1]); + else + stats_config(STATS_FREQ_FILE, (char *)0); + 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_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: + proto_config(PROTO_BROADCLIENT, 1, 0.); + break; + + case CONFIG_MULTICASTCLIENT: + case CONFIG_MANYCASTSERVER: + if (ntokens > 1) { + for (i = 1; i < ntokens; i++) { + if (getnetnum(tokens[i], &peeraddr, 1)) + proto_config(PROTO_MULTICAST_ADD, + peeraddr.sin_addr.s_addr, 0.); + } + } else + proto_config(PROTO_MULTICAST_ADD, + htonl(INADDR_NTP), 0.); + if (tok == CONFIG_MULTICASTCLIENT) { + sys_bclient = 1; +#ifdef DEBUG + if (debug) + printf("sys_bclient\n"); +#endif /* DEBUG */ + } + else if (tok == CONFIG_MANYCASTSERVER) { + sys_manycastserver = 1; +#ifdef DEBUG + if (debug) + printf("sys_manycastserver\n"); +#endif /* DEBUG */ + } + break; + + case CONFIG_AUTHENTICATE: + errflg = 0; + if (ntokens >= 2) { + if (STREQ(tokens[1], "yes")) + proto_config(PROTO_AUTHENTICATE, 1, 0.); + else if (STREQ(tokens[1], "no")) + proto_config(PROTO_AUTHENTICATE, 0, 0.); + else + errflg++; + } else { + errflg++; + } + + if (errflg) + msyslog(LOG_ERR, + "should be `authenticate yes|no'"); + break; + + case CONFIG_KEYS: + if (ntokens >= 2) { + getauthkeys(tokens[1]); + } + break; + + case CONFIG_REVOKE: + if (ntokens >= 2) { + sys_revoke = 1 << max(atoi(tokens[1]), 10); + } + break; + + case CONFIG_AUTOMAX: + if (ntokens >= 2) { + sys_automax = 1 << max(atoi(tokens[1]), 10); + } + break; + + case CONFIG_RESTRICT: + if (ntokens < 2) { + msyslog(LOG_ERR, "restrict requires an address"); + break; + } + if (STREQ(tokens[1], "default")) + peeraddr.sin_addr.s_addr = htonl(INADDR_ANY); + else if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + /* + * Use peerversion as flags, peerkey as mflags. Ick. + */ + peerversion = 0; + peerkey = 0; + errflg = 0; + maskaddr.sin_addr.s_addr = ~(u_int32)0; + for (i = 2; i < ntokens; i++) { + switch (matchkey(tokens[i], res_keywords)) { + case CONF_RES_MASK: + if (i >= ntokens-1) { + msyslog(LOG_ERR, + "mask keyword needs argument"); + errflg++; + break; + } + i++; + if (!getnetnum(tokens[i], &maskaddr, 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_LIMITED: + peerversion |= RES_LIMITED; + break; + + case CONFIG_UNKNOWN: + errflg++; + break; + } + } + if (SRCADR(&peeraddr) == htonl(INADDR_ANY)) + maskaddr.sin_addr.s_addr = 0; + 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); + } + } + break; + + case CONFIG_TRUSTEDKEY: + for (i = 1; i < ntokens; i++) { + u_long 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) { + u_long rkey; + + if (!atouint(tokens[1], &rkey)) { + msyslog(LOG_ERR, + "%s is undecodable as request key", + tokens[1]); + } else if (rkey == 0) { + msyslog(LOG_ERR, + "%s makes a poor request keyid", + tokens[1]); + } else { +#ifdef DEBUG + if (debug > 3) + printf( + "set info_auth_key to %lu\n", rkey); +#endif + info_auth_keyid = rkey; + } + } + break; + + case CONFIG_CONTROLKEY: + if (ntokens >= 2) { + u_long 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; + } + if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + /* + * Use peerversion for port number. Barf. + */ + errflg = 0; + peerversion = 0; + localaddr = 0; + for (i = 2; i < ntokens-1; i++) + switch (matchkey(tokens[i], trap_keywords)) { + 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; + } + + if (!getnetnum(tokens[++i], + &maskaddr, 1)) { + errflg = 1; + break; + } + + localaddr = findinterface(&maskaddr); + if (localaddr == NULL) { + msyslog(LOG_ERR, + "can't find interface with address %s", + ntoa(&maskaddr)); + errflg = 1; + } + break; + + case CONFIG_UNKNOWN: + errflg++; + break; + } + + if (!errflg) { + if (peerversion != 0) + peeraddr.sin_port = htons( (u_short) peerversion); + else + peeraddr.sin_port = htons(TRAPPORT); + if (localaddr == NULL) + localaddr = any_interface; + if (!ctlsettrap(&peeraddr, localaddr, 0, + NTP_VERSION)) + msyslog(LOG_ERR, + "can't set trap for %s, no resources", + ntoa(&peeraddr)); + } + break; + + case CONFIG_FUDGE: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no address for fudge command, line ignored"); + break; + } + if (!getnetnum(tokens[1], &peeraddr, 1)) + break; + + if (!ISREFCLOCKADR(&peeraddr)) { + msyslog(LOG_ERR, + "%s is inappropriate address for the fudge command, line ignored", + ntoa(&peeraddr)); + break; + } + + memset((void *)&clock_stat, 0, sizeof clock_stat); + errflg = 0; + for (i = 2; i < ntokens-1; i++) { + switch (c = matchkey(tokens[i], + fudge_keywords)) { + case CONF_FDG_TIME1: + if (sscanf(tokens[++i], "%lf", + &clock_stat.fudgetime1) != 1) { + msyslog(LOG_ERR, + "fudge %s time1 value in error", + ntoa(&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", + ntoa(&peeraddr)); + errflg = i; + break; + } + clock_stat.haveflags |= CLK_HAVETIME2; + break; + + + case CONF_FDG_STRATUM: + /* HMS: the (long *)_ may be trouble */ + if (!atoint(tokens[++i], + (long *)&clock_stat.fudgeval1)) + { + msyslog(LOG_ERR, + "fudge %s stratum value in error", + ntoa(&peeraddr)); + errflg = i; + break; + } + clock_stat.haveflags |= CLK_HAVEVAL1; + break; + + case CONF_FDG_REFID: + /* HMS: Endianness and 0 bytes? */ + /* XXX */ + strncpy((char *)&clock_stat.fudgeval2, + 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], &lpeerkey) + || lpeerkey > 1) { + msyslog(LOG_ERR, + "fudge %s flag value in error", + ntoa(&peeraddr)); + peerkey = lpeerkey; + errflg = i; + break; + } + peerkey = lpeerkey; + 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 (peerkey == 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)) { + 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); + 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, + RW | + ((((ntokens > 2) + && !strcmp(tokens[2], + "default"))) + ? DEF + : 0)); + } + break; + + case CONFIG_CLIENTLIMIT: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no value for clientlimit command - line ignored"); + } else { + u_long ui; + + if (!atouint(tokens[1], &ui) || !ui) { + msyslog(LOG_ERR, + "illegal value for clientlimit command - line ignored"); + } else { + char bp[80]; + +#ifdef DEBUG + if (debug) + sprintf(bp, "client_limit=%lu", ui); +#endif + set_sys_var(bp, strlen(bp)+1, RO); + client_limit = ui; + } + } + break; + + case CONFIG_CLIENTPERIOD: + if (ntokens < 2) { + msyslog(LOG_ERR, + "no value for clientperiod command - line ignored"); + } else { + u_long ui; + + if (!atouint(tokens[1], &ui) || ui < 64) { + msyslog(LOG_ERR, + "illegal value for clientperiod command - line ignored"); + } else { + char bp[80]; + + sprintf(bp, "client_limit_period=%ld", ui); + set_sys_var(bp, strlen(bp)+1, RO); + client_limit_period = ui; + } + } + break; + + case CONFIG_ENABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "enable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 1, 0.); + } + break; + + case CONFIG_DISABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + msyslog(LOG_ERR, + "disable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 0, 0.); + } + break; + + case CONFIG_PHONE: + for (i = 1; i < ntokens && i < MAXPHONE; i++) { + (void)strncpy(sys_phone[i - 1], + tokens[i], MAXDIAL); + } + sys_phone[i - 1][0] = '\0'; + break; + + case CONFIG_PPS: + if (ntokens < 2) { + msyslog(LOG_ERR, + "pps missing device name"); + break; + } + (void)strncpy(pps_device, tokens[1], MAXPPS); + for (i = 2; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], pps_keywords); + switch(flag) { + case CONF_PPS_ASSERT: + pps_assert = 1; + break; + case CONF_PPS_CLEAR: + pps_assert = 0; + break; + case CONF_PPS_HARDPPS: + pps_hardpps = 1; + break; + default: + msyslog(LOG_ERR, + "pps unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + if(errflg) + break; + } + break; + } + } + if (fp) (void)fclose(fp); +#ifdef HAVE_NETINFO + if (config_netinfo) free_netinfo_config(config_netinfo); +#endif /* HAVE_NETINFO */ + + if (res_fp != NULL) { + /* + * 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; + } + } + *ntokens = ntok + 1; + + config->val_index++; + + 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; + } + } + + /* + * Return the match + */ + *ntokens = ntok + 1; + ntok = matchkey(tokenlist[0], keywords); + 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 + ) +{ + for (;;) { + if (keys->keytype == CONFIG_UNKNOWN) { + 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_in *addr, + int complain + ) +{ + register const char *cp; + register char *bp; + register int i; + register int temp; + char buf[80]; /* will core dump on really stupid stuff */ + u_int32 netnum; + + /* XXX ELIMINATE replace with decodenetnum */ + cp = num; + netnum = 0; + for (i = 0; i < 4; i++) { + bp = buf; + while (isdigit((int)*cp)) + *bp++ = *cp++; + if (bp == buf) + break; + + if (i < 3) { + if (*cp++ != '.') + break; + } else if (*cp != '\0') + break; + + *bp = '\0'; + temp = atoi(buf); + if (temp > 255) + break; + netnum <<= 8; + netnum += temp; +#ifdef DEBUG + if (debug > 3) + printf("getnetnum %s step %d buf %s temp %d netnum %lu\n", + num, i, buf, temp, (u_long)netnum); +#endif + } + + if (i < 4) { + if (complain) + msyslog(LOG_ERR, + "getnetnum: \"%s\" invalid host number, line ignored", + num); +#ifdef DEBUG + if (debug > 3) + printf( + "getnetnum: \"%s\" invalid host number, line ignored\n", + num); +#endif + return 0; + } + + /* + * make up socket address. Clear it out for neatness. + */ + memset((void *)addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + addr->sin_port = htons(NTP_PORT); + addr->sin_addr.s_addr = htonl(netnum); +#ifdef DEBUG + if (debug > 1) + printf("getnetnum given %s, got %s (%lx)\n", + num, ntoa(addr), (u_long)netnum); +#endif + return 1; +} + + +#if !defined(VMS) +/* + * 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, + int flags, + int ttl, + u_long keyid + ) +{ +#ifndef SYS_VXWORKS + if (res_fp == NULL) { +#ifndef SYS_WINNT + (void) strcpy(res_file, RES_TEMPFILE); +#else + /* no /tmp directory under NT */ + { + DWORD len; + if(!(len = 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, "w"); + } +#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 %lu\n", name, mode, + version, minpoll, maxpoll, flags, ttl, keyid); +#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 */ +} + + +#define KEY_TYPE_MD5 4 + +/* + * 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, + "internal error in do_resolve_internal: res_fp == NULL"); + exit(1); + } + + /* we are done with this now */ + (void) fclose(res_fp); + res_fp = NULL; + +#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[8]; + + for (i = 0; i < 8; i++) + rankey[i] = RANDOM & 0xff; + authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey); + authtrust(req_keyid, 1); + } + + /* save keyid so we will accept config requests with it */ + info_auth_keyid = req_keyid; + req_file = res_file; /* set up pointer to res file */ +#ifndef SYS_WINNT + (void) signal_no_reset(SIGCHLD, catchchild); + +#ifndef SYS_VXWORKS + i = fork(); + 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 ane io associations + * shared with the NTP daemon. I currently don't want + * + * we also block SIGIO (currently no portes 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: + */ + + closelog(); + kill_asyncio(); + + (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 /* 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); + if (!(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 */ + msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres"); + 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..e63765f --- /dev/null +++ b/contrib/ntp/ntpd/ntp_control.c @@ -0,0 +1,2652 @@ +/* + * ntp_control.c - respond to control messages and send async traps + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_control.h" +#include "ntp_stdlib.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)); /* routine to handle request */ +}; + +/* + * Only one flag. Authentication required or not. + */ +#define NOAUTH 0 +#define AUTH 1 + +/* + * Request processing routines + */ +static void ctl_error P((int)); +static u_short ctlclkstatus P((struct refclockstat *)); +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)); +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 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_in *, 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, "phase" }, /* 11 */ + { CS_DRIFT, RO, "frequency" }, /* 12 */ + { CS_COMPLIANCE, RO, "jitter" }, /* 13 */ + { CS_CLOCK, RO, "clock" }, /* 14 */ + { CS_PROCESSOR, RO, "processor" }, /* 15 */ + { CS_SYSTEM, RO, "system" }, /* 16 */ + { CS_STABIL, RO, "stability" }, /* 17 */ + { CS_VARLIST, RO, "sys_var_list" }, /* 18 */ + { 0, EOV, "" } +}; + +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_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_COMPLIANCE, + CS_STABIL, + 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_VALID, RO, "valid" }, /* 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_DISP, PADDING,"" }, /* 36 */ + { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ + { 0, EOV, "" } +}; + + +/* + * Peer variables we print by default + */ +static u_char def_peer_var[] = { + CP_SRCADR, + CP_SRCPORT, + CP_DSTADR, + CP_DSTPORT, + CP_KEYID, + CP_STRATUM, + CP_PRECISION, + CP_ROOTDELAY, + CP_ROOTDISPERSION, + CP_REFID, + CP_REFTIME, + CP_DELAY, + CP_OFFSET, + CP_JITTER, + CP_DISPERSION, + CP_REACH, + CP_VALID, + CP_HMODE, + CP_PMODE, + CP_HPOLL, + CP_PPOLL, + CP_LEAP, + CP_FLASH, + CP_ORG, + CP_REC, + CP_XMT, + CP_FILTDELAY, + CP_FILTOFFSET, + CP_FILTERROR, + 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, "" } +}; + + +/* + * 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. These will change for the gizmo board. + */ +#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, /* 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) */ + 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, /* REFCLK_MSF_EES (14) */ + CTL_SST_TS_UHF, /* REFCLK_TRUETIME (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_TELEPHONE, /* REFCLK_PTB_ACTS (23) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_USNO (24) */ + CTL_SST_TS_UHF, /* REFCLK_TRUETIME (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 (32) */ + CTL_SST_TS_LF, /* REFCLK_ULINK (33) */ +}; + + +/* + * Keyid used for authenticating write requests. + */ +u_long 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 u_short res_associd; +static int res_offset; +static u_char * datapt; +static u_char * dataend; +static int datalinelen; +static int datanotbinflag; +static struct sockaddr_in *rmt_addr; +static struct interface *lcl_inter; + +static u_char res_authenticate; +static u_char res_authokay; +static u_long 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 > 1) + 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 >= 3) + printf( + "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n", + rbufp->recv_length, properlen, res_keyid, maclen); +#endif + if (!authistrusted(res_keyid)) { +#ifdef DEBUG + if (debug >= 2) + printf("invalid keyid %lu\n", res_keyid); +#endif + } else if (authdecrypt(res_keyid, (u_int32 *)pkt, + rbufp->recv_length - maclen, maclen)) { +#ifdef DEBUG + if (debug >= 3) + printf("authenticated okay\n"); +#endif + res_authokay = 1; + } else { +#ifdef DEBUG + if (debug >= 3) + 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 + */ +static u_short +ctlclkstatus( + struct refclockstat *this_clock + ) +{ + return ((u_short)(this_clock->currentstatus) << 8) + | (u_short)(this_clock->lastevent); +} + + + +/* + * ctlsysstatus - return the system status word + */ +u_short +ctlsysstatus(void) +{ + register u_char this_clock; + + this_clock = CTL_SST_TS_UNSPEC; + 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; + } + } + 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; + u_long 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_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 & 0xffffffffL, + ts->l_uf & 0xffffffffL); + while (*cp != '\0') + cp++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putadr - write a dotted quad IP address into the response + */ +static void +ctl_putadr( + const char *tag, + u_int32 addr + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + cq = numtoa(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; +#ifdef HAVE_UNAME + char str[50]; +#endif + + 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) + ctl_putadr(sys_var[CS_REFID].text, sys_refid); + 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_COMPLIANCE: + ctl_putdbl(sys_var[CS_COMPLIANCE].text, sys_error * 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 + (void)strcpy(str, utsnamebuf.sysname); + (void)strcat(str, utsnamebuf.release); + ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); +#endif /* HAVE_UNAME */ + 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 8-( - Killer */ + + 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; + } +} + + +/* + * ctl_putpeer - output a peer variable + */ +static void +ctl_putpeer( + int varid, + struct peer *peer + ) +{ + 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, + peer->srcadr.sin_addr.s_addr); + break; + case CP_SRCPORT: + ctl_putuint(peer_var[CP_SRCPORT].text, + ntohs(peer->srcadr.sin_port)); + break; + case CP_DSTADR: + ctl_putadr(peer_var[CP_DSTADR].text, + peer->processed ? + peer->cast_flags & MDF_BCAST ? + peer->dstadr->bcast.sin_addr.s_addr: + peer->cast_flags ? + peer->dstadr->sin.sin_addr.s_addr ? + peer->dstadr->sin.sin_addr.s_addr: + peer->dstadr->bcast.sin_addr.s_addr: + 8 : 12); + break; + case CP_DSTPORT: + ctl_putuint(peer_var[CP_DSTPORT].text, + (u_long)(peer->dstadr + ? ntohs(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->stratum > 1) + { + if (peer->flags & FLAG_REFCLOCK) + ctl_putadr(peer_var[CP_REFID].text, + peer->srcadr.sin_addr.s_addr); + else + ctl_putadr(peer_var[CP_REFID].text, + peer->refid); + } + 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: + ctl_puthex(peer_var[CP_FLASH].text, peer->flash); + break; + case CP_VALID: + ctl_putuint(peer_var[CP_VALID].text, peer->valid); + 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, + SQRT(peer->variance) * 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 8-( - Killer */ + + 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 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); + 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 8-( - Killer */ + + 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((int)*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((int)*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((int)*cp)) + cp++; + while (cp < reqend && *cp != ',') + *tp++ = *cp++; + if (cp < reqend) + cp++; + *tp = '\0'; + while (isspace((int)(*(tp-1)))) + *(--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((int)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 < 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((int)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((int)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; + /*int leapind, leapwarn;*/ + + /* + * 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()); + + /* + * Set flags to not-in-sync so we can tell when we get something. + */ + /* + leapind = ~0; + leapwarn = ~0; + */ + + /* + * 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); /* our fault, really */ + return; + } + } + } + + /* + * If we got anything, do it. + */ + /* + 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 < 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((int)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_in *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 = 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 clr a trap + */ +int +ctlclrtrap( + struct sockaddr_in *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_in *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 + && NSRCADR(raddr) == NSRCADR(&tp->tr_addr) + && NSRCPORT(raddr) == NSRCPORT(&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)) { + 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.sin_addr.s_addr); + else +#endif + src = ntoa(&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. Maybe more selective later + */ + for (i = 1; i <= CS_MAXCODE; i++) + 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++) + 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, + int 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, + int def + ) +{ + register struct ctl_var *k; + register const char *s; + register const char *t; + char *td; + + if (!data || !size) + return; + + if ((k = *kv)) + { + 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( + char *data, + u_long size, + int 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_filegen.c b/contrib/ntp/ntpd/ntp_filegen.c new file mode 100644 index 0000000..bcf3f9c --- /dev/null +++ b/contrib/ntp/ntpd/ntp_filegen.c @@ -0,0 +1,547 @@ +/* + * 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 */ + +/* + * 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 tpo 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 loosing 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 -= 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) { + 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 = type; + gen->flag = 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( + 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 + 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..1829914 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_intres.c @@ -0,0 +1,970 @@ +/* + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/time.h> +#include <netdb.h> +#include <signal.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_request.h" +#include "ntp_stdlib.h" +#include "ntp_syslog.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 */ +}; +#define ce_peeraddr ce_config.peeraddr +#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 + +/* + * 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 NUMTOK 8 + +#define MAXLINESIZE 512 + + +/* + * File descriptor for ntp request code. + */ +static int sockfd = -1; + + +/* stuff to be filled in by caller */ + +u_long req_keyid; /* request keyid */ +char *req_file; /* name of the file with configuration info */ + +/* end stuff to be filled in */ + + +static RETSIGTYPE bong P((int)); +static void checkparent P((void)); +static void removeentry P((struct conf_entry *)); +static void addentry P((char *, int, int, int, int, int, int, u_long)); +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)); + +/* + * assumes: req_key, req_keyid, conffile valid + * syslog still open + */ +void +ntp_intres(void) +{ + FILE *in; +#ifdef HAVE_SIGSUSPEND + sigset_t set; + + sigemptyset(&set); +#endif /* NTP_POSIX_SOURCE */ + +#ifdef DEBUG + if (debug) { + msyslog(LOG_INFO, "NTP_INTRES running"); + } +#endif + + /* check out auth stuff */ + if (sys_authenticate) { + if (!authistrusted(req_keyid)) { + msyslog(LOG_ERR, "invalid request keyid %lu", + req_keyid ); + 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); + exit(1); + } + readconf(in, req_file); + (void) fclose(in); + + if (!debug ) + (void) unlink(req_file); + + /* + * Sleep a little to make sure the server is completely up + */ + + sleep(SLEEPTIME); + + /* + * Make a first cut at resolving the bunch + */ + doconfigure(1); + if (confentries == NULL) +#if defined SYS_WINNT + ExitThread(0); /* Don't want to kill whole NT process */ +#else + exit(0); /* done that quick */ +#endif + + /* + * Here we've got some problem children. Set up the timer + * and wait for it. + */ + resolve_value = resolve_timer = MINRESOLVE; + config_timer = CONFIG_TIME; +#ifndef SYS_WINNT + (void) signal_no_reset(SIGALRM, bong); + alarm(ALARM_TIME); +#endif /* SYS_WINNT */ + + for (;;) { + if (confentries == NULL) + exit(0); + + checkparent(); + + if (resolve_timer == 0) { + if (resolve_value < MAXRESOLVE) + resolve_value <<= 1; + resolve_timer = resolve_value; +#ifdef DEBUG + msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer); +#endif + config_timer = CONFIG_TIME; + doconfigure(1); + continue; + } else if (config_timer == 0) { + config_timer = CONFIG_TIME; +#ifdef DEBUG + msyslog(LOG_INFO, "config_timer: 0->%d", config_timer); +#endif + doconfigure(0); + continue; + } +#ifndef SYS_WINNT + /* + * There is a race in here. Is okay, though, since + * all it does is delay things by 30 seconds. + */ +#ifdef HAVE_SIGSUSPEND + sigsuspend(&set); +#else + sigpause(0); +#endif /* HAVE_SIGSUSPEND */ +#else + if (config_timer > 0) + config_timer--; + if (resolve_timer > 0) + resolve_timer--; + sleep(ALARM_TIME); +#endif /* SYS_WINNT */ + } +} + + +#ifndef SYS_WINNT +/* + * bong - service and reschedule an alarm() interrupt + */ +static RETSIGTYPE +bong( + int sig + ) +{ + if (config_timer > 0) + config_timer--; + if (resolve_timer > 0) + resolve_timer--; + alarm(ALARM_TIME); +} +#endif /* SYS_WINNT */ + +/* + * 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"); + 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, + int flags, + int ttl, + u_long keyid + ) +{ + register char *cp; + register struct conf_entry *ce; + unsigned int len; + + 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; + 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; + 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 + * + * The routine sticks the address into the entry's ce_peeraddr if it + * gets 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 will still be zero. + */ +static int +findhostaddr( + struct conf_entry *entry + ) +{ + struct hostent *hp; + + checkparent(); /* make sure our guy is still running */ + + hp = gethostbyname(entry->ce_name); + + if (hp == NULL) { +#ifndef NODNS + /* + * If the resolver is in use, see if the failure is + * temporary. If so, return success. + */ + if (h_errno == TRY_AGAIN) + return (1); +#endif + return (0); + } + + /* + * Use the first address. We don't have any way to + * tell preferences and older gethostbyname() implementations + * only return one. + */ + memmove((char *)&(entry->ce_peeraddr), + (char *)hp->h_addr, + sizeof(struct in_addr)); + return (1); +} + + +/* + * openntp - open a socket to the ntp server + */ +static void +openntp(void) +{ + struct sockaddr_in saddr; + + if (sockfd >= 0) + return; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd == -1) { + msyslog(LOG_ERR, "socket() failed: %m"); + exit(1); + } + + memset((char *)&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(NTP_PORT); /* trash */ + saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */ + + /* + * 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"); + exit(1); + } +#else +#if defined(FNDELAY) + if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) { + msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m"); + 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"); + exit(1); /* Windows NT - set socket in non-blocking mode */ + } + } +#endif /* SYS_WINNT */ + + + if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { + msyslog(LOG_ERR, "connect() failed: %m"); + exit(1); + } +} + + +/* + * 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 < 0) + 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)); + 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 so 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, + (LPDWORD)&NumberOfBytesWritten, (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; + } +#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) + { + msyslog(LOG_ERR, "select() fails: %m"); + return 0; + } + else if (n == 0) + { + if(debug) + msyslog(LOG_DEBUG, "select() returned 0."); + 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, + (LPDWORD)&NumberOfBytesRead, (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 fails: %m"); + return 0; + } + continue; + } + 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, + "server reports implementation mismatch!!"); + return 0; + + case INFO_ERR_REQ: + msyslog(LOG_ERR, + "server claims configuration request is unknown"); + return 0; + + case INFO_ERR_FMT: + msyslog(LOG_ERR, + "server indicates a format error occurred(!!)"); + return 0; + + case INFO_ERR_NODATA: + msyslog(LOG_ERR, + "server indicates no data available (shouldn't happen)"); + return 0; + + case INFO_ERR_AUTH: + msyslog(LOG_ERR, + "server returns a permission denied error"); + return 0; + + default: + msyslog(LOG_ERR, + "server 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]; + 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); + exit(1); + } + } + + for (i = 1; i < NUMTOK; i++) { + if (!atouint(token[i], &intval[i])) { + msyslog(LOG_ERR, + "format error for integer token `%s', file `%s', quitting", + token[i], name); + 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); + 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); + 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); + 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); + exit(1); + } + + if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER | + FLAG_NOSELECT | FLAG_BURST | FLAG_SKEY)) + != 0) { + msyslog(LOG_ERR, "invalid flags (%ld) in file %s", + intval[TOK_FLAGS], name); + 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_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]); + } +} + + +/* + * 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, "doconfigure(%d)", dores); +#endif + + ce = confentries; + while (ce != NULL) { +#ifdef DEBUG + if (debug > 1) + msyslog(LOG_INFO, "doconfigure: <%s> has peeraddr %#x", + ce->ce_name, ce->ce_peeraddr); +#endif + if (dores && ce->ce_peeraddr == 0) { + 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; + } +#ifdef DEBUG + if (debug > 1) { + msyslog(LOG_INFO, + "doconfigure:findhostaddr() worked"); + } +#endif + } + + if (ce->ce_peeraddr != 0) { +#ifdef DEBUG + if (debug > 1) { + msyslog(LOG_INFO, + "doconfigure: calling request()"); + } +#endif + if (request(&ce->ce_config)) { +#ifdef DEBUG + if (debug > 1) { + msyslog(LOG_INFO, + "doconfigure: request() OK, removing this entry"); + } +#endif + 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..e920249 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_io.c @@ -0,0 +1,1754 @@ +/* + * 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 <stdio.h> +#include <signal.h> +#include <sys/types.h> +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif /* HAVE_SYS_PARAM_H */ +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#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 +#include <arpa/inet.h> + +#if _BSDI_VERSION >= 199510 +# include <ifaddrs.h> +#endif + +#include "ntp_machine.h" +#include "ntpd.h" +#include "ntp_io.h" +#include "iosignal.h" +#include "ntp_refclock.h" +#include "ntp_if.h" +#include "ntp_stdlib.h" + +#if defined(VMS) /* most likely UCX-specific */ + +#include <UCX$INETDEF.H> + +/* "un*x"-compatible names for some items in UCX$INETDEF.H */ +#define ifreq IFREQDEF +#define ifr_name IFR$T_NAME +#define ifr_addr IFR$R_DUMMY.IFR$T_ADDR +#define ifr_broadaddr IFR$R_DUMMY.IFR$T_BROADADDR +#define ifr_flags IFR$R_DUMMY.IFR$R_DUMMY_1_OVRL.IFR$W_FLAGS +#define IFF_UP IFR$M_IFF_UP +#define IFF_BROADCAST IFR$M_IFF_BROADCAST +#define IFF_LOOPBACK IFR$M_IFF_LOOPBACK + +#endif /* VMS */ + + +#if defined(VMS) || defined(SYS_WINNT) +/* structure used in SIOCGIFCONF request (after [KSR] OSF/1) */ +struct ifconf { + int ifc_len; /* size of buffer */ + union { + caddr_t ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +}; +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ + +#endif /* VMS */ + +#if defined(USE_TTY_SIGPOLL) || defined(USE_UDP_SIGPOLL) +# if defined(SYS_AIX) && defined(_IO) /* XXX Identify AIX some other way */ +# undef _IO +# endif +# include <stropts.h> +#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; /* pointer to default interface */ +struct interface *loopback_interface; /* point to loopback interface */ +static struct interface inter_list[MAXINTERFACES]; +static int ninterfaces; + +#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 */ + +/* + * File descriptor masks etc. for call to select + */ +fd_set activefds; +int maxactivefd; + +static int create_sockets P((u_int)); +static int open_socket P((struct sockaddr_in *, int, int)); +static void close_socket P((int)); +static void close_file P((int)); +static char * fdbits P((int, fd_set *)); + +/* + * init_io - initialize I/O data structures and call socket creation routine + */ +void +init_io(void) +{ +#ifdef SYS_WINNT + WORD wVersionRequested; + WSADATA wsaData; + 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 = 0; + +#ifdef REFCLOCK + refio = 0; +#endif + +#if defined(HAVE_SIGNALED_IO) + (void) set_signal(); +#endif + +#ifdef SYS_WINNT + wVersionRequested = MAKEWORD(1,1); + if (WSAStartup(wVersionRequested, &wsaData)) + { + msyslog(LOG_ERR, "No useable winsock.dll: %m"); + exit(1); + } +#endif /* SYS_WINNT */ + + /* + * Create the sockets + */ + BLOCKIO(); + (void) create_sockets(htons(NTP_PORT)); + UNBLOCKIO(); + +#ifdef DEBUG + if (debug) + printf("init_io: maxactivefd %d\n", maxactivefd); +#endif +} + +/* + * 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_int port + ) +{ +#if _BSDI_VERSION >= 199510 + int i, j; + struct ifaddrs *ifaddrs, *ifap; + struct sockaddr_in resmask; +#if _BSDI_VERSION < 199701 + struct ifaddrs *lp; + int num_if; +#endif +#else /* _BSDI_VERSION >= 199510 */ +# ifdef STREAMS_TLI + struct strioctl ioc; +# endif /* STREAMS_TLI */ + char buf[MAXINTERFACES*sizeof(struct ifreq)]; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + int n, i, j, vs, size = 0; + struct sockaddr_in resmask; +#endif /* _BSDI_VERSION >= 199510 */ + +#ifdef DEBUG + if (debug) + printf("create_sockets(%d)\n", ntohs( (u_short) port)); +#endif + + /* + * create pseudo-interface with wildcard address + */ + inter_list[0].sin.sin_family = AF_INET; + inter_list[0].sin.sin_port = port; + inter_list[0].sin.sin_addr.s_addr = htonl(INADDR_ANY); + (void) strncpy(inter_list[0].name, "wildcard", + sizeof(inter_list[0].name)); + inter_list[0].mask.sin_addr.s_addr = htonl(~ (u_int32)0); + inter_list[0].received = 0; + inter_list[0].sent = 0; + inter_list[0].notsent = 0; + inter_list[0].flags = INT_BROADCAST; + +#if _BSDI_VERSION >= 199510 +#if _BSDI_VERSION >= 199701 + if (getifaddrs(&ifaddrs) < 0) + { + msyslog(LOG_ERR, "getifaddrs: %m"); + exit(1); + } + i = 1; + for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) +#else + if (getifaddrs(&ifaddrs, &num_if) < 0) + { + msyslog(LOG_ERR, "create_sockets: getifaddrs() failed: %m"); + exit(1); + } + + i = 1; + + for (ifap = ifaddrs, lp = ifap + num_if; ifap < lp; ifap++) +#endif + { + struct sockaddr_in *sin; + + if (!ifap->ifa_addr) + continue; + + if (ifap->ifa_addr->sa_family != AF_INET) + continue; + + if ((ifap->ifa_flags & IFF_UP) == 0) + continue; + + if (ifap->ifa_flags & IFF_LOOPBACK) + { + sin = (struct sockaddr_in *)ifap->ifa_addr; + if (ntohl(sin->sin_addr.s_addr) != 0x7f000001) + { + continue; + } + } + + inter_list[i].flags = 0; + if (ifap->ifa_flags & IFF_BROADCAST) + inter_list[i].flags |= INT_BROADCAST; + + (void)strcpy(inter_list[i].name, ifap->ifa_name); + + sin = (struct sockaddr_in *)ifap->ifa_addr; + inter_list[i].sin = *sin; + inter_list[i].sin.sin_port = port; + + if (ifap->ifa_flags & IFF_LOOPBACK) + { + inter_list[i].flags = INT_LOOPBACK; + if (loopback_interface == NULL + || ntohl(sin->sin_addr.s_addr) != 0x7f000001) + loopback_interface = &inter_list[i]; + } + + if (inter_list[i].flags & INT_BROADCAST) + { + sin = (struct sockaddr_in *)ifap->ifa_broadaddr; + inter_list[i].bcast = *sin; + inter_list[i].bcast.sin_port = port; + } + + if (ifap->ifa_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) + { + inter_list[i].mask.sin_addr.s_addr = 0xffffffff; + } + else + { + sin = (struct sockaddr_in *)ifap->ifa_netmask; + inter_list[i].mask = *sin; + } + inter_list[i].mask.sin_family = AF_INET; + inter_list[i].mask.sin_len = sizeof *sin; + + /* + * look for an already existing source interface address. If + * the machine has multiple point to point interfaces, then + * the local address may appear more than once. + * + * A second problem exists if we have two addresses on + * the same network (via "ifconfig alias ..."). Don't + * make two xntp interfaces for the two aliases on the + * one physical interface. -wsr + */ + for (j=0; j < i; j++) + if (inter_list[j].sin.sin_addr.s_addr & + inter_list[j].mask.sin_addr.s_addr == + inter_list[i].sin.sin_addr.s_addr & + inter_list[i].mask.sin_addr.s_addr) + { + if (inter_list[j].flags & INT_LOOPBACK) + inter_list[j] = inter_list[i]; + break; + } + if (j == i) + i++; + if (i > MAXINTERFACES) + break; + } + free(ifaddrs); +#else /* _BSDI_VERSION >= 199510 */ +# ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG + if ((vs = open("/dev/ip", O_RDONLY)) < 0) + { + msyslog(LOG_ERR, "create_sockets: open(/dev/ip) failed: %m"); + exit(1); + } +# else /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */ + if ( + (vs = socket(AF_INET, SOCK_DGRAM, 0)) +# ifndef SYS_WINNT + < 0 +# else /* SYS_WINNT */ + == INVALID_SOCKET +# endif /* SYS_WINNT */ + ) { + msyslog(LOG_ERR, "create_sockets: socket(AF_INET, SOCK_DGRAM) failed: %m"); + exit(1); + } +# endif /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */ + + i = 1; +# if !defined(SYS_WINNT) + ifc.ifc_len = sizeof(buf); +# endif +# ifdef STREAMS_TLI + ioc.ic_cmd = SIOCGIFCONF; + ioc.ic_timout = 0; + ioc.ic_dp = (caddr_t)buf; + ioc.ic_len = sizeof(buf); + if(ioctl(vs, I_STR, &ioc) < 0 || + ioc.ic_len < sizeof(struct ifreq)) + { + msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFCONF) failed: %m - exiting"); + exit(1); + } +# ifdef SIZE_RETURNED_IN_BUFFER + ifc.ifc_len = ioc.ic_len - sizeof(int); + ifc.ifc_buf = buf + sizeof(int); +# else /* not SIZE_RETURNED_IN_BUFFER */ + ifc.ifc_len = ioc.ic_len; + ifc.ifc_buf = buf; +# endif /* not SIZE_RETURNED_IN_BUFFER */ + +# else /* not STREAMS_TLI */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; +# ifndef SYS_WINNT + if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) +# else + if (WSAIoctl(vs, SIO_GET_INTERFACE_LIST, 0, 0, ifc.ifc_buf, ifc.ifc_len, &ifc.ifc_len, 0, 0) == SOCKET_ERROR) +# endif /* SYS_WINNT */ +{ + msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFCONF) failed: %m - exiting"); + exit(1); +} + +# endif /* not STREAMS_TLI */ + + for(n = ifc.ifc_len, ifr = ifc.ifc_req; n > 0; + ifr = (struct ifreq *)((char *)ifr + size)) + { + extern int listen_to_virtual_ips; + + size = sizeof(*ifr); + +# ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR + if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr)) + size += ifr->ifr_addr.sa_len - sizeof(struct sockaddr); +# endif + n -= size; + +# if !defined(SYS_WINNT) + /* Exclude logical interfaces (indicated by ':' in the interface name) */ + if (debug) + printf("interface <%s> ", ifr->ifr_name); + if ((listen_to_virtual_ips == 0) + && (strchr(ifr->ifr_name, (int)':') != NULL)) { + if (debug) + printf("ignored\n"); + continue; + } + if (debug) + printf("OK\n"); + + if ( +# ifdef VMS /* VMS+UCX */ + (((struct sockaddr *)&(ifr->ifr_addr))->sa_family != AF_INET) +# else + (ifr->ifr_addr.sa_family != AF_INET) +# endif /* VMS+UCX */ + ) { + if (debug) + printf("ignoring %s - not AF_INET\n", + ifr->ifr_name); + continue; + } +# endif /* SYS_WINNT */ + ifreq = *ifr; + inter_list[i].flags = 0; + /* is it broadcast capable? */ +# ifndef SYS_WINNT +# ifdef STREAMS_TLI + ioc.ic_cmd = SIOCGIFFLAGS; + ioc.ic_timout = 0; + ioc.ic_dp = (caddr_t)&ifreq; + ioc.ic_len = sizeof(struct ifreq); + if(ioctl(vs, I_STR, &ioc)) { + msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFFLAGS) failed: %m"); + continue; + } +# else /* not STREAMS_TLI */ + if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + if (errno != ENXIO) + msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFFLAGS) failed: %m"); + continue; + } +# endif /* not STREAMS_TLI */ + if ((ifreq.ifr_flags & IFF_UP) == 0) { + if (debug) + printf("ignoring %s - interface not UP\n", + ifr->ifr_name); + continue; + } + inter_list[i].flags = 0; + if (ifreq.ifr_flags & IFF_BROADCAST) + inter_list[i].flags |= INT_BROADCAST; +# endif /* not SYS_WINNT */ +# if !defined(SUN_3_3_STINKS) + if ( +# if defined(IFF_LOCAL_LOOPBACK) /* defined(SYS_HPUX) && (SYS_HPUX < 8) */ + (ifreq.ifr_flags & IFF_LOCAL_LOOPBACK) +# elif defined(IFF_LOOPBACK) + (ifreq.ifr_flags & IFF_LOOPBACK) +# else /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */ + /* test against 127.0.0.1 (yuck!!) */ + (inter_list[i].sin.sin_addr.s_addr == inet_addr("127.0.0.1")) +# endif /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */ + ) + { +# ifndef SYS_WINNT + inter_list[i].flags |= INT_LOOPBACK; +# endif /* not SYS_WINNT */ + if (loopback_interface == 0) + { + loopback_interface = &inter_list[i]; + } + } +# endif /* not SUN_3_3_STINKS */ + +#if 0 +# ifndef SYS_WINNT +# ifdef STREAMS_TLI + ioc.ic_cmd = SIOCGIFADDR; + ioc.ic_timout = 0; + ioc.ic_dp = (caddr_t)&ifreq; + ioc.ic_len = sizeof(struct ifreq); + if (ioctl(vs, I_STR, &ioc)) + { + msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFADDR) failed: %m"); + continue; + } +# else /* not STREAMS_TLI */ + if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) + { + if (errno != ENXIO) + msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFADDR) failed: %m"); + continue; + } +# endif /* not STREAMS_TLI */ +# endif /* not SYS_WINNT */ +#endif /* 0 */ +# if defined(SYS_WINNT) + {int TODO_FillInTheNameWithSomeThingReasonble;} +# else + (void)strncpy(inter_list[i].name, ifreq.ifr_name, + sizeof(inter_list[i].name)); +# endif + inter_list[i].sin = *(struct sockaddr_in *)&ifr->ifr_addr; + inter_list[i].sin.sin_family = AF_INET; + inter_list[i].sin.sin_port = port; + +# if defined(SUN_3_3_STINKS) + /* + * Oh, barf! I'm too disgusted to even explain this + */ + if (SRCADR(&inter_list[i].sin) == 0x7f000001) + { + inter_list[i].flags |= INT_LOOPBACK; + if (loopback_interface == 0) + loopback_interface = &inter_list[i]; + } +# endif /* SUN_3_3_STINKS */ +# if !defined SYS_WINNT && !defined SYS_CYGWIN32 /* no interface flags on NT */ + if (inter_list[i].flags & INT_BROADCAST) { +# ifdef STREAMS_TLI + ioc.ic_cmd = SIOCGIFBRDADDR; + ioc.ic_timout = 0; + ioc.ic_dp = (caddr_t)&ifreq; + ioc.ic_len = sizeof(struct ifreq); + if(ioctl(vs, I_STR, &ioc)) { + msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFBRDADDR) failed: %m"); + exit(1); + } +# else /* not STREAMS_TLI */ + if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFBRDADDR) failed: %m"); + exit(1); + } +# endif /* not STREAMS_TLI */ + +# ifndef ifr_broadaddr + inter_list[i].bcast = + *(struct sockaddr_in *)&ifreq.ifr_addr; +# else + inter_list[i].bcast = + *(struct sockaddr_in *)&ifreq.ifr_broadaddr; +# endif /* ifr_broadaddr */ + inter_list[i].bcast.sin_family = AF_INET; + inter_list[i].bcast.sin_port = port; + } + +# ifdef STREAMS_TLI + ioc.ic_cmd = SIOCGIFNETMASK; + ioc.ic_timout = 0; + ioc.ic_dp = (caddr_t)&ifreq; + ioc.ic_len = sizeof(struct ifreq); + if(ioctl(vs, I_STR, &ioc)) { + msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFNETMASK) failed: %m"); + exit(1); + } +# else /* not STREAMS_TLI */ + if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFNETMASK) failed: %m"); + exit(1); + } +# endif /* not STREAMS_TLI */ + inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr; +# else + /* winnt here */ + inter_list[i].bcast = ifreq.ifr_broadaddr; + inter_list[i].bcast.sin_family = AF_INET; + inter_list[i].bcast.sin_port = port; + inter_list[i].mask = ifreq.ifr_mask; +# endif /* not SYS_WINNT */ + + /* + * look for an already existing source interface address. If + * the machine has multiple point to point interfaces, then + * the local address may appear more than once. + */ + for (j=0; j < i; j++) + if (inter_list[j].sin.sin_addr.s_addr == + inter_list[i].sin.sin_addr.s_addr) { + break; + } + if (j == i) + i++; + if (i > MAXINTERFACES) + break; + } + closesocket(vs); +#endif /* _BSDI_VERSION >= 199510 */ + + ninterfaces = i; + maxactivefd = 0; + FD_ZERO(&activefds); + for (i = 0; i < ninterfaces; i++) { + inter_list[i].fd = + open_socket(&inter_list[i].sin, + inter_list[i].flags & INT_BROADCAST, 0); + } + + /* + * Now that we have opened all the sockets, turn off the reuse flag for + * security. + */ + for (i = 0; i < ninterfaces; i++) { + int off = 0; + + /* + * if inter_list[ n ].fd is -1, we might have a adapter + * configured but not present + */ + if ( inter_list[ i ].fd != -1 ) { + if (setsockopt(inter_list[i].fd, SOL_SOCKET, + SO_REUSEADDR, (char *)&off, + sizeof(off))) { + msyslog(LOG_ERR, "create_sockets: setsockopt(SO_REUSEADDR,off) failed: %m"); + } + } + } + +#if defined(MCAST) + /* + * enable possible multicast reception on the broadcast socket + */ + inter_list[0].bcast.sin_addr.s_addr = htonl(INADDR_ANY); + inter_list[0].bcast.sin_family = AF_INET; + inter_list[0].bcast.sin_port = port; +#endif /* MCAST */ + + /* + * Blacklist all bound interface addresses + */ + resmask.sin_addr.s_addr = ~ (u_int32)0; + for (i = 1; i < ninterfaces; i++) + hack_restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask, + RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE); + + any_interface = &inter_list[0]; +#ifdef DEBUG + if (debug > 2) { + printf("create_sockets: ninterfaces=%d\n", ninterfaces); + for (i = 0; i < ninterfaces; i++) { + printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n", + i, + inter_list[i].fd, + inter_list[i].bfd, + inter_list[i].name, + inter_list[i].flags); + /* Leave these as three printf calls. */ + printf(" sin=%s", + inet_ntoa((inter_list[i].sin.sin_addr))); + if (inter_list[i].flags & INT_BROADCAST) + printf(" bcast=%s,", + inet_ntoa((inter_list[i].bcast.sin_addr))); + printf(" mask=%s\n", + inet_ntoa((inter_list[i].mask.sin_addr))); + } + } +#endif +#if defined (HAVE_IO_COMPLETION_PORT) + for (i = 0; i < ninterfaces; i++) { + io_completion_port_add_socket(&inter_list[i]); + } +#endif + return ninterfaces; +} + +/* + * io_setbclient - open the broadcast client sockets + */ +void +io_setbclient(void) +{ + int i; + + for (i = 1; i < ninterfaces; i++) + { + if (!(inter_list[i].flags & INT_BROADCAST)) + continue; + if (inter_list[i].flags & INT_BCASTOPEN) + continue; +#ifdef SYS_SOLARIS + inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY); +#endif +#ifdef OPEN_BCAST_SOCKET /* Was: !SYS_DOMAINOS && !SYS_LINUX */ + inter_list[i].bfd = open_socket(&inter_list[i].bcast, INT_BROADCAST, 1); + inter_list[i].flags |= INT_BCASTOPEN; +#endif + } +} + + +/* + * io_multicast_add() - add multicast group address + */ +void +io_multicast_add( + u_int32 addr + ) +{ +#ifdef MCAST + struct ip_mreq mreq; + int i = ninterfaces; /* Use the next interface */ + u_int32 haddr = ntohl(addr); + struct in_addr iaddr; + int s; + struct sockaddr_in *sinp; + + iaddr.s_addr = addr; + + if (!IN_CLASSD(haddr)) + { + msyslog(LOG_ERR, + "cannot add multicast address %s as it is not class D", + inet_ntoa(iaddr)); + return; + } + + for (i = 0; i < ninterfaces; i++) + { + /* Already have this address */ + if (inter_list[i].sin.sin_addr.s_addr == addr) return; + /* found a free slot */ + if (inter_list[i].sin.sin_addr.s_addr == 0 && + inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 && + inter_list[i].flags == 0) break; + } + sinp = &(inter_list[i].sin); + + memset((char *)&mreq, 0, sizeof(mreq)); + memset((char *)&inter_list[i], 0, sizeof inter_list[0]); + sinp->sin_family = AF_INET; + sinp->sin_addr = iaddr; + sinp->sin_port = htons(123); + + s = open_socket(sinp, 0, 1); + /* Try opening a socket for the specified class D address */ + /* This works under SunOS 4.x, but not OSF1 .. :-( */ + if (s < 0) + { + memset((char *)&inter_list[i], 0, sizeof inter_list[0]); + i = 0; + /* HACK ! -- stuff in an address */ + inter_list[i].bcast.sin_addr.s_addr = addr; + msyslog(LOG_ERR, "...multicast address %s using wildcard socket", + inet_ntoa(iaddr)); + } + else + { + inter_list[i].fd = s; + inter_list[i].bfd = -1; + (void) strncpy(inter_list[i].name, "multicast", + sizeof(inter_list[i].name)); + inter_list[i].mask.sin_addr.s_addr = htonl(~(u_int32)0); + } + + /* + * enable reception of multicast packets + */ + mreq.imr_multiaddr = iaddr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) == -1) + msyslog(LOG_ERR, + "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)", + mreq.imr_multiaddr.s_addr, mreq.imr_interface.s_addr, + inet_ntoa(iaddr)); + inter_list[i].flags |= INT_MULTICAST; + if (i >= ninterfaces) ninterfaces = i+1; +#else /* MCAST */ + struct in_addr iaddr; + + iaddr.s_addr = addr; + msyslog(LOG_ERR, "cannot add multicast address %s as no MCAST support", + inet_ntoa(iaddr)); +#endif /* MCAST */ +} + +/* + * io_unsetbclient - close the broadcast client sockets + */ +void +io_unsetbclient(void) +{ + int i; + + for (i = 1; i < ninterfaces; i++) + { + if (!(inter_list[i].flags & INT_BCASTOPEN)) + continue; + close_socket(inter_list[i].bfd); + inter_list[i].bfd = -1; + inter_list[i].flags &= ~INT_BCASTOPEN; + } +} + + +/* + * io_multicast_del() - delete multicast group address + */ +void +io_multicast_del( + u_int32 addr + ) +{ +#ifdef MCAST + int i; + struct ip_mreq mreq; + u_int32 haddr = ntohl(addr); + struct sockaddr_in sinaddr; + + if (!IN_CLASSD(haddr)) + { + sinaddr.sin_addr.s_addr = addr; + msyslog(LOG_ERR, + "invalid multicast address %s", ntoa(&sinaddr)); + return; + } + + /* + * Disable reception of multicast packets + */ + mreq.imr_multiaddr.s_addr = addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + for (i = 0; i < ninterfaces; i++) + { + if (!(inter_list[i].flags & INT_MULTICAST)) + continue; + if (!(inter_list[i].fd < 0)) + continue; + if (addr != inter_list[i].sin.sin_addr.s_addr) + continue; + if (i != 0) + { + /* we have an explicit fd, so we can close it */ + close_socket(inter_list[i].fd); + memset((char *)&inter_list[i], 0, sizeof inter_list[0]); + inter_list[i].fd = -1; + inter_list[i].bfd = -1; + } + else + { + /* We are sharing "any address" port :-( Don't close it! */ + if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (char *)&mreq, sizeof(mreq)) == -1) + msyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m"); + /* This is **WRONG** -- there may be others ! */ + /* There should be a count of users ... */ + inter_list[i].flags &= ~INT_MULTICAST; + } + } +#else /* not MCAST */ + msyslog(LOG_ERR, "this function requires multicast kernel"); +#endif /* not MCAST */ +} + + +/* + * open_socket - open a socket, returning the file descriptor + */ +static int +open_socket( + struct sockaddr_in *addr, + int flags, + int turn_off_reuse + ) +{ + int fd; + int on = 1, off = 0; + + /* create a datagram (UDP) socket */ + if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) +#ifndef SYS_WINNT + < 0 +#else + == INVALID_SOCKET +#endif /* SYS_WINNT */ + ) + { + msyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed: %m"); + exit(1); + /*NOTREACHED*/ + } + + /* set SO_REUSEADDR since we will be binding the same port + number on each interface */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on))) + { + msyslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m"); + } + + /* + * bind the local address. + */ + if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { + char buff[160]; + sprintf(buff, + "bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=%d fails: %%m", + fd, addr->sin_family, (int)ntohs(addr->sin_port), + ntoa(addr), + IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags); + msyslog(LOG_ERR, buff); + closesocket(fd); + + /* + * soft fail if opening a class D address + */ + if (IN_CLASSD(ntohl(addr->sin_addr.s_addr))) + return -1; +#if 0 + exit(1); +#else + return -1; +#endif + } +#ifdef DEBUG + if (debug) + printf("bind() fd %d, family %d, port %d, addr %s, flags=%d\n", + fd, + addr->sin_family, + (int)ntohs(addr->sin_port), + ntoa(addr), + flags); +#endif + if (fd > maxactivefd) + maxactivefd = fd; + FD_SET(fd, &activefds); + + /* + * 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) + { + msyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +#elif defined(FNDELAY) + if (fcntl(fd, F_SETFL, FNDELAY) < 0) + { + msyslog(LOG_ERR, "fcntl(FNDELAY) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +#elif defined(O_NDELAY) /* generally the same as FNDELAY */ + if (fcntl(fd, F_SETFL, O_NDELAY) < 0) + { + msyslog(LOG_ERR, "fcntl(O_NDELAY) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +#elif defined(FIONBIO) + if ( +# if defined(VMS) + (ioctl(fd,FIONBIO,&1) < 0) +# elif defined(SYS_WINNT) + (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) +# else + (ioctl(fd,FIONBIO,&on) < 0) +# endif + ) + { + msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +#elif defined(FIOSNBIO) + if (ioctl(fd,FIOSNBIO,&on) < 0) + { + msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m"); + exit(1); + /*NOTREACHED*/ + } +#else +# include "Bletch: Need non-blocking I/O!" +#endif + +#ifdef HAVE_SIGNALED_IO + init_socket_sig(fd); +#endif /* not HAVE_SIGNALED_IO */ + + /* + * Turn off the SO_REUSEADDR socket option. It apparently + * causes heartburn on systems with multicast IP installed. + * On normal systems it only gets looked at when the address + * is being bound anyway.. + */ + if (turn_off_reuse) + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char *)&off, sizeof(off))) + { + msyslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m"); + } + +#ifdef SO_BROADCAST + /* if this interface can support broadcast, set SO_BROADCAST */ + if (flags & INT_BROADCAST) + { + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, + (char *)&on, sizeof(on))) + { + msyslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m"); + } + } +#endif /* SO_BROADCAST */ + +#if !defined(SYS_WINNT) && !defined(VMS) +# ifdef DEBUG + if (debug > 1) + printf("flags for fd %d: 0%o\n", fd, + fcntl(fd, F_GETFL, 0)); +# endif +#endif /* SYS_WINNT || VMS */ + + return fd; +} + + +/* + * close_socket - close a socket and remove from the activefd list + */ +static void +close_socket( + int fd + ) +{ + int i, newmax; + + (void) closesocket(fd); + FD_CLR( (u_int) fd, &activefds); + + if (fd >= maxactivefd) + { + newmax = 0; + for (i = 0; i < maxactivefd; i++) + if (FD_ISSET(i, &activefds)) + newmax = i; + maxactivefd = newmax; + } +} + + +/* + * close_file - close a file and remove from the activefd list + * added 1/31/1997 Greg Schueman for Windows NT portability + */ +static void +close_file( + int fd + ) +{ + int i, newmax; + + (void) close(fd); + FD_CLR( (u_int) fd, &activefds); + + if (fd >= maxactivefd) + { + newmax = 0; + for (i = 0; i < maxactivefd; i++) + if (FD_ISSET(i, &activefds)) + newmax = i; + maxactivefd = newmax; + } +} + + +/* + * findbcastinter - find broadcast interface corresponding to address + */ +struct interface * +findbcastinter( + struct sockaddr_in *addr + ) +{ +#if defined(SIOCGIFCONF) || defined(SYS_WINNT) + register int i; + register u_int32 netnum; + + netnum = NSRCADR(addr); + for (i = 1; i < ninterfaces; i++) + { + if (!(inter_list[i].flags & INT_BROADCAST)) + continue; + if (NSRCADR(&inter_list[i].bcast) == netnum) + return &inter_list[i]; + if ((NSRCADR(&inter_list[i].sin) & NSRCADR(&inter_list[i].mask)) + == (netnum & NSRCADR(&inter_list[i].mask))) + return &inter_list[i]; + } +#endif /* SIOCGIFCONF */ + return any_interface; +} + + +/* 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_in *dest, + struct interface *inter, + int ttl, + struct pkt *pkt, + int len + ) +{ + int cc, slot; +#ifdef SYS_WINNT + DWORD err; +#endif /* SYS_WINNT */ + + /* + * Send error cache. Empty slots have port == 0 + * Set ERRORCACHESIZE to 0 to disable + */ + struct cache { + u_short port; + struct in_addr addr; + }; + +#ifndef ERRORCACHESIZE +#define ERRORCACHESIZE 8 +#endif +#if ERRORCACHESIZE > 0 + static struct cache badaddrs[ERRORCACHESIZE]; +#else +#define badaddrs ((struct cache *)0) /* Only used in empty loops! */ +#endif + + /* + * check if the source address is a multicast address - replace + * interface with any-interface if so. + */ + if (IN_MULTICAST(ntohl(inter->sin.sin_addr.s_addr))) + inter = any_interface; +#ifdef DEBUG + if (debug > 1) + printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n", + (ttl >= 0) ? "\tMCAST\t*****" : "", + inter->fd, ntoa(dest), + ntoa(&inter->sin), ttl, len); +#endif + +#ifdef MCAST + /* for the moment we use the bcast option to set multicast ttl */ + if (ttl >= 0 && ttl != inter->last_ttl) + { + char mttl = ttl; + + /* set the multicast ttl for outgoing packets */ + if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, + &mttl, sizeof(mttl)) == -1) + { + msyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m"); + } + else inter->last_ttl = ttl; + } +#endif /* MCAST */ + + for (slot = ERRORCACHESIZE; --slot >= 0; ) + if (badaddrs[slot].port == dest->sin_port && + badaddrs[slot].addr.s_addr == dest->sin_addr.s_addr) + break; + +#if defined(HAVE_IO_COMPLETION_PORT) + err = io_completion_port_sendto(inter, pkt, len, dest); + if (err != ERROR_SUCCESS) +#else + cc = sendto(inter->fd, (char *)pkt, len, 0, (struct sockaddr *)dest, + sizeof(struct sockaddr_in)); + if (cc == -1) +#endif + { + inter->notsent++; + packets_notsent++; +#if defined(HAVE_IO_COMPLETION_PORT) + if (err != WSAEWOULDBLOCK && err != WSAENOBUFS && slot < 0) +#else + if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0) +#endif + { + /* + * Remember this, if there's an empty slot + */ + for (slot = ERRORCACHESIZE; --slot >= 0; ) + if (badaddrs[slot].port == 0) + { + badaddrs[slot].port = dest->sin_port; + badaddrs[slot].addr = dest->sin_addr; + break; + } + msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest)); + } + } + else + { + inter->sent++; + packets_sent++; + /* + * He's not bad any more + */ + if (slot >= 0) + { + msyslog(LOG_INFO, "Connection re-established to %s", ntoa(dest)); + badaddrs[slot].port = 0; + } + } +} + +#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; +} + +/* + * input_handler - receive packets asynchronously + */ +extern void +input_handler( + l_fp *cts + ) +{ + register int i, n; + register struct recvbuf *rb; + register int doing; + register int fd; + struct timeval tvzero; + int fromlen; + l_fp ts; /* Timestamp at BOselect() gob */ + l_fp ts_e; /* Timestamp at EOselect() gob */ + fd_set fds; + int select_count = 0; + static int handler_count = 0; + + ++handler_count; + if (handler_count != 1) + msyslog(LOG_ERR, "input_handler: handler_count is %d!", handler_count); + handler_calls++; + ts = *cts; + + for (;;) + { + /* + * Do a poll to see who has data + */ + + fds = activefds; + tvzero.tv_sec = tvzero.tv_usec = 0; + + /* + * If we have something to do, freeze a timestamp. + * See below for the other cases (nothing (left) to do or error) + */ + while (0 < (n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero))) + { + ++select_count; + ++handler_pkts; + +#ifdef REFCLOCK + /* + * Check out the reference clocks first, if any + */ + if (refio != 0) + { + register struct refclockio *rp; + + for (rp = refio; rp != 0 && n > 0; rp = rp->next) + { + fd = rp->fd; + if (FD_ISSET(fd, &fds)) + { + n--; + if (free_recvbuffs() == 0) + { + char buf[RX_BUFF_SIZE]; + +#ifndef SYS_WINNT + (void) read(fd, buf, sizeof buf); +#else + (void) ReadFile((HANDLE)fd, buf, (DWORD)sizeof buf, NULL, NULL); +#endif /* SYS_WINNT */ + packets_dropped++; + goto select_again; + } + + rb = get_free_recv_buffer(); + + i = (rp->datalen == 0 + || rp->datalen > sizeof(rb->recv_space)) + ? sizeof(rb->recv_space) : rp->datalen; +#ifndef SYS_WINNT + rb->recv_length = + read(fd, (char *)&rb->recv_space, (unsigned)i) +#else /* SYS_WINNT */ + ReadFile((HANDLE)fd, (char *)&rb->recv_space, (DWORD)i, + (LPDWORD)&(rb->recv_length), NULL) +#endif /* SYS_WINNT */ + ; + + if (rb->recv_length == -1) + { + msyslog(LOG_ERR, "clock read fd %d: %m", fd); + freerecvbuf(rb); + goto select_again; + } + + /* + * Got one. Mark how and when it got here, + * put it on the full list and do bookkeeping. + */ + 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); +#if 1 + goto select_again; +#else + continue; +#endif + } + } + + add_full_recv_buffer(rb); + + rp->recvcount++; + packets_received++; + } + } + } +#endif /* REFCLOCK */ + + /* + * Loop through the interfaces looking for data to read. + */ + for (i = ninterfaces - 1; (i >= 0) && (n > 0); i--) + { + for (doing = 0; (doing < 2) && (n > 0); doing++) + { + if (doing == 0) + { + fd = inter_list[i].fd; + } + else + { + if (!(inter_list[i].flags & INT_BCASTOPEN)) + break; + fd = inter_list[i].bfd; + } + if (fd < 0) continue; + if (FD_ISSET(fd, &fds)) + { + n--; + + /* + * Get a buffer and read the frame. If we + * haven't got a buffer, or this is received + * on the wild card socket, just dump the + * packet. + */ + if ( +#ifdef UDP_WILDCARD_DELIVERY + /* + * these guys manage to put properly addressed + * packets into the wildcard queue + */ + (free_recvbuffs() == 0) +#else + ((i == 0) || (free_recvbuffs() == 0)) +#endif + ) + { + char buf[RX_BUFF_SIZE]; + struct sockaddr from; + + fromlen = sizeof from; + (void) recvfrom(fd, buf, sizeof(buf), 0, &from, &fromlen); +#ifdef DEBUG + if (debug) + printf("%s on %d(%lu) fd=%d from %s\n", + (i) ? "drop" : "ignore", + i, free_recvbuffs(), fd, + inet_ntoa(((struct sockaddr_in *) &from)->sin_addr)); +#endif + if (i == 0) + packets_ignored++; + else + packets_dropped++; + goto select_again; + } + + rb = get_free_recv_buffer(); + + fromlen = sizeof(struct sockaddr_in); + rb->recv_length = recvfrom(fd, + (char *)&rb->recv_space, + sizeof(rb->recv_space), 0, + (struct sockaddr *)&rb->recv_srcadr, + &fromlen); + if (rb->recv_length == 0 +#ifdef EWOULDBLOCK + || errno==EWOULDBLOCK +#endif +#ifdef EAGAIN + || errno==EAGAIN +#endif + ) { + freerecvbuf(rb); + continue; + } + else if (rb->recv_length < 0) + { + msyslog(LOG_ERR, "recvfrom() fd=%d: %m", fd); +#ifdef DEBUG + if (debug) + printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd); +#endif + freerecvbuf(rb); + continue; + } +#ifdef DEBUG + if (debug > 2) + printf("input_handler: fd=%d length %d from %08lx %s\n", + fd, rb->recv_length, + (u_long)ntohl(rb->recv_srcadr.sin_addr.s_addr) & + 0x00000000ffffffff, + inet_ntoa(rb->recv_srcadr.sin_addr)); +#endif + + /* + * Got one. Mark how and when it got here, + * put it on the full list and do bookkeeping. + */ + rb->dstadr = &inter_list[i]; + rb->fd = fd; + rb->recv_time = ts; + rb->receiver = receive; + + add_full_recv_buffer(rb); + + inter_list[i].received++; + packets_received++; + goto select_again; + } + /* Check more interfaces */ + } + } + select_again:; + /* + * Done everything from that select. Poll again. + */ + } + + /* + * If nothing more to do, try again. + * If nothing to do, just return. + * If an error occurred, complain and return. + */ + if (n == 0) + { + if (select_count == 0) /* We really had nothing to do */ + { + if (debug) + msyslog(LOG_DEBUG, "input_handler: select() returned 0"); + --handler_count; + return; + } + /* We've done our work */ + 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); + if (debug > 3) + msyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6)); + + /* just bail. */ + --handler_count; + return; + } + else if (n == -1) + { +#ifndef SYS_WINNT + int err = errno; +#else + DWORD err = WSAGetLastError(); +#endif /* SYS_WINNT */ + + /* + * extended FAU debugging output + */ + msyslog(LOG_ERR, "select(%d, %s, 0L, 0L, &0.000000) error: %m", + maxactivefd+1, fdbits(maxactivefd, &activefds)); + if ( +#ifndef SYS_WINNT + (err == EBADF) +#else + (err == WSAEBADF) +#endif /* SYS_WINNT */ + ) + { + int j, b; + + fds = activefds; + for (j = 0; j <= maxactivefd; j++) + if ( +#ifndef SYS_WINNT + (FD_ISSET(j, &fds) && (read(j, &b, 0) == -1)) +#else + (FD_ISSET(j, &fds) && (!ReadFile((HANDLE)j, &b, 0, NULL, NULL))) +#endif /* SYS_WINNT */ + ) + msyslog(LOG_ERR, "Bad file descriptor %d", j); + } + --handler_count; + return; + } + } + msyslog(LOG_ERR, "input_handler: fell out of infinite for(;;) loop!"); + --handler_count; + return; +} + +#endif + +/* + * findinterface - utility used by other modules to find an interface + * given an address. + */ +struct interface * +findinterface( + struct sockaddr_in *addr + ) +{ + register int i; + register u_int32 saddr; + + /* + * Just match the address portion. + */ + saddr = addr->sin_addr.s_addr; + for (i = 0; i < ninterfaces; i++) + { + if (inter_list[i].sin.sin_addr.s_addr == saddr) + return &inter_list[i]; + } + return (struct interface *)0; +} + + +/* + * 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 +/* + * This is a hack so that I don't have to fool with these ioctls in the + * pps driver ... we are already non-blocking and turn on SIGIO thru + * another mechanisim + */ +int +io_addclock_simple( + 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; + refio = rio; + + if (rio->fd > maxactivefd) + maxactivefd = rio->fd; + FD_SET(rio->fd, &activefds); + UNBLOCKIO(); + return 1; +} + +/* + * 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; + refio = rio; + +# ifdef HAVE_SIGNALED_IO + if (init_clock_sig(rio)) + { + refio = rio->next; + UNBLOCKIO(); + return 0; + } +# elif defined(HAVE_IO_COMPLETION_PORT) + if (io_completion_port_add_clock_io(rio)) + { + refio = rio->next; + UNBLOCKIO(); + return 0; + } +# endif + + if (rio->fd > maxactivefd) + maxactivefd = rio->fd; + FD_SET(rio->fd, &activefds); + + UNBLOCKIO(); + return 1; +} + +/* + * io_closeclock - close the clock in the I/O structure given + */ +void +io_closeclock( + struct refclockio *rio + ) +{ + /* + * Remove structure from the list + */ + if (refio == rio) + { + refio = rio->next; + } + else + { + register struct refclockio *rp; + + for (rp = refio; rp != 0; rp = rp->next) + if (rp->next == rio) + { + rp->next = rio->next; + break; + } + + if (rp == 0) + { + /* + * Internal error. Report it. + */ + msyslog(LOG_ERR, + "internal error: refclockio structure not found"); + return; + } + } + + /* + * Close the descriptor. + */ + close_file(rio->fd); +} +#endif /* REFCLOCK */ + +void +kill_asyncio(void) +{ + int i; + + BLOCKIO(); + for (i = 0; i <= maxactivefd; i++) + (void)close_socket(i); +} diff --git a/contrib/ntp/ntpd/ntp_loopfilter.c b/contrib/ntp/ntpd/ntp_loopfilter.c new file mode 100644 index 0000000..6e0e134 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_loopfilter.c @@ -0,0 +1,847 @@ +/* + * ntp_loopfilter.c - implements the NTP loop filter algorithm + * + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + + +#include <signal.h> +#include <setjmp.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.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 max offset (s) */ +#define CLOCK_PANIC 1000. /* default panic offset (s) */ +#define CLOCK_MAXSTAB 2e-6 /* max frequency stability */ +#define CLOCK_MAXERR 1e-2 /* max phase jitter (s) */ +#define SHIFT_PLL 4 /* PLL loop gain (shift) */ +#define CLOCK_AVG 4. /* FLL loop gain */ +#define CLOCK_MINSEC 256. /* min FLL update interval (s) */ +#define CLOCK_MINSTEP 900. /* step-change timeout (s) */ +#define CLOCK_DAY 86400. /* one day of seconds */ +#define CLOCK_LIMIT 30 /* poll-adjust threshold */ +#define CLOCK_PGATE 4. /* poll-adjust gate */ +#define CLOCK_ALLAN 1024. /* min Allan intercept (s) */ +#define CLOCK_ADF 1e11 /* Allan deviation factor */ + +/* + * Clock discipline state machine. This is used to control the + * synchronization behavior during initialization and following a + * timewarp. + */ +#define S_NSET 0 /* clock never set */ +#define S_FSET 1 /* frequency set from the drift file */ +#define S_TSET 2 /* time set */ +#define S_FREQ 3 /* frequency mode */ +#define S_SYNC 4 /* clock synchronized */ +#define S_SPIK 5 /* spike detected */ + +/* + * 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 correctios 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 PLL used instead. + * + * Each update to a prefer peer sets pps_update 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_update 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. + */ +#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */ + +/* + * Program variables + */ +static double clock_offset; /* clock offset adjustment (s) */ +double drift_comp; /* clock frequency (ppm) */ +double clock_stability; /* clock stability (ppm) */ +double clock_max = CLOCK_MAX; /* max offset allowed before step (s) */ +static double clock_panic = CLOCK_PANIC; /* max offset allowed before panic */ +u_long pps_control; /* last pps sample time */ +static void rstclock P((int)); /* state transition function */ + +#ifdef KERNEL_PLL +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 ext_enable; /* external clock enabled */ +int pps_update; /* pps update valid */ +int allow_set_backward = TRUE; /* step corrections allowed */ +int correct_any = FALSE; /* corrections > 1000 s allowed */ + +#ifdef STA_NANO +int pll_nano; /* nanosecond kernel switch */ +#endif /* STA_NANO */ + +/* + * Clock state machine variables + */ +u_char sys_poll; /* log2 of system poll interval */ +int state; /* clock discipline state */ +int tc_counter; /* poll-adjust counter */ +u_long last_time; /* time of last clock update (s) */ +double last_offset; /* last clock offset (s) */ +double allan_xpt; /* Allan intercept (s) */ +double sys_error; /* system standard error (s) */ + +#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) +static void pll_trap P((int)); /* configuration trap */ +#ifdef SIGSYS +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. + */ + rstclock(S_NSET); +} + +/* + * local_clock - the NTP logical clock loop filter. Returns 1 if the + * clock was stepped, 0 if it was slewed and -1 if it is hopeless. + */ +int +local_clock( + struct peer *peer, /* synch source peer structure */ + double fp_offset, /* clock offset (s) */ + double epsil /* jittter (square s*s) */ + ) +{ + double mu; /* interval since last update (s) */ + double oerror; /* previous error estimate */ + double flladj; /* FLL frequency adjustment (ppm) */ + double plladj; /* PLL frequency adjustment (ppm) */ + double clock_frequency; /* clock frequency adjustment (ppm) */ + double dtemp, etemp; /* double temps */ + int retval; /* return value */ + +#if defined(KERNEL_PLL) + struct timex ntv; /* kernel interface structure */ +#endif /* KERNEL_PLL */ + +#ifdef DEBUG + if (debug) + printf( + "local_clock: offset %.6f jitter %.6f state %d\n", + fp_offset, SQRT(epsil), state); +#endif + if (!ntp_enable) + return(0); + + /* + * If the clock is way off, don't tempt fate by correcting it. + */ +#ifndef SYS_WINNT + if (fabs(fp_offset) >= clock_panic && !correct_any) { + msyslog(LOG_ERR, + "time error %.0f over %d seconds; set clock manually)", + fp_offset, (int)clock_panic); + return (-1); + } +#endif + /* + * If the clock has never been set, set it and initialize the + * discipline parameters. We then switch to frequency mode to + * speed the inital convergence process. If lucky, after an hour + * the ntp.drift file is created and initialized and we don't + * get here again. + */ + if (state == S_NSET) { + step_systime(fp_offset); + NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT) + msyslog(LOG_NOTICE, "time set %.6f s", fp_offset); + rstclock(S_TSET); + rstclock(S_FREQ); + return (1); + } + + /* + * Update the jitter estimate. + */ + oerror = sys_error; + dtemp = SQUARE(sys_error); + sys_error = SQRT(dtemp + (epsil - dtemp) / CLOCK_AVG); + + /* + * 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 + * phase error exceeds the maximum allowed for ordinary tracking + * and otherwise when it does not. + */ + retval = 0; + clock_frequency = flladj = plladj = 0; + mu = current_time - last_time; + if (fabs(fp_offset) > clock_max) { + switch (state) { + + /* + * In S_TSET state the time has been set at the last + * valid update and the offset at that time set to zero. + * If following that we cruise outside the capture + * range, assume a really bad frequency error and switch + * to S_FREQ state. + */ + case S_TSET: + rstclock(S_FREQ); + last_offset = clock_offset = fp_offset; + return (0); + + /* + * In S_SYNC state we ignore outlyers. At the first + * outlyer after CLOCK_MINSTEP (900 s), switch to S_SPIK + * state. + */ + case S_SYNC: + if (mu < CLOCK_MINSTEP) + return (0); + rstclock(S_SPIK); + return (0); + + /* + * In S_FREQ state we ignore outlyers. At the first + * outlyer after CLOCK_MINSTEP (900 s), compute the + * apparent phase and frequency correction. + */ + case S_FREQ: + if (mu < CLOCK_MINSTEP) + return (0); + clock_frequency = (fp_offset - clock_offset) / + mu; + /* fall through to default */ + + /* + * In S_SPIK state a large correction is necessary. + * Since the outlyer may be due to a large frequency + * error, compute the apparent frequency correction. + */ + case S_SPIK: + clock_frequency = (fp_offset - clock_offset) / + mu; + /* fall through to default */ + + /* + * We get here directly in S_FSET state and indirectly + * from S_FREQ and S_SPIK states. The clock is either + * reset or shaken, but never stirred. + */ + default: + if (allow_set_backward) { + step_systime(fp_offset); + NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT) + msyslog(LOG_NOTICE, "time reset %.6f s", + fp_offset); + rstclock(S_TSET); + retval = 1; + } else { + NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT) + msyslog(LOG_NOTICE, "time slew %.6f s", + fp_offset); + rstclock(S_FREQ); + last_offset = clock_offset = fp_offset; + return (0); + } + break; + } + } else { + switch (state) { + + /* + * If this is the first update, initialize the + * discipline parameters and pretend we had just set the + * clock. We don't want to step the clock unless we have + * to. + */ + case S_FSET: + rstclock(S_TSET); + last_offset = clock_offset = fp_offset; + return (0); + + /* + * In S_FREQ state we ignore updates until CLOCK_MINSTEP + * (900 s). 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; + clock_offset = fp_offset; + rstclock(S_SYNC); + break; + + /* + * Either the clock has just been set or the previous + * update was a spike and ignored. Since this update is + * not an outlyer, fold the tent and resume life. + */ + case S_TSET: + case S_SPIK: + rstclock(S_SYNC); + /* fall through to default */ + + /* + * We come here in the normal case for linear phase and + * frequency adjustments. If the offset exceeds the + * previous time error estimate by CLOCK_SGATE and the + * interval since the last update is less than twice the + * poll interval, consider the update a popcorn spike + * and ignore it. + */ + default: + if (fabs(fp_offset - last_offset) > + CLOCK_SGATE * oerror && mu < + ULOGTOD(sys_poll + 1)) { +#ifdef DEBUG + if (debug) + printf("local_clock: popcorn %.6f %.6f\n", + fp_offset, last_offset); +#endif + last_offset = fp_offset; + return (0); + } + + /* + * Compute the FLL and PLL frequency adjustments + * conditioned on two weighting factors, one + * which limits the time constant determined + * from the Allan intercept, the other which + * limits the gain factor as a function of + * update interval. The net effect is to favor + * the PLL adjustments at the smaller update + * intervals and the FLL adjustments at the + * larger ones. + */ + dtemp = max(mu, allan_xpt); + etemp = min(max(0, mu - CLOCK_MINSEC) / + CLOCK_ALLAN, 1.); + flladj = fp_offset * etemp / (dtemp * + CLOCK_AVG); + dtemp = ULOGTOD(SHIFT_PLL + 2 + sys_poll); + plladj = fp_offset * mu / (dtemp * dtemp); + clock_offset = fp_offset; + break; + } + } + + /* + * 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. + */ +#if defined(KERNEL_PLL) + 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 drift file. + */ + memset((char *)&ntv, 0, sizeof ntv); + if (ext_enable) { + ntv.modes = MOD_STATUS; + } else { + ntv.modes = MOD_BITS; + if (clock_offset < 0) + dtemp = -.5; + else + dtemp = .5; +#ifdef STA_NANO + if (pll_nano) + ntv.offset = (int32)(clock_offset * + 1e9 + dtemp); + else +#endif /* STA_NANO */ + ntv.offset = (int32)(clock_offset * + 1e6 + dtemp); + if (clock_frequency != 0) { + ntv.modes |= MOD_FREQUENCY; + ntv.freq = (int32)((clock_frequency + + drift_comp) * 65536e6); + } +#ifdef STA_NANO + ntv.constant = sys_poll; +#else + ntv.constant = sys_poll - 4; +#endif /* STA_NANO */ + ntv.esterror = (u_int32)(sys_error * 1e6); + ntv.maxerror = (u_int32)((sys_rootdelay / 2 + + sys_rootdispersion) * 1e6); + ntv.status = STA_PLL; + + /* + * Set the leap bits in the status word. + */ + if (sys_leap == LEAP_NOTINSYNC) { + ntv.status |= STA_UNSYNC; + } else if (calleapwhen(sys_reftime.l_ui) < + CLOCK_DAY) { + if (sys_leap & LEAP_ADDSECOND) + ntv.status |= STA_INS; + else if (sys_leap & LEAP_DELSECOND) + ntv.status |= STA_DEL; + } + + /* + * Switch to FLL mode if the poll interval is + * greater than MAXDPOLL, so that the kernel + * loop behaves as the daemon loop; viz., + * selects the FLL when necessary, etc. For + * legacy only. + */ + if (sys_poll > NTP_MAXDPOLL) + ntv.status |= STA_FLL; + } + + /* + * Wiggle the PPS bits according to the health of the + * prefer peer. + */ + if (pll_status & STA_PPSSIGNAL) + ntv.status |= STA_PPSFREQ; + if (pll_status & STA_PPSFREQ && pps_update) + ntv.status |= STA_PPSTIME; + + /* + * Update the offset and frequency from the kernel + * variables. + */ + if (ntp_adjtime(&ntv) == TIME_ERROR) { + if (ntv.status != pll_status) + msyslog(LOG_ERR, + "kernel pll status change %x", + ntv.status); + } + pll_status = ntv.status; +#ifdef STA_NANO + if (pll_nano) + clock_offset = ntv.offset / 1e9; + else +#endif /* STA_NANO */ + clock_offset = ntv.offset / 1e6; +#ifdef STA_NANO + sys_poll = ntv.constant; +#else + sys_poll = ntv.constant + 4; +#endif /* STA_NANO */ + clock_frequency = ntv.freq / 65536e6 - drift_comp; + flladj = plladj = 0; + + /* + * If the kernel pps discipline is working, monitor its + * performance. + */ + if (ntv.status & STA_PPSTIME) { + if (!pps_control) + NLOG(NLOG_SYSEVENT)msyslog(LOG_INFO, + "pps sync enabled"); + pps_control = current_time; +#ifdef STA_NANO + if (pll_nano) + record_peer_stats( + &loopback_interface->sin, + ctlsysstatus(), ntv.offset / 1e9, + 0., ntv.jitter / 1e9, 0.); + else +#endif /* STA_NANO */ + record_peer_stats( + &loopback_interface->sin, + ctlsysstatus(), ntv.offset / 1e6, + 0., ntv.jitter / 1e6, 0.); + } + } +#endif /* KERNEL_PLL */ + + /* + * Adjust the clock frequency and calculate the stability. If + * kernel support is available, we use the results of the kernel + * discipline instead of the PLL/FLL discipline. In this case, + * drift_comp is a sham and used only for updating the drift + * file and for billboard eye candy. + */ + etemp = clock_frequency + flladj + plladj; + drift_comp += etemp; + if (drift_comp > sys_maxfreq) + drift_comp = sys_maxfreq; + else if (drift_comp <= -sys_maxfreq) + drift_comp = -sys_maxfreq; + dtemp = SQUARE(clock_stability); + etemp = SQUARE(etemp) - dtemp; + clock_stability = SQRT(dtemp + etemp / CLOCK_AVG); + allan_xpt = max(CLOCK_ALLAN, clock_stability * CLOCK_ADF); + + /* + * In SYNC state, adjust the poll interval. + */ + if (state == S_SYNC) { + if (clock_stability < CLOCK_MAXSTAB && + fabs(clock_offset) < CLOCK_PGATE * sys_error) { + 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--; + } + } + } + } + + /* + * Update the system time variables. + */ + last_time = current_time; + last_offset = clock_offset; + dtemp = peer->disp + SQRT(peer->variance + SQUARE(sys_error)); + if ((peer->flags & FLAG_REFCLOCK) == 0 && dtemp < MINDISPERSE) + dtemp = MINDISPERSE; + sys_rootdispersion = peer->rootdispersion + dtemp; + (void)record_loop_stats(); +#ifdef DEBUG + if (debug) + printf( + "local_clock: mu %.0f allan %.0f fadj %.3f fll %.3f pll %.3f\n", + mu, allan_xpt, clock_frequency * 1e6, flladj * 1e6, + plladj * 1e6); +#endif /* DEBUG */ +#ifdef DEBUG + if (debug) + printf( + "local_clock: jitter %.6f freq %.3f stab %.3f poll %d count %d\n", + sys_error, drift_comp * 1e6, clock_stability * 1e6, + sys_poll, tc_counter); +#endif /* DEBUG */ + return (retval); +} + + +/* + * adj_host_clock - Called once every second to update the local clock. + */ +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; + + /* + * 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_SYSEVENT) /* conditional if clause */ + msyslog(LOG_INFO, "pps sync disabled"); + pps_control = 0; + } + if (!ntp_enable) + return; + + /* + * If the phase-lock loop is implemented in the kernel, we + * have no business going further. + */ + if (pll_control && kern_enable) + return; + + /* + * Intricate wrinkle for legacy only. If the local clock driver + * is in use and selected for synchronization, somebody else may + * tinker the adjtime() syscall. If this is the case, the driver + * is marked prefer and we have to avoid calling adjtime(), + * since that may truncate the other guy's requests. + */ + if (sys_peer != 0) { + if (sys_peer->refclktype == REFCLK_LOCALCLOCK && + sys_peer->flags & FLAG_PREFER) + return; + } + adjustment = clock_offset / ULOGTOD(SHIFT_PLL + sys_poll); + clock_offset -= adjustment; + adj_systime(adjustment + drift_comp); +} + + +/* + * Clock state machine. Enter new state and set state variables. + */ +static void +rstclock( + int trans /* new state */ + ) +{ + state = trans; + switch (state) { + + /* + * Frequency mode. The clock has ben set, but the frequency has + * not yet been determined. Note that the Allan intercept is set + * insure the clock filter considers only the most recent + * measurements. + */ + case S_FREQ: + sys_poll = NTP_MINDPOLL; + allan_xpt = CLOCK_ALLAN; + last_time = current_time; + break; + + /* + * Synchronized mode. Discipline the poll interval. + */ + case S_SYNC: + sys_poll = NTP_MINDPOLL; + allan_xpt = CLOCK_ALLAN; + tc_counter = 0; + break; + + /* + * Don't do anything in S_SPIK state; just continue from S_SYNC + * state. + */ + case S_SPIK: + break; + + /* + * S_NSET, S_FSET and S_TSET states. These transient states set + * the time reference for future frequency updates. + */ + default: + sys_poll = NTP_MINDPOLL; + allan_xpt = CLOCK_ALLAN; + last_time = current_time; + last_offset = clock_offset = 0; + break; + } +} + + +/* + * loop_config - configure the loop filter + */ +void +loop_config( + int item, + double freq + ) +{ +#if defined(KERNEL_PLL) + struct timex ntv; +#endif /* KERNEL_PLL */ + +#ifdef DEBUG + if (debug) + printf("loop_config: state %d freq %.3f\n", item, freq * + 1e6); +#endif + switch (item) { + + case LOOP_DRIFTINIT: + case LOOP_DRIFTCOMP: + + /* + * The drift file is present and the initial frequency + * is available, so set the state to S_FSET + */ + rstclock(S_FSET); + drift_comp = freq; + if (drift_comp > sys_maxfreq) + drift_comp = sys_maxfreq; + if (drift_comp < -sys_maxfreq) + drift_comp = -sys_maxfreq; +#ifdef KERNEL_PLL + /* + * If the phase-lock code is implemented in the kernel, + * give the time_constant and saved frequency offset to + * the kernel. If not, no harm is done. Note the initial + * time constant is zero, but the first clock update + * will fix that. + */ + memset((char *)&ntv, 0, sizeof ntv); + pll_control = 1; +#ifdef MOD_NANO + ntv.modes = MOD_NANO; +#endif /* MOD_NANO */ +#ifdef SIGSYS + 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; + return; + } + + /* + * Use sigsetjmp() to save state and then call + * ntp_adjtime(); if it fails, then siglongjmp() is used + * to return control + */ + if (sigsetjmp(env, 1) == 0) + (void)ntp_adjtime(&ntv); + if ((sigaction(SIGSYS, &sigsys, + (struct sigaction *)NULL))) { + msyslog(LOG_ERR, + "sigaction() fails to restore SIGSYS trap: %m"); + pll_control = 0; + return; + } +#else /* SIGSYS */ + if (ntp_adjtime(&ntv) < 0) { + msyslog(LOG_ERR, + "loop_config: ntp_adjtime() failed: %m"); + pll_control = 0; + } +#endif /* SIGSYS */ + + /* + * If the kernel support is available and enabled, + * initialize the parameters, but only if the external + * clock is not present. + */ + if (pll_control && kern_enable) { + msyslog(LOG_NOTICE, + "using kernel phase-lock loop %04x", + ntv.status); +#ifdef STA_NANO + if (ntv.status & STA_NANO) + pll_nano = 1; +#endif /* STA_NANO */ +#ifdef STA_CLK + + if (ntv.status & STA_CLK) { + ext_enable = 1; + } else { + ntv.modes = MOD_BITS | MOD_FREQUENCY; + ntv.freq = (int32)(drift_comp * + 65536e6); + ntv.maxerror = MAXDISPERSE; + ntv.esterror = MAXDISPERSE; + ntv.status = STA_UNSYNC | STA_PLL; + (void)ntp_adjtime(&ntv); + } +#else + ntv.modes = MOD_BITS | MOD_FREQUENCY; + ntv.freq = (int32)(drift_comp * 65536e6); + ntv.maxerror = MAXDISPERSE; + ntv.esterror = MAXDISPERSE; + ntv.status = STA_UNSYNC | STA_PLL; + (void)ntp_adjtime(&ntv); +#endif /* STA_CLK */ + } +#endif /* KERNEL_PLL */ + } +} + + +#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..64dfb3e --- /dev/null +++ b/contrib/ntp/ntpd/ntp_monitor.c @@ -0,0 +1,354 @@ +/* + * ntp_monitor.c - monitor who is using the ntpd server + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <signal.h> +# ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif +# include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_if.h" +#include "ntp_stdlib.h" + +/* + * 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) ((int)(ntohl((addr)) & MON_HASH_MASK)) + +/* + * 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]; /* array of list ptrs */ +struct mon_data mon_mru_list; +struct mon_data mon_fifo_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; /* the free list or null if none */ + +static int mon_total_mem; /* total number of structures allocated */ +static int mon_mem_increments; /* number of times we've 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; +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((char *)&mon_hash[0], 0, sizeof mon_hash); + memset((char *)&mon_mru_list, 0, sizeof mon_mru_list); + memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_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; /* Ooops.. */ + + 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_fifo_list.fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev = &mon_fifo_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; + + mon_fifo_list.fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev = &mon_fifo_list; +} + + +/* + * ntp_monitor - record stats about this packet + */ +void +ntp_monitor( + struct recvbuf *rbufp + ) +{ + register struct pkt *pkt; + register struct mon_data *md; + register u_long netnum; + register int hash; + register int mode; + + if (mon_enabled == MON_OFF) + return; + + pkt = &rbufp->recv_pkt; + netnum = NSRCADR(&rbufp->recv_srcadr); + hash = MON_HASH(netnum); + mode = PKT_MODE(pkt->li_vn_mode); + + md = mon_hash[hash]; + while (md != NULL) { + if (md->rmtadr == netnum && + /* ?? md->interface == rbufp->dstadr && ?? */ + md->mode == (u_char)mode) { + md->lasttime = current_time; + md->count++; + md->version = PKT_VERSION(pkt->li_vn_mode); + md->rmtport = NSRCPORT(&rbufp->recv_srcadr); + + /* + * Shuffle him to the head of the + * mru list. What a crock. + */ + 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; + } + 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) { + /* + * Get it from MRU list + */ + md = mon_mru_list.mru_prev; + md->mru_prev->mru_next = &mon_mru_list; + mon_mru_list.mru_prev = md->mru_prev; + + remove_from_hash(md); + + /* + * Get it from FIFO list + */ + md->fifo_prev->fifo_next = md->fifo_next; + md->fifo_next->fifo_prev = md->fifo_prev; + + } else { + if (mon_free == NULL) /* if free list empty */ + mon_getmoremem(); /* then get more */ + md = mon_free; + mon_free = md->hash_next; + } + + /* + * Got one, initialize it + */ + md->lasttime = md->firsttime = current_time; + md->lastdrop = 0; + md->count = 1; + md->rmtadr = netnum; + 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 = ((rbufp->dstadr->flags & INT_MULTICAST) && + 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 + * and at bottom of FIFO 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; + + md->fifo_prev = mon_fifo_list.fifo_prev; + md->fifo_next = &mon_fifo_list; + mon_fifo_list.fifo_prev->fifo_next = md; + mon_fifo_list.fifo_prev = md; +} + + +/* + * 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..90646ab --- /dev/null +++ b/contrib/ntp/ntpd/ntp_peer.c @@ -0,0 +1,795 @@ +/* + * 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" + +/* + * Table of valid association combinations + * --------------------------------------- + * + * packet->mode + * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST + * ---------- | --------------------------------------------- + * NO_PEER | e 1 e 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 + * CONTROL | e 0 0 0 0 0 + * PRIVATE | e 0 0 0 0 0 + * BCLIENT | e 0 0 0 e 1 + * MCLIENT | e 0 0 0 0 0 + * + * 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. + */ + +int AM[AM_MODES][AM_MODES] = { +/* { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ + +/*NONE*/{ AM_ERR, AM_NEWPASS, AM_ERR, 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}, + +/*CNTL*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, + +/*PRIV*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, + +/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_ERR, AM_PROCPKT}, + +/*MCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH} +}; + +#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 corresponding peer data + * 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. + */ + +/* + * The peer hash table (imported by the protocol module). + */ +struct peer *peer_hash[HASH_SIZE]; +int peer_hash_count[HASH_SIZE]; /* count of peers in each bucket */ + +/* + * The association ID hash table. Used for lookups by association ID + */ +struct peer *assoc_hash[HASH_SIZE]; +int assoc_hash_count[HASH_SIZE]; + +/* + * The free list. Clean structures only, please. + */ +static struct peer *peer_free; +int peer_free_count; + +/* + * Association ID. We initialize this value randomly, the assign a new + * value every time the peer structure is incremented. + */ +static u_short current_association_ID; + +/* + * Memory allocation watermarks. + */ +#define INIT_PEER_ALLOC 15 /* initialize space for 15 peers */ +#define INC_PEER_ALLOC 5 /* when we run out, add 5 more */ + +/* + * Miscellaneous statistic counters which may be queried. + */ +u_long peer_timereset; /* time stat counters were zeroed */ +u_long findpeer_calls; /* number of calls to findpeer */ +u_long assocpeer_calls; /* number of calls to findpeerbyassoc */ +u_long peer_allocations; /* number of allocations from the free list */ +u_long peer_demobilizations; /* number of structs freed to free list */ +int total_peer_structs; /* number of peer structs in circulation */ +int peer_associations; /* number of active associations */ + +/* + * Our initial allocation of peer space + */ +static struct peer init_peer_alloc[INIT_PEER_ALLOC]; + +/* + * Initialization data. When configuring peers at initialization time, + * we try to get their poll update timers initialized to different values + * to prevent us from sending big clumps of data all at once. + */ +/* static u_long init_peer_starttime; */ + +static void getmorepeermem P((void)); +static void key_expire P((struct peer *)); + +/* + * 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 < 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; + + /* + * Initialization counter. + */ + /* init_peer_starttime = 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 + */ + current_association_ID = (u_short)ranp2(16); + if (current_association_ID == 0) + current_association_ID = 1; +} + + +/* + * 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_in *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[HASH_ADDR(addr)]; + else + peer = start_peer->next; + + while (peer != 0) { + if (NSRCADR(addr) == NSRCADR(&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_in *srcadr, + struct interface *dstadr, + int fd, + int pkt_mode, + int *action + ) +{ + register struct peer *peer; + int hash; + + findpeer_calls++; + hash = HASH_ADDR(srcadr); + for (peer = peer_hash[hash]; peer != 0; peer = peer->next) { + if (NSRCADR(srcadr) == NSRCADR(&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); + + /* + * Sigh! Check if BCLIENT peer in client + * server mode, else return error + */ + if ((*action == AM_POSSBCL) && + !(peer->cast_flags & FLAG_MCAST1)) { + *action = AM_ERR; + } + + /* 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; + } + } + +#ifdef DEBUG + if (debug > 1) + printf("pkt_mode %d action %d\n", pkt_mode, *action); +#endif + /* if no matching association is found */ + if (peer == 0) { + *action = MATCH_ASSOC(NO_PEER, pkt_mode); +#ifdef DEBUG + if (debug > 1) + printf("pkt_mode %d action %d\n", pkt_mode, *action); +#endif + return (struct peer *)0; + } + + /* reset the default interface to something more meaningful */ + if ((peer->dstadr == any_interface)) + peer->dstadr = dstadr; + return peer; +} + +/* + * findpeerbyassocid - find and return a peer using his association ID + */ +struct peer * +findpeerbyassoc( + int assoc + ) +{ + register struct peer *peer; + int hash; + + assocpeer_calls++; + + hash = assoc & HASH_MASK; + for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) { + if ((u_short)assoc == peer->associd) + return peer; /* got it! */ + } + + /* + * Out of luck. Return 0. + */ + return (struct peer *)0; +} + +/* + * findmanycastpeer - find and return an manycast peer if it exists + * + * + * the current implementation loops across all hash-buckets + * + * *** THERE IS AN URGENT NEED TO CHANGE THIS *** + */ +struct peer * +findmanycastpeer( + l_fp *p_org + ) +{ + register struct peer *peer; + register struct peer *manycast_peer = 0; + int i = 0; + + for (i = 0; i < 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 && + peer->flags & FLAG_CONFIG) { + if (L_ISEQU(&peer->xmt, p_org)) + return peer; /* got it */ + else + manycast_peer = peer; + } + } + } + + /* + * Out of luck. Return the manycastpeer for what it is worth. + */ + return manycast_peer; +} + +/* + * key_expire - garbage collect keys + */ +static void +key_expire( + struct peer *peer + ) +{ + int i; + + if (peer->keylist != 0) { + for (i = 0; i <= peer->keynumber; i++) + authtrust(peer->keylist[i], 0); + free(peer->keylist); + peer->keylist = 0; + } + if (peer->keyid > NTP_MAXKEY) { + authtrust(peer->keyid, 0); + peer->keyid = 0; + } +} + +/* + * key_rekey - expire all keys and roll a new private value. Note the + * 32-bit mask is necessary for 64-bit u_longs. + */ +void +key_expire_all( + ) +{ + struct peer *peer, *next_peer; + int n; + + for (n = 0; n < HASH_SIZE; n++) { + for (peer = peer_hash[n]; peer != 0; peer = next_peer) { + next_peer = peer->next; + key_expire(peer); + } + } + sys_private = (u_long)RANDOM & 0xffffffff; +#ifdef DEBUG + if (debug) + printf("key_expire_all: at %lu private %08lx\n", + current_time, sys_private); +#endif +} +/* + * unpeer - remove peer structure from hash table and free structure + */ +void +unpeer( + struct peer *peer_to_remove + ) +{ + int hash; + +#ifdef DEBUG + if (debug > 1) + printf("demobilize %u\n", peer_to_remove->associd); +#endif + key_expire(peer_to_remove); + hash = HASH_ADDR(&peer_to_remove->srcadr); + peer_hash_count[hash]--; + peer_demobilizations++; + peer_associations--; + +#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!", + ntoa(&peer->srcadr)); + } else { + peer->next = peer_to_remove->next; + } + } + + /* + * Remove him from the association hash as well. + */ + hash = peer_to_remove->associd & 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!", + ntoa(&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 peer + */ +struct peer * +peer_config( + struct sockaddr_in *srcadr, + struct interface *dstadr, + int hmode, + int version, + int minpoll, + int maxpoll, + int flags, + int ttl, + u_long key + ) +{ + register struct peer *peer; + + /* + * See if we have this guy in the tables already. If + * so just mark him configured. + */ + peer = findexistingpeer(srcadr, (struct peer *)0, hmode); + if (dstadr != 0) { + while (peer != 0) { + if (peer->dstadr == dstadr) + break; + peer = findexistingpeer(srcadr, peer, hmode); + } + } + + /* + * If we found one, just change his mode and mark him configured. + */ + if (peer != 0) { + peer->hmode = (u_char)hmode; + peer->version = (u_char)version; + peer->minpoll = (u_char)minpoll; + peer->maxpoll = (u_char)maxpoll; + peer->hpoll = peer->minpoll; + peer->ppoll = peer->minpoll; + peer->flags = flags | FLAG_CONFIG | + (peer->flags & FLAG_REFCLOCK); + peer->cast_flags = (hmode == MODE_BROADCAST) ? + IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) ? MDF_MCAST : MDF_BCAST : MDF_UCAST; + peer->ttl = (u_char)ttl; + peer->keyid = key; + peer->keynumber = 0; + return peer; + } + + /* + * If we're here this guy is unknown to us. Make a new peer + * structure for him. + */ + peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, + ttl, key); + if (peer != 0) { + peer->flags |= flags | FLAG_CONFIG; +#ifdef DEBUG + if (debug) + printf("peer_config: %s mode %d vers %d min %d max %d flags 0x%04x ttl %d key %lu\n", + ntoa(&peer->srcadr), peer->hmode, peer->version, + peer->minpoll, peer->maxpoll, peer->flags, + peer->ttl, peer->keyid); +#endif + } + return peer; +} + + +/* + * newpeer - initialize a new peer association + */ +struct peer * +newpeer( + struct sockaddr_in *srcadr, + struct interface *dstadr, + int hmode, + int version, + int minpoll, + int maxpoll, + int ttl, + u_long key + ) +{ + register struct peer *peer; + register int i; + + /* + * Some dirt here. 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++; + + /* + * Initialize the structure. This stuff is sort of part of + * the receive procedure and part of the clear procedure rolled + * into one. + * + * Zero the whole thing for now. We might be pickier later. + */ + memset((char *)peer, 0, sizeof(struct peer)); + + peer->srcadr = *srcadr; + if (dstadr != 0) + peer->dstadr = dstadr; + else if (hmode == MODE_BROADCAST) + peer->dstadr = findbcastinter(srcadr); + else + peer->dstadr = any_interface; + peer->cast_flags = (hmode == MODE_BROADCAST) ? + (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) ? MDF_MCAST : + MDF_BCAST : (hmode == MODE_BCLIENT || hmode == MODE_MCLIENT) ? + (peer->dstadr->flags & INT_MULTICAST) ? MDF_MCAST : MDF_BCAST : + MDF_UCAST; + /* Set manycast flags if appropriate */ + if (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) && hmode == MODE_CLIENT) + peer->cast_flags = MDF_ACAST; + peer->hmode = (u_char)hmode; + peer->keyid = key; + peer->version = (u_char)version; + peer->minpoll = (u_char)minpoll; + peer->maxpoll = (u_char)maxpoll; + peer->hpoll = peer->minpoll; + peer->ppoll = peer->minpoll; + peer->ttl = ttl; + peer->leap = LEAP_NOTINSYNC; + peer->precision = sys_precision; + peer->variance = MAXDISPERSE; + peer->epoch = current_time; + peer->stratum = STRATUM_UNSPEC; + peer_clear(peer); + peer->update = peer->outdate = current_time; + peer->nextdate = peer->outdate + RANDPOLL(NTP_MINPOLL); + if (peer->flags & FLAG_BURST) + peer->burst = NTP_SHIFT; + + /* + * Assign him an association ID and increment the system variable + */ + peer->associd = current_association_ID; + if (++current_association_ID == 0) + ++current_association_ID; + + /* + * 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 + */ + peer->next = peer_free; + peer_free = peer; + peer_free_count++; + return 0; + } + } +#endif + + /* + * Put him in the hash tables. + */ + i = HASH_ADDR(&peer->srcadr); + peer->next = peer_hash[i]; + peer_hash[i] = peer; + peer_hash_count[i]++; + + i = peer->associd & HASH_MASK; + peer->ass_next = assoc_hash[i]; + assoc_hash[i] = peer; + assoc_hash_count[i]++; +#ifdef DEBUG + if (debug > 1) + printf("mobilize %u next %lu\n", peer->associd, + peer->nextdate - peer->outdate); +#endif + return peer; +} + + +/* + * peer_unconfig - remove the configuration bit from a peer + */ +int +peer_unconfig( + struct sockaddr_in *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_copy_manycast - copy manycast peer variables to new association + * (right now it simply copies the transmit timestamp) + */ +void +peer_config_manycast( + struct peer *peer1, + struct peer *peer2 + ) +{ + peer2->cast_flags = MDF_ACAST; + peer2->xmt = peer1->xmt; +} + +/* + * 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->selbroken = 0; + peer->seltooold = 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 < HASH_SIZE; hash++) + for (peer = peer_hash[hash]; peer != 0; peer = peer->next) + peer_reset(peer); +} diff --git a/contrib/ntp/ntpd/ntp_proto.c b/contrib/ntp/ntpd/ntp_proto.c new file mode 100644 index 0000000..6d54291 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_proto.c @@ -0,0 +1,2165 @@ +/* + * ntp_proto.c - NTP version 4 protocol machinery + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_stdlib.h" +#include "ntp_unixtime.h" +#include "ntp_control.h" +#include "ntp_string.h" + +#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/ +#include "ntp_refclock.h" +#endif + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include <sys/sysctl.h> +#endif + +/* + * 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 */ +double sys_rootdelay; /* distance to current sync source */ +double sys_rootdispersion; /* dispersion of system clock */ +u_int32 sys_refid; /* reference source for local clock */ +static double sys_offset; /* current local clock offset */ +l_fp sys_reftime; /* time we were last updated */ +struct peer *sys_peer; /* our current peer */ +u_long sys_automax; /* maximum session key lifetime */ + +/* + * Nonspecified system state variables. + */ +int sys_bclient; /* we set our time to broadcasts */ +double sys_bdelay; /* broadcast client default delay */ +int sys_authenticate; /* requre authentication for config */ +l_fp sys_authdelay; /* authentication delay */ +static u_long sys_authdly[2]; /* authentication delay shift reg */ +static u_char leap_consensus; /* consensus of survivor leap bits */ +static double sys_maxd; /* select error (squares) */ +static double sys_epsil; /* system error (squares) */ +u_long sys_private; /* private value for session seed */ +int sys_manycastserver; /* 1 => respond to manycast client pkts */ + +/* + * Statistics counters + */ +u_long sys_stattime; /* time when we started recording */ +u_long sys_badstratum; /* packets with invalid stratum */ +u_long sys_oldversionpkt; /* old version packets received */ +u_long sys_newversionpkt; /* new version packets received */ +u_long sys_unknownversion; /* don't know version packets */ +u_long sys_badlength; /* packets with bad length */ +u_long sys_processed; /* packets processed */ +u_long sys_badauth; /* packets dropped because of auth */ +u_long sys_limitrejected; /* pkts rejected due to client count per net */ + +static double root_distance P((struct peer *)); +static double clock_combine P((struct peer **, int)); +static void peer_xmit P((struct peer *)); +static void fast_xmit P((struct recvbuf *, int, u_long)); +static void clock_update P((void)); +#ifdef MD5 +static void make_keylist P((struct peer *)); +#endif /* MD5 */ + +/* + * transmit - Transmit Procedure. See Section 3.4.2 of the + * specification. + */ +void +transmit( + struct peer *peer /* peer structure pointer */ + ) +{ + int hpoll; + + hpoll = peer->hpoll; + if (peer->burst == 0) { + u_char oreach; + + /* + * Determine reachability and diddle things if we + * haven't heard from the host for a while. If the peer + * is not configured and not likely to stay around, + * we exhaust it. + */ + oreach = peer->reach; + if (oreach & 0x01) + peer->valid++; + if (oreach & 0x80) + peer->valid--; + if (!(peer->flags & FLAG_CONFIG) && + peer->valid > NTP_SHIFT / 2 && (peer->reach & 0x80) && + peer->status < CTL_PST_SEL_SYNCCAND) + peer->reach = 0; + peer->reach <<= 1; + if (peer->reach == 0) { + + /* + * If this is an uncofigured association and + * has become unreachable, demobilize it. + */ + if (oreach != 0) { + report_event(EVNT_UNREACH, peer); + peer->timereachable = current_time; + peer_clear(peer); + if (!(peer->flags & FLAG_CONFIG)) { + unpeer(peer); + return; + } + } + + /* + * We would like to respond quickly when the + * peer comes back to life. If the probes since + * becoming unreachable are less than + * NTP_UNREACH, clamp the poll interval to the + * minimum. In order to minimize the network + * traffic, the interval gradually ramps up the + * the maximum after that. + */ + peer->ppoll = peer->maxpoll; + if (peer->unreach < NTP_UNREACH) { + if (peer->hmode == MODE_CLIENT) + peer->unreach++; + hpoll = peer->minpoll; + } else { + hpoll++; + } + if (peer->flags & FLAG_BURST) + peer->burst = 2; + + } else { + + /* + * Here the peer is reachable. If there is no + * system peer or if the stratum of the system + * peer is greater than this peer, clamp the + * poll interval to the minimum. If less than + * two samples are in the reachability register, + * reduce the interval; if more than six samples + * are in the register, increase the interval. + */ + peer->unreach = 0; + if (sys_peer == 0) + hpoll = peer->minpoll; + else if (sys_peer->stratum > peer->stratum) + hpoll = peer->minpoll; + if ((peer->reach & 0x03) == 0) { + clock_filter(peer, 0., 0., MAXDISPERSE); + clock_select(); + } + if (peer->valid <= 2) + hpoll--; + else if (peer->valid >= NTP_SHIFT - 2) + hpoll++; + if (peer->flags & FLAG_BURST) + peer->burst = NTP_SHIFT; + } + } else { + peer->burst--; + if (peer->burst == 0) { + if (peer->flags & FLAG_MCAST2) { + peer->flags &= ~FLAG_BURST; + peer->hmode = MODE_BCLIENT; + } + clock_select(); + poll_update(peer, hpoll); + return; + } + } + + /* + * We need to be very careful about honking uncivilized time. If + * not operating in broadcast mode, honk in all except broadcast + * client mode. If operating in broadcast mode and synchronized + * to a real source, honk except when the peer is the local- + * clock driver and the prefer flag is not set. In other words, + * in broadcast mode we never honk unless known to be + * synchronized to real time. + */ + if (peer->hmode != MODE_BROADCAST) { + if (peer->hmode != MODE_BCLIENT) + peer_xmit(peer); + } else if (sys_peer != 0 && sys_leap != LEAP_NOTINSYNC) { + if (!(sys_peer->refclktype == REFCLK_LOCALCLOCK && + !(sys_peer->flags & FLAG_PREFER))) + peer_xmit(peer); + } + peer->outdate = current_time; + poll_update(peer, hpoll); +} + +/* + * receive - Receive Procedure. See section 3.4.3 in the specification. + */ +void +receive( + struct recvbuf *rbufp + ) +{ + register struct peer *peer; + register struct pkt *pkt; + int hismode; + int oflags; + int restrict_mask; + int has_mac; /* has MAC field */ + int authlen; /* length of MAC field */ + int is_authentic; /* cryptosum ok */ + int is_mystic; /* session key exists */ + int is_error; /* parse error */ +/* u_long pkeyid; */ + u_long skeyid, tkeyid; + struct peer *peer2; + int retcode = AM_NOMATCH; + + /* + * Monitor the packet and get restrictions + */ + ntp_monitor(rbufp); + restrict_mask = restrictions(&rbufp->recv_srcadr); +#ifdef DEBUG + if (debug > 1) + printf("receive: from %s restrict %02x\n", + ntoa(&rbufp->recv_srcadr), restrict_mask); +#endif + if (restrict_mask & RES_IGNORE) + return; + + /* + * Discard packets with invalid version number. + */ + pkt = &rbufp->recv_pkt; + if (PKT_VERSION(pkt->li_vn_mode) >= NTP_VERSION) + sys_newversionpkt++; + else if (PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION) + sys_oldversionpkt++; + else { + sys_unknownversion++; + return; + } + + /* + * Restrict control/private mode packets. Note that packet + * length has to be checked in the control/private mode protocol + * module. + */ + if (PKT_MODE(pkt->li_vn_mode) == MODE_PRIVATE) { + if (restrict_mask & RES_NOQUERY) + return; + process_private(rbufp, ((restrict_mask & RES_NOMODIFY) == + 0)); + return; + } + if (PKT_MODE(pkt->li_vn_mode) == MODE_CONTROL) { + if (restrict_mask & RES_NOQUERY) + return; + process_control(rbufp, restrict_mask); + return; + } + + /* + * Restrict revenue packets. + */ + if (restrict_mask & RES_DONTSERVE) + return; + + /* + * See if we only accept limited number of clients from the net + * this guy is from. Note: the flag is determined dynamically + * within restrictions() + */ + if (restrict_mask & RES_LIMITED) { + sys_limitrejected++; + return; + } + + /* + * If we are not a broadcast client, ignore broadcast packets. + */ + if ((PKT_MODE(pkt->li_vn_mode) == MODE_BROADCAST && !sys_bclient)) + return; + + /* + * This is really awful ugly. We figure out whether an extension + * field is present and then measure the MAC size. If the number + * of words following the packet header is less than or equal to + * 5, no extension field is present and these words constitute the + * MAC. If the number of words is greater than 5, an extension + * field is present and the first word contains the length of + * the extension field and the MAC follows that. + */ + has_mac = 0; +/* pkeyid = 0; */ + skeyid = tkeyid = 0; + authlen = LEN_PKT_NOMAC; + has_mac = rbufp->recv_length - authlen; + if (has_mac <= 5 * sizeof(u_int32)) { + skeyid = (u_long)ntohl(pkt->keyid1) & 0xffffffff; + } else { + authlen += (u_long)ntohl(pkt->keyid1) & 0xffffffff; + has_mac = rbufp->recv_length - authlen; + if (authlen <= 0) { + sys_badlength++; + return; + } + + /* + * Note that keyid3 is actually the key ident of the + * MAC itself. + */ +/* pkeyid = (u_long)ntohl(pkt->keyid2) & 0xffffffff; */ + skeyid = tkeyid = (u_long)ntohl(pkt->keyid3) & 0xffffffff; + } + + /* + * Figure out his mode and validate it. + */ + hismode = (int)PKT_MODE(pkt->li_vn_mode); + if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION && hismode == + 0) { + /* + * Easy. If it is from the NTP port it is + * a sym act, else client. + */ + if (SRCPORT(&rbufp->recv_srcadr) == NTP_PORT) + hismode = MODE_ACTIVE; + else + hismode = MODE_CLIENT; + } else { + if (hismode != MODE_ACTIVE && hismode != MODE_PASSIVE && + hismode != MODE_SERVER && hismode != MODE_CLIENT && + hismode != MODE_BROADCAST) + return; + } + + /* + * If he included a mac field, decrypt it to see if it is + * authentic. + */ + is_authentic = is_mystic = 0; + if (has_mac == 0) { +#ifdef DEBUG + if (debug) + printf("receive: at %ld from %s mode %d\n", + current_time, ntoa(&rbufp->recv_srcadr), + hismode); +#endif + } else { + is_mystic = authistrusted(skeyid); +#ifdef MD5 + if (skeyid > NTP_MAXKEY && !is_mystic) { + + /* + * For multicast mode, generate the session key + * and install in the key cache. For client mode, + * generate the session key for the unicast + * address. For server mode, the session key should + * already be in the key cache, since it was + * generated when the last request was sent. + */ + if (hismode == MODE_BROADCAST) { + tkeyid = session_key( + ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr), + ntohl(rbufp->dstadr->bcast.sin_addr.s_addr), + skeyid, (u_long)(4 * (1 << pkt->ppoll))); + } else if (hismode != MODE_SERVER) { + tkeyid = session_key( + ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr), + ntohl(rbufp->dstadr->sin.sin_addr.s_addr), + skeyid, (u_long)(4 * (1 << pkt->ppoll))); + } + + } +#endif /* MD5 */ + + /* + * Compute the cryptosum. Note a clogging attack may + * succceed in bloating the key cache. + */ + if (authdecrypt(skeyid, (u_int32 *)pkt, authlen, has_mac)) + is_authentic = 1; + else + sys_badauth++; +#ifdef DEBUG + if (debug) + printf( + "receive: at %ld %s mode %d keyid %08lx mac %d auth %d\n", + current_time, ntoa(&rbufp->recv_srcadr), + hismode, skeyid, has_mac, is_authentic); +#endif + } + + /* + * Find the peer. This will return a null if this guy isn't in + * the database. + */ + peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd, + hismode, &retcode); + /* + * The new association matching rules are driven by a table specified + * in ntp.h. We have replaced the *default* behaviour of replying + * to bogus packets in server mode in this version. + * A packet must now match an association in order to be processed. + * In the event that no association exists, then an association is + * mobilized if need be. Two different associations can be mobilized + * a) passive associations + * b) client associations due to broadcasts or manycasts. + */ + is_error = 0; + switch (retcode) { + case AM_FXMIT: + /* + * If the client is configured purely as a broadcast client and + * not as an manycast server, it has no business being a server. + * Simply go home. Otherwise, send a MODE_SERVER response and go + * home. Note that we don't do a authentication check here, + * since we can't set the system clock; but, we do set the + * key ID to zero to tell the caller about this. + */ + if (!sys_bclient || sys_manycastserver) { + if (is_authentic) + fast_xmit(rbufp, MODE_SERVER, skeyid); + else + fast_xmit(rbufp, MODE_SERVER, 0); + } + + /* + * We can't get here if an association is mobilized, so just + * toss the key, if appropriate. + */ + if (!is_mystic && skeyid > NTP_MAXKEY) + authtrust(skeyid, 0); + return; + + case AM_MANYCAST: + /* + * This could be in response to a multicast packet sent by + * the "manycast" mode association. Find peer based on the + * originate timestamp in the packet. Note that we don't + * mobilize a new association, unless the packet is properly + * authenticated. The response must be properly authenticated + * and it's darn funny of the manycaster isn't around now. + */ + if ((sys_authenticate && !is_authentic)) { + is_error = 1; + break; + } + peer2 = (struct peer *)findmanycastpeer(&pkt->org); + if (peer2 == 0) { + is_error = 1; + break; + } + + /* + * Create a new association and copy the peer variables to it. + * If something goes wrong, carefully pry the new association + * away and return its marbles to the candy store. + */ + peer = newpeer(&rbufp->recv_srcadr, + rbufp->dstadr, MODE_CLIENT, PKT_VERSION(pkt->li_vn_mode), + NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid); + if (peer == 0) { + is_error = 1; + break; + } + peer_config_manycast(peer2, peer); + break; + + case AM_ERR: + /* + * Something bad happened. Dirty floor will be mopped by the + * code at the end of this adventure. + */ + is_error = 1; + break; + + case AM_NEWPASS: + /* + * Okay, we're going to keep him around. Allocate him some + * memory. But, don't do that unless the packet is properly + * authenticated. + */ + if ((sys_authenticate && !is_authentic)) { + is_error = 1; + break; + } + peer = newpeer(&rbufp->recv_srcadr, + rbufp->dstadr, MODE_PASSIVE, PKT_VERSION(pkt->li_vn_mode), + NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid); + break; + + case AM_NEWBCL: + /* + * Broadcast client being set up now. Do this only if the + * packet is properly authenticated. + */ + if ((restrict_mask & RES_NOPEER) || !sys_bclient || + (sys_authenticate && !is_authentic)) { + is_error = 1; + break; + } + peer = newpeer(&rbufp->recv_srcadr, + rbufp->dstadr, MODE_MCLIENT, PKT_VERSION(pkt->li_vn_mode), + NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid); + if (peer == 0) + break; + peer->flags |= FLAG_MCAST1 | FLAG_MCAST2 | FLAG_BURST; + peer->hmode = MODE_CLIENT; + break; + + case AM_POSSBCL: + case AM_PROCPKT: + /* + * It seems like it is okay to process the packet now + */ + break; + + default: + /* + * shouldn't be getting here, but simply return anyway! + */ + is_error = 1; + } + if (is_error) { + + /* + * Error stub. If we get here, something broke. We scuttle + * the autokey if necessary and sink the ship. This can + * occur only upon mobilization, so we can throw the + * structure away without fear of breaking anything. + */ + if (!is_mystic && skeyid > NTP_MAXKEY) + authtrust(skeyid, 0); + if (peer != 0) + if (!(peer->flags & FLAG_CONFIG)) + unpeer(peer); +#ifdef DEBUG + if (debug) + printf("match error code %d assoc %d\n", retcode, + peer_associations); +#endif + return; + } + + /* + * If the peer isn't configured, set his keyid and authenable + * status based on the packet. + */ + oflags = peer->flags; + peer->timereceived = current_time; + if (!(peer->flags & FLAG_CONFIG) && has_mac) { + peer->flags |= FLAG_AUTHENABLE; + if (skeyid > NTP_MAXKEY) { + if (peer->flags & FLAG_MCAST2) + peer->keyid = skeyid; + else + peer->flags |= FLAG_SKEY; + } + } + + /* + * Determine if this guy is basically trustable. If not, flush + * the bugger. If this is the first packet that is authenticated, + * flush the clock filter. This is to foil clogging attacks that + * might starve the poor dear. + */ + peer->flash = 0; + if (is_authentic) + peer->flags |= FLAG_AUTHENTIC; + else + peer->flags &= ~FLAG_AUTHENTIC; + if (peer->hmode == MODE_BROADCAST && (restrict_mask & RES_DONTTRUST)) + peer->flash |= TEST10; /* access denied */ + if (peer->flags & FLAG_AUTHENABLE) { + if (!(peer->flags & FLAG_AUTHENTIC)) + peer->flash |= TEST5; /* authentication failed */ + else if (skeyid == 0) + peer->flash |= TEST9; /* peer not authenticated */ + else if (!(oflags & FLAG_AUTHENABLE)) { + peer_clear(peer); + report_event(EVNT_PEERAUTH, peer); + } + } + if ((peer->flash & ~(u_int)TEST9) != 0) { + + /* + * The packet is bogus, so we throw it away before becoming + * a denial-of-service hazard. We don't throw the current + * association away if it is configured or if it has prior + * reachable friends. + */ + if (!is_mystic && skeyid > NTP_MAXKEY) + authtrust(skeyid, 0); + if (!(peer->flags & FLAG_CONFIG) && peer->reach == 0) + unpeer(peer); +#ifdef DEBUG + if (debug) + printf( + "invalid packet 0x%02x code %d assoc %d\n", + peer->flash, retcode, peer_associations); +#endif + return; + } + +#ifdef MD5 + /* + * The autokey dance. The cha-cha requires that the hash of the + * current session key matches the previous key identifier. Heaps + * of trouble if the steps falter. + */ + if (skeyid > NTP_MAXKEY) { + int i; + + /* + * In the case of a new autokey, verify the hash matches + * one of the previous four hashes. If not, raise the + * authentication flasher and hope the next one works. + */ + if (hismode == MODE_SERVER) { + peer->pkeyid = peer->keyid; + } else if (peer->flags & FLAG_MCAST2) { + if (peer->pkeyid > NTP_MAXKEY) + authtrust(peer->pkeyid, 0); + for (i = 0; i < 4 && tkeyid != peer->pkeyid; i++) { + tkeyid = session_key( + ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr), + ntohl(rbufp->dstadr->bcast.sin_addr.s_addr), + tkeyid, 0); + } + } else { + if (peer->pkeyid > NTP_MAXKEY) + authtrust(peer->pkeyid, 0); + for (i = 0; i < 4 && tkeyid != peer->pkeyid; i++) { + tkeyid = session_key( + ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr), + ntohl(rbufp->dstadr->sin.sin_addr.s_addr), + tkeyid, 0); + } + } +#ifdef XXX /* temp until certificate code is mplemented */ + if (tkeyid != peer->pkeyid) + peer->flash |= TEST9; /* peer not authentic */ +#endif + peer->pkeyid = skeyid; + } +#endif /* MD5 */ + + /* + * Gawdz, it's come to this. Process the dang packet. If something + * breaks and the association doesn't deserve to live, toss it. + * Be careful in active mode and return a packet anyway. + */ + process_packet(peer, pkt, &(rbufp->recv_time)); + if (!(peer->flags & FLAG_CONFIG) && peer->reach == 0) { + if (peer->hmode == MODE_PASSIVE) { + if (is_authentic) + fast_xmit(rbufp, MODE_PASSIVE, skeyid); + else + fast_xmit(rbufp, MODE_PASSIVE, 0); + } + unpeer(peer); + } +} + + +/* + * 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. + */ +int +process_packet( + register struct peer *peer, + register struct pkt *pkt, + l_fp *recv_ts + ) +{ + l_fp t10, t23; + double p_offset, p_del, p_disp; + double dtemp; + l_fp p_rec, p_xmt, p_org, p_reftime; + l_fp ci; + int pmode; + + /* + * Swap header fields and keep the books. + */ + 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); + if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) + NTOHL_FP(&pkt->org, &p_org); + else + p_org = peer->rec; + peer->rec = *recv_ts; + peer->ppoll = pkt->ppoll; + pmode = PKT_MODE(pkt->li_vn_mode); + + /* + * Test for old or duplicate packets (tests 1 through 3). + */ + if (L_ISHIS(&peer->org, &p_xmt)) /* count old packets */ + peer->oldpkt++; + if (L_ISEQU(&peer->org, &p_xmt)) /* test 1 */ + peer->flash |= TEST1; /* duplicate packet */ + if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) { + if (!L_ISEQU(&peer->xmt, &p_org)) { /* test 2 */ + peer->bogusorg++; + peer->flash |= TEST2; /* bogus packet */ + } + if (L_ISZERO(&p_rec) || L_ISZERO(&p_org)) + peer->flash |= TEST3; /* unsynchronized */ + } else { + if (L_ISZERO(&p_org)) + peer->flash |= TEST3; /* unsynchronized */ + } + peer->org = p_xmt; + + /* + * Test for valid header (tests 5 through 10) + */ + ci = p_xmt; + L_SUB(&ci, &p_reftime); + LFPTOD(&ci, dtemp); + if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || /* test 6 */ + PKT_TO_STRATUM(pkt->stratum) >= NTP_MAXSTRATUM || + dtemp < 0) + peer->flash |= TEST6; /* peer clock unsynchronized */ + if (!(peer->flags & FLAG_CONFIG) && sys_peer != 0) { /* test 7 */ + if (PKT_TO_STRATUM(pkt->stratum) > sys_stratum) { + peer->flash |= TEST7; /* peer stratum too high */ + sys_badstratum++; + } + } + if (fabs(p_del) >= MAXDISPERSE /* test 8 */ + || p_disp >= MAXDISPERSE) + peer->flash |= TEST8; /* delay/dispersion too high */ + + /* + * If the packet header is invalid (tests 5 through 10), exit. + * XXX we let TEST9 sneak by until the certificate code is + * implemented, but only to mobilize the association. + */ + if (peer->flash & (TEST5 | TEST6 | TEST7 | TEST8 | TEST10)) { +#ifdef DEBUG + if (debug) + printf( + "invalid packet header 0x%02x mode %d\n", + peer->flash, pmode); +#endif + return (0); + } + + /* + * Valid header; update our state. + */ + record_raw_stats(&peer->srcadr, &peer->dstadr->sin, + &p_org, &p_rec, &p_xmt, &peer->rec); + + peer->leap = PKT_LEAP(pkt->li_vn_mode); + peer->pmode = pmode; /* unspec */ + peer->stratum = PKT_TO_STRATUM(pkt->stratum); + peer->precision = pkt->precision; + peer->rootdelay = p_del; + peer->rootdispersion = p_disp; + peer->refid = pkt->refid; + peer->reftime = p_reftime; + if (peer->reach == 0) { + report_event(EVNT_REACH, peer); + peer->timereachable = current_time; + } + peer->reach |= 1; + poll_update(peer, peer->hpoll); + + /* + * If running in a client/server association, calculate the + * clock offset c, roundtrip delay d and dispersion e. We use + * the equations (reordered from those in the spec). Note that, + * in a broadcast association, 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 originate time. + * + * c = ((t2 - t3) + (t1 - t0)) / 2 + * d = (t2 - t3) - (t1 - t0) + * e = (org - rec) (seconds only) + */ + t10 = p_xmt; /* compute t1 - t0 */ + L_SUB(&t10, &peer->rec); + t23 = p_rec; /* compute t2 - t3 */ + L_SUB(&t23, &p_org); + ci = t10; + p_disp = CLOCK_PHI * (peer->rec.l_ui - p_org.l_ui); + + /* + * 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 client/server mode, + * calculate the clock offset, using the engineered refinement + * algorithms, while also receiving broadcasts. When a broadcast + * is received in client/server mode, we calculate a correction + * factor to use after switching back to broadcast mode. We know + * NTP_SKEWFACTOR == 16, which accounts for the simplified ei + * calculation. + * + * If FLAG_MCAST2 is set, we are a broadcast/multicast client. + * If FLAG_MCAST1 is set, we haven't calculated the propagation + * delay. If hmode is MODE_CLIENT, we haven't set the local + * clock in client/server mode. Initially, we come up + * MODE_CLIENT. When the clock is first updated and FLAG_MCAST2 + * is set, we switch from MODE_CLIENT to MODE_BCLIENT. + */ + if (pmode == MODE_BROADCAST) { + if (peer->flags & FLAG_MCAST1) { + if (peer->hmode == MODE_BCLIENT) + peer->flags &= ~FLAG_MCAST1; + LFPTOD(&ci, p_offset); + peer->estbdelay = peer->offset - p_offset; + return (1); + + } + DTOLFP(peer->estbdelay, &t10); + L_ADD(&ci, &t10); + p_del = peer->delay; + } else { + L_ADD(&ci, &t23); + L_RSHIFT(&ci); + L_SUB(&t23, &t10); + LFPTOD(&t23, p_del); + } + LFPTOD(&ci, p_offset); + if (fabs(p_del) >= MAXDISPERSE || p_disp >= MAXDISPERSE) /* test 4 */ + peer->flash |= TEST4; /* delay/dispersion too big */ + + /* + * If the packet data are invalid (tests 1 through 4), exit. + */ + if (peer->flash) { +#ifdef DEBUG + if (debug) + printf("invalid packet data 0x%02x mode %d\n", + peer->flash, pmode); +#endif + return(1); + } + + + /* + * This one is valid. Mark it so, give it to clock_filter(). + */ + clock_filter(peer, p_offset, p_del, fabs(p_disp)); + clock_select(); + record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), + peer->offset, peer->delay, peer->disp, SQRT(peer->variance)); + return(1); +} + + +/* + * clock_update - Called at system process update intervals. + */ +static void +clock_update(void) +{ + u_char oleap; + u_char ostratum; + int i; + struct peer *peer; + + /* + * Reset/adjust the system clock. Do this only if there is a + * system peer and we haven't seen that peer lately. Watch for + * timewarps here. + */ + if (sys_peer == 0) + return; + if (sys_peer->pollsw == FALSE || sys_peer->burst > 0) + return; + sys_peer->pollsw = FALSE; +#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, sys_epsil)) { + + case -1: + /* + * Clock is too screwed up. Just exit for now. + */ + report_event(EVNT_SYSFAULT, (struct peer *)0); + exit(1); + /*NOTREACHED*/ + + case 1: + /* + * Clock was stepped. Clear filter registers + * of all peers. + */ + for (i = 0; i < HASH_SIZE; i++) { + for (peer = peer_hash[i]; peer != 0; + peer =peer->next) + peer_clear(peer); + } + NLOG(NLOG_SYNCSTATUS) + msyslog(LOG_INFO, "synchronisation lost"); + sys_peer = 0; + sys_stratum = STRATUM_UNSPEC; + report_event(EVNT_CLOCKRESET, (struct peer *)0); + break; + + default: + /* + * Update the system stratum, leap bits, root delay, + * root dispersion, reference ID and reference time. We + * also update select dispersion and max frequency + * error. + */ + sys_stratum = sys_peer->stratum + 1; + if (sys_stratum == 1) + sys_refid = sys_peer->refid; + else + sys_refid = sys_peer->srcadr.sin_addr.s_addr; + sys_reftime = sys_peer->rec; + sys_rootdelay = sys_peer->rootdelay + fabs(sys_peer->delay); + sys_leap = leap_consensus; + } + if (oleap != sys_leap) + report_event(EVNT_SYNCCHG, (struct peer *)0); + if (ostratum != sys_stratum) + report_event(EVNT_PEERSTCHG, (struct peer *)0); +} + + +/* + * poll_update - update peer poll interval. See Section 3.4.9 of the + * spec. + */ +void +poll_update( + struct peer *peer, + int hpoll + ) +{ + long update; + + /* + * The wiggle-the-poll-interval dance. Broadcasters dance only + * the minpoll beat. Reference clock partners sit this one out. + * Dancers surviving the clustering algorithm beat to the system + * clock. Broadcast clients are usually lead by their broadcast + * partner, but faster in the initial mating dance. + */ + if (peer->hmode == MODE_BROADCAST) { + peer->hpoll = peer->minpoll; + } else if (peer->flags & FLAG_SYSPEER) { + peer->hpoll = sys_poll; + } else { + if (hpoll > peer->maxpoll) + peer->hpoll = peer->maxpoll; + else if (hpoll < peer->minpoll) + peer->hpoll = peer->minpoll; + else + peer->hpoll = hpoll; + } + if (peer->burst > 0) { + if (peer->nextdate != current_time) + return; + if (peer->flags & FLAG_REFCLOCK) + peer->nextdate++; + else if (peer->reach & 0x1) + peer->nextdate += RANDPOLL(BURST_INTERVAL2); + else + peer->nextdate += RANDPOLL(BURST_INTERVAL1); + } else { + update = max(min(peer->ppoll, peer->hpoll), peer->minpoll); + peer->nextdate = peer->outdate + RANDPOLL(update); + } +#ifdef DEBUG + if (debug > 1) + printf("poll_update: at %lu %s poll %d burst %d last %lu next %lu\n", + current_time, ntoa(&peer->srcadr), hpoll, peer->burst, + peer->outdate, peer->nextdate); +#endif +} + + +/* + * clear - clear peer filter registers. See Section 3.4.8 of the spec. + */ +void +peer_clear( + register struct peer *peer + ) +{ + register int i; + + memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO); + peer->estbdelay = sys_bdelay; + peer->hpoll = peer->minpoll; + peer->pollsw = FALSE; + peer->variance = MAXDISPERSE; + peer->epoch = current_time; + for (i = 0; i < NTP_SHIFT; i++) { + peer->filter_order[i] = i; + peer->filter_disp[i] = MAXDISPERSE; + peer->filter_epoch[i] = current_time; + } + poll_update(peer, peer->minpoll); + + /* + * Since we have a chance to correct possible funniness in + * our selection of interfaces on a multihomed host, do so + * by setting us to no particular interface. + * WARNING: do so only in non-broadcast mode! + */ + if (peer->hmode != MODE_BROADCAST) + peer->dstadr = any_interface; +} + + +/* + * clock_filter - add incoming clock sample to filter register and run + * the filter procedure to find the best sample. + */ +void +clock_filter( + register struct peer *peer, + double sample_offset, + double sample_delay, + double sample_disp + ) +{ + register int i, j, k, n = 0; + register u_char *ord; + double distance[NTP_SHIFT]; + double x, y, z, off; + + /* + * Update error bounds and calculate distances. Also initialize + * sort index vector. + */ + x = CLOCK_PHI * (current_time - peer->update); + peer->update = current_time; + ord = peer->filter_order; + j = peer->filter_nextpt; + for (i = 0; i < NTP_SHIFT; i++) { + peer->filter_disp[j] += x; + if (peer->filter_disp[j] > MAXDISPERSE) + peer->filter_disp[j] = MAXDISPERSE; + distance[i] = fabs(peer->filter_delay[j]) / 2 + + peer->filter_disp[j]; + ord[i] = j; + if (--j < 0) + j += NTP_SHIFT; + } + + /* + * Insert the new sample at the beginning of the register. + */ + peer->filter_offset[peer->filter_nextpt] = sample_offset; + peer->filter_delay[peer->filter_nextpt] = sample_delay; + x = LOGTOD(peer->precision) + LOGTOD(sys_precision) + sample_disp; + peer->filter_disp[peer->filter_nextpt] = min(x, MAXDISPERSE); + peer->filter_epoch[peer->filter_nextpt] = current_time; + distance[0] = min(x + fabs(sample_delay) / 2, MAXDISTANCE); + peer->filter_nextpt++; + if (peer->filter_nextpt >= NTP_SHIFT) + peer->filter_nextpt = 0; + + /* + * Sort the samples in the register by distance. The winning + * sample will be in ord[0]. Sort the samples only if they + * are younger than the Allen intercept. + */ + y = min(allan_xpt, NTP_SHIFT * ULOGTOD(sys_poll)); + for (n = 0; n < NTP_SHIFT && current_time - + peer->filter_epoch[ord[n]] <= y; n++) { + for (j = 0; j < n; j++) { + if (distance[j] > distance[n]) { + x = distance[j]; + k = ord[j]; + distance[j] = distance[n]; + ord[j] = ord[n]; + distance[n] = x; + ord[n] = k; + } + } + } + + /* + * Compute the error bound and standard error. + */ + x = y = z = off = 0.; + for (i = NTP_SHIFT - 1; i >= 0; i--) { + x = NTP_FWEIGHT * (x + peer->filter_disp[ord[i]]); + if (i < n) { + z += 1. / distance[i]; + off += peer->filter_offset[ord[i]] / distance[i]; + y += DIFF(peer->filter_offset[ord[i]], + peer->filter_offset[ord[0]]); + } + } + peer->delay = peer->filter_delay[ord[0]]; + peer->variance = min(y / n, MAXDISPERSE); + peer->disp = min(x, MAXDISPERSE); + peer->epoch = current_time; + x = peer->offset; + if (peer->flags & FLAG_BURST) + peer->offset = off / z; + else + peer->offset = peer->filter_offset[ord[0]]; + + /* + * A new sample is useful only if it is younger than the last + * one used. + */ + if (peer->filter_epoch[ord[0]] > peer->epoch) { +#ifdef DEBUG + if (debug) + printf("clock_filter: discard %lu\n", + peer->filter_epoch[ord[0]] - peer->epoch); +#endif + return; + } + + /* + * If the offset exceeds the dispersion 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 (fabs(x - peer->offset) > CLOCK_SGATE && + peer->filter_epoch[ord[0]] - peer->epoch < (1 << + (sys_poll + 1))) { +#ifdef DEBUG + if (debug) + printf("clock_filter: popcorn spike %.6f\n", x); +#endif + return; + } + peer->epoch = peer->filter_epoch[ord[0]]; + peer->pollsw = TRUE; +#ifdef DEBUG + if (debug) + printf( + "clock_filter: offset %.6f delay %.6f disp %.6f std %.6f, age %lu\n", + peer->offset, peer->delay, peer->disp, + SQRT(peer->variance), current_time - peer->epoch); +#endif +} + + +/* + * clock_select - find the pick-of-the-litter clock + */ +void +clock_select(void) +{ + register struct peer *peer; + int i; + int nlist, nl3; + double d, e, f; + int j; + int n; + int allow, found, k; + double high, low; + double synch[NTP_MAXCLOCK], error[NTP_MAXCLOCK]; + struct peer *osys_peer; + struct peer *typeacts = 0; + struct peer *typelocal = 0; + struct peer *typepps = 0; + struct peer *typeprefer = 0; + struct peer *typesystem = 0; + + static int list_alloc = 0; + static struct endpoint *endpoint = NULL; + static int *index = NULL; + static struct peer **peer_list = NULL; + static u_int endpoint_size = 0; + static u_int index_size = 0; + static u_int peer_list_size = 0; + + /* + * Initialize. If a prefer peer does not survive this thing, + * the pps_update switch will remain zero. + */ + pps_update = 0; + nlist = 0; + low = 1e9; + high = -1e9; + for (n = 0; n < HASH_SIZE; n++) + nlist += peer_hash_count[n]; + if (nlist > list_alloc) { + if (list_alloc > 0) { + free(endpoint); + free(index); + free(peer_list); + } + while (list_alloc < nlist) { + list_alloc += 5; + endpoint_size += 5 * 3 * sizeof *endpoint; + index_size += 5 * 3 * sizeof *index; + peer_list_size += 5 * sizeof *peer_list; + } + endpoint = (struct endpoint *)emalloc(endpoint_size); + index = (int *)emalloc(index_size); + peer_list = (struct peer **)emalloc(peer_list_size); + } + + /* + * This first chunk of code is supposed to go through all + * peers we know about to find the peers which are most likely + * to succeed. We run through the list doing the sanity checks + * and trying to insert anyone who looks okay. + */ + nlist = nl3 = 0; /* none yet */ + for (n = 0; n < HASH_SIZE; n++) { + for (peer = peer_hash[n]; peer != 0; peer = peer->next) { + peer->flags &= ~FLAG_SYSPEER; + peer->status = CTL_PST_SEL_REJECT; + if (peer->flags & FLAG_NOSELECT) + continue; /* noselect (survey only) */ + if (peer->reach == 0) + continue; /* unreachable */ + if (peer->stratum > 1 && peer->refid == + peer->dstadr->sin.sin_addr.s_addr) + continue; /* sync loop */ + if (root_distance(peer) >= MAXDISTANCE + 2 * + CLOCK_PHI * ULOGTOD(sys_poll)) { + peer->seldisptoolarge++; + continue; /* too noisy or broken */ + } + + /* + * Don't allow the local-clock or acts drivers + * in the kitchen at this point, unless the + * prefer peer. Do that later, but only if + * nobody else is around. + */ + if (peer->refclktype == REFCLK_LOCALCLOCK +#if defined(VMS) && defined(VMS_LOCALUNIT) + /* wjm: local unit VMS_LOCALUNIT taken seriously */ + && REFCLOCKUNIT(&peer->srcadr) != VMS_LOCALUNIT +#endif /* VMS && VMS_LOCALUNIT */ + ) { + typelocal = peer; + if (!(peer->flags & FLAG_PREFER)) + continue; /* no local clock */ + } + if (peer->sstclktype == CTL_SST_TS_TELEPHONE) { + typeacts = peer; + if (!(peer->flags & FLAG_PREFER)) + continue; /* no acts */ + } + + /* + * If we get this far, we assume the peer is + * acceptable. + */ + 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[index[i]].val) + break; + index[i + 3] = index[i]; + } + index[i + 3] = nl3; + endpoint[nl3].type = 1; + endpoint[nl3++].val = e; + + e = e - f; /* Center point */ + for ( ; i >= 0; i--) { + if (e >= endpoint[index[i]].val) + break; + index[i + 2] = index[i]; + } + index[i + 2] = nl3; + endpoint[nl3].type = 0; + endpoint[nl3++].val = e; + + e = e - f; /* Lower end */ + for ( ; i >= 0; i--) { + if (e >= endpoint[index[i]].val) + break; + index[i + 1] = index[i]; + } + index[i + 1] = nl3; + endpoint[nl3].type = -1; + endpoint[nl3++].val = e; + } + } +#ifdef DEBUG + if (debug > 1) + for (i = 0; i < nl3; i++) + printf("select: endpoint %2d %.6f\n", + endpoint[index[i]].type, endpoint[index[i]].val); +#endif + i = 0; + j = nl3 - 1; + allow = nlist; /* falsetickers assumed */ + found = 0; + while (allow > 0) { + allow--; + for (n = 0; i <= j; i++) { + n += endpoint[index[i]].type; + if (n < 0) + break; + if (endpoint[index[i]].type == 0) + found++; + } + for (n = 0; i <= j; j--) { + n += endpoint[index[j]].type; + if (n > 0) + break; + if (endpoint[index[j]].type == 0) + found++; + } + if (found > allow) + break; + low = endpoint[index[i++]].val; + high = endpoint[index[j--]].val; + } + + /* + * If no survivors remain at this point, check if the acts or + * local clock drivers have been found. If so, nominate one of + * them as the only survivor. Otherwise, give up and declare us + * unsynchronized. + */ + if ((allow << 1) >= nlist) { + if (typeacts != 0) { + typeacts->status = CTL_PST_SEL_SANE; + peer_list[0] = typeacts; + nlist = 1; + } else if (typelocal != 0) { + typelocal->status = CTL_PST_SEL_SANE; + peer_list[0] = typelocal; + nlist = 1; + } else { + if (sys_peer != 0) { + report_event(EVNT_PEERSTCHG, + (struct peer *)0); + NLOG(NLOG_SYNCSTATUS) + msyslog(LOG_INFO, "synchronisation lost"); + } + sys_peer = 0; + return; + } + } +#ifdef DEBUG + if (debug > 1) + printf("select: low %.6f high %.6f\n", low, high); +#endif + + /* + * Clustering algorithm. Process intersection list to discard + * outlyers. Construct candidate list in cluster order + * determined by the sum of peer synchronization distance plus + * scaled stratum. We must find at least one peer. + */ + j = 0; + for (i = 0; i < nlist; i++) { + peer = peer_list[i]; + if (nlist > 1 && (low >= peer->offset || + peer->offset >= high)) + continue; + peer->status = CTL_PST_SEL_CORRECT; + d = root_distance(peer) + peer->stratum * MAXDISPERSE; + if (j >= NTP_MAXCLOCK) { + if (d >= synch[j - 1]) + continue; + else + j--; + } + for (k = j; k > 0; k--) { + if (d >= synch[k - 1]) + break; + synch[k] = synch[k - 1]; + peer_list[k] = peer_list[k - 1]; + } + peer_list[k] = peer; + synch[k] = d; + j++; + } + nlist = j; + +#ifdef DEBUG + if (debug > 1) + for (i = 0; i < nlist; i++) + printf("select: %s distance %.6f\n", + ntoa(&peer_list[i]->srcadr), synch[i]); +#endif + + /* + * Now, prune outlyers by root dispersion. Continue as long as + * there are more than NTP_MINCLOCK survivors and the minimum + * select dispersion is greater than the maximum peer + * dispersion. Stop if we are about to discard a prefer peer. + */ + for (i = 0; i < nlist; i++) { + peer = peer_list[i]; + error[i] = peer->variance; + if (i < NTP_CANCLOCK) + peer->status = CTL_PST_SEL_SELCAND; + else + peer->status = CTL_PST_SEL_DISTSYSPEER; + } + while (1) { + sys_maxd = 0; + d = error[0]; + for (k = i = nlist - 1; i >= 0; i--) { + double sdisp = 0; + + for (j = nlist - 1; j > 0; j--) { + sdisp = NTP_SWEIGHT * (sdisp + + DIFF(peer_list[i]->offset, + peer_list[j]->offset)); + } + if (sdisp > sys_maxd) { + sys_maxd = sdisp; + k = i; + } + if (error[i] < d) + d = error[i]; + } + +#ifdef DEBUG + if (debug > 1) + printf( + "select: survivors %d select %.6f peer %.6f\n", + nlist, SQRT(sys_maxd), SQRT(d)); +#endif + if (nlist <= NTP_MINCLOCK || sys_maxd <= d || + peer_list[k]->flags & FLAG_PREFER) + break; + for (j = k + 1; j < nlist; j++) { + peer_list[j - 1] = peer_list[j]; + error[j - 1] = error[j]; + } + nlist--; + } +#ifdef DEBUG + if (debug > 1) { + for (i = 0; i < nlist; i++) + printf( + "select: %s offset %.6f, distance %.6f poll %d\n", + ntoa(&peer_list[i]->srcadr), peer_list[i]->offset, + synch[i], peer_list[i]->pollsw); + } +#endif + + /* + * What remains is a list of not greater than NTP_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. First record their order, diddle the + * flags and clamp the poll intervals. Then, consider the peers + * at the lowest stratum. Of these, OR the leap bits on the + * assumption that, if some of them honk nonzero bits, they must + * know what they are doing. Also, check for prefer and pps + * peers. If a prefer peer is found within clock_max, update the + * pps switch. Of the other peers not at the lowest stratum, + * check if the system peer is among them and, if found, zap + * him. We note that the head of the list is at the lowest + * stratum and that unsynchronized peers cannot survive this + * far. + */ + leap_consensus = 0; + for (i = nlist - 1; i >= 0; i--) { + peer_list[i]->status = CTL_PST_SEL_SYNCCAND; + peer_list[i]->flags |= FLAG_SYSPEER; + poll_update(peer_list[i], peer_list[i]->hpoll); + if (peer_list[i]->stratum == peer_list[0]->stratum) { + leap_consensus |= peer_list[i]->leap; + if (peer_list[i]->refclktype == REFCLK_ATOM_PPS) + typepps = peer_list[i]; + if (peer_list[i] == sys_peer) + typesystem = peer_list[i]; + if (peer_list[i]->flags & FLAG_PREFER) { + typeprefer = peer_list[i]; + if (fabs(typeprefer->offset) < clock_max) + pps_update = 1; + } + } else { + if (peer_list[i] == sys_peer) + sys_peer = 0; + } + } + + /* + * Mitigation rules of the game. There are several types of + * peers that make a difference here: (1) prefer local peers + * (type REFCLK_LOCALCLOCK with FLAG_PREFER) or prefer modem + * peers (type REFCLK_NIST_ATOM etc with FLAG_PREFER), (2) pps peers + * (type REFCLK_ATOM_PPS), (3) remaining prefer peers (flag + * FLAG_PREFER), (4) the existing system peer, if any, (5) the + * head of the survivor list. Note that only one peer can be + * declared prefer. The order of preference is in the order + * stated. Note that all of these must be at the lowest stratum, + * i.e., the stratum of the head of the survivor list. + */ + osys_peer = sys_peer; + if (typeprefer && (typeprefer->refclktype == REFCLK_LOCALCLOCK || + typeprefer->sstclktype == CTL_SST_TS_TELEPHONE || !typepps)) { + sys_peer = typeprefer; + sys_peer->status = CTL_PST_SEL_SYSPEER; + sys_offset = sys_peer->offset; + sys_epsil = sys_peer->variance; +#ifdef DEBUG + if (debug > 1) + printf("select: prefer offset %.6f\n", sys_offset); +#endif + } else if (typepps && pps_update) { + sys_peer = typepps; + sys_peer->status = CTL_PST_SEL_PPS; + sys_offset = sys_peer->offset; + sys_epsil = sys_peer->variance; + if (!pps_control) + NLOG(NLOG_SYSEVENT) /* conditional syslog */ + 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 { + if (!typesystem) + sys_peer = peer_list[0]; + sys_peer->status = CTL_PST_SEL_SYSPEER; + sys_offset = clock_combine(peer_list, nlist); + sys_epsil = sys_peer->variance + sys_maxd; +#ifdef DEBUG + if (debug > 1) + printf("select: combine offset %.6f\n", + sys_offset); +#endif + } + if (osys_peer != sys_peer) + report_event(EVNT_PEERSTCHG, (struct peer *)0); + clock_update(); +} + +/* + * clock_combine - combine offsets from selected peers + */ +static double +clock_combine( + struct peer **peers, + int npeers + ) +{ + int i; + double x, y, z; + y = z = 0; + for (i = 0; i < npeers; i++) { + x = root_distance(peers[i]); + y += 1. / x; + z += peers[i]->offset / x; + } + return (z / y); +} + +/* + * root_distance - compute synchronization distance from peer to root + */ +static double +root_distance( + struct peer *peer + ) +{ + return ((fabs(peer->delay) + peer->rootdelay) / 2 + + peer->rootdispersion + peer->disp + + SQRT(peer->variance) + CLOCK_PHI * (current_time - + peer->update)); +} + +/* + * peer_xmit - send packet for persistent association. + */ +static void +peer_xmit( + struct peer *peer /* peer structure pointer */ + ) +{ + struct pkt xpkt; + int find_rtt = (peer->cast_flags & MDF_MCAST) && + peer->hmode != MODE_BROADCAST; + int sendlen; + + /* + * Initialize protocol fields. + */ + xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, + peer->version, peer->hmode); + xpkt.stratum = STRATUM_TO_PKT(sys_stratum); + xpkt.ppoll = peer->hpoll; + xpkt.precision = sys_precision; + xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); + xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion + + LOGTOD(sys_precision))); + xpkt.refid = sys_refid; + HTONL_FP(&sys_reftime, &xpkt.reftime); + HTONL_FP(&peer->org, &xpkt.org); + HTONL_FP(&peer->rec, &xpkt.rec); + + /* + * Authenticate the packet if enabled and either configured or + * the previous packet was authenticated. If for some reason the + * key associated with the key identifier is not in the key + * cache, then honk key zero. + */ + sendlen = LEN_PKT_NOMAC; + if (peer->flags & FLAG_AUTHENABLE) { + u_long xkeyid; + l_fp xmt_tx; + + /* + * Transmit encrypted packet compensated for the + * encryption delay. + */ +#ifdef MD5 + if (peer->flags & FLAG_SKEY) { + + /* + * In SKEY mode, allocate and initialize a key list 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 we have to wait until the receive side of the + * socket is bound and the server address confirmed. + */ + if (ntohl(peer->dstadr->sin.sin_addr.s_addr) == 0 && + ntohl(peer->dstadr->bcast.sin_addr.s_addr) == 0) + peer->keyid = 0; + else { + if (peer->keylist == 0) { + make_keylist(peer); + } else { + authtrust(peer->keylist[peer->keynumber], 0); + if (peer->keynumber == 0) + make_keylist(peer); + else { + peer->keynumber--; + xkeyid = peer->keylist[peer->keynumber]; + if (!authistrusted(xkeyid)) + make_keylist(peer); + } + } + peer->keyid = peer->keylist[peer->keynumber]; + xpkt.keyid1 = htonl(2 * sizeof(u_int32)); + xpkt.keyid2 = htonl(sys_private); + sendlen += 2 * sizeof(u_int32); + } + } +#endif /* MD5 */ + xkeyid = peer->keyid; + get_systime(&peer->xmt); + L_ADD(&peer->xmt, &sys_authdelay); + HTONL_FP(&peer->xmt, &xpkt.xmt); + sendlen += authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); + get_systime(&xmt_tx); + sendpkt(&peer->srcadr, find_rtt ? any_interface : + peer->dstadr, + ((peer->cast_flags & MDF_MCAST) && !find_rtt) ? + ((peer->cast_flags & MDF_ACAST) ? -7 : peer->ttl) : -7, + &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 DEBUG + if (debug) + printf( + "transmit: at %ld to %s mode %d keyid %08lx index %d\n", + current_time, ntoa(&peer->srcadr), + peer->hmode, xkeyid, peer->keynumber); +#endif + } else { + /* + * Transmit non-authenticated packet. + */ + get_systime(&(peer->xmt)); + HTONL_FP(&peer->xmt, &xpkt.xmt); + sendpkt(&(peer->srcadr), find_rtt ? any_interface : + peer->dstadr, + ((peer->cast_flags & MDF_MCAST) && !find_rtt) ? + ((peer->cast_flags & MDF_ACAST) ? -7 : peer->ttl) : -8, + &xpkt, sendlen); + peer->sent++; +#ifdef DEBUG + if (debug) + printf("transmit: at %ld to %s mode %d\n", + current_time, ntoa(&peer->srcadr), + peer->hmode); +#endif + } +} + +/* + * fast_xmit - Send packet for nonpersistent association. + */ +static void +fast_xmit( + struct recvbuf *rbufp, /* receive packet pointer */ + int xmode, /* transmit mode */ + u_long xkeyid /* transmit key ID */ + ) +{ + struct pkt xpkt; + struct pkt *rpkt; + int sendlen; + l_fp xmt_ts; + + /* + * Initialize transmit packet header fields in the receive + * buffer provided. We leave some fields intact as received. + */ + rpkt = &rbufp->recv_pkt; + 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.ppoll = rpkt->ppoll; + xpkt.precision = sys_precision; + xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); + xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion + + LOGTOD(sys_precision))); + xpkt.refid = sys_refid; + HTONL_FP(&sys_reftime, &xpkt.reftime); + xpkt.org = rpkt->xmt; + HTONL_FP(&rbufp->recv_time, &xpkt.rec); + sendlen = LEN_PKT_NOMAC; + if (rbufp->recv_length > sendlen) { + l_fp xmt_tx; + + /* + * Transmit encrypted packet compensated for the + * encryption delay. + */ + if (xkeyid > NTP_MAXKEY) { + xpkt.keyid1 = htonl(2 * sizeof(u_int32)); + xpkt.keyid2 = htonl(sys_private); + sendlen += 2 * sizeof(u_int32); + } + get_systime(&xmt_ts); + L_ADD(&xmt_ts, &sys_authdelay); + HTONL_FP(&xmt_ts, &xpkt.xmt); + sendlen += authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); + get_systime(&xmt_tx); + sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -9, &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 to %s mode %d keyid %08lx\n", + current_time, ntoa(&rbufp->recv_srcadr), + xmode, xkeyid); +#endif + } else { + + /* + * Transmit non-authenticated packet. + */ + get_systime(&xmt_ts); + HTONL_FP(&xmt_ts, &xpkt.xmt); + sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -10, &xpkt, + sendlen); +#ifdef DEBUG + if (debug) + printf("transmit: at %ld to %s mode %d\n", + current_time, ntoa(&rbufp->recv_srcadr), + xmode); +#endif + } +} + +#ifdef MD5 +/* + * Compute key list + */ +static void +make_keylist( + struct peer *peer + ) +{ + int i; + u_long keyid; + u_long ltemp; + + /* + * Allocate the key list if necessary. + */ + if (peer->keylist == 0) + peer->keylist = (u_long *)emalloc(sizeof(u_long) * + NTP_MAXSESSION); + + /* + * Generate an initial key ID which is unique and greater than + * NTP_MAXKEY. + */ + while (1) { + keyid = (u_long)RANDOM & 0xffffffff; + if (keyid <= NTP_MAXKEY) + continue; + 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. + */ + ltemp = sys_automax; + for (i = 0; i < NTP_MAXSESSION; i++) { + peer->keylist[i] = keyid; + peer->keynumber = i; + keyid = session_key( + ntohl(peer->dstadr->sin.sin_addr.s_addr), + (peer->hmode == MODE_BROADCAST || (peer->flags & + FLAG_MCAST2)) ? + ntohl(peer->dstadr->bcast.sin_addr.s_addr): + ntohl(peer->srcadr.sin_addr.s_addr), + keyid, ltemp); + ltemp -= 1 << peer->hpoll; + if (auth_havekey(keyid) || keyid <= NTP_MAXKEY || + ltemp <= (1 << (peer->hpoll + 1))) + break; + } +} +#endif /* MD5 */ + +/* + * Find the precision of this particular machine + */ +#define DUSECS 1000000 /* us in a s */ +#define HUSECS (1 << 20) /* approx DUSECS for shifting etc */ +#define MINSTEP 5 /* minimum clock increment (us) */ +#define MAXSTEP 20000 /* maximum clock increment (us) */ +#define MINLOOPS 5 /* minimum number of step samples */ + +/* + * This routine calculates the differences between successive calls to + * gettimeofday(). If a difference is less than zero, the us field + * has rolled over to the next second, so we add a second in us. If + * the difference is greater than zero and less than MINSTEP, the + * clock has been advanced by a small amount to avoid standing still. + * If the clock has advanced by a greater amount, then a timer interrupt + * has occurred and this amount represents the precision of the clock. + * In order to guard against spurious values, which could occur if we + * happen to hit a fat interrupt, we do this for MINLOOPS times and + * keep the minimum value obtained. + */ +int +default_get_precision(void) +{ + struct timeval tp; +#if !defined(SYS_WINNT) && !defined(VMS) && !defined(_SEQUENT_) + struct timezone tzp; +#elif defined(VMS) || defined(_SEQUENT_) + struct timezone { + int tz_minuteswest; + int tz_dsttime; + } tzp; +#endif /* defined(VMS) || defined(_SEQUENT_) */ + long last; + int i; + long diff; + long val; + long usec; +#ifdef HAVE_GETCLOCK + struct timespec ts; +#endif +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + u_long freq; + int j; + + /* Try to see if we can find the frequency of of the counter + * which drives our timekeeping + */ + j = sizeof freq; + i = sysctlbyname("kern.timecounter.frequency", + &freq, &j , 0, 0); + if (i) + i = sysctlbyname("machdep.tsc_freq", + &freq, &j , 0, 0); + if (i) + i = sysctlbyname("machdep.i586_freq", + &freq, &j , 0, 0); + if (i) + i = sysctlbyname("machdep.i8254_freq", + &freq, &j , 0, 0); + if (!i) { + for (i = 1; freq ; i--) + freq >>= 1; + return (i); + } +#endif + usec = 0; + val = MAXSTEP; +#ifdef HAVE_GETCLOCK + (void) getclock(TIMEOFDAY, &ts); + tp.tv_sec = ts.tv_sec; + tp.tv_usec = ts.tv_nsec / 1000; +#else /* not HAVE_GETCLOCK */ + GETTIMEOFDAY(&tp, &tzp); +#endif /* not HAVE_GETCLOCK */ + last = tp.tv_usec; + for (i = 0; i < MINLOOPS && usec < HUSECS;) { +#ifdef HAVE_GETCLOCK + (void) getclock(TIMEOFDAY, &ts); + tp.tv_sec = ts.tv_sec; + tp.tv_usec = ts.tv_nsec / 1000; +#else /* not HAVE_GETCLOCK */ + GETTIMEOFDAY(&tp, &tzp); +#endif /* not HAVE_GETCLOCK */ + diff = tp.tv_usec - last; + last = tp.tv_usec; + if (diff < 0) + diff += DUSECS; + usec += diff; + if (diff > MINSTEP) { + i++; + if (diff < val) + val = diff; + } + } + NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, "precision = %ld usec", val); + if (usec >= HUSECS) + val = MINSTEP; /* val <= MINSTEP; fast machine */ + diff = HUSECS; + for (i = 0; diff > val; i--) + diff >>= 1; + return (i); +} + +/* + * init_proto - initialize the protocol module's data + */ +void +init_proto(void) +{ + l_fp dummy; + + /* + * Fill in the sys_* stuff. Default is don't listen to + * broadcasting, authenticate. + */ + sys_leap = LEAP_NOTINSYNC; + sys_stratum = STRATUM_UNSPEC; + sys_precision = (s_char)default_get_precision(); + sys_rootdelay = 0; + sys_rootdispersion = 0; + sys_refid = 0; + L_CLR(&sys_reftime); + sys_peer = 0; + get_systime(&dummy); + sys_bclient = 0; + sys_bdelay = DEFBROADDELAY; +#if defined(DES) || defined(MD5) + sys_authenticate = 1; +#else + sys_authenticate = 0; +#endif + L_CLR(&sys_authdelay); + sys_authdly[0] = sys_authdly[1] = 0; + sys_stattime = 0; + sys_badstratum = 0; + sys_oldversionpkt = 0; + sys_newversionpkt = 0; + sys_badlength = 0; + sys_unknownversion = 0; + sys_processed = 0; + sys_badauth = 0; + sys_manycastserver = 0; + sys_automax = 1 << NTP_AUTOMAX; + + /* + * Default these to enable + */ + ntp_enable = 1; +#ifndef KERNEL_FLL_BUG + kern_enable = 1; +#endif + msyslog(LOG_DEBUG, "kern_enable is %d", kern_enable); + stats_control = 1; + + /* + * Some system clocks should only be adjusted in 10ms increments. + */ +#if defined RELIANTUNIX_CLOCK + systime_10ms_ticks = 1; /* Reliant UNIX */ +#elif defined SCO5_CLOCK + if (sys_precision >= (s_char)-10) /* pre- SCO OpenServer 5.0.6 */ + systime_10ms_ticks = 1; +#endif + if (systime_10ms_ticks) + msyslog(LOG_INFO, "using 10ms tick adjustments"); +} + + +/* + * proto_config - configure the protocol module + */ +void +proto_config( + int item, + u_long value, + double dvalue + ) +{ + /* + * Figure out what he wants to change, then do it + */ + switch (item) { + case PROTO_KERNEL: + /* + * Turn on/off kernel discipline + */ + kern_enable = (int)value; + break; + + case PROTO_NTP: + /* + * Turn on/off clock discipline + */ + ntp_enable = (int)value; + break; + + case PROTO_MONITOR: + /* + * Turn on/off monitoring + */ + if (value) + mon_start(MON_ON); + else + mon_stop(MON_ON); + break; + + case PROTO_FILEGEN: + /* + * Turn on/off statistics + */ + stats_control = (int)value; + break; + + case PROTO_BROADCLIENT: + /* + * Turn on/off facility to listen to broadcasts + */ + sys_bclient = (int)value; + if (value) + io_setbclient(); + else + io_unsetbclient(); + break; + + case PROTO_MULTICAST_ADD: + /* + * Add muliticast group address + */ + io_multicast_add(value); + break; + + case PROTO_MULTICAST_DEL: + /* + * Delete multicast group address + */ + io_multicast_del(value); + break; + + case PROTO_BROADDELAY: + /* + * Set default broadcast delay + */ + sys_bdelay = dvalue; + break; + + case PROTO_AUTHENTICATE: + /* + * Specify the use of authenticated data + */ + sys_authenticate = (int)value; + break; + + default: + /* + * Log this error + */ + msyslog(LOG_ERR, "proto_config: illegal item %d, value %ld", + item, value); + break; + } +} + + +/* + * proto_clr_stats - clear protocol stat counters + */ +void +proto_clr_stats(void) +{ + sys_badstratum = 0; + sys_oldversionpkt = 0; + sys_newversionpkt = 0; + sys_unknownversion = 0; + sys_badlength = 0; + sys_processed = 0; + sys_badauth = 0; + sys_stattime = current_time; + 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..c7b9166 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_refclock.c @@ -0,0 +1,1322 @@ +/* + * ntp_refclock - processing support for reference clocks + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif /* HAVE_SYS_IOCTL_H */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +#ifdef REFCLOCK + +#ifdef TTYCLK +# ifdef SCO5_CLOCK +# include <sys/sio.h> +# else +# include <sys/clkdefs.h> +# endif +#endif /* TTYCLK */ + +#ifdef HAVE_PPSCLOCK_H +#include <sys/ppsclock.h> +#endif /* HAVE_PPSCLOCK_H */ + +#ifdef HAVE_PPSAPI +#include <sys/timepps.h> +#endif /* HAVE_PPSAPI */ + +/* + * 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, 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. + * + * The routines include support for the 1-pps signal provided by some + * radios and connected via a level converted described in the gadget + * directory. The signal is captured using a serial port and one of + * three STREAMS modules described in the refclock_atom.c file. For the + * highest precision, the signal is captured using the carrier-detect + * line of a serial port and either the ppsclock or ppsapi streams + * module or some devilish ioctl() folks keep slipping in as a patch. Be + * advised ALL support for other than the duly standardized ppsapi + * interface will eventually be withdrawn. + */ +#define MAXUNIT 4 /* max units */ + +#if defined(PPS) || defined(HAVE_PPSAPI) +int fdpps; /* pps file descriptor */ +#endif /* PPS HAVE_PPSAPI */ + +#define FUDGEFAC .1 /* fudge correction factor */ + +/* + * 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 *)); + +#ifdef HAVE_PPSAPI +extern int pps_assert; /* capture edge 1:assert, 0:clear */ +extern int pps_hardpps; /* PPS kernel 1:on, 0:off */ +#endif /* HAVE_PPSAPI */ + +/* + * 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; + + if (!(pp = peer->procptr)) + return; + if (code == CEVNT_BADREPLY) + pp->badformat++; + if (code == CEVNT_BADTIME) + pp->baddata++; + if (code == CEVNT_TIMEOUT) + pp->noreply++; + if (pp->currentstatus != code) { + pp->currentstatus = code; + pp->lastevent = code; + if (code == CEVNT_FAULT) + msyslog(LOG_ERR, + "clock %s event '%s' (0x%02x)", + refnumtoa(peer->srcadr.sin_addr.s_addr), + ceventstr(code), code); + else { + NLOG(NLOG_CLOCKEVENT) + msyslog(LOG_INFO, + "clock %s event '%s' (0x%02x)", + refnumtoa(peer->srcadr.sin_addr.s_addr), + ceventstr(code), code); + } + } +#ifdef DEBUG + if (debug) + printf("clock %s event '%s' (0x%02x)\n", + refnumtoa(peer->srcadr.sin_addr.s_addr), + ceventstr(code), code); +#endif +} + + +/* + * 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 (!ISREFCLOCKADR(&peer->srcadr)) { + msyslog(LOG_ERR, + "refclock_newpeer: clock address %s invalid", + ntoa(&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); + } + refclock_unpeer(peer); + + /* + * Allocate and initialize interface structure + */ + if (!(pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc)))) + return (0); + memset((char *)pp, 0, sizeof(struct refclockproc)); + typeunit[clktype][unit] = peer; + peer->procptr = pp; + + /* + * Initialize structures + */ + peer->refclktype = clktype; + peer->refclkunit = unit; + peer->flags |= FLAG_REFCLOCK; + peer->stratum = STRATUM_REFCLOCK; + peer->refid = peer->srcadr.sin_addr.s_addr; + peer->maxpoll = peer->minpoll; + + pp->type = clktype; + pp->timestarted = current_time; + + /* + * If the interface has been set to any_interface, set it to the + * loopback address if we have one. This is so that peers which + * are unreachable are easy to see in the peer display. + */ + if (peer->dstadr == any_interface && loopback_interface != 0) + peer->dstadr = loopback_interface; + + /* + * 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))) { + free(pp); + return (0); + } + peer->hpoll = peer->minpoll; + peer->ppoll = peer->maxpoll; + if (peer->stratum <= 1) + peer->refid = pp->refid; + else + peer->refid = peer->srcadr.sin_addr.s_addr; + 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_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; + int hpoll; + u_long next; + + clktype = peer->refclktype; + unit = peer->refclkunit; + peer->sent++; + + /* + * 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. + */ + hpoll = peer->hpoll; + next = peer->outdate; + if (peer->burst == 0) { + u_char oreach; +#ifdef DEBUG + if (debug) + printf("refclock_transmit: at %ld %s\n", + current_time, ntoa(&(peer->srcadr))); +#endif + + /* + * Update reachability and poll variables like the + * network code. + */ + oreach = peer->reach; + if (oreach & 0x01) + peer->valid++; + if (oreach & 0x80) + peer->valid--; + peer->reach <<= 1; + if (peer->reach == 0) { + if (oreach != 0) { + report_event(EVNT_UNREACH, peer); + peer->timereachable = current_time; + peer_clear(peer); + } + } else { + if ((oreach & 0x03) == 0) { + clock_filter(peer, 0., 0., MAXDISPERSE); + clock_select(); + } + if (peer->valid <= 2) { + hpoll--; + } else if (peer->valid > NTP_SHIFT - 2) + hpoll++; + if (peer->flags & FLAG_BURST) + peer->burst = NSTAGE; + } + next = current_time; + } + get_systime(&peer->xmt); + if (refclock_conf[clktype]->clock_poll != noentry) + (refclock_conf[clktype]->clock_poll)(unit, peer); + peer->outdate = next; + poll_update(peer, hpoll); + if (peer->burst > 0) + peer->burst--; + poll_update(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, + l_fp offset, + l_fp lastrec, + double fudge + ) +{ + double doffset; + + pp->lastref = offset; + pp->lastrec = lastrec; + pp->variance = 0; + L_SUB(&offset, &lastrec); + LFPTOD(&offset, 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. +*/ +int +refclock_process( + struct refclockproc *pp + ) +{ + l_fp offset; + + /* + * 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); + if (pp->usec) { + TVUTOTSF(pp->usec, offset.l_uf); + } else { + MSUTOTSF(pp->msec, offset.l_uf); + } + 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 mean-square variance. 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 0. + */ +static int +refclock_sample( + struct refclockproc *pp + ) +{ + int i, j, k, n; + double offset, disp; + double off[MAXSTAGE]; + + /* + * Copy the raw offsets and sort into ascending order. Don't do + * anything if the buffer is empty. + */ + if (pp->codeproc == pp->coderecv) + return (0); + n = 0; + while (pp->codeproc != pp->coderecv) + off[n++] = pp->filter[pp->codeproc++ % MAXSTAGE]; + if (n > 1) + qsort((char *)off, 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; + k = n - (n * 2) / NSTAGE; + while ((j - i) > k) { + 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 variance. + */ + offset = disp = 0; + for (; i < j; i++) { + offset += off[i]; + disp += SQUARE(off[i]); + } + offset /= k; + pp->offset = offset; + pp->variance += disp / k - SQUARE(offset); +#ifdef DEBUG + if (debug) + printf( + "refclock_sample: n %d offset %.6f disp %.6f std %.6f\n", + n, pp->offset, pp->disp, SQRT(pp->variance)); +#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, ntoa(&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. + */ + peer->received++; + pp = peer->procptr; + peer->processed++; + peer->timereceived = current_time; + peer->leap = pp->leap; + if (peer->leap == LEAP_NOTINSYNC) { + refclock_report(peer, CEVNT_FAULT); + return; + } + if (peer->reach == 0) + report_event(EVNT_REACH, peer); + peer->reach |= 1; + peer->reftime = peer->org = pp->lastrec; + peer->rootdispersion = pp->disp + SQRT(pp->variance); + get_systime(&peer->rec); + if (!refclock_sample(pp)) + return; + clock_filter(peer, pp->offset, 0., 0.); + clock_select(); + record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), + peer->offset, peer->delay, CLOCK_PHI * (current_time - + peer->epoch), SQRT(peer->variance)); + if (pps_control && pp->sloppyclockflag & CLK_FLAG1) + pp->fudgetime1 -= pp->offset * FUDGEFAC; +} + +/* + * refclock_gtlin - groom next input line and extract timestamp + * + * This routine processes the timecode received from the clock and + * removes the parity bit and control characters. 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. The routine return code is the number of characters in + * the line. + */ +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 *dpt, *dpend, *dp; + int i; + l_fp trtmp, tstmp; + char c; +#ifdef TIOCDCDTIMESTAMP + struct timeval dcd_time; +#endif /* TIOCDCDTIMESTAMP */ +#ifdef HAVE_PPSAPI + pps_info_t pi; + struct timespec timeout, *tsp; + double a; +#endif /* HAVE_PPSAPI */ + + /* + * 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_space; + dpend = dpt + rbufp->recv_length; + trtmp = rbufp->recv_time; + +#ifdef HAVE_PPSAPI + timeout.tv_sec = 0; + timeout.tv_nsec = 0; + if ((rbufp->fd == fdpps) && + (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &pi, &timeout) >= 0)) { + if(pps_assert) + tsp = &pi.assert_timestamp; + else + tsp = &pi.clear_timestamp; + a = tsp->tv_nsec; + a /= 1e9; + tstmp.l_uf = a * 4294967296.0; + tstmp.l_ui = tsp->tv_sec; + tstmp.l_ui += JAN_1970; + L_SUB(&trtmp, &tstmp); + if (trtmp.l_ui == 0) { +#ifdef DEBUG + if (debug > 1) { + printf( + "refclock_gtlin: fd %d time_pps_fetch %s", + fdpps, lfptoa(&tstmp, 6)); + printf(" sigio %s\n", lfptoa(&trtmp, 6)); + } +#endif + trtmp = tstmp; + goto gotit; + } else + trtmp = rbufp->recv_time; + } +#endif /* HAVE_PPSAPI */ +#ifdef TIOCDCDTIMESTAMP + if(ioctl(rbufp->fd, TIOCDCDTIMESTAMP, &dcd_time) != -1) { + TVTOTS(&dcd_time, &tstmp); + tstmp.l_ui += JAN_1970; + L_SUB(&trtmp, &tstmp); + if (trtmp.l_ui == 0) { +#ifdef DEBUG + if (debug > 1) { + printf( + "refclock_gtlin: fd %d DCDTIMESTAMP %s", + rbufp->fd, lfptoa(&tstmp, 6)); + printf(" sigio %s\n", lfptoa(&trtmp, 6)); + } +#endif + trtmp = tstmp; + goto gotit; + } else + trtmp = rbufp->recv_time; + } + else + /* XXX fallback to old method if kernel refuses TIOCDCDTIMESTAMP */ +#endif /* TIOCDCDTIMESTAMP */ + 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; + } + } + +#if defined(HAVE_PPSAPI) || defined(TIOCDCDTIMESTAMP) +gotit: +#endif + /* + * Edit timecode to remove control chars. Don't monkey with the + * line buffer if the input buffer contains no ASCII printing + * characters. + */ + if (dpend - dpt > bmax - 1) + dpend = dpt + bmax - 1; + for (dp = lineptr; dpt < dpend; dpt++) { + c = *dpt & 0x7f; + if (c >= ' ') + *dp++ = c; + } + i = dp - lineptr; + if (i > 0) + *dp = '\0'; +#ifdef DEBUG + if (debug > 1 && i > 0) + printf("refclock_gtlin: 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 */ + int speed, /* serial port speed (code) */ + int flags /* line discipline flags */ + ) +{ + int fd, i; +#ifdef HAVE_TERMIOS + struct termios ttyb, *ttyp; +#endif /* HAVE_TERMIOS */ +#ifdef HAVE_SYSV_TTYS + struct termio ttyb, *ttyp; +#endif /* HAVE_SYSV_TTYS */ +#ifdef HAVE_BSD_TTYS + struct sgttyb ttyb, *ttyp; +#endif /* HAVE_BSD_TTYS */ +#ifdef TIOCMGET + u_long ltemp; +#endif /* TIOCMGET */ + + /* + * Open serial port and set default options + */ +#ifdef O_NONBLOCK + fd = open(dev, O_RDWR | O_NONBLOCK, 0777); +#else + fd = open(dev, O_RDWR, 0777); +#endif /* O_NONBLOCK */ + if (fd == -1) { + msyslog(LOG_ERR, "refclock_open: %s: %m", dev); + return (0); + } + + /* + * The following sections initialize the serial line port in + * canonical (line-oriented) mode and set the specified line + * speed, 8 bits and no parity. The modem control, break, erase + * and kill functions are normally disabled. There is a + * different section for each terminal interface, as selected at + * compile time. + */ + ttyp = &ttyb; + +#ifdef HAVE_TERMIOS + /* + * POSIX serial line parameters (termios interface) + */ + if (tcgetattr(fd, ttyp) < 0) { + msyslog(LOG_ERR, + "refclock_open: 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. + */ + ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; + ttyp->c_oflag = 0; + ttyp->c_cflag = CS8 | CLOCAL | CREAD; + (void)cfsetispeed(&ttyb, (u_int)speed); + (void)cfsetospeed(&ttyb, (u_int)speed); + ttyp->c_lflag = ICANON; + for (i = 0; i < NCCS; ++i) + { + ttyp->c_cc[i] = '\0'; + } + + /* + * Some special cases + */ + if (flags & LDISC_RAW) { + ttyp->c_iflag = 0; + ttyp->c_lflag = 0; + ttyp->c_cc[VMIN] = 1; + } +#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. + */ + ltemp = 0; + if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) + msyslog(LOG_ERR, + "refclock_open: fd %d TIOCMGET failed: %m", fd); +#ifdef DEBUG + if (debug) + printf("refclock_open: fd %d modem status 0x%lx\n", + fd, ltemp); +#endif + if (ltemp & TIOCM_DSR) + ttyp->c_cflag &= ~CLOCAL; +#endif /* TIOCMGET */ + if (tcsetattr(fd, TCSANOW, ttyp) < 0) { + msyslog(LOG_ERR, + "refclock_open: fd %d TCSANOW failed: %m", fd); + return (0); + } + if (tcflush(fd, TCIOFLUSH) < 0) { + msyslog(LOG_ERR, + "refclock_open: fd %d TCIOFLUSH failed: %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_open: fd %d TCGETA failed: %m", fd); + return (0); + } + + /* + * Set canonical mode and local connection; set specified speed, + * 8 bits and no parity; map CR to NL; ignore break. + */ + ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; + ttyp->c_oflag = 0; + ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD; + ttyp->c_lflag = ICANON; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; + + /* + * Some special cases + */ + if (flags & LDISC_RAW) { + ttyp->c_iflag = 0; + ttyp->c_lflag = 0; + } +#ifdef TIOCMGET + /* + * 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. + */ + ltemp = 0; + if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) + msyslog(LOG_ERR, + "refclock_open: fd %d TIOCMGET failed: %m", fd); +#ifdef DEBUG + if (debug) + printf("refclock_open: fd %d modem status %lx\n", + fd, ltemp); +#endif + if (ltemp & TIOCM_DSR) + ttyp->c_cflag &= ~CLOCAL; +#endif /* TIOCMGET */ + if (ioctl(fd, TCSETA, ttyp) < 0) { + msyslog(LOG_ERR, + "refclock_open: fd %d TCSETA failed: %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_open: fd %d TIOCGETP %m", fd); + return (0); + } + ttyp->sg_ispeed = ttyp->sg_ospeed = speed; + ttyp->sg_flags = EVENP | ODDP | CRMOD; + if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) { + msyslog(LOG_ERR, + "refclock_open: TIOCSETP failed: %m"); + return (0); + } +#endif /* HAVE_BSD_TTYS */ + if (!refclock_ioctl(fd, flags)) { + (void)close(fd); + msyslog(LOG_ERR, + "refclock_open: fd %d ioctl failed: %m", fd); + return (0); + } + + /* + * If this is the PPS device, so say and initialize the thing. + */ + if (strcmp(dev, pps_device) == 0) + (void)refclock_ioctl(fd, LDISC_PPS); + return (fd); +} +#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, ppsclock and ppsapi, as well as + * their many other variants. The routine returns 1 if success and 0 if + * failure. + */ +int +refclock_ioctl( + int fd, /* file descriptor */ + int flags /* 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 TTYCLK +#ifdef HAVE_TERMIOS + struct termios ttyb, *ttyp; +#endif /* HAVE_TERMIOS */ +#ifdef HAVE_SYSV_TTYS + struct termio ttyb, *ttyp; +#endif /* HAVE_SYSV_TTYS */ +#ifdef HAVE_BSD_TTYS + struct sgttyb ttyb, *ttyp; +#endif /* HAVE_BSD_TTYS */ +#endif /* TTYCLK */ + +#ifdef DEBUG + if (debug) + printf("refclock_ioctl: fd %d flags 0x%x\n", fd, flags); +#endif + + /* + * The following sections select optional features, such as + * modem control, PPS capture and so forth. Some require + * specific operating system support in the form of STREAMS + * modules, which can be loaded and unloaded at run time without + * rebooting the kernel. The STREAMS modules require System + * V STREAMS support. The checking frenzy is attenuated here, + * since the device is already open. + * + * Note that the tty_clk and ppsclock modules are optional; if + * configured and unavailable, the dang thing still works, but + * the accuracy improvement using them will not be available. + * The only known implmentations of these moldules are specific + * to SunOS 4.x. Use the ppsclock module ONLY with Sun baseboard + * ttya or ttyb. Using it with the SPIF multipexor crashes the + * kernel. + * + * The preferred way to capture PPS timestamps is using the + * ppsapi interface, which is machine independent. The SunOS 4.x + * and Digital Unix 4.x interfaces use STREAMS modules and + * support both the ppsapi specification and ppsclock + * functionality, but other systems may vary widely. + */ + if (flags == 0) + return (1); +#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS)) + if (flags & (LDISC_CLK | LDISC_PPS | LDISC_ACTS)) { + msyslog(LOG_ERR, + "refclock_ioctl: unsupported terminal interface"); + return (0); + } +#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */ +#ifdef TTYCLK + ttyp = &ttyb; +#endif /* TTYCLK */ + + /* + * The following features may or may not require System V + * STREAMS support, depending on the particular implementation. + */ +#if defined(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 (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) { + int rval = 0; + + if (ioctl(fd, I_PUSH, "clk") < 0) { + msyslog(LOG_NOTICE, + "refclock_ioctl: I_PUSH clk failed: %m"); + } else { + char *str; + + if (flags & LDISC_CLKPPS) + str = "\377"; + else if (flags & LDISC_ACTS) + str = "*"; + else + str = "\n"; +#ifdef CLK_SETSTR + if ((rval = ioctl(fd, CLK_SETSTR, str)) < 0) + msyslog(LOG_ERR, + "refclock_ioctl: CLK_SETSTR failed: %m"); + if (debug) + printf("refclock_ioctl: fd %d CLK_SETSTR %d str %s\n", + fd, rval, str); +#endif + } + } +#endif /* TTYCLK */ + +#if defined(PPS) && !defined(HAVE_PPSAPI) + /* + * The PPS option provides timestamping at the driver level. + * It uses a 1-pps signal and level converter (gadget box) and + * requires the ppsclock streams module and System V STREAMS + * support. This option has been superseded by the ppsapi + * option and may be withdrawn in future. + */ + if (flags & LDISC_PPS) { + int rval = 0; +#ifdef HAVE_TIOCSPPS /* Solaris */ + int one = 1; +#endif /* HAVE_TIOCSPPS */ + + if (fdpps > 0) { + msyslog(LOG_ERR, + "refclock_ioctl: PPS already configured"); + return (0); + } +#ifdef HAVE_TIOCSPPS /* Solaris */ + if (ioctl(fd, TIOCSPPS, &one) < 0) { + msyslog(LOG_NOTICE, + "refclock_ioctl: TIOCSPPS failed: %m"); + return (0); + } + if (debug) + printf("refclock_ioctl: fd %d TIOCSPPS %d\n", + fd, rval); +#else + if (ioctl(fd, I_PUSH, "ppsclock") < 0) { + msyslog(LOG_NOTICE, + "refclock_ioctl: I_PUSH ppsclock failed: %m"); + return (0); + } + if (debug) + printf("refclock_ioctl: fd %d ppsclock %d\n", + fd, rval); +#endif /* not HAVE_TIOCSPPS */ + fdpps = fd; + } +#endif /* PPS HAVE_PPSAPI */ + +#ifdef HAVE_PPSAPI + /* + * The PPSAPI option provides timestamping at the driver level. + * It uses a 1-pps signal and level converter (gadget box) and + * requires ppsapi compiled into the kernel on non STREAMS + * systems. This is the preferred way to capture PPS timestamps + * and is expected to become an IETF cross-platform standard. + */ + if (flags & (LDISC_PPS | LDISC_CLKPPS)) { + pps_params_t pp; + int mode, temp; + pps_handle_t handle; + + memset((char *)&pp, 0, sizeof(pp)); + if (fdpps > 0) { + msyslog(LOG_ERR, + "refclock_ioctl: ppsapi already configured"); + return (0); + } + if (time_pps_create(fd, &handle) < 0) { + msyslog(LOG_ERR, + "refclock_ioctl: time_pps_create failed: %m"); + return (0); + } + if (time_pps_getcap(handle, &mode) < 0) { + msyslog(LOG_ERR, + "refclock_ioctl: time_pps_getcap failed: %m"); + return (0); + } + pp.mode = mode & PPS_CAPTUREBOTH; + if (time_pps_setparams(handle, &pp) < 0) { + msyslog(LOG_ERR, + "refclock_ioctl: time_pps_setparams failed: %m"); + return (0); + } + if (!pps_hardpps) + temp = 0; + else if (pps_assert) + temp = mode & PPS_CAPTUREASSERT; + else + temp = mode & PPS_CAPTURECLEAR; + if (time_pps_kcbind(handle, PPS_KC_HARDPPS, temp, + PPS_TSFMT_TSPEC) < 0) { + msyslog(LOG_ERR, + "refclock_ioctl: time_pps_kcbind failed: %m"); + return (0); + } + (void)time_pps_getparams(handle, &pp); + fdpps = (int)handle; + if (debug) + printf( + "refclock_ioctl: fd %d ppsapi vers %d mode 0x%x cap 0x%x\n", + fdpps, pp.api_version, pp.mode, mode); + } +#endif /* HAVE_PPSAPI */ +#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_in *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 (!ISREFCLOCKADR(srcadr)) + return; + clktype = (u_char)REFCLOCKTYPE(srcadr); + unit = REFCLOCKUNIT(srcadr); + if (clktype >= num_refclock_conf || unit >= MAXUNIT) + return; + if (!(peer = typeunit[clktype][unit])) + 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 = (u_char) in->fudgeval1; + if (in->haveflags & CLK_HAVEVAL2) + pp->refid = in->fudgeval2; + if (peer->stratum <= 1) + peer->refid = pp->refid; + else + peer->refid = peer->srcadr.sin_addr.s_addr; + 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 = peer->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_in *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 (!ISREFCLOCKADR(srcadr)) + return; + clktype = (u_char) REFCLOCKTYPE(srcadr); + unit = REFCLOCKUNIT(srcadr); + if (clktype >= num_refclock_conf || unit >= MAXUNIT) + return; + if (!(peer = typeunit[clktype][unit])) + 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->msec; + 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..3743118 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_request.c @@ -0,0 +1,2334 @@ +/* + * ntp_request.c - respond to information requests + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <signal.h> +#include <sys/time.h> + +#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 "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) + +struct req_proc { + short request_code; /* defined request code */ + short needs_auth; /* true when authentication needed */ + short sizeofitem; /* size of request data item */ + void (*handler) P((struct sockaddr_in *, 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_in *, struct interface *, struct req_pkt *, int)); +static char * prepare_pkt P((struct sockaddr_in *, 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_in *, struct interface *, struct req_pkt *)); +static void peer_list_sum P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void peer_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void peer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void sys_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void sys_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void mem_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void io_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void timer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void loop_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_conf P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_unconf P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void clr_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long)); +static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_ressubflags P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_unrestrict P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void mon_getlist_0 P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void mon_getlist_1 P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_peer P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_key_reread P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void trust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void untrust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_trustkey P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void get_auth_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void reset_auth_stats P((void)); +static void req_get_traps P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void req_set_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void req_clr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void do_setclr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *, int)); +static void set_request_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_control_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void get_ctl_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#ifdef KERNEL_PLL +static void get_kernel_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* KERNEL_PLL */ +#ifdef REFCLOCK +static void get_clock_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +static void set_clock_fudge P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* REFCLOCK */ +#ifdef REFCLOCK +static void get_clkbug_info P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +#endif /* REFCLOCK */ + +/* + * ntpd request codes + */ +static struct req_proc ntp_codes[] = { + { REQ_PEER_LIST, NOAUTH, 0, peer_list }, + { REQ_PEER_LIST_SUM, NOAUTH, 0, peer_list_sum }, + { REQ_PEER_INFO, NOAUTH, sizeof(struct info_peer_list), peer_info }, + { REQ_PEER_STATS, NOAUTH, sizeof(struct info_peer_list), peer_stats }, + { REQ_SYS_INFO, NOAUTH, 0, sys_info }, + { REQ_SYS_STATS, NOAUTH, 0, sys_stats }, + { REQ_IO_STATS, NOAUTH, 0, io_stats }, + { REQ_MEM_STATS, NOAUTH, 0, mem_stats }, + { REQ_LOOP_INFO, NOAUTH, 0, loop_info }, + { REQ_TIMER_STATS, NOAUTH, 0, timer_stats }, + { REQ_CONFIG, AUTH, sizeof(struct conf_peer), do_conf }, + { REQ_UNCONFIG, AUTH, sizeof(struct conf_unpeer), do_unconf }, + { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), set_sys_flag }, + { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), clr_sys_flag }, + { REQ_GET_RESTRICT, NOAUTH, 0, list_restrict }, + { REQ_RESADDFLAGS, AUTH, sizeof(struct conf_restrict), do_resaddflags }, + { REQ_RESSUBFLAGS, AUTH, sizeof(struct conf_restrict), do_ressubflags }, + { REQ_UNRESTRICT, AUTH, sizeof(struct conf_restrict), do_unrestrict }, + { REQ_MON_GETLIST, NOAUTH, 0, mon_getlist_0 }, + { REQ_MON_GETLIST_1, NOAUTH, 0, mon_getlist_1 }, + { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), reset_stats }, + { REQ_RESET_PEER, AUTH, sizeof(struct conf_unpeer), reset_peer }, + { REQ_REREAD_KEYS, AUTH, 0, do_key_reread }, + { REQ_TRUSTKEY, AUTH, sizeof(u_long), trust_key }, + { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), untrust_key }, + { REQ_AUTHINFO, NOAUTH, 0, get_auth_info }, + { REQ_TRAPS, NOAUTH, 0, req_get_traps }, + { REQ_ADD_TRAP, AUTH, sizeof(struct conf_trap), req_set_trap }, + { REQ_CLR_TRAP, AUTH, sizeof(struct conf_trap), req_clr_trap }, + { REQ_REQUEST_KEY, AUTH, sizeof(u_long), set_request_keyid }, + { REQ_CONTROL_KEY, AUTH, sizeof(u_long), set_control_keyid }, + { REQ_GET_CTLSTATS, NOAUTH, 0, get_ctl_stats }, +#ifdef KERNEL_PLL + { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info }, +#endif +#ifdef REFCLOCK + { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), get_clock_info }, + { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge), set_clock_fudge }, + { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), get_clkbug_info }, +#endif + { NO_REQUEST, NOAUTH, 0, 0 } +}; + + +/* + * Authentication keyid used to authenticate requests. Zero means we + * don't allow writing anything. + */ +u_long 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_in *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_in *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_in *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, reqest 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 sockaddr_in *srcadr; + struct interface *inter; + struct req_proc *proc; + + /* + * Initialize pointers, for convenience + */ + inpkt = (struct req_pkt *)&rbufp->recv_pkt; + srcadr = &rbufp->recv_srcadr; + inter = rbufp->dstadr; + +#ifdef DEBUG + if (debug > 2) + printf("prepare_pkt: impl %d req %d\n", + inpkt->implementation, inpkt->request); +#endif + + /* + * Do some sanity checks on the packet. Return a format + * error if it fails. + */ + if (ISRESPONSE(inpkt->rm_vn_mode) + || ISMORE(inpkt->rm_vn_mode) + || INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION + || INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION + || INFO_SEQ(inpkt->auth_seq) != 0 + || INFO_ERR(inpkt->err_nitems) != 0 + || INFO_MBZ(inpkt->mbz_itemsize) != 0 + || rbufp->recv_length > REQ_LEN_MAC + || rbufp->recv_length < REQ_LEN_NOMAC) { + 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) + 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 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 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(inpkt->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(inpkt->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 + 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); +#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(&inpkt->tstamp, &ftmp); + L_SUB(&ftmp, &rbufp->recv_time); + LFPTOD(&ftmp, dtemp); + if (fabs(dtemp) >= INFO_TS_MAXSKEW) { + /* + * He's a loser. Tell him. + */ + 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, + REQ_LEN_NOMAC, (int)(rbufp->recv_length - REQ_LEN_NOMAC))) { + req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH); + return; + } + } + + /* + * If we need data, check to see if we have some. If we + * don't, check to see that there is none (picky, picky). + */ + if (INFO_ITEMSIZE(inpkt->mbz_itemsize) != proc->sizeofitem) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + if (proc->sizeofitem != 0) + if (proc->sizeofitem*INFO_NITEMS(inpkt->err_nitems) + > sizeof(inpkt->data)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + 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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_list *ip; + register struct peer *pp; + register int i; + + ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_list)); + for (i = 0; i < HASH_SIZE && ip != 0; i++) { + pp = peer_hash[i]; + while (pp != 0 && ip != 0) { + ip->address = pp->srcadr.sin_addr.s_addr; + ip->port = pp->srcadr.sin_port; + 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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_peer_summary *ips; + register struct peer *pp; + register int i; + l_fp ltmp; + +#ifdef DEBUG + if (debug > 2) + printf("wants peer list summary\n"); +#endif + + ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_summary)); + for (i = 0; i < 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 + ips->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 1 : 5; + ips->srcadr = pp->srcadr.sin_addr.s_addr; + ips->srcport = pp->srcadr.sin_port; + 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, <mp); + HTONL_FP(<mp, &ips->offset); + ips->dispersion = HTONS_FP(DTOUFP(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_in *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_in addr; + extern struct peer *sys_peer; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + items = INFO_NITEMS(inpkt->err_nitems); + ipl = (struct info_peer_list *) inpkt->data; + ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer)); + while (items-- > 0 && ip != 0) { + addr.sin_port = ipl->port; + addr.sin_addr.s_addr = ipl->address; + ipl++; + if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0) + continue; + ip->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 2 : 6; + ip->srcadr = NSRCADR(&pp->srcadr); + 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->valid = pp->valid; + ip->reach = pp->reach; + ip->unreach = pp->unreach; + ip->flash = (u_char)pp->flash; + ip->flash2 = 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], <mp); + HTONL_FP(<mp, &ip->filtoffset[i]); + ip->order[i] = (pp->filter_nextpt+NTP_SHIFT-1) + - pp->filter_order[i]; + if (ip->order[i] >= NTP_SHIFT) + ip->order[i] -= NTP_SHIFT; + } + DTOLFP(pp->offset, <mp); + HTONL_FP(<mp, &ip->offset); + ip->delay = HTONS_FP(DTOFP(pp->delay)); + ip->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp))); + ip->selectdisp = HTONS_FP(DTOUFP(SQRT(pp->variance))); + ip = (struct info_peer *)more_pkt(); + } + flush_pkt(); +} + + +/* + * peer_stats - send statistics for one or more peers + */ +static void +peer_stats ( + struct sockaddr_in *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_in addr; + extern struct peer *sys_peer; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + items = INFO_NITEMS(inpkt->err_nitems); + ipl = (struct info_peer_list *) inpkt->data; + ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_peer_stats)); + while (items-- > 0 && ip != 0) { + addr.sin_port = ipl->port; + addr.sin_addr.s_addr = ipl->address; + ipl++; + if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0) + continue; + ip->dstadr = (pp->processed) ? + pp->cast_flags == MDF_BCAST ? + pp->dstadr->bcast.sin_addr.s_addr: + pp->cast_flags ? + pp->dstadr->sin.sin_addr.s_addr ? + pp->dstadr->sin.sin_addr.s_addr: + pp->dstadr->bcast.sin_addr.s_addr: + 3 : 7; + ip->srcadr = NSRCADR(&pp->srcadr); + 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->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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_sys *is; + + /* + * Importations from the protocol module + */ + extern u_char sys_leap; + extern u_char sys_stratum; + extern s_char sys_precision; + extern double sys_rootdelay; + extern double sys_rootdispersion; + extern u_int32 sys_refid; + extern l_fp sys_reftime; + extern u_char sys_poll; + extern struct peer *sys_peer; + extern int sys_bclient; + extern double sys_bdelay; + extern l_fp sys_authdelay; + extern double clock_stability; + extern double sys_error; + + is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_sys)); + + if (sys_peer != 0) { + is->peer = NSRCADR(&sys_peer->srcadr); + is->peer_mode = sys_peer->hmode; + } else { + is->peer = 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_error)); + is->stability = htonl(DTOUFP(clock_stability * 1e6)); + is->refid = sys_refid; + HTONL_FP(&sys_reftime, &is->reftime); + + is->poll = sys_poll; + + is->flags = 0; + if (sys_bclient) + is->flags |= INFO_FLAG_BCLIENT; + if (sys_authenticate) + is->flags |= INFO_FLAG_AUTHENTICATE; + if (kern_enable) + is->flags |= INFO_FLAG_KERNEL; + if (ntp_enable) + is->flags |= INFO_FLAG_NTP; + if (pll_control) + is->flags |= INFO_FLAG_PLL_SYNC; + if (pps_control) + is->flags |= INFO_FLAG_PPS_SYNC; + if (mon_enabled != MON_OFF) + is->flags |= INFO_FLAG_MONITOR; + 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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_sys_stats *ss; + + /* + * Importations from the protocol module + */ + extern u_long sys_stattime; + extern u_long sys_badstratum; + extern u_long sys_oldversionpkt; + extern u_long sys_newversionpkt; + extern u_long sys_unknownversion; + extern u_long sys_badlength; + extern u_long sys_processed; + extern u_long sys_badauth; + extern u_long sys_limitrejected; + + 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->badstratum = htonl((u_int32)sys_badstratum); + 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); + (void) more_pkt(); + flush_pkt(); +} + + +/* + * mem_stats - return memory statistics + */ +static void +mem_stats( + struct sockaddr_in *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[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 < 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_in *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_in *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_in *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 last_time; + + li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_loop)); + + DTOLFP(last_offset, <mp); + HTONL_FP(<mp, &li->last_offset); + DTOLFP(drift_comp * 1e6, <mp); + HTONL_FP(<mp, &li->drift_comp); + li->compliance = htonl((u_int32)(tc_counter)); + li->watchdog_timer = htonl((u_int32)(current_time - last_time)); + + (void) more_pkt(); + flush_pkt(); +} + + +/* + * do_conf - add a peer to the configuration list + */ +static void +do_conf( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_peer *cp; + register int items; + struct sockaddr_in peeraddr; + int fl; + + /* + * 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; + + fl = 0; + while (items-- > 0 && !fl) { + if (((cp->version) > NTP_VERSION) + || ((cp->version) < NTP_OLDVERSION)) + fl = 1; + if (cp->hmode != MODE_ACTIVE + && cp->hmode != MODE_CLIENT + && cp->hmode != MODE_BROADCAST) + fl = 1; + if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER + | CONF_FLAG_NOSELECT | CONF_FLAG_BURST | CONF_FLAG_SKEY)) + fl = 1; + cp++; + } + + 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; + 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)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + while (items-- > 0) { + fl = 0; + if (cp->flags & CONF_FLAG_AUTHENABLE) + fl |= FLAG_AUTHENABLE; + if (cp->flags & CONF_FLAG_PREFER) + fl |= FLAG_PREFER; + if (cp->flags & CONF_FLAG_NOSELECT) + fl |= FLAG_NOSELECT; + if (cp->flags & CONF_FLAG_BURST) + fl |= FLAG_BURST; + if (cp->flags & CONF_FLAG_SKEY) + fl |= FLAG_SKEY; + peeraddr.sin_addr.s_addr = cp->peeraddr; + /* XXX W2DO? minpoll/maxpoll arguments ??? */ + if (peer_config(&peeraddr, (struct interface *)0, + cp->hmode, cp->version, cp->minpoll, cp->maxpoll, + fl, cp->ttl, cp->keyid) == 0) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * do_unconf - remove a peer from the configuration list + */ +static void +do_unconf( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_unpeer *cp; + register int items; + register struct peer *peer; + struct sockaddr_in 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. + */ + peeraddr.sin_family = AF_INET; + peeraddr.sin_port = htons(NTP_PORT); + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + + bad = 0; + while (items-- > 0 && !bad) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + found = 0; + peer = (struct peer *)0; + 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++; + } + + 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) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer_unconfig(&peeraddr, (struct interface *)0, -1); + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * set_sys_flag - set system flags + */ +static void +set_sys_flag( + struct sockaddr_in *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_in *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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + u_long set + ) +{ + register u_long flags; + + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + flags = ((struct conf_sys_flags *)inpkt->data)->flags; + + if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_AUTHENTICATE | + SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR | + SYS_FLAG_FILEGEN)) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + if (flags & SYS_FLAG_BCLIENT) + proto_config(PROTO_BROADCLIENT, set, 0.); + if (flags & SYS_FLAG_AUTHENTICATE) + proto_config(PROTO_AUTHENTICATE, set, 0.); + if (flags & SYS_FLAG_NTP) + proto_config(PROTO_NTP, set, 0.); + if (flags & SYS_FLAG_KERNEL) + proto_config(PROTO_KERNEL, set, 0.); + if (flags & SYS_FLAG_MONITOR) + proto_config(PROTO_MONITOR, set, 0.); + if (flags & SYS_FLAG_FILEGEN) + proto_config(PROTO_FILEGEN, set, 0.); + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * list_restrict - return the restrict list + */ +static void +list_restrict( + struct sockaddr_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct info_restrict *ir; + register struct restrictlist *rl; + extern struct restrictlist *restrictlist; + +#ifdef DEBUG + if (debug > 2) + printf("wants peer list summary\n"); +#endif + + ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt, + sizeof(struct info_restrict)); + for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) { + ir->addr = htonl(rl->addr); + 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(); + } + flush_pkt(); +} + + + +/* + * do_resaddflags - add flags to a restrict entry (or create one) + */ +static void +do_resaddflags( + struct sockaddr_in *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_in *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_in *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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int op + ) +{ + register struct conf_restrict *cr; + register int items; + struct sockaddr_in matchaddr; + struct sockaddr_in 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; + while (items-- > 0 && !bad) { + if (cr->mflags & ~(RESM_NTPONLY)) + bad = 1; + if (cr->flags & ~(RES_ALLFLAGS)) + bad = 1; + if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY)) + bad = 1; + cr++; + } + + if (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_in)); + memset((char *)&matchmask, 0, sizeof(struct sockaddr_in)); + matchaddr.sin_family = AF_INET; + matchmask.sin_family = AF_INET; + + while (items-- > 0) { + matchaddr.sin_addr.s_addr = cr->addr; + matchmask.sin_addr.s_addr = cr->mask; + 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_in *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, + sizeof(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)(current_time - md->lasttime)); + im->firsttime = htonl((u_int32)(current_time - md->firsttime)); + if (md->lastdrop) + im->lastdrop = htonl((u_int32)(current_time - md->lastdrop)); + else + im->lastdrop = 0; + im->count = htonl((u_int32)(md->count)); + im->addr = md->rmtadr; + 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_in *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; + +#ifdef DEBUG + if (debug > 2) + printf("wants monitor 1 list\n"); +#endif + if (!mon_enabled) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + + im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt, + sizeof(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)(current_time - md->lasttime)); + im->firsttime = htonl((u_int32)(current_time - md->firsttime)); + if (md->lastdrop) + im->lastdrop = htonl((u_int32)(current_time - md->lastdrop)); + else + im->lastdrop = 0; + im->count = htonl((u_int32)md->count); + im->addr = md->rmtadr; + im->daddr = + (md->cast_flags == MDF_BCAST) + ? md->interface->bcast.sin_addr.s_addr + : (md->cast_flags + ? (md->interface->sin.sin_addr.s_addr + ? md->interface->sin.sin_addr.s_addr + : md->interface->bcast.sin_addr.s_addr + ) + : 4); + im->flags = 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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long flags; + struct reset_entry *rent; + + if (INFO_NITEMS(inpkt->err_nitems) > 1) { + req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); + return; + } + + flags = ((struct reset_flags *)inpkt->data)->flags; + + if (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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_unpeer *cp; + register int items; + register struct peer *peer; + struct sockaddr_in peeraddr; + int bad; + + /* + * We check first to see that every peer exists. If not, + * we return an error. + */ + peeraddr.sin_family = AF_INET; + peeraddr.sin_port = htons(NTP_PORT); + + items = INFO_NITEMS(inpkt->err_nitems); + cp = (struct conf_unpeer *)inpkt->data; + + bad = 0; + while (items-- > 0 && !bad) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer = findexistingpeer(&peeraddr, (struct peer *)0, -1); + if (peer == (struct peer *)0) + bad++; + cp++; + } + + 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) { + peeraddr.sin_addr.s_addr = cp->peeraddr; + peer = findexistingpeer(&peeraddr, (struct peer *)0, -1); + while (peer != 0) { + peer_reset(peer); + peer = findexistingpeer(&peeraddr, (struct peer *)peer, -1); + } + cp++; + } + + req_ack(srcadr, inter, inpkt, INFO_OKAY); +} + + +/* + * do_key_reread - reread the encryption key file + */ +static void +do_key_reread( + struct sockaddr_in *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_in *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_in *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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int 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_in *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_in *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, + sizeof(struct info_trap)); + + for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) { + if (tr->tr_flags & TRAP_INUSE) { + if (tr->tr_localaddr == any_interface) + it->local_address = 0; + else + it->local_address + = NSRCADR(&tr->tr_localaddr->sin); + it->trap_address = NSRCADR(&tr->tr_addr); + 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_in *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_in *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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt, + int set + ) +{ + register struct conf_trap *ct; + register struct interface *linter; + int res; + struct sockaddr_in laddr; + + /* + * Prepare sockaddr_in structure + */ + memset((char *)&laddr, 0, sizeof laddr); + laddr.sin_family = AF_INET; + laddr.sin_port = ntohs(NTP_PORT); + + /* + * Restrict ourselves to one item only. This eliminates + * the error reporting problem. + */ + if (INFO_NITEMS(inpkt->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 { + laddr.sin_addr.s_addr = ct->local_address; + linter = findinterface(&laddr); + if (linter == NULL) { + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); + return; + } + } + + laddr.sin_addr.s_addr = ct->trap_address; + if (ct->trap_port != 0) + laddr.sin_port = ct->trap_port; + else + laddr.sin_port = 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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long keyid; + + /* + * Restrict ourselves to one item only. + */ + if (INFO_NITEMS(inpkt->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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + u_long keyid; + extern u_long ctl_auth_keyid; + + /* + * Restrict ourselves to one item only. + */ + if (INFO_NITEMS(inpkt->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_in *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_in *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_in *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_in addr; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = 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) { + addr.sin_addr.s_addr = *clkaddr++; + if (!ISREFCLOCKADR(&addr) || + 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 = addr.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, <mp); + HTONL_FP(<mp, &ic->fudgetime1); + DTOLFP(clock_stat.fudgetime1, <mp); + HTONL_FP(<mp, &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_in *srcadr, + struct interface *inter, + struct req_pkt *inpkt + ) +{ + register struct conf_fudge *cf; + register int items; + struct refclockstat clock_stat; + struct sockaddr_in addr; + l_fp ltmp; + + memset((char *)&addr, 0, sizeof addr); + memset((char *)&clock_stat, 0, sizeof clock_stat); + addr.sin_family = AF_INET; + addr.sin_port = htons(NTP_PORT); + items = INFO_NITEMS(inpkt->err_nitems); + cf = (struct conf_fudge *) inpkt->data; + + while (items-- > 0) { + addr.sin_addr.s_addr = cf->clockadr; + if (!ISREFCLOCKADR(&addr) || + 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, <mp); + LFPTOD(<mp, clock_stat.fudgetime1); + clock_stat.haveflags = CLK_HAVETIME1; + break; + case FUDGE_TIME2: + NTOHL_FP(&cf->fudgetime, <mp); + LFPTOD(<mp, 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: + 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_in *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_in addr; + + memset((char *)&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = 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) { + addr.sin_addr.s_addr = *clkaddr++; + if (!ISREFCLOCKADR(&addr) || + 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 = addr.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 diff --git a/contrib/ntp/ntpd/ntp_restrict.c b/contrib/ntp/ntpd/ntp_restrict.c new file mode 100644 index 0000000..0e5b9dc --- /dev/null +++ b/contrib/ntp/ntpd/ntp_restrict.c @@ -0,0 +1,460 @@ +/* + * ntp_restrict.c - find out what restrictions this host is running under + */ +#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. + */ + +/* + * 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 + +/* + * The restriction list + */ +struct restrictlist *restrictlist; +static int restrictcount; /* count of entries in the restriction list */ + +/* + * The free list and associated counters. Also some uninteresting + * stat counters. + */ +static struct restrictlist *resfree; +static int numresfree; /* number of structures on free list */ + +static u_long res_calls; +static u_long res_found; +static u_long res_not_found; +/* static u_long res_timereset; */ + +/* + * Parameters of the RES_LIMITED restriction option. + * client_limit is the number of hosts allowed per source net + * client_limit_period is the number of seconds after which an entry + * is no longer considered for client limit determination + */ +u_long client_limit; +u_long client_limit_period; +/* + * 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; + +/* + * Our initial allocation of list entries. + */ +static struct restrictlist resinit[INITRESLIST]; + +/* + * init_restrict - initialize the restriction data structures + */ +void +init_restrict(void) +{ + register int i; + char bp[80]; + + /* + * Zero the list and put all but one on the free list + */ + resfree = 0; + memset((char *)resinit, 0, sizeof resinit); + + for (i = 1; i < INITRESLIST; i++) { + resinit[i].next = resfree; + resfree = &resinit[i]; + } + + numresfree = 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; + restrictlist = &resinit[0]; + restrictcount = 1; + + + /* + * fix up stat counters + */ + res_calls = 0; + res_found = 0; + res_not_found = 0; + /* res_timereset = 0; */ + + /* + * set default values for RES_LIMIT functionality + */ + client_limit = 3; + client_limit_period = 3600; + res_limited_refcnt = 0; + + sprintf(bp, "client_limit=%ld", client_limit); + set_sys_var(bp, strlen(bp)+1, RO); + sprintf(bp, "client_limit_period=%ld", client_limit_period); + set_sys_var(bp, strlen(bp)+1, RO); +} + + +/* + * restrictions - return restrictions for this host + */ +int +restrictions( + struct sockaddr_in *srcadr + ) +{ + register struct restrictlist *rl; + register struct restrictlist *match; + register u_int32 hostaddr; + register int isntpport; + + res_calls++; + /* + * 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(ntohl(srcadr->sin_addr.s_addr))) + 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 != 0 && 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++; + + /* + * The following implements limiting the number of clients + * accepted from a given network. The notion of "same network" + * is determined by the mask and addr fields of the restrict + * list entry. The monitor mechanism has to be enabled for + * collecting info on current clients. + * + * The policy is as follows: + * - take the list of clients recorded + * from the given "network" seen within the last + * client_limit_period seconds + * - if there are at most client_limit entries: + * --> access allowed + * - otherwise sort by time first seen + * - current client among the first client_limit seen + * hosts? + * if yes: access allowed + * else: eccess denied + */ + if (match->flags & RES_LIMITED) { + int lcnt; + struct mon_data *md, *this_client; + +#ifdef DEBUG + if (debug > 2) + printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n", + client_limit, client_limit_period, + (u_long)netof(hostaddr)); +#endif /*DEBUG*/ + if (mon_enabled == MON_OFF) { +#ifdef DEBUG + if (debug > 4) + printf("no limit - monitoring is off\n"); +#endif + return (int)(match->flags & ~RES_LIMITED); + } + + /* + * How nice, MRU list provides our current client as the + * first entry in the list. + * Monitoring was verified to be active above, thus we + * know an entry for our client must exist, or some + * brain dead set the memory limit for mon entries to ZERO!!! + */ + this_client = mon_mru_list.mru_next; + + for (md = mon_fifo_list.fifo_next,lcnt = 0; + md != &mon_fifo_list; + md = md->fifo_next) { + if ((current_time - md->lasttime) + > client_limit_period) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore: too old: %ld\n", + numtoa(md->rmtadr), + current_time - md->lasttime); +#endif + continue; + } + if (md->mode == MODE_BROADCAST || + md->mode == MODE_CONTROL || + md->mode == MODE_PRIVATE) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: ignore mode %d\n", + numtoa(md->rmtadr), + md->mode); +#endif + continue; + } + if (netof(md->rmtadr) != + netof(hostaddr)) { +#ifdef DEBUG + if (debug > 5) + printf("checking: %s: different net 0x%lX\n", + numtoa(md->rmtadr), + (u_long)netof(md->rmtadr)); +#endif + continue; + } + lcnt++; + if (lcnt > (int) client_limit || + md->rmtadr == hostaddr) { +#ifdef DEBUG + if (debug > 5) + printf("considering %s: found host\n", + numtoa(md->rmtadr)); +#endif + break; + } +#ifdef DEBUG + else { + if (debug > 5) + printf("considering %s: same net\n", + numtoa(md->rmtadr)); + } +#endif + + } +#ifdef DEBUG + if (debug > 4) + printf("this one is rank %d in list, limit is %lu: %s\n", + lcnt, client_limit, + (lcnt <= (int) client_limit) ? "ALLOW" : "REJECT"); +#endif + if (lcnt <= (int) client_limit) { + this_client->lastdrop = 0; + return (int)(match->flags & ~RES_LIMITED); + } else { + this_client->lastdrop = current_time; + } + } + return (int)match->flags; +} + + +/* + * hack_restrict - add/subtract/manipulate entries on the restrict list + */ +void +hack_restrict( + int op, + struct sockaddr_in *resaddr, + struct sockaddr_in *resmask, + int mflags, + int flags + ) +{ + register u_int32 addr; + register u_int32 mask; + register struct restrictlist *rl; + register struct restrictlist *rlprev; + int i; + + /* + * Get address and mask in host byte order + */ + addr = SRCADR(resaddr); + mask = SRCADR(resmask); + addr &= mask; /* make sure low bits are zero */ + + /* + * If this is the default address, point at first on list. Else + * go searching for it. + */ + if (addr == htonl(INADDR_ANY)) { + rlprev = 0; + rl = restrictlist; + } else { + rlprev = restrictlist; + rl = rlprev->next; + while (rl != 0) { + if (rl->addr > addr) { + rl = 0; + break; + } else if (rl->addr == addr) { + if (rl->mask == mask) { + if ((mflags & RESM_NTPONLY) + == (rl->mflags & RESM_NTPONLY)) + break; /* exact match */ + if (!(mflags & RESM_NTPONLY)) { + /* + * No flag fits before flag + */ + rl = 0; + break; + } + /* continue on */ + } else if (rl->mask > mask) { + rl = 0; + break; + } + } + rlprev = rl; + rl = rl->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 + */ + switch (op) { + case RESTRICT_FLAGS: + /* + * Here we add bits to the flags. If this is a new + * restriction add it. + */ + if (rl == 0) { + if (numresfree == 0) { + 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; + + rl->next = rlprev->next; + rlprev->next = rl; + restrictcount++; + } + if ((rl->flags ^ (u_short)flags) & RES_LIMITED) { + res_limited_refcnt++; + mon_start(MON_RES); /* ensure data gets collected */ + } + 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 != 0) { + 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: + /* + * 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 != 0 + && rl->addr != htonl(INADDR_ANY) + && !(rl->mflags & RESM_INTERFACE)) { + rlprev->next = 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: + /* Oh, well */ + break; + } + + /* done! */ +} diff --git a/contrib/ntp/ntpd/ntp_timer.c b/contrib/ntp/ntpd/ntp_timer.c new file mode 100644 index 0000000..0e2dc88 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_timer.c @@ -0,0 +1,308 @@ +/* + * ntp_timer.c - event timer support routines + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/signal.h> + +#include "ntp_machine.h" +#include "ntpd.h" +#include "ntp_stdlib.h" +#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. + */ + +/* + * 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 hourly_timer; /* hour timer */ +static u_long revoke_timer; /* keys revoke timer */ +u_long sys_revoke = KEY_REVOKE; /* keys revoke timeout */ + +/* + * 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 */ + + +/* + * init_timer - initialize the timer data structures + */ +void +init_timer(void) +{ +#if !defined(VMS) +# if !defined SYS_WINNT || defined(SYS_CYGWIN32) +# ifndef HAVE_TIMER_SETTIME + struct itimerval itimer; +# else + static timer_t ntpd_timerid; /* should be global if we ever want */ + /* to kill timer without rebooting ... */ + struct itimerspec itimer; +# endif /* HAVE_TIMER_SETTIME */ +# else /* SYS_WINNT */ + HANDLE hToken; + TOKEN_PRIVILEGES tkp; +# endif /* SYS_WINNT */ +#endif /* !VMS */ + + /* + * Initialize... + */ + alarm_flag = 0; + alarm_overflow = 0; + adjust_timer = 1; + hourly_timer = HOUR; + 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; + int n; + + current_time += (1<<EVENT_TIMEOUT); + + /* + * Adjustment timeout first. + */ + if (adjust_timer <= current_time) { + adjust_timer += 1; + adj_host_clock(); + } + + /* + * 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 < 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(); + } + + /* + * Garbage collect revoked keys + */ + if (revoke_timer <= current_time) { + revoke_timer += RANDPOLL(sys_revoke); + key_expire_all(); + } + + /* + * Finally, call the hourly routine. + */ + if (hourly_timer <= current_time) { + hourly_timer += HOUR; + hourly_stats(); + } +} + + +#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 */ + + +/* + * 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..c53255a --- /dev/null +++ b/contrib/ntp/ntpd/ntp_util.c @@ -0,0 +1,640 @@ +/* + * ntp_util.c - stuff I didn't have any other place for + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +# ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif +# include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_filegen.h" +#include "ntp_if.h" +#include "ntp_stdlib.h" + +#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; + +/* + * 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; + +/* + * 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; + +/* + * init_util - initialize the utilities + */ +void +init_util(void) +{ + stats_drift_file = 0; + stats_temp_file = 0; + key_file_name = 0; + +#define PEERNAME "peerstats" +#define LOOPNAME "loopstats" +#define CLOCKNAME "clockstats" +#define RAWNAME "rawstats" + peerstats.fp = NULL; + peerstats.prefix = &statsdir[0]; + peerstats.basename = (char*)emalloc(strlen(PEERNAME)+1); + strcpy(peerstats.basename, PEERNAME); + peerstats.id = 0; + peerstats.type = FILEGEN_DAY; + peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ + filegen_register("peerstats", &peerstats); + + loopstats.fp = NULL; + loopstats.prefix = &statsdir[0]; + loopstats.basename = (char*)emalloc(strlen(LOOPNAME)+1); + strcpy(loopstats.basename, LOOPNAME); + loopstats.id = 0; + loopstats.type = FILEGEN_DAY; + loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ + filegen_register("loopstats", &loopstats); + + clockstats.fp = NULL; + clockstats.prefix = &statsdir[0]; + clockstats.basename = (char*)emalloc(strlen(CLOCKNAME)+1); + strcpy(clockstats.basename, CLOCKNAME); + clockstats.id = 0; + clockstats.type = FILEGEN_DAY; + clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ + filegen_register("clockstats", &clockstats); + + rawstats.fp = NULL; + rawstats.prefix = &statsdir[0]; + rawstats.basename = (char*)emalloc(strlen(RAWNAME)+1); + strcpy(rawstats.basename, RAWNAME); + rawstats.id = 0; + rawstats.type = FILEGEN_DAY; + rawstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ + filegen_register("rawstats", &rawstats); + +#undef PEERNAME +#undef LOOPNAME +#undef CLOCKNAME +#undef RAWNAME + +} + + +/* + * hourly_stats - print some interesting stats + */ +void +hourly_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_error, sys_poll); + + 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, + char *invalue /* only one type so far */ + ) +{ + FILE *fp; + char *value; + double old_drift; + 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 ((fp = fopen(stats_drift_file, "r")) == NULL) { + break; + } + if (fscanf(fp, "%lf", &old_drift) != 1) { + msyslog(LOG_ERR, "invalid frequency from %s", + stats_drift_file); + (void) fclose(fp); + break; + } + (void) fclose(fp); + msyslog(LOG_INFO, "frequency initialized %.3f from %s", + old_drift, stats_drift_file); + loop_config(LOOP_DRIFTCOMP, old_drift / 1e6); + 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); + } + } + 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_in *addr, + int status, + double offset, + double delay, + double dispersion, + double skew + ) +{ + struct timeval tv; +#ifdef HAVE_GETCLOCK + struct timespec ts; +#endif + u_long day, sec, msec; + + if (!stats_control) + return; +#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 */ + day = tv.tv_sec / 86400 + MJD_1970; + sec = tv.tv_sec % 86400; + msec = tv.tv_usec / 1000; + + filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970)); + if (peerstats.fp != NULL) { + fprintf(peerstats.fp, + "%lu %lu.%03lu %s %x %.9f %.9f %.9f %.9f\n", + day, sec, msec, ntoa(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(void) +{ + struct timeval tv; +#ifdef HAVE_GETCLOCK + struct timespec ts; +#endif + u_long day, sec, msec; + + if (!stats_control) + return; +#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 */ + day = tv.tv_sec / 86400 + MJD_1970; + sec = tv.tv_sec % 86400; + msec = tv.tv_usec / 1000; + + filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970)); + if (loopstats.fp != NULL) { + fprintf(loopstats.fp, "%lu %lu.%03lu %.9f %.6f %.9f %.6f %d\n", + day, sec, msec, last_offset, drift_comp * 1e6, + sys_error, clock_stability * 1e6, sys_poll); + 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_in *addr, + const char *text + ) +{ + struct timeval tv; +#ifdef HAVE_GETCLOCK + struct timespec ts; +#endif + u_long day, sec, msec; + + if (!stats_control) + return; +#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 */ + day = tv.tv_sec / 86400 + MJD_1970; + sec = tv.tv_sec % 86400; + msec = tv.tv_usec / 1000; + + filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970)); + if (clockstats.fp != NULL) { + fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n", + day, sec, msec, ntoa(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_in *srcadr, + struct sockaddr_in *dstadr, + l_fp *t1, + l_fp *t2, + l_fp *t3, + l_fp *t4 + ) +{ + struct timeval tv; +#ifdef HAVE_GETCLOCK + struct timespec ts; +#endif + u_long day, sec, msec; + + if (!stats_control) + return; +#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 */ + day = tv.tv_sec / 86400 + MJD_1970; + sec = tv.tv_sec % 86400; + msec = tv.tv_usec / 1000; + + filegen_setup(&rawstats, (u_long)(tv.tv_sec + JAN_1970)); + if (rawstats.fp != NULL) { + fprintf(rawstats.fp, "%lu %lu.%03lu %s %s %s %s %s %s\n", + day, sec, msec, ntoa(srcadr), ntoa(dstadr), + ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9), + ulfptoa(t4, 9)); + fflush(rawstats.fp); + } +} + +/* + * getauthkeys - read the authentication keys from the specified file + */ +void +getauthkeys( + 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); +} diff --git a/contrib/ntp/ntpd/ntpd.c b/contrib/ntp/ntpd/ntpd.c new file mode 100644 index 0000000..923b1f7 --- /dev/null +++ b/contrib/ntp/ntpd/ntpd.c @@ -0,0 +1,1121 @@ +/* + * ntpd.c - main program for the fixed point NTP daemon + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#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*/ +# include <sys/param.h> +# endif /* VMS */ +# include <sys/signal.h> +# ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif /* HAVE_SYS_IOCTL_H */ +# include <sys/time.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 "../libntp/log.h" +# include <crtdbg.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 "ntpd.h" +#include "ntp_io.h" + +#include "ntp_stdlib.h" +#include "recvbuff.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 + +/* + * 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 */ + +#if defined SYS_WINNT || defined SYS_CYGWIN32 +/* handles for various threads, process, and objects */ +HANDLE ResolverThreadHandle = NULL; +/* variables used to inform the Service Control Manager of our current state */ +SERVICE_STATUS ssStatus; +SERVICE_STATUS_HANDLE sshStatusHandle; +HANDLE WaitHandles[3] = { NULL, NULL, NULL }; +char szMsgPath[255]; +static BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType); +#endif /* SYS_WINNT */ + +/* + * Scheduling priority we run at + */ +#if !defined SYS_WINNT +# define NTPD_PRIO (-12) +#else +# define NTPD_PRIO REALTIME_PRIORITY_CLASS +#endif + +/* + * Debugging flag + */ +volatile int debug; + +/* + * No-fork flag. If set, we do not become a background daemon. + */ +int nofork; + +/* + * Initializing flag. All async routines watch this and only do their + * thing when it is clear. + */ +int initializing; + +/* + * Version declaration + */ +extern const char *Version; + +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 +static RETSIGTYPE moredebug P((int)); +static RETSIGTYPE lessdebug P((int)); +#else /* not DEBUG */ +static RETSIGTYPE no_debug P((int)); +#endif /* not DEBUG */ + +int ntpdmain P((int, char **)); +static void set_process_priority P((void)); + + +#ifdef NO_MAIN_ALLOWED +CALL(ntpd,"ntpd",ntpdmain); +#else +int +main( + int argc, + char *argv[] + ) +{ + return ntpdmain(argc, argv); +} +#endif + +#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) +{ + int done = 0; + +#ifdef SYS_WINNT + if (!SetPriorityClass(GetCurrentProcess(), (DWORD) REALTIME_PRIORITY_CLASS)) + msyslog(LOG_ERR, "SetPriorityClass: %m"); + else + ++done; +#else /* not SYS_WINNT */ +# if defined(HAVE_SCHED_SETSCHEDULER) + + if (!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 + ++done; + } +# endif /* HAVE_SCHED_SETSCHEDULER */ +# if defined(HAVE_RTPRIO) +# ifdef RTP_SET + if (!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 + ++done; + } +# else /* not RTP_SET */ + if (!done) { + if (rtprio(0, 120) < 0) + msyslog(LOG_ERR, "rtprio() error: %m"); + else + ++done; + } +# endif /* not RTP_SET */ +# endif /* HAVE_RTPRIO */ +# if defined(NTPD_PRIO) && NTPD_PRIO != 0 +# ifdef HAVE_ATT_NICE + if (!done) { + errno = 0; + if (-1 == nice (NTPD_PRIO) && errno != 0) + msyslog(LOG_ERR, "nice() error: %m"); + else + ++done; + } +# endif /* HAVE_ATT_NICE */ +# ifdef HAVE_BSD_NICE + if (!done) { + if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO)) + msyslog(LOG_ERR, "setpriority() error: %m"); + else + ++done; + } +# endif /* HAVE_BSD_NICE */ +# endif /* NTPD_PRIO && NTPD_PRIO != 0 */ +#endif /* not SYS_WINNT */ + if (!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; + char *cp; + struct recvbuf *rbuflist; + struct recvbuf *rbuf; +#ifdef _AIX /* HMS: ifdef SIGDANGER? */ + struct sigaction sa; +#endif + + initializing = 1; /* mark that we are initializing */ + debug = 0; /* no debugging by default */ + nofork = 0; /* will fork by default */ + +#ifdef HAVE_UMASK + { + unsigned int uv; + + uv = umask(0); + if(uv) + (void) umask(uv); + else + (void) umask(022); + } +#endif + +#ifdef HAVE_GETUID + { + uid_t uid; + + uid = getuid(); + if (uid) + { + msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid); + exit(1); + } + } +#endif + +#ifdef SYS_WINNT + /* Set the Event-ID message-file name. */ + if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) { + msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n"); + exit(1); + } + addSourceToRegistry("NTP", szMsgPath); +#endif + + get_systime(&now); + SRANDOM((int)(now.l_i * now.l_uf)); + getstartup(argc, argv); /* startup configuration, may set debug */ + +#if !defined(VMS) +# ifndef NODETACH + /* + * Detach us from the terminal. May need an #ifndef GIZMO. + */ +# ifdef DEBUG + if (!debug && !nofork) +# else /* DEBUG */ + if (!nofork) +# endif /* DEBUG */ + { +# 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 */ + + /* + * From 'Writing Reliable AIX Daemons,' SG24-4946-00, + * by Eric Agar (saves us from doing 32767 system + * calls) + */ +#if defined(F_CLOSEM) + 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 */ +#else /* SYS_WINNT */ + + { + SERVICE_TABLE_ENTRY dispatchTable[] = { + { TEXT("NetworkTimeProtocol"), (LPSERVICE_MAIN_FUNCTION)service_main }, + { NULL, NULL } + }; + + /* daemonize */ + if (!StartServiceCtrlDispatcher(dispatchTable)) + { + msyslog(LOG_ERR, "StartServiceCtrlDispatcher: %m"); + ExitProcess(2); + } + } +#endif /* SYS_WINNT */ + } +#endif /* NODETACH */ +#if defined(SYS_WINNT) && !defined(NODETACH) + else + service_main(argc, argv); + return 0; /* must return a value */ +} /* end main */ + +/* + * If this runs as a service under NT, the main thread will block at + * StartServiceCtrlDispatcher() and another thread will be started by the + * Service Control Dispatcher which will begin execution at the routine + * specified in that call (viz. service_main) + */ +void +service_main( + DWORD argc, + LPTSTR *argv + ) +{ + char *cp; + struct recvbuf *rbuflist; + struct recvbuf *rbuf; + + if(!debug) + { + /* register our service control handler */ + if (!(sshStatusHandle = RegisterServiceCtrlHandler( TEXT("NetworkTimeProtocol"), + (LPHANDLER_FUNCTION)service_ctrl))) + { + msyslog(LOG_ERR, "RegisterServiceCtrlHandler failed: %m"); + return; + } + + /* report pending status to Service Control Manager */ + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwCurrentState = SERVICE_START_PENDING; + ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + ssStatus.dwWin32ExitCode = NO_ERROR; + ssStatus.dwServiceSpecificExitCode = 0; + ssStatus.dwCheckPoint = 1; + ssStatus.dwWaitHint = 5000; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + return; + } + + } /* debug */ +#endif /* defined(SYS_WINNT) && !defined(NODETACH) */ +#endif /* VMS */ + + /* + * Logging. This may actually work on the gizmo board. Find a name + * to log with by using the basename of argv[0] + */ + cp = strrchr(argv[0], '/'); + if (cp == 0) + cp = argv[0]; + else + cp++; + + debug = 0; /* will be immediately re-initialized 8-( */ + getstartup(argc, argv); /* startup configuration, catch logfile this time */ + +#if !defined(SYS_WINNT) && !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); +#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 + +#endif /* !SYS_WINNT && !VMS */ + + NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "%s", Version); + +#ifdef SYS_WINNT + /* GMS 1/18/1997 + * TODO: lock the process in memory using SetProcessWorkingSetSize() and VirtualLock() functions + * + process_handle = GetCurrentProcess(); + if (SetProcessWorkingSetSize(process_handle, 2097152 , 4194304 ) == TRUE) { + if (VirtualLock(0 , 4194304) == FALSE) + msyslog(LOG_ERR, "VirtualLock() failed: %m"); + } else { + msyslog(LOG_ERR, "SetProcessWorkingSetSize() failed: %m"); + } + */ +#endif /* SYS_WINNT */ + +#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\n"); + 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) + /* + * 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 && !defined SYS_CYGWIN32 + (void) signal_no_reset(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#if defined SYS_WINNT + if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) { + msyslog(LOG_ERR, "Can't set console control handler: %m"); + } +#endif + + /* + * Call the init_ routines to initialize the data structures. + */ +#if defined (HAVE_IO_COMPLETION_PORT) + init_io_completion_port(); + init_winnt_time(); +#endif + init_auth(); + init_util(); + init_restrict(); + init_mon(); + init_timer(); + init_lib(); + init_random(); + init_request(); + init_control(); + init_peer(); +#ifdef REFCLOCK + init_refclock(); +#endif + set_process_priority(); + init_proto(); + init_io(); + init_loopfilter(); + + mon_start(MON_ON); /* monitor on by default now */ + /* turn off in config if unwanted */ + + /* + * Get configuration. This (including argument list parsing) is + * done in a separate module since this will definitely be different + * for the gizmo board. + */ + getconfig(argc, argv); + + initializing = 0; + +#if defined(SYS_WINNT) && !defined(NODETACH) +# if defined(DEBUG) + if(!debug) + { +#endif + /* report to the service control manager that the service is running */ + ssStatus.dwCurrentState = SERVICE_RUNNING; + ssStatus.dwWin32ExitCode = NO_ERROR; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + if (ResolverThreadHandle != NULL) + CloseHandle(ResolverThreadHandle); + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + return; + } +# if defined(DEBUG) + } +#endif +#endif + + /* + * 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) + { + WaitHandles[0] = CreateEvent(NULL, FALSE, FALSE, NULL); /* exit reques */ + WaitHandles[1] = get_recv_buff_event(); + WaitHandles[2] = get_timer_handle(); + + for (;;) { + DWORD Index = MsgWaitForMultipleObjectsEx(sizeof(WaitHandles)/sizeof(WaitHandles[0]), WaitHandles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE); + switch (Index) { + case WAIT_OBJECT_0 + 0 : /* exit request */ + exit(0); + break; + + case WAIT_OBJECT_0 + 1 : {/* recv buffer */ + if (NULL != (rbuf = get_full_recv_buffer())) { + if (rbuf->receiver != NULL) { + rbuf->receiver(rbuf); + } + freerecvbuf(rbuf); + } + } + break; + + case WAIT_OBJECT_0 + 2 : /* 1 second timer */ + timer(); + break; + + case WAIT_OBJECT_0 + 3 : { /* Windows message */ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + exit(0); + } + DispatchMessage(&msg); + } + } + break; + + case WAIT_IO_COMPLETION : /* loop */ + case WAIT_TIMEOUT : + break; + + } + + } + } +# else /* normal I/O */ + + was_alarmed = 0; + rbuflist = (struct recvbuf *)0; + for (;;) + { +# if !defined(HAVE_SIGNALED_IO) + extern fd_set activefds; + extern int maxactivefd; + + fd_set rdfdes; + int nfound; +# elif defined(HAVE_SIGNALED_IO) + block_io_and_alarm(); +# endif + + rbuflist = getrecvbufs(); /* get received buffers */ + if (alarm_flag) /* alarmed? */ + { + was_alarmed = 1; + alarm_flag = 0; + } + + if (!was_alarmed && rbuflist == (struct recvbuf *)0) + { + /* + * 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) + msyslog(LOG_ERR, "select() error: %m"); + else if (debug) { +# if !defined SYS_VXWORKS && !defined SYS_CYGWIN32 && !defined SCO5_CLOCK /* to unclutter log */ + msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound); +# endif + } +# else /* HAVE_SIGNALED_IO */ + + wait_for_signal(); +# endif /* HAVE_SIGNALED_IO */ + if (alarm_flag) /* alarmed? */ + { + was_alarmed = 1; + alarm_flag = 0; + } + rbuflist = getrecvbufs(); /* get received buffers */ + } +# ifdef HAVE_SIGNALED_IO + unblock_io_and_alarm(); +# endif /* HAVE_SIGNALED_IO */ + + /* + * Out here, signals are unblocked. Call timer routine + * to process expiry. + */ + if (was_alarmed) + { + timer(); + was_alarmed = 0; + } + + /* + * Call the data procedure to handle each received + * packet. + */ + while (rbuflist != (struct recvbuf *)0) + { + rbuf = rbuflist; + rbuflist = rbuf->next; + (rbuf->receiver)(rbuf); + freerecvbuf(rbuf); + } +# if defined DEBUG && defined SYS_WINNT + if (debug > 4) + printf("getrecvbufs: %ld handler interrupts, %ld frames\n", + handler_calls, handler_pkts); +# endif + + /* + * Go around again + */ + } +# endif /* HAVE_IO_COMPLETION_PORT */ + exit(1); /* unreachable */ + return 1; /* DEC OSF cc braindamage */ +} + + +#ifdef SIGDIE2 +/* + * finish - exit gracefully + */ +static RETSIGTYPE +finish( + int sig + ) +{ + + msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig); + + 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 +/* + * 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; +} +#else /* not DEBUG */ +/* + * 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 DEBUG */ + +#ifdef SYS_WINNT +/* service_ctrl - control handler for NTP service + * signals the service_main routine of start/stop requests + * from the control panel or other applications making + * win32API calls + */ +void +service_ctrl( + DWORD dwCtrlCode + ) +{ + DWORD dwState = SERVICE_RUNNING; + + /* Handle the requested control code */ + switch(dwCtrlCode) + { + case SERVICE_CONTROL_PAUSE: + /* see no reason to support this */ + break; + + case SERVICE_CONTROL_CONTINUE: + /* see no reason to support this */ + break; + + case SERVICE_CONTROL_STOP: + dwState = SERVICE_STOP_PENDING; + /* + * Report the status, specifying the checkpoint and waithint, + * before setting the termination event. + */ + ssStatus.dwCurrentState = dwState; + ssStatus.dwWin32ExitCode = NO_ERROR; + ssStatus.dwWaitHint = 3000; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + } + if (WaitHandles[0] != NULL) { + SetEvent(WaitHandles[0]); + } + return; + + case SERVICE_CONTROL_INTERROGATE: + /* Update the service status */ + break; + + default: + /* invalid control code */ + break; + + } + + ssStatus.dwCurrentState = dwState; + ssStatus.dwWin32ExitCode = NO_ERROR; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + } +} + +static BOOL WINAPI +OnConsoleEvent( + DWORD dwCtrlType + ) +{ + switch (dwCtrlType) { + case CTRL_BREAK_EVENT : + if (debug > 0) { + debug <<= 1; + } + else { + debug = 1; + } + if (debug > 8) { + debug = 0; + } + printf("debug level %d\n", debug); + break ; + + case CTRL_C_EVENT : + case CTRL_CLOSE_EVENT : + case CTRL_SHUTDOWN_EVENT : + if (WaitHandles[0] != NULL) { + SetEvent(WaitHandles[0]); + } + break; + + default : + return FALSE; + + + } + return TRUE;; +} + + +/* + * NT version of exit() - all calls to exit() should be routed to + * this function. + */ +void +service_exit( + int status + ) +{ + if (!debug) { /* did not become a service, simply exit */ + /* service mode, need to have the service_main routine + * register with the service control manager that the + * service has stopped running, before exiting + */ + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + + } + uninit_io_completion_port(); + reset_winnt_time(); + +# if defined _MSC_VER + _CrtDumpMemoryLeaks(); +# endif +#undef exit + exit(status); +} + +#endif /* SYS_WINNT */ diff --git a/contrib/ntp/ntpd/refclock_acts.c b/contrib/ntp/ntpd/refclock_acts.c new file mode 100644 index 0000000..9c367b5 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_acts.c @@ -0,0 +1,981 @@ +/* + * refclock_acts - clock driver for the NIST/PTB Automated Computer Time + * Service aka Amalgamated Containerized Trash Service (ACTS) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS)) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif /* HAVE_SYS_IOCTL_H */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" +#include "ntp_control.h" + +/* MUST BE AFTER LAST #include <config.h> !!! */ + +#if defined(CLOCK_ACTS) && defined(CLOCK_PTBACTS) +# if defined(KEEPPTBACTS) +# undef CLOCK_ACTS +# else /* not KEEPPTBACTS */ +# undef CLOCK_PTBACTS +# endif /* not KEEPPTBACTS */ +#endif /* CLOCK_ACTS && CLOCK_PTBACTS */ + +/* + * This driver supports the NIST Automated Computer Time Service (ACTS). + * It periodically dials a prespecified telephone number, receives the + * NIST timecode data and calculates the local clock correction. It is + * designed primarily for use as a backup when neither a radio clock nor + * connectivity to Internet time servers is available. For the best + * accuracy, the individual telephone line/modem delay needs to be + * calibrated using outside sources. + * + * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A + * toll call from a residence telephone in Newark, DE, costs between 14 + * and 27 cents, depending on time of day, and from a campus telephone + * between 3 and 4 cents, although it is not clear what carrier and time + * of day discounts apply in this case. The modem dial string will + * differ depending on local telephone configuration, etc., and is + * specified by the phone command in the configuration file. The + * argument to this command is an AT command for a Hayes compatible + * modem. + * + * The accuracy produced by this driver should be in the range of a + * millisecond or two, but may need correction due to the delay + * characteristics of the individual modem involved. For undetermined + * reasons, some modems work with the ACTS echo-delay measurement scheme + * and some don't. This driver tries to do the best it can with what it + * gets. Initial experiments with a Practical Peripherals 9600SA modem + * here in Delaware suggest an accuracy of a millisecond or two can be + * achieved without the scheme by using a fudge time1 value of 65.0 ms. + * In either case, the dispersion for a single call involving ten + * samples is about 1.3 ms. + * + * The driver can operate in either of three modes, as determined by + * the mode parameter in the server configuration command. In mode 0 + * (automatic) the driver operates continuously at intervals depending + * on the prediction error, as measured by the driver, usually in the + * order of several hours. In mode 1 (backup) the driver is enabled in + * automatic mode only when no other source of synchronization is + * available and when more than MAXOUTAGE (3600 s) have elapsed since + * last synchronized by other sources. In mode 2 (manual) the driver + * operates only when enabled using a fudge flags switch, as described + * below. + * + * For reliable call management, this driver requires a 1200-bps modem + * with a Hayes-compatible command set and control over the modem data + * terminal ready (DTR) control line. Present restrictions require the + * use of a POSIX-compatible programming interface, although other + * interfaces may work as well. The modem setup string is hard-coded in + * the driver and may require changes for nonstandard modems or special + * circumstances. + * + * Further information can be found in the README.refclock file in the + * ntp - Version 3 distribution. + * + * Fudge Factors + * + * Ordinarily, the propagation time correction is computed automatically + * by ACTS and the driver. When this is not possible or erratic due to + * individual modem characteristics, the fudge flag2 switch should be + * set to disable the ACTS echo-delay scheme. In any case, the fudge + * time1 parameter can be used to adjust the propagation delay as + * required. + * + * The ACTS call interval is determined in one of three ways. In manual + * mode a call is initiated by setting fudge flag1 using ntpdc, either + * manually or via a cron job. In AUTO mode this flag is set by the peer + * timer, which is controlled by the sys_poll variable in response to + * measured errors. In backup mode the driver is ordinarily asleep, but + * awakes (in auto mode) if all other synchronization sources are lost. + * In either auto or backup modes, the call interval increases as long + * as the measured errors do not exceed the value of the fudge time2 + * parameter. + * + * When the fudge flag1 is set, the ACTS calling program is activated. + * This program dials each number listed in the phones command of the + * configuration file in turn. If a call attempt fails, the next number + * in the list is dialed. The fudge flag1 and counter are 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 using + * ntpdc. + * + * In automatic and backup modes, the driver determines the call + * interval using a procedure depending on the measured prediction + * error and the fudge time2 parameter. If the error exceeds time2 for a + * number of times depending on the current interval, the interval is + * decreased, but not less than about 1000 s. If the error is less than + * time2 for some number of times, the interval is increased, but not + * more than about 18 h. With the default value of zero for fudge time2, + * the interval will increase from 1000 s to the 4000-8000-s range, in + * which the expected accuracy should be in the 1-2-ms range. Setting + * fudge time2 to a large value, like 0.1 s, may result in errors of + * that order, but increase the call interval to the maximum. The exact + * value for each configuration will depend on the modem and operating + * system involved, so some experimentation may be necessary. + */ + +/* + * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS) + * (reformatted from ACTS on-line computer help information) + * + * The following is transmitted (at 1200 baud) following completion of + * the telephone connection. + * + * 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) * + * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) * + * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) * + * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) * + * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) # + * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) # + * etc..etc...etc....... + * + * UTC = Universal Time Coordinated, the official world time referred to + * the zero meridian. + * + * DST Daylight savings time characters, valid for the continental + * U.S., are set as follows: + * + * 00 We are on standard time (ST). + * 01-49 Now on DST, go to ST when your local time is 2:00 am and + * the count is 01. The count is decremented daily at 00 + * (UTC). + * 50 We are on DST. + * 51-99 Now on ST, go to DST when your local time is 2:00 am and + * the count is 51. The count is decremented daily at 00 + * (UTC). + * + * The two DST characters provide up to 48 days advance notice of a + * change in time. The count remains at 00 or 50 at other times. + * + * LS Leap second flag is set to "1" to indicate that a leap second is + * to be added as 23:59:60 (UTC) on the last day of the current UTC + * month. The LS flag will be reset to "0" starting with 23:59:60 + * (UTC). The flag will remain on for the entire month before the + * second is added. Leap seconds are added as needed at the end of + * any month. Usually June and/or December are chosen. + * + * The leap second flag will be set to a "2" to indicate that a + * leap second is to be deleted at 23:59:58--00:00:00 on the last + * day of the current month. (This latter provision is included per + * international recommendation, however it is not likely to be + * required in the near future.) + * + * DUT1 Approximate difference between earth rotation time (UT1) and + * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC. + * + * MJD Modified Julian Date, often used to tag certain scientific data. + * + * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity. + * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud + * the MJD and DUT1 values are deleted and the time is transmitted only + * on even seconds. + * + * Maximum on line time will be 56 seconds. If all lines are busy at any + * time, the oldest call will be terminated if it has been on line more + * than 28 seconds, otherwise, the call that first reaches 28 seconds + * will be terminated. + * + * Current time is valid at the "on-time" marker (OTM), either "*" or + * "#". The nominal on-time marker (*) will be transmitted 45 ms early + * to account for the 8 ms required to send 1 character at 1200 Baud, + * plus an additional 7 ms for delay from NIST to the user, and + * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems. + * If the caller echoes all characters, NIST will measure the round trip + * delay and advance the on-time marker so that the midpoint of the stop + * bit arrives at the user on time. The amount of msADV will reflect the + * actual required advance in milliseconds and the OTM will be a "#". + * + * (The NIST system requires 4 or 5 consecutive delay measurements which + * are consistent before switching from "*" to "#". If the user has a + * 1200 Baud modem with the same internal delay as that used by NIST, + * then the "#" OTM should arrive at the user within +-2 ms of the + * correct time. + * + * However, NIST has studied different brands of 1200 Baud modems and + * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM + * of +-10 ms. For many computer users, +-10 ms accuracy should be more + * than adequate since many computer internal clocks can only be set + * with granularity of 20 to 50 ms. In any case, the repeatability of + * the offset for the "#" OTM should be within +-2 ms, if the dial-up + * path is reciprocal and the user doesn't change the brand or model of + * modem used. + * + * This should be true even if the dial-up path on one day is a land- + * line of less than 40 ms (one way) and on the next day is a satellite + * link of 260 to 300 ms. In the rare event that the path is one way by + * satellite and the other way by land line with a round trip + * measurement in the range of 90 to 260 ms, the OTM will remain a "*" + * indicating 45 ms advance. + * + * For user comments write: + * NIST-ACTS + * Time and Frequency Division + * Mail Stop 847 + * 325 Broadway + * Boulder, CO 80303 + * + * Software for setting (PC)DOS compatable machines is available on a + * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference + * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301) + * 975-6776 + * + * PTB timecode service (+49 531 512038) + * The Physikalisch-Technische Bundesanstalt (Germany) + * also supports a modem time service + * as the data formats are very similar this driver can also be compiled for + * utilizing the PTB time code service. + * + * Data format + * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7 + * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9 + * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 * + * A B C D EF G H IJ K L M N O P Q R S T U V W XY Z<CR><LF> + * + * A year + * B month + * C day + * D hour + * E : normally + * A for DST to ST switch first hour + * B for DST to ST switch second hour if not marked in H + * F minute + * G second + * H timezone + * I day of week + * J week of year + * K day of year + * L month for next ST/DST changes + * M day + * N hour + * O UTC year + * P UTC month + * Q UTC day + * R UTC hour + * S UTC minute + * T modified julian day (MJD) + * U DUT1 + * V direction and month if leap second + * W signal delay (assumed/measured) + * X sequence number for additional text line in Y + * Y additional text + * Z on time marker (* - assumed delay / # measured delay) + * <CR>!<LF> ! is second change ! + * + * This format is also used by the National Physical Laboratory (NPL)'s + * TRUETIME service in the UK. In this case the timezone field is + * UTC+0 or UTC+1 for standard and daylight saving time. The phone + * number for this service (a premium rate number) is 0891 516 333. + * It is not clear whether the echo check is implemented. + * + * For more detail, see http://www.npl.co.uk/npl/cetm/taf/truetime.html. + */ + +/* + * Interface definitions + */ +#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#ifdef CLOCK_ACTS +# define REFID "ACTS" /* reference ID */ +# define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */ +# define LENCODE 50 /* length of valid timecode string */ +# define DEVICE "/dev/acts%d" /* device name and unit */ +# define REF_ENTRY refclock_acts +#else /* not CLOCK_ACTS */ +# define REFID "TPTB" /* reference ID */ +# define DESCRIPTION "PTB Automated Computer Time Service" +# define LENCODE 78 /* length of valid timecode string */ +# define DEVICE "/dev/ptb%d" /* device name and unit */ +# define REF_ENTRY refclock_ptb +#endif /* not CLOCK_ACTS */ +#define MODE_AUTO 0 /* automatic mode */ +#define MODE_BACKUP 1 /* backup mode */ +#define MODE_MANUAL 2 /* manual mode */ + +#define MSGCNT 10 /* we need this many ACTS messages */ +#define SMAX 80 /* max token string length */ +#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */ +#define ACTS_MAXPOLL 18 /* log2 max poll interval (16384 s) */ +#define MAXOUTAGE 3600 /* max before ACTS kicks in (s) */ + +/* + * Modem control strings. These may have to be changed for some modems. + * + * AT command prefix + * B1 initiate call negotiation using Bell 212A + * &C1 enable 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 untill carrier detect + * Q0 return result codes + * V1 return result codes as English words + */ +#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */ +#define MODEM_HANGUP "ATH" /* modem disconnect */ + +/* + * Timeouts + */ +#define IDLE 60 /* idle timeout (s) */ +#define WAIT 2 /* wait timeout (s) */ +#define ANSWER 30 /* answer timeout (s) */ +#define CONNECT 10 /* connect timeout (s) */ +#define TIMECODE 15 /* timecode timeout (s) */ + +/* + * 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 actsunit { + int pollcnt; /* poll message counter */ + int state; /* the first one was Delaware */ + int run; /* call program run switch */ + int msgcnt; /* count of ACTS messages received */ + long redial; /* interval to next automatic call */ + double msADV; /* millisecond advance of last message */ +}; + +/* + * 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_poll P((int, struct peer *)); +static void acts_timeout P((struct peer *)); +static void acts_disc P((struct peer *)); +static int acts_write P((struct peer *, const char *)); + +/* + * Transfer vector (conditional structure name) + */ +struct refclock REF_ENTRY = { + acts_start, /* start up driver */ + acts_shutdown, /* shut down driver */ + acts_poll, /* transmit poll message */ + noentry, /* not used (old acts_control) */ + noentry, /* not used (old acts_init) */ + noentry, /* not used (old acts_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * acts_start - open the devices and initialize data for processing + */ + +static int +acts_start ( + int unit, + struct peer *peer + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + int fd; + char device[20]; + int dtr = TIOCM_DTR; + + /* + * Open serial port. Use ACTS line discipline, if available. It + * pumps a timestamp into the data stream at every on-time + * character '*' found. Note: the port must have modem control + * or deep pockets for the phone bill. HP-UX 9.03 users should + * have very deep pockets. + */ + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) + return (0); + if (ioctl(fd, TIOCMBIS, (char *)&dtr) < 0) { + msyslog(LOG_ERR, "clock %s ACTS no modem control", + ntoa(&peer->srcadr)); + return (0); + } + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct actsunit *) + emalloc(sizeof(struct actsunit)))) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct actsunit)); + pp = peer->procptr; + pp->io.clock_recv = acts_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); + peer->minpoll = ACTS_MINPOLL; + peer->maxpoll = ACTS_MAXPOLL; + peer->sstclktype = CTL_SST_TS_TELEPHONE; + + /* + * Initialize modem and kill DTR. We skedaddle if this comes + * bum. + */ + if (!acts_write(peer, MODEM_SETUP)) { + (void) close(fd); + free(up); + return (0); + } + + /* + * Set up the driver timeout + */ + peer->nextdate = current_time + WAIT; + return (1); +} + + +/* + * acts_shutdown - shut down the clock + */ +static void +acts_shutdown ( + int unit, + struct peer *peer + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + + +/* + * acts_receive - receive data from the serial interface + */ +static void +acts_receive ( + struct recvbuf *rbufp + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + struct peer *peer; + char str[SMAX]; + int i; + char hangup = '%'; /* ACTS hangup */ + int day; /* day of the month */ + int month; /* month of the year */ + u_long mjd; /* Modified Julian Day */ + double dut1; /* DUT adjustment */ + double msADV; /* ACTS transmit advance (ms) */ + char flag; /* calibration flag */ +#ifndef CLOCK_PTBACTS + char utc[10]; /* this is NIST and you're not */ + u_int dst; /* daylight/standard time indicator */ + u_int leap; /* leap-second indicator */ +#else + char leapdir; /* leap direction */ + u_int leapmonth; /* month of leap */ +#endif + /* + * Initialize pointers and read the timecode and timestamp. If + * the OK modem status code, leave it where folks can find it. + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, + &pp->lastrec); + if (pp->lencode == 0) { + if (strcmp(pp->a_lastcode, "OK") == 0) + pp->lencode = 2; + return; + } +#ifdef DEBUG + if (debug) + printf("acts: state %d timecode %d %*s\n", up->state, + pp->lencode, pp->lencode, pp->a_lastcode); +#endif + + switch (up->state) { + + case 0: + + /* + * State 0. We are not expecting anything. Probably + * modem disconnect noise. Go back to sleep. + */ + return; + + case 1: + + /* + * State 1. We are waiting for the call to be answered. + * All we care about here is CONNECT as the first token + * in the string. If the modem signals BUSY, ERROR, NO + * ANSWER, NO CARRIER or NO DIALTONE, we immediately + * hang up the phone. If CONNECT doesn't happen after + * ANSWER seconds, hang up the phone. If everything is + * okay, start the connect timeout and slide into state + * 2. + */ + if( strcmp(pp->a_lastcode, " ") == 0) { + acts_disc(peer); + return; + } + if( strcmp(sys_phone[0],"DIRECT") != 0 ) { + (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX); + if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") == + 0 || strcmp(str, "NO") == 0) { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s ACTS modem status %s", + ntoa(&peer->srcadr), pp->a_lastcode); + acts_disc(peer); + } else if (strcmp(str, "CONNECT") == 0) { + peer->nextdate = current_time + CONNECT; + up->msgcnt = 0; + up->state++; + } + } else { + (void) strncpy(str,"CONNECT",7); + peer->nextdate = current_time + CONNECT; + up->msgcnt = 0; + up->state++; + } + return; + + case 2: + + /* + * State 2. The call has been answered and we are + * waiting for the first ACTS message. If this doesn't + * happen within the timecode timeout, hang up the + * phone. We probably got a wrong number or ACTS is + * down. + */ + peer->nextdate = current_time + TIMECODE; + up->state++; + } + + /* + * Real yucky things here. Ignore everything except timecode + * messages, as determined by the message length. We told the + * terminal routines to end the line with '*' and the line + * discipline to strike a timestamp on that character. However, + * when the ACTS echo-delay scheme works, the '*' eventually + * becomes a '#'. In this case the message is ended by the <CR> + * that comes about 200 ms after the '#' and the '#' cannot be + * echoed at the proper time. But, this may not be a lose, since + * we already have good data from prior messages and only need + * the millisecond advance calculated by ACTS. So, if the + * message is long enough and has an on-time character at the + * right place, we consider the message (but not neccesarily the + * timestmap) to be valid. + */ + if (pp->lencode != LENCODE) + return; + +#ifndef CLOCK_PTBACTS + /* + * We apparently have a valid timecode message, so dismember it + * with sscan(). This routine does a good job in spotting syntax + * errors without becoming overly pedantic. + * + * D L D + * MJD YR MO DA H M S ST S UT1 msADV OTM + * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) * + */ + if (sscanf(pp->a_lastcode, + "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %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; + } +#else + /* + * Data format + * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7 + * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9 + * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 * + */ + if (sscanf(pp->a_lastcode, + "%*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; + } +#endif + /* + * Some modems can't be trusted (the Practical Peripherals + * 9600SA comes to mind) and, even if they manage to unstick + * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2 + * to disable echoes, if neccessary. + */ + if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag & + CLK_FLAG2)) + (void)write(pp->io.fd, &flag, 1); + + /* + * Yes, I know this code incorrectly thinks that 2000 is a leap + * year. The ACTS timecode format croaks then anyway. Life is + * short. Would only the timecode mavens resist the urge to + * express months of the year and days of the month in favor of + * days of the year. + * NOTE: year 2000 IS a leap year!!! ghealton Y2KFixes + */ + if (month < 1 || month > 12 || day < 1) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + if ( pp->year <= YEAR_PIVOT ) pp->year += 100; /* Y2KFixes */ + if ( !isleap_tm(pp->year) ) { /* Y2KFixes */ + 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; + +#ifndef CLOCK_PTBACTS + if (leap == 1) + pp->leap = LEAP_ADDSECOND; + else if (pp->leap == 2) + pp->leap = LEAP_DELSECOND; +#else + if (leapmonth == month) { + if (leapdir == '+') + pp->leap = LEAP_ADDSECOND; + else if (leapdir == '-') + pp->leap = LEAP_DELSECOND; + } +#endif + + /* + * Colossal hack here. We process each sample in a trimmed-mean + * filter and determine the reference clock offset and + * dispersion. The fudge time1 value is added to each sample as + * received. If we collect MSGCNT samples before the '#' on-time + * character, we use the results of the filter as is. If the '#' + * is found before that, the adjusted msADV is used to correct + * the propagation delay. + */ + up->msgcnt++; + if (flag == '#') { + pp->offset += (msADV - up->msADV) * 1000 * 1e-6; + } else { + up->msADV = msADV; + if (!refclock_process(pp)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } else if (up->msgcnt < MSGCNT) + return; + } + + /* + * We have a filtered sample offset ready for peer processing. + * 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. Finaly, we unhook the + * timeout, arm for the next call, fold the tent and go home. + * The little dance with the '%' character is an undocumented + * ACTS feature that hangs up the phone real quick without + * waiting for carrier loss or long-space disconnect, but we do + * these clumsy things anyway. + */ + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + pp->sloppyclockflag &= ~CLK_FLAG1; + up->pollcnt = 0; + (void)write(pp->io.fd, &hangup, 1); + up->state = 0; + acts_disc(peer); +} + + +/* + * acts_poll - called by the transmit routine + */ +static void +acts_poll ( + int unit, + struct peer *peer + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + + /* + * If the driver is running, we set the enable flag (fudge + * flag1), which causes the driver timeout routine to initiate a + * call to ACTS. If not, the enable flag can be set using + * ntpdc. If this is the sustem peer, then follow the system + * poll interval. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + + if (up->run) { + pp->sloppyclockflag |= CLK_FLAG1; + if (peer == sys_peer) + peer->hpoll = sys_poll; + else + peer->hpoll = peer->minpoll; + } + acts_timeout (peer); + return; +} + + +/* + * acts_timeout - called by the timer interrupt + */ +static void +acts_timeout ( + struct peer *peer + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + int dtr = TIOCM_DTR; + + /* + * If a timeout occurs in other than state 0, the call has + * failed. If in state 0, we just see if there is other work to + * do. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + if (up->state) { + acts_disc(peer); + return; + } + switch (peer->ttl) { + + /* + * In manual mode the ACTS calling program is activated + * by the ntpdc program using the enable flag (fudge + * flag1), either manually or by a cron job. + */ + case MODE_MANUAL: + up->run = 0; + break; + + /* + * In automatic mode the ACTS calling program runs + * continuously at intervals determined by the sys_poll + * variable. + */ + case MODE_AUTO: + if (!up->run) + pp->sloppyclockflag |= CLK_FLAG1; + up->run = 1; + break; + + /* + * In backup mode the ACTS calling program is disabled, + * unless no system peer has been selected for MAXOUTAGE + * (3600 s). Once enabled, it runs until some other NTP + * peer shows up. + */ + case MODE_BACKUP: + if (!up->run && sys_peer == 0) { + if (current_time - last_time > MAXOUTAGE) { + up->run = 1; + peer->hpoll = peer->minpoll; + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s ACTS backup started ", + ntoa(&peer->srcadr)); + } + } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) { + peer->hpoll = peer->minpoll; + up->run = 0; + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s ACTS backup stopped", + ntoa(&peer->srcadr)); + } + break; + + default: + msyslog(LOG_ERR, + "clock %s ACTS invalid mode", ntoa(&peer->srcadr)); + } + + /* + * The fudge flag1 is used as an enable/disable; if set either + * by the code or via ntpdc, the ACTS calling program is + * started; if reset, the phones stop ringing. + */ + if (!(pp->sloppyclockflag & CLK_FLAG1)) { + up->pollcnt = 0; + peer->nextdate = current_time + IDLE; + return; + } + + /* + * Initiate a call to the ACTS service. If we wind up here in + * other than state 0, a successful call could not be completed + * within minpoll seconds. We advance to the next modem dial + * string. If none are left, we log a notice and clear the + * enable flag. For future enhancement: call the site RP and + * leave an obscene message in his voicemail. + */ + if (sys_phone[up->pollcnt][0] == '\0') { + refclock_report(peer, CEVNT_TIMEOUT); + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s ACTS calling program terminated", + ntoa(&peer->srcadr)); + pp->sloppyclockflag &= ~CLK_FLAG1; +#ifdef DEBUG + if (debug) + printf("acts: calling program terminated\n"); +#endif + up->pollcnt = 0; + peer->nextdate = current_time + IDLE; + return; + } + + /* + * Raise DTR, call ACTS and start the answer timeout. We think + * it strange if the OK status has not been received from the + * modem, but plow ahead anyway. + */ + if (strcmp(pp->a_lastcode, "OK") != 0) + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "clock %s ACTS no modem status", + ntoa(&peer->srcadr)); + (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); + (void)acts_write(peer, sys_phone[up->pollcnt]); + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "clock %s ACTS calling %s\n", + ntoa(&peer->srcadr), sys_phone[up->pollcnt]); + up->state = 1; + up->pollcnt++; + pp->polls++; + peer->nextdate = current_time + ANSWER; + return; +} + + +/* + * acts_disc - disconnect the call and wait for the ruckus to cool + */ +static void +acts_disc ( + struct peer *peer + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + int dtr = TIOCM_DTR; + + /* + * We should never get here other than in state 0, unless a call + * has timed out. We drop DTR, which will reliably get the modem + * off the air, even while ACTS is hammering away full tilt. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); + if (up->state > 0) { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "clock %s ACTS call failed %d", + ntoa(&peer->srcadr), up->state); +#ifdef DEBUG + if (debug) + printf("acts: call failed %d\n", up->state); +#endif + up->state = 0; + } + peer->nextdate = current_time + WAIT; +} + + +/* + * acts_write - write a message to the serial port + */ +static int +acts_write ( + struct peer *peer, + const char *str + ) +{ + register struct actsunit *up; + struct refclockproc *pp; + int len; + int code; + char cr = '\r'; + + /* + * Not much to do here, other than send the message, handle + * debug and report faults. + */ + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + len = strlen(str); +#ifdef DEBUG + if (debug) + printf("acts: state %d send %d %s\n", up->state, len, + str); +#endif + code = write(pp->io.fd, str, (unsigned)len) == len; + code &= write(pp->io.fd, &cr, 1) == 1; + if (!code) + refclock_report(peer, CEVNT_FAULT); + return (code); +} + +#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..045a93a --- /dev/null +++ b/contrib/ntp/ntpd/refclock_arbiter.c @@ -0,0 +1,429 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.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) and elevation (LH) (meters) 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. + */ + +/* + * 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 30 /* max length of status string */ +#define MAXPOS 70 /* 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; /* synchronization indicator */ + + /* + * 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, 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. 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 commant. + */ + if (!strncmp(pp->a_lastcode, "TQ", 2)) { + up->qualchar = pp->a_lastcode[2]; + write(pp->io.fd, "SR", 2); + } else if (!strncmp(pp->a_lastcode, "SR", 2)) { + strcpy(up->status, pp->a_lastcode + 2); + if (pp->sloppyclockflag & CLK_FLAG4) + write(pp->io.fd, "LA", 2); + else { + write(pp->io.fd, "B5", 2); + up->tcswitch++; + } + } else if (!strncmp(pp->a_lastcode, "LA", 2)) { + strcpy(up->latlon, pp->a_lastcode + 2); + write(pp->io.fd, "LO", 2); + } else if (!strncmp(pp->a_lastcode, "LO", 2)) { + strcat(up->latlon, " "); + strcat(up->latlon, pp->a_lastcode + 2); + write(pp->io.fd, "LH", 2); + } else if (!strncmp(pp->a_lastcode, "LH", 2)) { + strcat(up->latlon, " "); + strcat(up->latlon, pp->a_lastcode + 2); + write(pp->io.fd, "DB", 2); + } else if (!strncmp(pp->a_lastcode, "DB", 2)) { + strcat(up->latlon, " "); + strcat(up->latlon, pp->a_lastcode + 2); + record_clock_stats(&peer->srcadr, up->latlon); + write(pp->io.fd, "B5", 2); + up->tcswitch++; + } + return; + } + pp->lencode = temp; + + /* + * 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. + */ + if (pp->lencode == LENARB) { + /* + * Timecode format B5: "i yy ddd hh:mm:ss.000 " + */ + pp->a_lastcode[LENARB - 2] = up->qualchar; + strcat(pp->a_lastcode, up->status); + 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; + } + } else { + write(pp->io.fd, "B0", 2); + return; + } + up->tcswitch++; + + /* + * We decode the clock dispersion from the time quality + * character. + */ + switch (up->qualchar) { + + case '0': /* locked, max accuracy */ + pp->disp = 1e-7; + 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; +#ifdef DEBUG + if (debug) + printf("arbiter: timecode %d %s\n", pp->lencode, + pp->a_lastcode); +#endif + if (up->tcswitch >= NSTAGE) + write(pp->io.fd, "B0", 2); + + /* + * Process the new sample in the median filter and determine the + * timecode timestamp. + */ + if (!refclock_process(pp)) + refclock_report(peer, CEVNT_BADTIME); +} + + +/* + * 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. If nothing is + * heard from the clock for two polls, declare a timeout and + * keep going. + */ + pp = peer->procptr; + up = (struct arbunit *)pp->unitptr; + up->tcswitch = 0; + if (write(pp->io.fd, "TQ", 2) != 2) { + refclock_report(peer, CEVNT_FAULT); + } else + pp->polls++; + if (pp->coderecv == pp->codeproc) { + refclock_report(peer, CEVNT_TIMEOUT); + return; + } + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); +} + +#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..1db8a9a --- /dev/null +++ b/contrib/ntp/ntpd/refclock_arc.c @@ -0,0 +1,1323 @@ +/* + * refclock_arc - clock driver for ARCRON MSF receivers + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF) +static const char arc_version[] = { "V1.1 1997/06/23" }; + +#undef ARCRON_DEBUG /* Define only while in development... */ + +#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. + +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. + +------------------------------------------------------------------------------- + +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 <stdio.h> +#include <ctype.h> +#include <sys/time.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 + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the ARCRON MSF 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 DESCRIPTION "ARCRON MSF Receiver" + +#define NSAMPLESLONG 8 /* Stages of long filter. */ + +#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 + }; + +/* Chose filter length dependent on fudge flag 4. */ +#define CHOSENSAMPLES(pp) \ +(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES) + /* +Chose how many filter samples to keep. Several factors are in play. + + 1) Discard at least one sample to allow a spike value to be + discarded. + + 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes. + + 3) Keep an odd number of samples to avoid median value being biased + high or low. +*/ +#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1) + +#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 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 { \ + if((up)->ev.next != 0) { break; } /* WHOOPS! */ \ + peer->nextdate = current_time + QUEUETICK; \ + } while(0) + +#if 0 +/* Placeholder event handler---does nothing safely---soaks up lose tick. */ +static void +dummy_event_handler( + struct peer *peer + ) +{ +#ifdef ARCRON_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 ARCRON_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 ARCRON_DEBUG + else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } +#endif + } +} +#endif /* 0 */ + +/* + * 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 ARCRON_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 ARCRON_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 ARCRON_DEBUG + if(debug) + { printf("Opened RS232 port with file descriptor %d.\n", fd); } +#endif + +#ifdef HAVE_TERMIOS + + 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; + memcpy((char *)&pp->refid, REFID, 4); + /* 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 + return(1); +} + + +/* + * arc_shutdown - shut down the clock + */ +static void +arc_shutdown( + int unit, + struct peer *peer + ) +{ + register struct arcunit *up; + struct refclockproc *pp; + + 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 ARCRON_DEBUG + if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } +#endif + if(spaceleft < sl) { /* Should not normally happen... */ +#ifdef ARCRON_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) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; } + + 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, bst, status; + int arc_last_offset; + + /* + * 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 ARCRON_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(×tamp, charoffsets[arc_last_offset]); +#ifdef ARCRON_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), ×tamp)) +#endif + { +#ifdef ARCRON_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, ×tamp); + 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 ARCRON_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 ARCRON_DEBUG + if(debug > 1) { printf("arc: clearing timestamp.\n"); } +#endif + } + } + + /* 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 ARCRON_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. */ + up->quality = (q & 0xf); +#ifdef DEBUG + if(debug) { printf("arc: signal quality %d.\n", up->quality); } +#endif + } else if( /* (r == '2') && */ up->resyncing) { +#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. */ + +#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 ARCRON_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 ARCRON_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(). */ + record_clock_stats(&peer->srcadr, pp->a_lastcode); + + /* We don't use the micro-/milli- second part... */ + pp->usec = 0; + pp->msec = 0; + + n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d", + &pp->hour, &pp->minute, &pp->second, + &wday, &pp->day, &month, &pp->year, &bst, &status); + + /* Validate format and numbers. */ + if(n != 9) { +#ifdef ARCRON_DEBUG + /* Would expect to have caught major problems already... */ + if(debug) { printf("arc: badly formatted data.\n"); } +#endif + refclock_report(peer, CEVNT_BADREPLY); + return; + } + /* + 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. */ + refclock_report(peer, CEVNT_BADREPLY); + return; + } + /* Check that BST/UTC bits are the complement of one another. */ + if(!(bst & 2) == !(bst & 4)) { + 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, bst, 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; + refclock_report(peer, CEVNT_FAULT); + return; + } + } + up->status = status; + + 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) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + } + } + + /* If clock signal quality is unknown, revert to default PRECISION...*/ + if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; } + /* ...else improve precision if flag3 is set... */ + 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 ARCRON_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: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp)); + printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp)); + 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)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + 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 ARCRON_DEBUG + msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit); +#endif + 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; + pp->lencode = 0; + memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); + +#if 0 + /* Flush input. */ + tcflush(pp->io.fd, TCIFLUSH); +#endif + + /* Resync if our next scheduled resync time is here or has passed. */ + resync_needed = (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 + 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..2c10440 --- /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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.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.%3d", &pp->year, + &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->msec) + != 6) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * 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..63e17c1 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_atom.c @@ -0,0 +1,488 @@ +/* + * refclock_atom - clock driver for 1-pps signals + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_ATOM) + +#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_SYS_TIME_H +# include <sys/time.h> +#endif +#ifdef HAVE_SYS_TERMIOS_H +# include <sys/termios.h> +#endif +#ifdef HAVE_SYS_PPSCLOCK_H +# include <sys/ppsclock.h> +#endif +#ifdef HAVE_PPSAPI +#include <sys/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. + * + * In order for this driver to work, the local clock must be set to + * within +-500 ms by another means, such as a radio clock or NTP + * itself. The 1-pps signal is connected via a serial port and gadget + * box consisting of a one-shot flopflop and RS232 level converter. + * Conntection is either via the carrier detect (DCD) lead or via the + * receive data (RD) lead. The incidental jitter using the DCD lead is + * essentially the interrupt latency. The incidental jitter using the RD + * lead has an additional component due to the line sampling clock. When + * operated at 38.4 kbps, this arrangement has a worst-case jitter less + * than 26 us. + * + * There are four ways in which this driver can be used. They are + * described in decreasing order of merit below. The first way uses the + * ppsapi STREAMS module and the LDISC_PPS line discipline, while the + * second way uses the ppsclock STREAMS module and the LDISC_PPS line + * discipline. Either of these works only for the baseboard serial ports + * of the Sun SPARC IPC and clones. However, the ppsapi uses the + * proposed IETF interface expected to become standard for PPS signals. + * The serial port to be used is specified by the pps command in the + * configuration file. This driver reads the timestamp directly by a + * designated ioctl() system call. + * + * The third way uses the LDISC_CLKPPS line discipline and works for + * any architecture supporting a serial port. If after a few seconds + * this driver finds no ppsclock module configured, it attempts to open + * a serial port device /dev/pps%d, where %d is the unit number, and + * assign the LDISC_CLKPPS line discipline to it. If the line discipline + * fails, no harm is done except the accuracy is reduced somewhat. The + * pulse generator in the gadget box is adjusted to produce a start bit + * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line + * discipline, this produces an ASCII DEL character ('\377') followed by + * a timestamp at each seconds epoch. + * + * The fourth way involves an auxiliary radio clock driver which calls + * the PPS driver with a timestamp captured by that driver. This use is + * documented in the source code for the driver(s) involved. Note that + * some drivers collect the sample information themselves before calling + * pps_sample(), and others call knowing only that they are running + * shortly after an on-time tick and they expect to retrieve the PPS + * offset, fudge their result, and insert it into the timestream. + * + * Fudge Factors + * + * There are no special fudge factors other than the generic. The fudge + * time1 parameter can be used to compensate for miscellaneous UART and + * OS delays. Allow about 247 us for uart delays at 38400 bps and about + * 1 ms for STREAMS nonsense with older workstations. Velocities may + * vary with modern workstations. + */ +/* + * Interface definitions + */ +#ifdef HAVE_PPSAPI +extern int pps_assert; +#endif /* HAVE_PPSAPI */ +#ifdef TTYCLK +#define DEVICE "/dev/pps%d" /* device name and unit */ +#ifdef B38400 +#define SPEED232 B38400 /* uart speed (38400 baud) */ +#else +#define SPEED232 EXTB /* as above */ +#endif +#endif /* TTYCLK */ + +#define PRECISION (-20) /* precision assumed (about 1 us) */ +#define REFID "PPS\0" /* reference ID */ +#define DESCRIPTION "PPS Clock Discipline" /* WRU */ + +#define FLAG_TTY 0x01 /* tty_clk heard from */ +#define FLAG_PPS 0x02 /* ppsclock heard from */ +#define FLAG_AUX 0x04 /* auxiliary PPS source */ + +static struct peer *pps_peer; /* atom driver for auxiliary PPS sources */ + +#ifdef TTYCLK +static void atom_receive P((struct recvbuf *)); +#endif /* TTYCLK */ + +/* + * Unit control structure + */ +struct atomunit { +#ifdef HAVE_PPSAPI + pps_info_t pps_info; /* pps_info control */ +#endif /* HAVE_PPSAPI */ +#ifdef PPS + struct ppsclockev ev; /* ppsclock control */ +#endif /* PPS */ + int flags; /* flags that wave */ +}; + +/* + * Function prototypes + */ +static int atom_start P((int, struct peer *)); +static void atom_shutdown P((int, struct peer *)); +static void atom_poll P((int, struct peer *)); +#if defined(PPS) || defined(HAVE_PPSAPI) +static int atom_pps P((struct peer *)); +#endif /* PPS || HAVE_PPSAPI */ + +/* + * Transfer vector + */ +struct refclock refclock_atom = { + atom_start, /* start up driver */ + atom_shutdown, /* shut down driver */ + atom_poll, /* transmit poll message */ + noentry, /* not used (old atom_control) */ + noentry, /* initialize driver */ + noentry, /* not used (old atom_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * atom_start - initialize data for processing + */ +static int +atom_start( + int unit, + struct peer *peer + ) +{ + register struct atomunit *up; + struct refclockproc *pp; + int flags; +#ifdef TTYCLK + int fd = 0; + char device[20]; + int ldisc = LDISC_CLKPPS; +#endif /* TTYCLK */ + + pps_peer = peer; + flags = 0; + +#ifdef TTYCLK +# if defined(SCO5_CLOCK) + ldisc = LDISC_RAW; /* DCD timestamps without any line discipline */ +# endif + /* + * Open serial port. Use LDISC_CLKPPS line discipline only + * if the LDISC_PPS line discipline is not availble, + */ +# if defined(PPS) || defined(HAVE_PPSAPI) + if (fdpps <= 0) +# endif + { + (void)sprintf(device, DEVICE, unit); + if ((fd = refclock_open(device, SPEED232, ldisc)) != 0) + flags |= FLAG_TTY; + } +#endif /* TTYCLK */ + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct atomunit *)emalloc(sizeof(struct atomunit)))) { +#ifdef TTYCLK + if (flags & FLAG_TTY) + (void) close(fd); +#endif /* TTYCLK */ + return (0); + } + memset((char *)up, 0, sizeof(struct atomunit)); + pp = peer->procptr; + pp->unitptr = (caddr_t)up; +#ifdef TTYCLK + if (flags & FLAG_TTY) { + pp->io.clock_recv = atom_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); + } + } +#endif /* TTYCLK */ + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->flags = flags; + return (1); +} + + +/* + * atom_shutdown - shut down the clock + */ +static void +atom_shutdown( + int unit, + struct peer *peer + ) +{ + register struct atomunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; +#ifdef TTYCLK + if (up->flags & FLAG_TTY) + io_closeclock(&pp->io); +#endif /* TTYCLK */ + if (pps_peer == peer) + pps_peer = 0; + free(up); +} + + +#if defined(PPS) || defined(HAVE_PPSAPI) +/* + * atom_pps - receive data from the LDISC_PPS discipline + */ +static int +atom_pps( + struct peer *peer + ) +{ + register struct atomunit *up; + struct refclockproc *pp; +#ifdef HAVE_PPSAPI + struct timespec timeout; +# ifdef HAVE_TIMESPEC + struct timespec ts; +# else + struct timeval ts; +# endif /* HAVE_TIMESPEC */ +#endif /* HAVE_PPSAPI */ + l_fp lftmp; + double doffset; + int i; +#if !defined(HAVE_PPSAPI) + int request = +# ifdef HAVE_CIOGETEV + CIOGETEV +# endif +# ifdef HAVE_TIOCGPPSEV + TIOCGPPSEV +# endif + ; +#endif /* HAVE_PPSAPI */ + + /* + * This routine is called once per second when the LDISC_PPS + * discipline 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. + */ + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + + /* + * 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. Round the nanoseconds + * to microseconds with great care. + */ + if (fdpps <= 0) + return (1); +#ifdef HAVE_PPSAPI + timeout.tv_sec = 0; + timeout.tv_nsec = 0; + i = up->pps_info.assert_sequence; + if (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &up->pps_info, &timeout) + < 0) + return (2); + if (i == up->pps_info.assert_sequence) + return (3); + if (pps_assert) + ts = up->pps_info.assert_timestamp; + else + ts = up->pps_info.clear_timestamp; + pp->lastrec.l_ui = ts.tv_sec + JAN_1970; + ts.tv_nsec = (ts.tv_nsec + 500) / 1000; + if (ts.tv_nsec > 1000000) { + ts.tv_nsec -= 1000000; + ts.tv_sec++; + } + TVUTOTSF(ts.tv_nsec, pp->lastrec.l_uf); +#else + i = up->ev.serial; + if (ioctl(fdpps, request, (caddr_t)&up->ev) < 0) + return (2); + if (i == up->ev.serial) + return (3); + pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970; + TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf); +#endif /* HAVE_PPSAPI */ + up->flags |= FLAG_PPS; + L_CLR(&lftmp); + L_ADDF(&lftmp, pp->lastrec.l_f); + LFPTOD(&lftmp, doffset); + SAMPLE(-doffset + pp->fudgetime1); + return (0); +} +#endif /* PPS || HAVE_PPSAPI */ + +#ifdef TTYCLK +/* + * atom_receive - receive data from the LDISC_CLK discipline + */ +static void +atom_receive( + struct recvbuf *rbufp + ) +{ + register struct atomunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp lftmp; + double doffset; + + /* + * This routine is called once per second when the serial + * interface is in use. It snatches the timestamp from the + * buffer and saves the sign-extended fraction in a circular + * buffer for processing at the next poll event. + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, + &pp->lastrec); + + /* + * Save the timestamp for billboards. Sign-extend the fraction + * and stash in the buffer. No harm is done if previous data are + * overwritten. Do this only if the ppsclock gizmo is not + * working. + */ + if (up->flags & FLAG_PPS) + return; + L_CLR(&lftmp); + L_ADDF(&lftmp, pp->lastrec.l_f); + LFPTOD(&lftmp, doffset); + SAMPLE(-doffset + pp->fudgetime1); +} +#endif /* TTYCLK */ + +/* + * pps_sample - receive PPS data from some other clock driver + */ +int +pps_sample( + l_fp *offset + ) +{ + register struct peer *peer; + register struct atomunit *up; + struct refclockproc *pp; + l_fp lftmp; + double doffset; + + /* + * 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. + */ + peer = pps_peer; + if (peer == 0) /* nobody home */ + return 1; + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + + /* + * 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. + */ + up->flags |= FLAG_AUX; + 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, + struct peer *peer + ) +{ +#if defined(PPS) || defined(HAVE_PPSAPI) + register struct atomunit *up; +#endif /* PPS || HAVE_PPSAPI */ + struct refclockproc *pp; + + /* + * Accumulate samples in the median filter. At the end of each + * poll interval, do a little bookeeping and process the + * samples. + */ + pp = peer->procptr; +#if defined(PPS) || defined(HAVE_PPSAPI) + up = (struct atomunit *)pp->unitptr; + if (!(up->flags & !(FLAG_AUX | FLAG_TTY))) { + int err; + + err = atom_pps(peer); + if (err > 0) { + refclock_report(peer, CEVNT_FAULT); + return; + } + } +#endif /* PPS || HAVE_PPSAPI */ + pp->polls++; + if (peer->burst > 0) + return; + if (pp->coderecv == pp->codeproc) { + refclock_report(peer, CEVNT_TIMEOUT); + return; + } + + /* + * Valid time (leap bits zero) is returned only if the prefer + * peer has survived the intersection algorithm and within + * clock_max 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. + */ + if (pps_update) { + pp->leap = LEAP_NOWARNING; + } else { + pp->leap = LEAP_NOTINSYNC; + return; + } + pp->variance = 0; + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + peer->burst = MAXSTAGE; +} + +#else +int refclock_atom_bs; +#endif /* REFCLOCK */ diff --git a/contrib/ntp/ntpd/refclock_bancomm.c b/contrib/ntp/ntpd/refclock_bancomm.c new file mode 100644 index 0000000..95c211b --- /dev/null +++ b/contrib/ntp/ntpd/refclock_bancomm.c @@ -0,0 +1,483 @@ +/* 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 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_BANC) +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.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 )) +#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; + unsigned long frac; + unsigned short status; +}; + +/* END OF STUFF FROM RES */ + +/* + * Definitions + */ +#define MAXUNITS 2 /* max number of VME units */ + +/* + * 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 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 */ +}; + +/* + * Keep the fudge factors separately so they can be set even + * when no clock is configured. + */ +static double fudgefactor[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char sloppyclockflag[MAXUNITS]; + +/* + * Function prototypes + */ +static void vme_init (void); +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 *); + +/* + * Transfer vector + */ +struct refclock refclock_bancomm = { + vme_start, + vme_shutdown, + vme_poll, + noentry, /* not used (old vme_control) */ + vme_init, + noentry, /* not used (old vme_buginfo) */ + NOFLAGS +}; + +int fd_vme; /* file descriptor for ioctls */ +int regvalue; + +/* + * vme_init - initialize internal vme driver data + */ +static void +vme_init(void) +{ + register int i; + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor[i] = 0.0; + stratumtouse[i] = 0; + sloppyclockflag[i] = 0; + } +} + +/* + * 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]; + + /* + * Check configuration info. + */ + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); + return (0); + } + + /* + * 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 { /* Release capture lockout in case it was set from 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, ®value ) ) + msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); + } + + /* + * 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. + */ + pp->leap = LEAP_NOWARNING; + peer->precision = VMEPRECISION; + peer->stratum = stratumtouse[unit]; + memcpy( (char *)&peer->refid, USNOREFID,4); + + peer->refid = htonl(VMEHSREFID); + + 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; + + pp = peer->procptr; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + vme = (struct vmeunit *)pp->unitptr; + io_closeclock(&pp->io); + pp->unitptr = NULL; + free(vme); +} + + +/* + * 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->usec = tptr->frac; + +#ifdef DEBUG + if (debug) + printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", + pp->day, pp->hour, pp->minute, pp->second, + pp->usec, 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; + } + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); +} + +struct vmedate * +get_datumtime(struct vmedate *time_vme) +{ + unsigned short status; + char cbuf[7]; + struct btfp_time vts; + + if ( time_vme == (struct vmedate *)NULL) { + time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); + } + + if( ioctl(fd_vme, READTIME, &vts)) + msyslog(LOG_ERR, "get_datumtime error: %m"); + + /* if you want to actually check the validity of these registers, do a + define of CHECK above this. I didn't find it necessary. - RES + */ + +#ifdef CHECK + + /* Get day */ + sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); + + if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) + time_vme->day = (unsigned short)atoi(cbuf); + else + time_vme->day = (unsigned short) 0; + + /* Get hour */ + sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); + + if (isdigit(cbuf[0]) && isdigit(cbuf[1])) + time_vme->hr = (unsigned short)atoi(cbuf); + else + time_vme->hr = (unsigned short) 0; + + /* Get minutes */ + sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); + if (isdigit(cbuf[0]) && isdigit(cbuf[1])) + time_vme->mn = (unsigned short)atoi(cbuf); + else + time_vme->mn = (unsigned short) 0; + + /* Get seconds */ + sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); + + if (isdigit(cbuf[0]) && isdigit(cbuf[1])) + time_vme->sec = (unsigned short)atoi(cbuf); + else + time_vme->sec = (unsigned short) 0; + + /* 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); + + if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) + && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5])) + time_vme->frac = (u_long) atoi(cbuf); + else + time_vme->frac = (u_long) 0; +#else + + /* DONT CHECK just trust the card */ + + /* 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); + +#endif /* CHECK */ + + /* Get status bit */ + status = (vts.btfp_time[0] & 0x0010) >>4; + time_vme->status = status; /* Status=0 if locked to ref. */ + /* Status=1 if flywheeling */ + if (status) { /* lost lock ? */ + return ((void *)NULL); + } + else + return (time_vme); +} + +#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..2982ecc --- /dev/null +++ b/contrib/ntp/ntpd/refclock_chronolog.c @@ -0,0 +1,343 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.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. + */ + pp->msec = 0; + 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; + } + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + 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..0b2ae78 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_chu.c @@ -0,0 +1,1464 @@ +/* + * refclock_chu - clock driver for Canadian radio CHU receivers + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_CHU) + +/* #define AUDIO_CHUa */ + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> +#include <math.h> + +#ifdef AUDIO_CHU +#ifdef HAVE_SYS_AUDIOIO_H +#include <sys/audioio.h> +#endif /* HAVE_SYS_AUDIOIO_H */ +#ifdef HAVE_SUN_AUDIOIO_H +#include <sun/audioio.h> +#endif /* HAVE_SUN_AUDIOIO_H */ +#endif /* AUDIO_CHU */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.h" + +/* + * Clock driver for Canadian radio CHU receivers + * + * 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 the minimuf and icom programs as + * propagation conditions change throughout the day and night. + * + * 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 -d -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. When the modem driver + * is compiled, fudge flag3 enables the ppsclock line discipline. 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 speaker volume must be set before the driver is started. + */ + +/* + * Interface definitions + */ +#define SPEED232 B300 /* uart speed (300 baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "CHU" /* reference ID */ +#ifdef AUDIO_CHU +#define DESCRIPTION "CHU Modem Receiver" /* WRU */ + +/* + * Audio demodulator definitions + */ +#define AUDIO_BUFSIZ 160 /* codec buffer size (Solaris only) */ +#define SAMPLE 8000 /* nominal sample rate (Hz) */ +#define BAUD 300 /* modulation rate (bps) */ +#define OFFSET 128 /* companded sample offset */ +#define SIZE 256 /* decompanding table size */ +#define MAXSIG 6000. /* maximum signal level */ +#define DRPOUT 100. /* dropout signal level */ +#define LIMIT 1000. /* soft limiter threshold */ +#define AGAIN 6. /* baseband gain */ +#define LAG 10 /* discriminator lag */ +#else +#define DEVICE "/dev/chu%d" /* device name and unit */ +#define SPEED232 B300 /* UART speed (300 baud) */ +#define DESCRIPTION "CHU Audio Receiver" /* WRU */ +#endif /* AUDIO_CHU */ + +/* + * 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 MINSYNC 8 /* min sync distance (of 16) */ +#define MINDEC .5 /* decoder majority rule (of 1.) */ +#define MINSTAMP 20 /* min timestamps (of 60) */ + +/* + * Hex extension codes (>= 16) + */ +#define HEX_MISS 16 /* miss */ +#define HEX_SOFT 17 /* soft error */ +#define HEX_HARD 18 /* hard error */ + +/* + * Error flags (up->errflg) + */ +#define CHU_ERR_RUNT 0x001 /* runt burst */ +#define CHU_ERR_NOISE 0x002 /* noise burst */ +#define CHU_ERR_BFRAME 0x004 /* invalid format B frame sync */ +#define CHU_ERR_BFORMAT 0x008 /* invalid format B data */ +#define CHU_ERR_AFRAME 0x010 /* invalid format A frame sync */ +#define CHU_ERR_DECODE 0x020 /* invalid data decode */ +#define CHU_ERR_STAMP 0x040 /* too few timestamps */ +#define CHU_ERR_AFORMAT 0x080 /* invalid format A data */ +#ifdef AUDIO_CHU +#define CHU_ERR_ERROR 0x100 /* codec error (overrun) */ +#endif /* AUDIO_CHU */ + +#ifdef AUDIO_CHU +struct surv { + double shift[12]; /* mark register */ + double max, min; /* max/min envelope signals */ + double dist; /* sample distance */ + int uart; /* decoded character */ +}; +#endif /* AUDIO_CHU */ + +/* + * 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 bufptr; /* buffer index pointer */ + int pollcnt; /* poll message counter */ + + /* + * 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 */ + +#ifdef AUDIO_CHU + /* + * Audio codec variables + */ + double comp[SIZE]; /* decompanding table */ + int port; /* codec port */ + int gain; /* codec gain */ + int bufcnt; /* samples in buffer */ + 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 /* AUDIO_CHU */ +}; + +/* + * 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_update P((struct peer *, int)); +static void chu_year P((struct peer *, int)); +static int chu_dist P((int, int)); +#ifdef AUDIO_CHU +static void chu_uart P((struct surv *, double)); +static void chu_rf P((struct peer *, double)); +static void chu_gain P((struct peer *)); +static int chu_audio P((void)); +static void chu_debug P((void)); +#endif /* AUDIO_CHU */ + +/* + * Global variables + */ +static char hexchar[] = "0123456789abcdef_-="; +#ifdef AUDIO_CHU +#ifdef HAVE_SYS_AUDIOIO_H +struct audio_device device; /* audio device ident */ +#endif /* HAVE_SYS_AUDIOIO_H */ +static struct audio_info info; /* audio device info */ +static int chu_ctl_fd; /* audio control file descriptor */ +#endif /* AUDIO_CHU */ + +/* + * 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; + + /* + * Local variables + */ + int fd; /* file descriptor */ +#ifdef AUDIO_CHU + int i; /* index */ + double step; /* codec adjustment */ + + /* + * Open audio device + */ + fd = open("/dev/audio", O_RDWR | O_NONBLOCK, 0777); + if (fd == -1) { + perror("chu: audio"); + return (0); + } +#else + char device[20]; /* device name */ + + /* + * Open serial port. Use RAW line discipline (required). + */ + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_RAW))) { + return (0); + } +#endif /* AUDIO_CHU */ + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct chuunit *) + emalloc(sizeof(struct chuunit)))) { + (void) 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)) { + (void)close(fd); + free(up); + return (0); + } + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + DTOLFP(CHAR, &up->charstamp); + up->pollcnt = 2; +#ifdef AUDIO_CHU + up->gain = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) / 2; + if (chu_audio() < 0) { + io_closeclock(&pp->io); + free(up); + return (0); + } + + /* + * 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. / SAMPLE, &up->tick); +#endif /* AUDIO_CHU */ + 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; + io_closeclock(&pp->io); + free(up); +} + +#ifdef AUDIO_CHU + +/* + * chu_receive - receive data from the audio device + */ +static void +chu_receive( + struct recvbuf *rbufp /* receive buffer structure pointer */ + ) +{ + struct chuunit *up; + struct refclockproc *pp; + struct peer *peer; + + /* + * Local variables + */ + double sample; /* codec sample */ + u_char *dpt; /* buffer pointer */ + l_fp ltemp; /* l_fp temp */ + double dtemp; /* double temp */ + int isneg; /* parity flag */ + int i, j; /* index temps */ + + 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. + */ + up->timestamp = rbufp->recv_time; + up->bufcnt = rbufp->recv_length; + DTOLFP(up->bufcnt * 1. / SAMPLE, <emp); + L_SUB(&up->timestamp, <emp); + dpt = (u_char *)&rbufp->recv_space; + for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) { + sample = up->comp[~*dpt & 0xff]; + + /* + * Clip noise spikes greater than MAXSIG. If no clips, + * increase the gain a tad; if the clips are too high, + * decrease a tad. + */ + if (sample > MAXSIG) { + sample = MAXSIG; + up->clipcnt++; + } else if (sample < -MAXSIG) { + sample = -MAXSIG; + up->clipcnt++; + } + up->seccnt = (up->seccnt + 1) % SAMPLE; + if (up->seccnt == 0) { + if (pp->sloppyclockflag & CLK_FLAG2) + up->port = AUDIO_LINE_IN; + else + up->port = AUDIO_MICROPHONE; + chu_gain(peer); + up->clipcnt = 0; + } + chu_rf(peer, sample); + + /* + * During development, it is handy to have an audio + * monitor that can be switched to various signals. This + * code converts the linear signal left in up->monitor + * to codec format. If we can get the grass out of this + * thing and improve modem performance, this expensive + * code will be permanently nixed. + */ + isneg = 0; + dtemp = up->monitor; + if (sample < 0) { + isneg = 1; + dtemp-= dtemp; + } + i = 0; + j = OFFSET >> 1; + while (j != 0) { + if (dtemp > up->comp[i]) + i += j; + else if (dtemp < up->comp[i]) + i -= j; + else + break; + j >>= 1; + } + if (isneg) + *dpt = ~(i + OFFSET); + else + *dpt = ~i; + dpt++; + L_ADD(&up->timestamp, &up->tick); + } + + /* + * Squawk to the monitor speaker if enabled. + */ + if (pp->sloppyclockflag & CLK_FLAG3) + if (write(pp->io.fd, (u_char *)&rbufp->recv_space, + (u_int)up->bufcnt) < 0) + perror("chu:"); +} + + +/* + * 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. + */ +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; /* index temps */ + + 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; +/* +printf("%8.3f %8.3f\n", disc, lpf); +return; +*/ + /* + * 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. / SAMPLE; + if (up->baud > 1. / (BAUD * 8.)) { + up->baud -= 1. / (BAUD * 8.); + sp = &up->surv[up->decptr]; + span = sp->max - sp->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. + */ +void +chu_uart( + struct surv *sp, /* survivor structure pointer */ + double sample /* baseband signal */ + ) +{ + /* + * Local variables + */ + double max, min; /* max/min envelope */ + double slice; /* slice level */ + double dist; /* distance */ + double dtemp; /* double temp */ + int i; /* index temp */ + + /* + * Save the sample and shift right. At the same time, measure + * the maximum and minimum over all eleven samples. + */ + max = -1e6; + min = 1e6; + sp->shift[0] = sample; + for (i = 11; i > 0; i--) { + sp->shift[i] = sp->shift[i - 1]; + if (sp->shift[i] > max) + max = sp->shift[i]; + if (sp->shift[i] < min) + 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 = (max + 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 - min; + } else if (i == 10) { + dist += max - dtemp; + } else { + if (dtemp > slice) + dist += dtemp - min; + else + dist += max - dtemp; + } + } + sp->max = max; + sp->min = min; + sp->dist = dist / (11 * (max - min)); +} + + +#else /* AUDIO_CHU */ +/* + * chu_receive - receive data from the serial interface + */ +static void +chu_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); +} +#endif /* AUDIO_CHU */ + + +/* + * chu_decode - decode the data + */ +static void +chu_decode( + struct peer *peer, /* peer structure pointer */ + int hexhex /* data character */ + ) +{ + struct refclockproc *pp; + struct chuunit *up; + + /* + * Local variables + */ + l_fp tstmp; /* timestamp temp */ + double dtemp; /* double temp */ + + 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; + + /* + * Local variables + */ + int i; /* index temp */ + + 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->errflg |= CHU_ERR_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; otherwise, it is a noise burst and + * of no use to anybody. + */ + if (up->burdist >= MINDIST) { + chu_update(peer, up->ndx); + } else if (up->burdist <= -MINDIST) { + chu_year(peer, up->ndx); + } else { + up->errflg |= CHU_ERR_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) + up->pollcnt = 2; + else + peer->nextdate = current_time + 10; +} + + +/* + * chu_year - decode format B burst + */ +static void +chu_year( + struct peer *peer, + int nchar + ) +{ + struct refclockproc *pp; + struct chuunit *up; + + /* + * Local variables + */ + u_char code[11]; /* decoded timecode */ + l_fp offset; /* timestamp offset */ + int leap; /* leap/dut code */ + int dut; /* UTC1 correction */ + int tai; /* TAI - UTC correction */ + int dst; /* Canadian DST code */ + int i; /* index temp */ + + 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. + */ + sprintf(pp->a_lastcode, "%2d %2d ", nchar, -up->burdist); + for (i = 0; i < nchar; i++) + sprintf(&pp->a_lastcode[strlen(pp->a_lastcode)], "%02x", + up->cbuf[i]); + pp->lencode = strlen(pp->a_lastcode); + if (pp->sloppyclockflag & CLK_FLAG4) + record_clock_stats(&peer->srcadr, pp->a_lastcode); +#ifdef DEBUG + if (debug > 2) + printf("chu: %s\n", pp->a_lastcode); +#endif + if (-up->burdist < 40) { + up->errflg |= CHU_ERR_BFRAME; + return; + } + + /* + * Convert the burst data to internal format. If this succeeds, + * save the timestamps for later. The leap, dut, tai and dst are + * presently unused. + */ + 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", &leap, &dut, + &pp->year, &tai, &dst) != 5) { + up->errflg |= CHU_ERR_BFORMAT; + return; + } + 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) + up->ntstamp++; + } +} + + +/* + * chu_update - decode format A burst + */ +static void +chu_update( + struct peer *peer, + int nchar + ) +{ + struct refclockproc *pp; + struct chuunit *up; + + /* + * Local variables + */ + l_fp offset; /* timestamp offset */ + int val; /* distance */ + int temp; /* common temp */ + int i, j, k; /* index temps */ + + 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 AUDIO_CHU + sprintf(pp->a_lastcode, "%3d %4.0f %2d %2d %2d %2d %1d ", + up->gain, up->maxsignal, nchar, up->burdist, k, up->syndist, + temp); +#else + sprintf(pp->a_lastcode, "%2d %2d %2d %2d %1d ", nchar, + up->burdist, k, up->syndist, temp); +#endif /* AUDIO_CHU */ + for (i = 0; i < nchar; i++) + sprintf(&pp->a_lastcode[strlen(pp->a_lastcode)], "%02x", + up->cbuf[i]); + pp->lencode = strlen(pp->a_lastcode); + if (pp->sloppyclockflag & CLK_FLAG4) + record_clock_stats(&peer->srcadr, pp->a_lastcode); +#ifdef DEBUG + if (debug > 2) + printf("chu: %s\n", pp->a_lastcode); +#endif + if (up->syndist < MINSYNC) { + up->errflg |= CHU_ERR_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) { + 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) + 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 > 19) { + i += 2; + continue; + } + up->decode[i++][up->cbuf[j] & 0xf]++; + up->decode[i++][(up->cbuf[j] >> 4) & 0xf]++; + } + up->burstcnt++; +} + + +/* + * chu_poll - called by the transmit procedure + */ +static void +chu_poll( + int unit, + struct peer *peer + ) +{ + struct refclockproc *pp; + struct chuunit *up; + + /* + * Local variables + */ + u_char code[11]; /* decoded timecode */ + l_fp toffset, offset; /* l_fp temps */ + int mindist; /* minimum distance */ + int val1, val2; /* maximum distance */ + int synchar; /* should be a 6 in traffic */ + double dtemp; /* double temp */ + int temp; /* common temp */ + int i, j, k; /* index temps */ + + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; + + /* + * Process the last burst, if still in the burst buffer. + * Don't mess with anything if nothing has been heard. + */ + chu_burst(peer); + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + if (up->burstcnt == 0) { + chu_clear(peer); + return; + } + + /* + * Majority decoder. Select the character with the most + * occurrences for each burst position. The distance for the + * character is this number of occurrences. If no occurrences + * are found, assume a miss '_'; if only a single occurrence is + * found, assume a soft error '-'; if two different characters + * with the same distance are found, assume a hard error '='. + * The decoding distance is defined as the minimum of the + * character distances. + */ + mindist = 16; + for (i = 0; i < 10; 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 && val1 == val2) + code[i] = HEX_HARD; + else if (val1 < 2) + code[i] = HEX_SOFT; + else + code[i] = k; + if (val1 < mindist) + mindist = val1; + code[i] = hexchar[code[i]]; + } + code[i] = 0; + if (mindist < up->burstcnt * 2 * MINDEC) + up->errflg |= CHU_ERR_DECODE; + if (up->ntstamp < MINSTAMP) + up->errflg |= CHU_ERR_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->errflg |= CHU_ERR_AFORMAT; + sprintf(pp->a_lastcode, + "%02x %4d %3d %02d:%02d:%02d %2d %2d %2d", + up->errflg, pp->year, pp->day, pp->hour, pp->minute, + pp->second, up->burstcnt, mindist, up->ntstamp); + pp->lencode = strlen(pp->a_lastcode); + record_clock_stats(&peer->srcadr, pp->a_lastcode); +#ifdef DEBUG + if (debug > 2) + printf("chu: %s\n", pp->a_lastcode); +#endif + if (up->errflg & (CHU_ERR_DECODE | CHU_ERR_STAMP | + CHU_ERR_AFORMAT)) { + refclock_report(peer, CEVNT_BADREPLY); + chu_clear(peer); + return; + } + L_CLR(&offset); + if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, + up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { + refclock_report(peer, CEVNT_BADTIME); + chu_clear(peer); + return; + } + pp->polls++; + pp->leap = LEAP_NOWARNING; + pp->lastref = offset; + pp->variance = 0; + for (i = 0; i < up->ntstamp; i++) { + toffset = offset; + L_SUB(&toffset, &up->tstamp[i]); + LFPTOD(&toffset, dtemp); + SAMPLE(dtemp + FUDGE + pp->fudgetime1); + } + if (i > 0) + refclock_receive(peer); + chu_clear(peer); +} + + +/* + * chu_clear - clear decoding matrix + */ +static void +chu_clear( + struct peer *peer + ) +{ + struct refclockproc *pp; + struct chuunit *up; + + /* + * Local variables + */ + int i, j; /* index temps */ + + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; + + /* + * Clear stuff for following minute. + */ + up->ndx = up->ntstamp = up->prevsec = 0; + up->errflg = 0; + up->burstcnt = 0; + for (i = 0; i < 20; i++) { + for (j = 0; j < 16; j++) + up->decode[i][j] = 0; + } +} + + +/* + * 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 */ + ) +{ + /* + * Local variables + */ + int val; /* bit count */ + int temp; /* misc temporary */ + int i; /* index temporary */ + + /* + * 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 AUDIO_CHU +/* + * 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. Set the new bits in the structure + * and call AUDIO_SETINFO. Upon return, the old bits are in the + * structure. + */ + if (up->clipcnt == 0) { + up->gain += 4; + if (up->gain > AUDIO_MAX_GAIN) + up->gain = AUDIO_MAX_GAIN; + } else if (up->clipcnt > SAMPLE / 100) { + up->gain -= 4; + if (up->gain < AUDIO_MIN_GAIN) + up->gain = AUDIO_MIN_GAIN; + } + AUDIO_INITINFO(&info); + info.record.port = up->port; + info.record.gain = up->gain; + info.record.error = 0; + ioctl(chu_ctl_fd, (int)AUDIO_SETINFO, &info); + if (info.record.error) + up->errflg |= CHU_ERR_ERROR; +} + + +/* + * chu_audio - initialize audio device + * + * This code works with SunOS 4.1.3 and Solaris 2.6; however, it is + * believed generic and applicable to other systems with a minor twid + * or two. All it does is open the device, set the buffer size (Solaris + * only), preset the gain and set the input port. It assumes that the + * codec sample rate (8000 Hz), precision (8 bits), number of channels + * (1) and encoding (ITU-T G.711 mu-law companded) have been set by + * default. + */ +static int +chu_audio( + ) +{ + /* + * Open audio control device + */ + if ((chu_ctl_fd = open("/dev/audioctl", O_RDWR)) < 0) { + perror("audioctl"); + return(-1); + } +#ifdef HAVE_SYS_AUDIOIO_H + /* + * Set audio device parameters. + */ + AUDIO_INITINFO(&info); + info.record.buffer_size = AUDIO_BUFSIZ; + if (ioctl(chu_ctl_fd, (int)AUDIO_SETINFO, &info) < 0) { + perror("AUDIO_SETINFO"); + close(chu_ctl_fd); + return(-1); + } +#endif /* HAVE_SYS_AUDIOIO_H */ +#ifdef DEBUG + chu_debug(); +#endif /* DEBUG */ + return(0); +} + + +#ifdef DEBUG +/* + * chu_debug - display audio parameters + * + * This code doesn't really do anything, except satisfy curiousity and + * verify the ioctl's work. + */ +static void +chu_debug( + ) +{ + if (debug == 0) + return; +#ifdef HAVE_SYS_AUDIOIO_H + ioctl(chu_ctl_fd, (int)AUDIO_GETDEV, &device); + printf("chu: name %s, version %s, config %s\n", + device.name, device.version, device.config); +#endif /* HAVE_SYS_AUDIOIO_H */ + ioctl(chu_ctl_fd, (int)AUDIO_GETINFO, &info); + printf( + "chu: samples %d, channels %d, precision %d, encoding %d\n", + info.record.sample_rate, info.record.channels, + info.record.precision, info.record.encoding); +#ifdef HAVE_SYS_AUDIOIO_H + printf("chu: gain %d, port %d, buffer %d\n", + info.record.gain, info.record.port, + info.record.buffer_size); +#else /* HAVE_SYS_AUDIOIO_H */ + printf("chu: gain %d, port %d\n", + info.record.gain, info.record.port); +#endif /* HAVE_SYS_AUDIOIO_H */ + printf( + "chu: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n", + info.record.samples, info.record.eof, + info.record.pause, info.record.error, + info.record.waiting, info.record.balance); + printf("chu: monitor %d, muted %d\n", + info.monitor_gain, info.output_muted); +} +#endif /* DEBUG */ +#endif /* AUDIO_CHU */ + +#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..d203dd7 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_conf.c @@ -0,0 +1,262 @@ +/* + * 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 + +#ifdef CLOCK_TRAK +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_WWVB +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(PPS) +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 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_PTBACTS +extern struct refclock refclock_ptb; +#else +#define refclock_ptb refclock_none +#endif + +#ifdef CLOCK_USNO +extern struct refclock refclock_usno; +#else +#define refclock_usno 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 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 + +#ifdef CLOCK_ONCORE +extern struct refclock refclock_oncore; +#else +#define refclock_oncore refclock_none +#endif + +#if defined(CLOCK_JUPITER) && defined(PPS) +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 + + +/* + * 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_trak, /* 2 REFCLK_GPS_TRAK */ + &refclock_pst, /* 3 REFCLK_WWV_PST */ + &refclock_wwvb, /* 4 REFCLK_WWVB_SPECTRACOM */ + &refclock_true, /* 5 REFCLK_TRUETIME */ + &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */ + &refclock_chu, /* 7 REFCLK_CHU */ + &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_msfees, /* 14 REFCLK_MSF_EES */ + &refclock_true, /* 15 alias for REFCLK_TRUETIME */ + &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */ + &refclock_datum, /* 17 REFCLK_GPS_DATUM */ + &refclock_acts, /* 18 REFCLK_NIST_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_ptb, /* 23 REFCLK_PTB_ACTS */ + &refclock_usno, /* 24 REFCLK_USNO */ + &refclock_true, /* 25 alias for REFCLK_TRUETIME */ + &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 */ +}; + +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..74a37db --- /dev/null +++ b/contrib/ntp/ntpd/refclock_datum.c @@ -0,0 +1,873 @@ +/* +** 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.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 */ + +#if defined (WWVBPPS) +#include <sys/ppsclock.h> +#endif /* WWVBPPS */ + +#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 PTSPRECISION (-10) /* precision assumed 1/1024 ms */ +#define DATMREFID "DATM" /* reference id */ +#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */ +#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */ + +#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; + +#ifdef HAVE_TERMIOS + struct termios arg; +#endif + +#ifdef DEBUG_DATUM_PTC + if (debug) + printf("Starting Datum PTS unit %d\n", unit); +#endif + + /* + ** 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 */ + + /* + ** Open the Datum PTS device + */ + + datum_pts->PTS_fd = open("/dev/datum",O_RDWR); + + 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); + + 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; + } + + peer->precision = PTSPRECISION; + peer->stratum = 0; + memcpy((char *)&peer->refid, DATMREFID, 4); + + /* + ** 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 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. + */ + + index = -1; + for (i=0; i<nunits; i++) { + if (datum_pts_unit[i]->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 (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) ) { + + 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);*/ + 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..8138d9e --- /dev/null +++ b/contrib/ntp/ntpd/refclock_dumbclock.c @@ -0,0 +1,381 @@ +/* + * 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(REFCLOCK) && defined(CLOCK_DUMBCLOCK) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.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 + if (!(fd = refclock_open(device, SPEED232, 0))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct dumbclock_unit *) + emalloc(sizeof(struct dumbclock_unit)))) { + (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 = 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... + */ + pp->msec = 0; + 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; + } + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + up->lasthour = 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_gpsvme.c b/contrib/ntp/ntpd/refclock_gpsvme.c new file mode 100644 index 0000000..a7c6d23 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_gpsvme.c @@ -0,0 +1,613 @@ +/* + * refclock_gpsvme.c NTP clock driver for the TrueTime GPS-VME + * R. Schmidt, Time Service, US Naval Obs. res@tuttle.usno.navy.mil + * + * The refclock type has been defined as 16 (until new id assigned). + * These DEFS are included in the Makefile: + * DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9 + * DEFS_LOCAL= -DREFCLOCK + * CLOCKDEFS= -DGPSVME + * The file map_vme.c does the VME memory mapping, and includes vme_init(). + * map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo! + * The file gps.h provides TrueTime register info. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_GPSVME) +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> + +#include "gps.h" +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" +#include "/etc/conf/h/io.h" + +/* GLOBAL STUFF BY RES */ + +#include <time.h> + +#define PRIO 120 /* set the realtime priority */ +#define NREGS 7 /* number of registers we will use */ + +extern int init_vme(); /* This is just a call to map_vme() */ + /* It doesn't have to be extern */ +unsigned short *greg[NREGS]; /* GPS registers defined in gps.h */ +void *gps_base; /* Base address of GPS VME card returned by */ + /* the map_vme() call */ +extern caddr_t map_vme (); +extern void unmap_vme(); /* Unmaps the VME space */ + +struct vmedate { /* structure needed by ntp */ + unsigned short year; /* *tptr is a pointer to this */ + unsigned short doy; + unsigned short hr; + unsigned short mn; + unsigned short sec; + unsigned long frac; + unsigned short status; +}; + +struct vmedate *get_gpsvme_time(); + +/* END OF STUFF FROM RES */ + +/* + * Definitions + */ +#define MAXUNITS 2 /* max number of VME units */ +#define BMAX 50 /* timecode buffer length */ + +/* + * VME interface parameters. + */ +#define VMEPRECISION (-21) /* precision assumed (1 us) */ +#define USNOREFID "USNO\0" /* Or whatever? */ +#define VMEREFID "GPS" /* reference id */ +#define VMEDESCRIPTION "GPS" /* who we are */ +#define VMEHSREFID 0x7f7f1001 /* 127.127.16.01 refid hi strata */ + +/* I'm using clock type 16 until one is assigned */ +/* This is set also in vme_control, below */ + + +#define GMT 0 /* hour offset from Greenwich */ + +/* + * VME unit control structure. + */ +struct vmeunit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + struct vmedate vmedata; /* data returned from vme read */ + l_fp lastrec; /* last local time */ + l_fp lastref; /* last timecode time */ + char lastcode[BMAX]; /* last timecode received */ + u_short lencode; /* length of last timecode */ + u_long lasttime; /* last time clock heard from */ + u_short unit; /* unit number for this guy */ + u_short status; /* clock status */ + u_short lastevent; /* last clock event */ + u_short year; /* year of eternity */ + u_short day; /* day of year */ + u_short hour; /* hour of day */ + u_short minute; /* minute of hour */ + u_short second; /* seconds of minute */ + u_long usec; /* microsecond of second */ + u_long yearstart; /* start of current year */ + u_short leap; /* leap indicators */ + /* + * Status tallies + */ + u_long polls; /* polls sent */ + u_long noreply; /* no replies to polls */ + u_long coderecv; /* timecodes received */ + u_long badformat; /* bad format */ + u_long baddata; /* bad data */ + u_long timestarted; /* time we started this */ +}; + +/* + * Data space for the unit structures. Note that we allocate these on + * the fly, but never give them back. + */ +static struct vmeunit *vmeunits[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 fudgefactor[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char sloppyclockflag[MAXUNITS]; + +/* + * Function prototypes + */ +static void vme_init (void); +static int vme_start (u_int, struct peer *); +static void vme_shutdown (int); +static void vme_report_event (struct vmeunit *, int); +static void vme_receive (struct recvbuf *); +static void vme_poll (int unit, struct peer *); +static void vme_control (u_int, struct refclockstat *, struct refclockstat *); +static void vme_buginfo (int, struct refclockbug *); + +/* + * Transfer vector + */ +struct refclock refclock_gpsvme = { + vme_start, vme_shutdown, vme_poll, + vme_control, vme_init, vme_buginfo, NOFLAGS +}; + +int fd_vme; /* file descriptor for ioctls */ +int regvalue; + +/* + * vme_init - initialize internal vme driver data + */ +static void +vme_init(void) +{ + register int i; + /* + * Just zero the data arrays + */ + /* + bzero((char *)vmeunits, sizeof vmeunits); + bzero((char *)unitinuse, sizeof unitinuse); + */ + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor[i].l_ui = 0; + fudgefactor[i].l_uf = 0; + stratumtouse[i] = 0; + sloppyclockflag[i] = 0; + } +} + +/* + * vme_start - open the VME device and initialize data for processing + */ +static int +vme_start( + u_int unit, + struct peer *peer + ) +{ + register struct vmeunit *vme; + register int i; + int dummy; + char vmedev[20]; + + /* + * Check configuration info. + */ + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); + return (0); + } + if (unitinuse[unit]) { + msyslog(LOG_ERR, "vme_start: unit %d in use", unit); + return (0); + } + + /* + * Open VME device + */ +#ifdef DEBUG + + printf("Opening VME DEVICE \n"); +#endif + init_vme(); /* This is in the map_vme.c external file */ + + /* + * Allocate unit structure + */ + if (vmeunits[unit] != 0) { + vme = vmeunits[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && vmeunits[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + vme = vmeunits[i]; + vmeunits[i] = 0; + } else { + vme = (struct vmeunit *) + emalloc(sizeof(struct vmeunit)); + } + } + bzero((char *)vme, sizeof(struct vmeunit)); + vmeunits[unit] = vme; + + /* + * Set up the structures + */ + vme->peer = peer; + vme->unit = (u_short)unit; + vme->timestarted = current_time; + + vme->io.clock_recv = vme_receive; + vme->io.srcclock = (caddr_t)vme; + vme->io.datalen = 0; + vme->io.fd = fd_vme; + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = VMEPRECISION; + peer->stratum = stratumtouse[unit]; + memcpy( (char *)&peer->refid, USNOREFID,4); + + /* peer->refid = htonl(VMEHSREFID); */ + + unitinuse[unit] = 1; + return (1); +} + + +/* + * vme_shutdown - shut down a VME clock + */ +static void +vme_shutdown( + int unit + ) +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + unmap_vme(); + vme = vmeunits[unit]; + io_closeclock(&vme->io); + unitinuse[unit] = 0; +} + +/* + * vme_report_event - note the occurance of an event + * + * This routine presently just remembers the report and logs it, but + * does nothing heroic for the trap handler. + */ +static void +vme_report_event( + struct vmeunit *vme, + int code + ) +{ + struct peer *peer; + + peer = vme->peer; + if (vme->status != (u_short)code) { + vme->status = (u_short)code; + if (code != CEVNT_NOMINAL) + vme->lastevent = (u_short)code; + msyslog(LOG_INFO, + "clock %s event %x", ntoa(&peer->srcadr), code); + } +} + + +/* + * 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; + l_fp tstmp; + time_t tloc; + struct tm *tadr; + + + vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *)); + tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *)); + + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit); + return; + } + vme = vmeunits[unit]; /* Here is the structure */ + vme->polls++; + + tptr = &vme->vmedata; + + if ((tptr = get_gpsvme_time()) == NULL ) { + vme_report_event(vme, CEVNT_BADREPLY); + return; + } + + get_systime(&vme->lastrec); + 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(vme->lastcode, + "%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0", + tptr->doy, tptr->hr, tptr->mn, + tptr->sec, tptr->frac, tptr->status); + + record_clock_stats(&(vme->peer->srcadr), vme->lastcode); + vme->lencode = (u_short) strlen(vme->lastcode); + + vme->day = tptr->doy; + vme->hour = tptr->hr; + vme->minute = tptr->mn; + vme->second = tptr->sec; + vme->usec = tptr->frac; + +#ifdef DEBUG + if (debug) + printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n", + vme->day, vme->hour, vme->minute, vme->second, + vme->usec, tptr->status); +#endif + if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ + vme_report_event(vme, 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 (!clocktime(vme->day, vme->hour, vme->minute, + vme->second, GMT, vme->lastrec.l_ui, + &vme->yearstart, &vme->lastref.l_ui)) { + vme->baddata++; + vme_report_event(vme, CEVNT_BADTIME); + msyslog(LOG_ERR, "refclock_gpsvme: bad data!!"); + return; + } + TVUTOTSF(vme->usec, vme->lastref.l_uf); + tstmp = vme->lastref; + + L_SUB(&tstmp, &vme->lastrec); + vme->coderecv++; + + L_ADD(&tstmp, &(fudgefactor[vme->unit])); + + refclock_receive(vme->peer); +} + +/* + * vme_control - set fudge factors, return statistics + */ +static void +vme_control( + u_int unit, + struct refclockstat *in, + struct refclockstat *out + ) +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVEVAL1) { + stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); + if (unitinuse[unit]) { + struct peer *peer; + + /* + * Should actually reselect clock, but + * will wait for the next timecode + */ + vme = vmeunits[unit]; + peer = vme->peer; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) + memcpy( (char *)&peer->refid, USNOREFID,4); + else + peer->refid = htonl(VMEHSREFID); + } + } + if (in->haveflags & CLK_HAVEFLAG1) { + sloppyclockflag[unit] = in->flags & CLK_FLAG1; + } + } + + if (out != 0) { + out->type = 16; /*set by RES SHOULD BE CHANGED */ + out->haveflags + = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1; + out->clockdesc = VMEDESCRIPTION; + out->fudgetime1 = fudgefactor[unit]; + out->fudgetime2.l_ui = 0; + out->fudgetime2.l_uf = 0; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = 0; + out->flags = sloppyclockflag[unit]; + if (unitinuse[unit]) { + vme = vmeunits[unit]; + out->lencode = vme->lencode; + out->lastcode = vme->lastcode; + out->timereset = current_time - vme->timestarted; + out->polls = vme->polls; + out->noresponse = vme->noreply; + out->badformat = vme->badformat; + out->baddata = vme->baddata; + out->lastevent = vme->lastevent; + out->currentstatus = vme->status; + } else { + out->lencode = 0; + out->lastcode = ""; + out->polls = out->noresponse = 0; + out->badformat = out->baddata = 0; + out->timereset = 0; + out->currentstatus = out->lastevent = CEVNT_NOMINAL; + } + } +} + +/* + * vme_buginfo - return clock dependent debugging info + */ +static void +vme_buginfo( + int unit, + register struct refclockbug *bug + ) +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit); + return; + } + + if (!unitinuse[unit]) + return; + vme = vmeunits[unit]; + + bug->nvalues = 11; + bug->ntimes = 5; + if (vme->lasttime != 0) + bug->values[0] = current_time - vme->lasttime; + else + bug->values[0] = 0; + bug->values[2] = (u_long)vme->year; + bug->values[3] = (u_long)vme->day; + bug->values[4] = (u_long)vme->hour; + bug->values[5] = (u_long)vme->minute; + bug->values[6] = (u_long)vme->second; + bug->values[7] = (u_long)vme->usec; + bug->values[9] = vme->yearstart; + bug->stimes = 0x1c; + bug->times[0] = vme->lastref; + bug->times[1] = vme->lastrec; +} +/* -------------------------------------------------------*/ +/* get_gpsvme_time() */ +/* R. Schmidt, USNO, 1995 */ +/* It's ugly, but hey, it works and its free */ + +#include "gps.h" /* defines for TrueTime GPS-VME */ + +#define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */ + +struct vmedate * +get_gpsvme_time(void) +{ + struct vmedate *time_vme; + unsigned short set, hr, min, sec, ums, hms, status; + int ret; + char ti[3]; + + long tloc ; + time_t mktime(),time(); + struct tm *gmtime(), *gmt; + char *gpsmicro; + gpsmicro = (char *) malloc(7); + + time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); + *greg = (unsigned short *)malloc(sizeof(short) * NREGS); + + + /* reference the freeze command address general register 1 */ + set = *greg[0]; + /* read the registers : */ + /* get year */ + time_vme->year = (unsigned short) *greg[6]; + /* Get doy */ + time_vme->doy = (unsigned short) (*greg[5] & MASKDAY); + /* Get hour */ + time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8); + /* Get minutes */ + time_vme->mn = (unsigned short) (*greg[4] & MASKLO); + /* Get seconds */ + time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8; + /* get microseconds in 2 parts and put together */ + ums = *greg[2]; + hms = *greg[3] & MASKLO; + + time_vme->status = (unsigned short) *greg[5] >>13; + + /* reference the unfreeze command address general register 1 */ + set = *greg[1]; + + sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums); + time_vme->frac = (u_long) gpsmicro; + + /* unmap_vme(); */ + + if (!status) { + return ((void *)NULL); + } + else + return (time_vme); +} + +#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..9d297d1 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_heath.c @@ -0,0 +1,487 @@ +/* + * refclock_heath - clock driver for Heath GC-1000 Most Accurate Clock + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_HEATH) + +#include <stdio.h> +#include <ctype.h> +#ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# ifdef TM_IN_SYS_TIME +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif /* not HAVE_SYS_IOCTL_H */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.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. + * + * The internal DIPswitches should be set to operate at 1200 baud in + * MANUAL mode and the current year. The external DIPswitches should be + * set to GMT and 24-hour format, or to the host local time zone (with + * DST) and 12-hour format. 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. In 12-hour mode + * with DST selected the clock will be incorrect by an hour for an + * indeterminate amount of time between 0000Z and 0200 on the day DST + * changes. + * + * 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. + * + * 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 SPEED232 B1200 /* uart speed (1200 baud) */ +#define PRECISION (-4) /* precision assumed (about 100 ms) */ +#define REFID "WWV\0" /* reference ID */ +#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */ + +#define LENHEATH 23 /* 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 heathunit { + int pollcnt; /* poll message counter */ + l_fp tstamp; /* timestamp of last poll */ +}; + +/* + * 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 */ +}; + +#if 0 +/* + * Gee, Unix so thoughfully omitted code to convert from a struct tm to + * a long, so I'll just have to ferret out the inverse myself, the hard way. + * (Newton's method.) + */ +#define timelocal(x) invert(x, localtime) +/* + * comparetm compares two tm structures and returns -1 if the first + * is less than the second, 0 if they are equal, and +1 if the first + * is greater than the second. Only the year, month, day, hour, minute + * and second are compared. The yearday (Julian), day of week, and isdst + * are not compared. + */ + +static int +comparetm( + struct tm *a, + struct tm *b + ) +{ + if (a->tm_year < b->tm_year ) return -1; + if (a->tm_year > b->tm_year ) return 1; + if (a->tm_mon < b->tm_mon ) return -1; + if (a->tm_mon > b->tm_mon ) return 1; + if (a->tm_mday < b->tm_mday ) return -1; + if (a->tm_mday > b->tm_mday ) return 1; + if (a->tm_hour < b->tm_hour ) return -1; + if (a->tm_hour > b->tm_hour ) return 1; + if (a->tm_min < b->tm_min ) return -1; + if (a->tm_min > b->tm_min ) return 1; + if (a->tm_sec < b->tm_sec ) return -1; + if (a->tm_sec > b->tm_sec ) return 1; + return 0; +} + +static long +invert ( + struct tm *x, + struct tm *(*func)() + ) +{ + struct tm *y; + int result; + long trial; + long lower=0L; + long upper=(long)((unsigned long)(~lower) >> 1); + + do { + trial = (upper + lower) / 2L; + y = (*func)(&trial); + result = comparetm(x, y); + if (result < 0) upper = trial; + if (result > 0) lower = trial; + } while (result != 0); + return trial; +} +#endif /* 0 */ + + +/* + * heath_start - open the devices and initialize data for processing + */ +static int +heath_start( + int unit, + struct peer *peer + ) +{ + register struct heathunit *up; + struct refclockproc *pp; + int fd; + char device[20]; + + /* + * Open serial port + */ + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, 0))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct heathunit *) + emalloc(sizeof(struct heathunit)))) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct heathunit)); + 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); + 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->pollcnt = 2; + return (1); +} + + +/* + * heath_shutdown - shut down the clock + */ +static void +heath_shutdown( + int unit, + struct peer *peer + ) +{ + register struct heathunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct heathunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + + +/* + * heath_receive - receive data from the serial interface + */ +static void +heath_receive( + struct recvbuf *rbufp + ) +{ + register struct heathunit *up; + 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; + up = (struct heathunit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); + + /* + * We get a buffer and timestamp for each <cr>; however, we use + * the timestamp captured at the RTS modem control line toggle + * on the assumption that's what the radio bases the timecode + * on. Apparently, the radio takes about a second to make up its + * mind to send a timecode, so the receive timestamp is + * worthless. + */ + pp->lastrec = up->tstamp; + up->pollcnt = 2; +#ifdef DEBUG + if (debug) + printf("heath: 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 < LENHEATH) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Timecode format: "hh:mm:ss.f AM mm/dd/yy" + */ + 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; + } + + /* + * If AM or PM is received, assume the clock is displaying local + * time. First, convert to 24-hour format. + */ + + switch (a[1]) { + case 'P': + if (12 > pp->hour) + pp->hour += 12; + break; + + case 'A': + if (12 == pp->hour) + pp->hour -= 12; + break; + } + + /* + * Now make a struct tm out of it, convert to UTC, and + * repopulate pp-> + */ + + if (' ' != a[1]) { + struct tm t, *q; + time_t l; + + t.tm_sec = pp->second; + t.tm_min = pp->minute; + t.tm_hour = pp->hour; + t.tm_mday = day; /* not converted to yday yet */ + t.tm_mon = month-1; /* ditto */ + t.tm_year = pp->year; + if ( t.tm_year < YEAR_PIVOT ) t.tm_year += 100; /* Y2KFixes */ + + t.tm_wday = -1; /* who knows? */ + t.tm_yday = -1; /* who knows? */ + t.tm_isdst = -1; /* who knows? */ + + l = mktime(&t); + if (l == -1) { + /* HMS: do we want to do this? */ + refclock_report(peer, CEVNT_BADTIME); + return; + } + q = gmtime(&l); + + pp->year = q->tm_year; + month = q->tm_mon+1; + day = q->tm_mday; /* still not converted */ + pp->hour = q->tm_hour; + /* pp->minute = q->tm_min; GC-1000 cannot adjust timezone */ + /* pp->second = q->tm_sec; by other than hour increments */ + } + + + + /* + * 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. + * + * Yes, I know this code incorrectly thinks that 2000 is a leap + * year; but, the latest year that can be set by the DIPswitches + * is 1997 anyay. Life is short. + * Hey! Year 2000 IS a leap year! Y2KFixes + */ + 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->msec = (dsec - '0') * 100; + 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 + ) +{ + register struct heathunit *up; + 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. + */ + pp = peer->procptr; + up = (struct heathunit *)pp->unitptr; + pp->polls++; + + /* + * We toggle the RTS modem control lead 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. + */ + if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0) + refclock_report(peer, CEVNT_FAULT); + get_systime(&up->tstamp); + ioctl(pp->io.fd, TIOCMBIS, (char *)&bits); + 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; +} + +#else +int refclock_heath_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..30aa494 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_hpgps.c @@ -0,0 +1,603 @@ +/* + * refclock_hpgps - clock driver for HP 58503A GPS receiver + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_HPGPS) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.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 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 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. + */ + (void)sprintf(device, DEVICE, unit); + 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; + } + 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..eccbda8 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_irig.c @@ -0,0 +1,1079 @@ +/* + * refclock_irig - audio IRIG-B/E demodulator/decoder + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_IRIG) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <math.h> +#ifdef HAVE_SYS_AUDIOIO_H +#include <sys/audioio.h> +#endif /* HAVE_SYS_AUDIOIO_H */ +#ifdef HAVE_SUN_AUDIOIO_H +#include <sun/audioio.h> +#endif /* HAVE_SUN_AUDIOIO_H */ +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif /* HAVE_SYS_IOCTL_H */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.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 -d -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 47 20 0.083 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), + * field phase (0-79), time constant (2-20), modulation index (0-1), + * carrier phase error (0+-0.5) 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. With an UltrSPARC 30, this thing can keep + * the clock within a few tens of microseconds relative to the IRIG-B + * signal. Accuracy with IRIG-E is about ten times worse. + * + * 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 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 speaker volume + * must be set before the driver is started. Fudge flag4 causes the + * debugging output described above to be recorded in the clockstats + * file. Any of these flags can be changed during operation with the + * ntpdc program. + */ + +/* + * Interface definitions + */ +#define PRECISION (-17) /* precision assumed (about 10 us) */ +#define REFID "IRIG" /* reference ID */ +#define DESCRIPTION "Generic IRIG Audio Driver" /* WRU */ + +#define AUDIO_BUFSIZ 160 /* codec buffer size (Solaris only) */ +#define SAMPLES 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 MAXSIG 6000. /* maximum signal level */ +#define DRPOUT 100. /* dropout signal level */ +#define MODMIN 0.5 /* minimum modulation index */ +#define MAXFREQ (250e-6 * SAMPLES) /* freq tolerance (.025%) */ +#define PI 3.1415926535 /* the real thing */ + +/* + * Experimentally determined fudge factors + */ +#define IRIG_B .0019 /* IRIG-B phase delay */ +#define IRIG_E .0019 /* IRIG-E phase 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) */ + +/* + * 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 comp[SIZE]; /* decompanding table */ + double integ[BAUD]; /* baud integrator */ + double phase, freq; /* logical clock phase and frequency */ + double zxing; /* phase detector integrator */ + double yxing; /* phase detector display */ + double modndx; /* modulation index */ + double irig_b; /* IRIG-B signal amplitude */ + double irig_e; /* IRIG-E signal amplitude */ + int errflg; /* error flags */ + int bufcnt; /* samples in buffer */ + int bufptr; /* buffer index pointer */ + int pollcnt; /* poll counter */ + int port; /* codec port */ + int gain; /* codec gain */ + int clipcnt; /* sample clipped count */ + int seccnt; /* second interval counter */ + int decim; /* sample decimation factor */ + + /* + * 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 xxing; /* phase detector interpolated output */ + double fdelay; /* filter delay */ + 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 + */ + l_fp montime; /* reference timestamp for eyeball */ + int timecnt; /* timecode counter */ + 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 */ +}; + +/* + * 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 *)); +static int irig_audio P((void)); +static void irig_debug P((void)); + +/* + * 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 + */ +#ifdef HAVE_SYS_AUDIOIO_H +struct audio_device device; /* audio device ident */ +#endif /* HAVE_SYS_AUDIOIO_H */ +static struct audio_info info; /* audio device info */ +static int irig_ctl_fd; /* audio control file descriptor */ +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 (not used) */ + 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 = open("/dev/audio", O_RDWR | O_NONBLOCK, 0777); + if (fd == -1) { + perror("audio"); + return (0); + } + + /* + * 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 = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) / 2; + if (irig_audio() < 0) { + io_closeclock(&pp->io); + return(0); + } + up->pollcnt = 2; + + /* + * 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. / SAMPLES, &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 */ + 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. + */ + up->timestamp = rbufp->recv_time; + up->bufcnt = rbufp->recv_length; + DTOLFP((double)up->bufcnt / SAMPLES, <emp); + L_SUB(&up->timestamp, <emp); + dpt = rbufp->recv_buffer; + for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) { + sample = up->comp[~*dpt++ & 0xff]; + + /* + * Clip noise spikes greater than MAXSIG. If no clips, + * increase the gain a tad; if the clips are too high, + * decrease a tad. Choose either IRIG-B or IRIG-E + * according to the energy at the respective filter + * output. + */ + if (sample > MAXSIG) { + sample = MAXSIG; + up->clipcnt++; + } else if (sample < -MAXSIG) { + sample = -MAXSIG; + up->clipcnt++; + } + + /* + * Variable frequency oscillator. A phase change of one + * unit produces a change of 360 degrees; a frequency + * change of one unit produces a change of 1 Hz. + */ + up->phase += up->freq / SAMPLES; + 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, codec + * port and gain. + */ + up->seccnt = (up->seccnt + 1) % SAMPLES; + 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; + } + if (pp->sloppyclockflag & CLK_FLAG2) + up->port = AUDIO_LINE_IN; + else + up->port = AUDIO_MICROPHONE; + irig_gain(peer); + up->clipcnt = 0; + up->irig_b = up->irig_e = 0; + } + } + + /* + * Squawk to the monitor speaker if enabled. + */ + if (pp->sloppyclockflag & CLK_FLAG3) + if (write(pp->io.fd, (u_char *)&rbufp->recv_space, + (u_int)up->bufcnt) < 0) + perror("irig:"); +} + +/* + * 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 lope; /* integrator output */ + double env; /* envelope detector output */ + double dtemp; /* double temp */ + int i; /* index 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) { + up->xxing = lope / (up->lastsig - lope); + up->zxing += (up->carphase - 4 + up->xxing) / 8.; + } + 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->intmax > 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; + dtemp = BAUD - up->zxing; + i = up->envxing - up->envphase; + if (i < 0) + i -= i; + if (i <= 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 { + dtemp -= 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. + */ + DTOLFP(up->decim * (dtemp / SAMPLES + 1.) + up->fdelay, + <emp); + pp->lastrec = up->timestamp; + L_SUB(&pp->lastrec, <emp); + + /* + * 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; + + /* + * Local variables + */ + char syncchar; /* sync character (Spectracom only) */ + 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; + up->montime = pp->lastrec; + if (up->errflg == 0) { + up->timecnt++; + refclock_process(pp); + } + if (up->timecnt >= MAXSTAGE) { + refclock_receive(peer); + up->timecnt = 0; + up->pollcnt = 2; + } + 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, adjust the + * gain and set the input port. Set the port + * here on the assumption somebody might even + * change it on-wing. + */ + 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->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 %2d %6.3f %6.1f %s", + up->errflg, syncchar, pp->year, pp->day, + pp->hour, pp->minute, pp->second, + up->maxsignal, info.record.gain, up->modndx, + up->envxing, up->tc, up->yxing, up->freq * + 1e6 / SAMPLES, ulfptoa(&up->montime, 6)); + pp->lencode = strlen(pp->a_lastcode); + if (up->timecnt == 0 || pp->sloppyclockflag & + CLK_FLAG4) + record_clock_stats(&peer->srcadr, + pp->a_lastcode); +#ifdef DEBUG + if (debug > 2) + printf("irig: %s\n", pp->a_lastcode); +#endif /* DEBUG */ + } + } + up->lastbit = bit; +} + + +/* + * irig_poll - called by the transmit procedure + * + * This routine keeps track of status. If nothing is heard for two + * successive poll intervals, 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; + + /* + * Keep book for tattletales + */ + if (up->pollcnt == 0) { + refclock_report(peer, CEVNT_TIMEOUT); + up->timecnt = 0; + return; + } + up->pollcnt--; + 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. Set the new bits in the structure + * and call AUDIO_SETINFO. Upon return, the old bits are in the + * structure. + */ + if (up->clipcnt == 0) { + up->gain += 4; + if (up->gain > AUDIO_MAX_GAIN) + up->gain = AUDIO_MAX_GAIN; + } else if (up->clipcnt > SAMPLES / 100) { + up->gain -= 4; + if (up->gain < AUDIO_MIN_GAIN) + up->gain = AUDIO_MIN_GAIN; + } + AUDIO_INITINFO(&info); + info.record.port = up->port; + info.record.gain = up->gain; + info.record.error = 0; + ioctl(irig_ctl_fd, (int)AUDIO_SETINFO, &info); + if (info.record.error) + up->errflg |= IRIG_ERR_ERROR; +} + + +/* + * irig_audio - initialize audio device + * + * This code works with SunOS 4.1.3 and Solaris 2.6; however, it is + * believed generic and applicable to other systems with a minor twid + * or two. All it does is open the device, set the buffer size (Solaris + * only), preset the gain and set the input port. It assumes that the + * codec sample rate (8000 Hz), precision (8 bits), number of channels + * (1) and encoding (ITU-T G.711 mu-law companded) have been set by + * default. + */ +static int +irig_audio( + ) +{ + /* + * Open audio control device + */ + if ((irig_ctl_fd = open("/dev/audioctl", O_RDWR)) < 0) { + perror("audioctl"); + return(-1); + } +#ifdef HAVE_SYS_AUDIOIO_H + /* + * Set audio device parameters. + */ + AUDIO_INITINFO(&info); + info.record.buffer_size = AUDIO_BUFSIZ; + if (ioctl(irig_ctl_fd, (int)AUDIO_SETINFO, &info) < 0) { + perror("AUDIO_SETINFO"); + close(irig_ctl_fd); + return(-1); + } +#endif /* HAVE_SYS_AUDIOIO_H */ +#ifdef DEBUG + irig_debug(); +#endif /* DEBUG */ + return(0); +} + + +#ifdef DEBUG +/* + * irig_debug - display audio parameters + * + * This code doesn't really do anything, except satisfy curiousity and + * verify the ioctl's work. + */ +static void +irig_debug( + ) +{ + if (debug == 0) + return; +#ifdef HAVE_SYS_AUDIOIO_H + ioctl(irig_ctl_fd, (int)AUDIO_GETDEV, &device); + printf("irig: name %s, version %s, config %s\n", + device.name, device.version, device.config); +#endif /* HAVE_SYS_AUDIOIO_H */ + ioctl(irig_ctl_fd, (int)AUDIO_GETINFO, &info); + printf( + "irig: samples %d, channels %d, precision %d, encoding %d\n", + info.record.sample_rate, info.record.channels, + info.record.precision, info.record.encoding); +#ifdef HAVE_SYS_AUDIOIO_H + printf("irig: gain %d, port %d, buffer %d\n", + info.record.gain, info.record.port, + info.record.buffer_size); +#else /* HAVE_SYS_AUDIOIO_H */ + printf("irig: gain %d, port %d\n", + info.record.gain, info.record.port); +#endif /* HAVE_SYS_AUDIOIO_H */ + printf( + "irig: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n", + info.record.samples, info.record.eof, + info.record.pause, info.record.error, + info.record.waiting, info.record.balance); +#ifdef __NetBSD__ + printf("irig: monitor %d, blocksize %d, hiwat %d, lowat %d, mode %d\n", + info.monitor_gain, info.blocksize, info.hiwat, info.lowat, info.mode); +#else /* __NetBSD__ */ + printf("irig: monitor %d, muted %d\n", + info.monitor_gain, info.output_muted); +#endif /* __NetBSD__ */ +} +#endif /* DEBUG */ + +#else +int refclock_irig_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..f22714b --- /dev/null +++ b/contrib/ntp/ntpd/refclock_jupiter.c @@ -0,0 +1,1262 @@ +/* + * Copyright (c) 1997, 1998 + * 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(PPS) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" +#include "ntp_calendar.h" + +#include "jupiter.h" + +#include <sys/ppsclock.h> + +#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 */ + +/* + * The number of raw samples which we acquire to derive a single estimate. + * NSAMPLES ideally should not exceed the default poll interval 64. + * NKEEP must be a power of 2 to simplify the averaging process. + */ +#define NSAMPLES 64 +#define NKEEP 8 +#define REFCLOCKMAXDISPERSE .25 /* max sample dispersion */ + +/* + * 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 jupiterunit { + 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 */ + u_int gweek; /* current GPS week number */ + u_int32 lastsweek; /* last seconds into GPS week */ + u_int32 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_long sloppyclockflag; /* fudge flags */ + u_int known; /* position known yet? */ + int coderecv; /* total received samples */ + int nkeep; /* number of samples to preserve */ + int rshift; /* number of rshifts for division */ + l_fp filter[NSAMPLES]; /* offset filter */ + l_fp lastref; /* last reference timestamp */ + u_short sbuf[512]; /* local input buffer */ + int ssize; /* space used in sbuf */ +}; + +/* + * Function prototypes + */ +static void jupiter_canmsg P((struct peer *, u_int)); +static u_short jupiter_cksum P((u_short *, u_int)); +#ifdef QSORT_USES_VOID_P + int jupiter_cmpl_fp P((const void *, const void *)); +#else + int jupiter_cmpl_fp P((const l_fp *, const l_fp *)); +#endif /* not QSORT_USES_VOID_P */ +static void jupiter_config P((struct peer *)); +static void jupiter_debug P((struct peer *, char *, ...)) + __attribute__ ((format (printf, 2, 3))); +static char * jupiter_offset P((struct peer *)); +static char * jupiter_parse_t P((struct peer *, u_short *)); +static void jupiter_platform P((struct peer *, u_int)); +static void jupiter_poll P((int, struct peer *)); +static int jupiter_pps P((struct peer *)); +static char * jupiter_process P((struct peer *)); +static int jupiter_recv P((struct peer *)); +static void jupiter_receive P((register struct recvbuf *rbufp)); +static void jupiter_reqmsg P((struct peer *, u_int, u_int)); +static void jupiter_reqonemsg P((struct peer *, u_int)); +static char * jupiter_send P((struct peer *, struct jheader *)); +static void jupiter_shutdown P((int, struct peer *)); +static int jupiter_start P((int, struct peer *)); +static int jupiter_ttyinit P((struct peer *, int)); + +/* + * Transfer vector + */ +struct refclock refclock_jupiter = { + jupiter_start, /* start up driver */ + jupiter_shutdown, /* shut down driver */ + jupiter_poll, /* transmit poll message */ + noentry, /* (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( + register int unit, + register struct peer *peer + ) +{ + struct refclockproc *pp; + register struct jupiterunit *up; + register int fd; + char gpsdev[20]; + + /* + * Open serial port + */ + (void)sprintf(gpsdev, DEVICE, unit); + fd = open(gpsdev, O_RDWR +#ifdef O_NONBLOCK + | O_NONBLOCK +#endif + , 0); + if (fd < 0) { + jupiter_debug(peer, "jupiter_start: open %s: %s\n", + gpsdev, strerror(errno)); + return (0); + } + if (!jupiter_ttyinit(peer, fd)) + return (0); + + /* Allocate unit structure */ + if ((up = (struct jupiterunit *) + emalloc(sizeof(struct jupiterunit))) == NULL) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct jupiterunit)); + 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(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 */ + jupiter_config(peer); + + /* Turn on pulse gathering by requesting the first sample */ + if (ioctl(fd, CIOGETEV, (caddr_t)&up->ppsev) < 0) { + jupiter_debug(peer, "jupiter_ttyinit: CIOGETEV: %s\n", + strerror(errno)); + (void) close(fd); + free(up); + return (0); + } + up->lastserial = up->ppsev.serial; + memset(&up->ppsev, 0, sizeof(up->ppsev)); + return (1); +} + +/* + * jupiter_shutdown - shut down the clock + */ +static void +jupiter_shutdown(register int unit, register struct peer *peer) +{ + register struct jupiterunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + +/* + * jupiter_config - Configure the receiver + */ +static void +jupiter_config(register struct peer *peer) +{ + register int i; + register struct jupiterunit *up; + register struct refclockproc *pp; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* + * Initialize the unit variables + * + * STRANGE BEHAVIOUR WARNING: The fudge flags are not available + * at the time jupiter_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, "jupiter_config: mobile platform"); + } else { + up->moving = 0; /* Static Installation */ + } + + /* XXX fludge flags don't make the trip from the config to here... */ +#ifdef notdef + /* Configure for trailing edge triggers */ +#ifdef CIOSETTET + i = ((pp->sloppyclockflag & CLK_FLAG3) != 0); + jupiter_debug(peer, "jupiter_configure: (sloppyclockflag 0x%lx)\n", + pp->sloppyclockflag); + if (ioctl(pp->io.fd, CIOSETTET, (char *)&i) < 0) + msyslog(LOG_DEBUG, "jupiter_configure: CIOSETTET %d: %m", i); +#else + if (pp->sloppyclockflag & CLK_FLAG3) + msyslog(LOG_DEBUG, "jupiter_configure: \ +No kernel support for trailing edge trigger"); +#endif +#endif + + up->pollcnt = 2; + up->polled = 0; + up->known = 0; + up->gweek = 0; + up->lastsweek = 2 * WEEKSECS; + up->timecode = 0; + up->stime = 0; + up->ssize = 0; + up->coderecv = 0; + up->nkeep = NKEEP; + if (up->nkeep > NSAMPLES) + up->nkeep = NSAMPLES; + if (up->nkeep >= 1) + up->rshift = 0; + if (up->nkeep >= 2) + up->rshift = 1; + if (up->nkeep >= 4) + up->rshift = 2; + if (up->nkeep >= 8) + up->rshift = 3; + if (up->nkeep >= 16) + up->rshift = 4; + if (up->nkeep >= 32) + up->rshift = 5; + if (up->nkeep >= 64) + up->rshift = 6; + up->nkeep = 1; + i = up->rshift; + while (i > 0) { + up->nkeep *= 2; + i--; + } + + /* Stop outputting all messages */ + jupiter_canmsg(peer, JUPITER_ALL); + + /* Request the receiver id so we can syslog the firmware version */ + jupiter_reqonemsg(peer, JUPITER_O_ID); + + /* Flag that this the id was requested (so we don't get called again) */ + up->wantid = 1; + + /* Request perodic time mark pulse messages */ + jupiter_reqmsg(peer, JUPITER_O_PULSE, 1); + + /* Set application platform type */ + if (up->moving) + jupiter_platform(peer, JUPITER_I_PLAT_MED); + else + jupiter_platform(peer, JUPITER_I_PLAT_LOW); +} + +/* + * jupiter_poll - jupiter watchdog routine + */ +static void +jupiter_poll(register int unit, register struct peer *peer) +{ + register struct jupiterunit *up; + register struct refclockproc *pp; + + pp = peer->procptr; + up = (struct jupiterunit *)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 the receiver id to trigger a reconfig */ + jupiter_reqonemsg(peer, JUPITER_O_ID); + up->wantid = 0; + } + + /* + * polled every 64 seconds. Ask jupiter_receive to hand in + * a timestamp. + */ + up->polled = 1; + pp->polls++; +} + +/* + * jupiter_receive - receive gps data + * Gag me! + */ +static void +jupiter_receive(register struct recvbuf *rbufp) +{ + register int bpcnt, cc, size, ppsret; + register u_int32 last_timecode, laststime; + register char *cp; + register u_char *bp; + register u_short *sp; + register u_long sloppyclockflag; + register struct jupiterunit *up; + register struct jid *ip; + register struct jheader *hp; + register struct refclockproc *pp; + register struct peer *peer; + + /* Initialize pointers and read the timecode and timestamp */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* + * If operating mode has been changed, then reinitialize the receiver + * before doing anything else. + */ +/* XXX Sloppy clock flags are broken!! */ + sloppyclockflag = up->sloppyclockflag; + up->sloppyclockflag = pp->sloppyclockflag; + if ((pp->sloppyclockflag & CLK_FLAG2) != + (sloppyclockflag & CLK_FLAG2)) { + jupiter_debug(peer, + "jupiter_receive: mode switch: reset receiver\n"); + jupiter_config(peer); + return; + } + + up->pollcnt = 2; + + bp = (u_char *)rbufp->recv_buffer; + bpcnt = rbufp->recv_length; + + /* This shouldn't happen */ + if (bpcnt > sizeof(up->sbuf) - up->ssize) + bpcnt = sizeof(up->sbuf) - up->ssize; + + /* Append to input buffer */ + memcpy((u_char *)up->sbuf + up->ssize, bp, bpcnt); + up->ssize += bpcnt; + + /* While there's at least a header and we parse a intact message */ + while (up->ssize > sizeof(*hp) && (cc = jupiter_recv(peer)) > 0) { + hp = (struct jheader *)up->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\n", + 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 = up->stime; + up->stime = DS2UI(((struct jpulse *)sp)->stime); + if (laststime != 0 && up->stime - laststime <= 21) { + jupiter_debug(peer, "jupiter_receive: \ +avoided firmware bug (stime %.2f, laststime %.2f)\n", + (double)up->stime * 0.01, (double)laststime * 0.01); + break; + } + + /* Retrieve pps timestamp */ + ppsret = jupiter_pps(peer); + + /* Parse timecode (even when there's no pps) */ + last_timecode = up->timecode; + if ((cp = jupiter_parse_t(peer, sp)) != NULL) { + jupiter_debug(peer, + "jupiter_receive: pulse: %s\n", 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 */ + if ((cp = jupiter_offset(peer)) != NULL) { + jupiter_debug(peer, + "jupiter_receive: offset: %s\n", cp); + refclock_report(peer, CEVNT_BADTIME); + break; + } + + /* + * 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) + break; + + /* + * It's a live one! Remember this time. + */ + pp->lasttime = current_time; + + /* + * Determine the reference clock offset and + * dispersion. NKEEP of NSAMPLE offsets are + * passed through a median filter. + * Save the (filtered) offset and dispersion in + * pp->offset and pp->disp. + */ + if ((cp = jupiter_process(peer)) != NULL) { + jupiter_debug(peer, + "jupiter_receive: process: %s\n", cp); + refclock_report(peer, CEVNT_BADTIME); + break; + } + /* + * 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. + */ + jupiter_debug(peer, + "jupiter_receive: process time: \ +%4d-%03d %02d:%02d:%02d at %s, %s\n", + pp->year, pp->day, + pp->hour, pp->minute, pp->second, + prettydate(&pp->lastrec), lfptoa(&pp->offset, 6)); + + refclock_receive(peer); + + /* + * We have succeeded in answering the poll. + * Turn off the flag and return + */ + up->polled = 0; + break; + + case JUPITER_O_ID: + if (size != sizeof(struct jid)) { + jupiter_debug(peer, + "jupiter_receive: id: len %d != %u\n", + size, (int)sizeof(struct jid)); + refclock_report(peer, CEVNT_BADREPLY); + break; + } + /* + * If we got this message because the Jupiter + * just powered up, it needs to be reconfigured. + */ + ip = (struct jid *)sp; + jupiter_debug(peer, + "jupiter_receive: >> %s chan ver %s, %s (%s)\n", + ip->chans, ip->vers, ip->date, ip->opts); + msyslog(LOG_DEBUG, + "jupiter_receive: %s chan ver %s, %s (%s)\n", + ip->chans, ip->vers, ip->date, ip->opts); + if (up->wantid) + up->wantid = 0; + else { + jupiter_debug(peer, + "jupiter_receive: reset receiver\n"); + jupiter_config(peer); + /* Rese since jupiter_config() just zeroed it */ + up->ssize = cc; + } + break; + + default: + jupiter_debug(peer, + "jupiter_receive: >> unknown message id %d\n", + getshort(hp->id)); + break; + } + up->ssize -= cc; + if (up->ssize < 0) { + fprintf(stderr, "jupiter_recv: negative ssize!\n"); + abort(); + } else if (up->ssize > 0) + memcpy(up->sbuf, (u_char *)up->sbuf + cc, up->ssize); + } + record_clock_stats(&peer->srcadr, "<timecode is binary>"); +} + +/* + * jupiter_offset - Calculate the offset, and add to the rolling filter. + */ +static char * +jupiter_offset(register struct peer *peer) +{ + register struct jupiterunit *up; + register struct refclockproc *pp; + register int i; + l_fp offset; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* + * Calculate the offset + */ + if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, + pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui)) { + return ("jupiter_process: clocktime failed"); + } + if (pp->usec) { + TVUTOTSF(pp->usec, offset.l_uf); + } else { + MSUTOTSF(pp->msec, offset.l_uf); + } + L_ADD(&offset, &pp->fudgetime1); + up->lastref = offset; /* save last reference time */ + L_SUB(&offset, &pp->lastrec); /* form true offset */ + + /* + * A rolling filter. Initialize first time around. + */ + i = ((up->coderecv)) % NSAMPLES; + + up->filter[i] = offset; + if (up->coderecv == 0) + for (i = 1; (u_int) i < NSAMPLES; i++) + up->filter[i] = up->filter[0]; + up->coderecv++; + + return (NULL); +} + +/* + * jupiter_process - process the sample from the clock, + * passing it through a median filter and optionally averaging + * the samples. Returns offset and dispersion in "up" structure. + */ +static char * +jupiter_process(register struct peer *peer) +{ + register struct jupiterunit *up; + register struct refclockproc *pp; + register int i, n; + register int j, k; + l_fp offset, median, lftmp; + u_fp disp; + l_fp off[NSAMPLES]; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* + * Copy the raw offsets and sort into ascending order + */ + for (i = 0; i < NSAMPLES; i++) + off[i] = up->filter[i]; + qsort((char *)off, NSAMPLES, sizeof(l_fp), jupiter_cmpl_fp); + + /* + * Reject the furthest from the median of NSAMPLES samples until + * NKEEP samples remain. + */ + i = 0; + n = NSAMPLES; + while ((n - i) > up->nkeep) { + lftmp = off[n - 1]; + median = off[(n + i) / 2]; + L_SUB(&lftmp, &median); + L_SUB(&median, &off[i]); + if (L_ISHIS(&median, &lftmp)) { + /* reject low end */ + i++; + } else { + /* reject high end */ + n--; + } + } + + /* + * Copy key values to the billboard to measure performance. + */ + pp->lastref = up->lastref; + pp->coderecv = up->coderecv; + pp->filter[0] = off[0]; /* smallest offset */ + pp->filter[1] = off[NSAMPLES-1]; /* largest offset */ + for (j = 2, k = i; k < n; j++, k++) + pp->filter[j] = off[k]; /* offsets actually examined */ + + /* + * Compute the dispersion based on the difference between the + * extremes of the remaining offsets. Add to this the time since + * the last clock update, which represents the dispersion + * increase with time. We know that NTP_MAXSKEW is 16. If the + * sum is greater than the allowed sample dispersion, bail out. + * If the loop is unlocked, return the most recent offset; + * otherwise, return the median offset. + */ + lftmp = off[n - 1]; + L_SUB(&lftmp, &off[i]); + disp = LFPTOFP(&lftmp); + if (disp > REFCLOCKMAXDISPERSE) + return ("Maximum dispersion exceeded"); + + /* + * Now compute the offset estimate. If fudge flag 1 + * is set, average the remainder, otherwise pick the + * median. + */ + if (pp->sloppyclockflag & CLK_FLAG1) { + L_CLR(&lftmp); + while (i < n) { + L_ADD(&lftmp, &off[i]); + i++; + } + i = up->rshift; + while (i > 0) { + L_RSHIFT(&lftmp); + i--; + } + offset = lftmp; + } else { + i = (n + i) / 2; + offset = off[i]; + } + + /* + * The payload: filtered offset and dispersion. + */ + + pp->offset = offset; + pp->disp = disp; + + return (NULL); + +} + +/* Compare two l_fp's, used with qsort() */ +int +#ifdef QSORT_USES_VOID_P +jupiter_cmpl_fp(register const void *p1, register const void *p2) +#else +jupiter_cmpl_fp(register const l_fp *fp1, register const l_fp *fp2) +#endif +{ +#ifdef QSORT_USES_VOID_P + register const l_fp *fp1 = (const l_fp *)p1; + register const l_fp *fp2 = (const l_fp *)p2; +#endif + + if (!L_ISGEQ(fp1, fp2)) + return (-1); + if (L_ISEQU(fp1, fp2)) + return (0); + return (1); +} + +static char * +jupiter_parse_t(register struct peer *peer, register u_short *sp) +{ + register struct refclockproc *pp; + register struct jupiterunit *up; + register struct tm *tm; + register char *cp; + register struct jpulse *jp; + register struct calendar *jt; + register u_int32 sweek; + register u_int32 last_timecode; + register u_short flags; + time_t t; + struct calendar cal; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + jp = (struct jpulse *)sp; + + /* The timecode is presented as seconds into the current GPS week */ + sweek = DS2UI(jp->sweek); + + /* + * 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 (up->gweek == 0) + up->gweek = (time(NULL) - GPS_EPOCH) / WEEKSECS; + else if (sweek == 0 && up->lastsweek == WEEKSECS - 1) { + ++up->gweek; + jupiter_debug(peer, + "jupiter_parse_t: NEW gps week %u\n", up->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 (up->lastsweek == sweek) + jupiter_debug(peer, + "jupiter_parse_t: gps sweek not incrementing (%d)\n", + sweek); + else if (up->lastsweek != 2 * WEEKSECS && + up->lastsweek + 1 != sweek && + !(sweek == 0 && up->lastsweek == WEEKSECS - 1)) + jupiter_debug(peer, + "jupiter_parse_t: gps sweek jumped (was %d, now %d)\n", + up->lastsweek, sweek); + up->lastsweek = sweek; + + /* This timecode describes next pulse */ + last_timecode = up->timecode; + up->timecode = (u_int32)JAN_1970 + + GPS_EPOCH + (up->gweek * WEEKSECS) + sweek; + + if (last_timecode == 0) + /* XXX debugging */ + jupiter_debug(peer, + "jupiter_parse_t: UTC <none> (gweek/sweek %u/%u)\n", + up->gweek, sweek); + else { + /* XXX debugging */ + t = last_timecode - (u_int32)JAN_1970; + tm = gmtime(&t); + cp = asctime(tm); + + jupiter_debug(peer, + "jupiter_parse_t: UTC %.24s (gweek/sweek %u/%u)\n", + cp, up->gweek, sweek); + + /* Billboard last_timecode (which is now the current time) */ + jt = &cal; + caljulian(last_timecode, jt); + pp = peer->procptr; + pp->year = jt->year; + pp->day = jt->yearday; + pp->hour = jt->hour; + pp->minute = jt->minute; + pp->second = jt->second; + pp->msec = 0; + pp->usec = 0; + } + + /* XXX debugging */ + tm = gmtime(&up->ppsev.tv.tv_sec); + cp = asctime(tm); + flags = getshort(jp->flags); + jupiter_debug(peer, + "jupiter_parse_t: PPS %.19s.%06lu %.4s (serial %u)%s\n", + cp, up->ppsev.tv.tv_usec, cp + 20, up->ppsev.serial, + (flags & JUPITER_O_PULSE_VALID) == 0 ? + " NOT VALID" : ""); + + /* Toss if not designated "valid" by the gps */ + if ((flags & JUPITER_O_PULSE_VALID) == 0) { + refclock_report(peer, CEVNT_BADTIME); + return ("time mark not valid"); + } + + /* We better be sync'ed to UTC... */ + if ((flags & JUPITER_O_PULSE_UTC) == 0) { + refclock_report(peer, CEVNT_BADTIME); + return ("time mark not sync'ed to UTC"); + } + + return (NULL); +} + +/* + * Process a PPS signal, returning a timestamp. + */ +static int +jupiter_pps(register struct peer *peer) +{ + register struct refclockproc *pp; + register struct jupiterunit *up; + register int firsttime; + struct timeval ntp_tv; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* + * Grab the timestamp of the PPS signal. + */ + firsttime = (up->ppsev.tv.tv_sec == 0); + if (ioctl(pp->io.fd, CIOGETEV, (caddr_t)&up->ppsev) < 0) { + /* XXX Actually, if this fails, we're pretty much screwed */ + jupiter_debug(peer, "jupiter_pps: CIOGETEV: %s\n", + strerror(errno)); + refclock_report(peer, CEVNT_FAULT); + return (1); + } + + /* + * Check pps serial number against last one + */ + if (!firsttime && up->lastserial + 1 != up->ppsev.serial) { + if (up->ppsev.serial == up->lastserial) + jupiter_debug(peer, "jupiter_pps: no new pps event\n"); + else + jupiter_debug(peer, + "jupiter_pps: missed %d pps events\n", + up->ppsev.serial - up->lastserial - 1); + up->lastserial = up->ppsev.serial; + refclock_report(peer, CEVNT_FAULT); + return (1); + } + up->lastserial = up->ppsev.serial; + + /* + * Return the timestamp in pp->lastrec + */ + ntp_tv = up->ppsev.tv; + ntp_tv.tv_sec += (u_int32)JAN_1970; + TVTOTS(&ntp_tv, &pp->lastrec); + + return (0); +} + +/* + * jupiter_debug - print debug messages + */ +#if __STDC__ +static void +jupiter_debug(struct peer *peer, char *fmt, ...) +#else +static void +jupiter_debug(peer, fmt, va_alist) + struct peer *peer; + char *fmt; +#endif +{ + va_list ap; + + if (debug) { + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + /* + * Print debug message to stdout + * In the future, we may want to get get more creative... + */ + vfprintf(stderr, fmt, ap); + + va_end(ap); + } +} + +/* Checksum and transmit a message to the Jupiter */ +static char * +jupiter_send(register struct peer *peer, register struct jheader *hp) +{ + register u_int len, size; + register int cc; + register 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(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, putshort(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(register struct peer *peer, register u_int id, + register u_int interval) +{ + register struct jheader *hp; + register struct jrequest *rp; + register 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(peer, hp)) != NULL) + jupiter_debug(peer, "jupiter_reqmsg: %u: %s\n", id, cp); +} + +/* Cancel periodic message output */ +static struct jheader canmsg = { + putshort(JUPITER_SYNC), 0, 0, 0, + putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC), + 0 +}; + +static void +jupiter_canmsg(register struct peer *peer, register u_int id) +{ + register struct jheader *hp; + register char *cp; + + hp = &canmsg; + hp->id = putshort(id); + if ((cp = jupiter_send(peer, hp)) != NULL) + jupiter_debug(peer, "jupiter_canmsg: %u: %s\n", id, cp); +} + +/* Request a single message output */ +static struct jheader reqonemsg = { + putshort(JUPITER_SYNC), 0, 0, 0, + putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY), + 0 +}; + +static void +jupiter_reqonemsg(register struct peer *peer, register u_int id) +{ + register struct jheader *hp; + register char *cp; + + hp = &reqonemsg; + hp->id = putshort(id); + if ((cp = jupiter_send(peer, hp)) != NULL) + jupiter_debug(peer, "jupiter_reqonemsg: %u: %s\n", 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, + putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK), 0 }, + { 0, 0, 0 } +}; + +static void +jupiter_platform(register struct peer *peer, register u_int platform) +{ + register struct jheader *hp; + register struct jplat *pp; + register char *cp; + + hp = &platmsg.jheader; + pp = &platmsg.jplat; + pp->platform = putshort(platform); + if ((cp = jupiter_send(peer, hp)) != NULL) + jupiter_debug(peer, "jupiter_platform: %u: %s\n", platform, cp); +} + +/* Checksum "len" shorts */ +static u_short +jupiter_cksum(register u_short *sp, register u_int len) +{ + register 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(register struct peer *peer) +{ + register int n, len, size, cc; + register struct refclockproc *pp; + register struct jupiterunit *up; + register struct jheader *hp; + register u_char *bp; + register u_short *sp; + + pp = peer->procptr; + up = (struct jupiterunit *)pp->unitptr; + + /* Must have at least a header's worth */ + cc = sizeof(*hp); + size = up->ssize; + if (size < cc) + return (0); + + /* Search for the sync short if missing */ + sp = up->sbuf; + hp = (struct jheader *)sp; + if (getshort(hp->sync) != JUPITER_SYNC) { + /* Wasn't at the front, sync up */ + jupiter_debug(peer, "syncing"); + bp = (u_char *)sp; + n = size; + while (n >= 2) { + if (bp[0] != (JUPITER_SYNC & 0xff)) { + jupiter_debug(peer, "{0x%x}", bp[0]); + ++bp; + --n; + continue; + } + if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff)) + break; + jupiter_debug(peer, "{0x%x 0x%x}", bp[0], bp[1]); + bp += 2; + n -= 2; + } + jupiter_debug(peer, "\n"); + /* Shuffle data to front of input buffer */ + if (n > 0) + memcpy(sp, bp, n); + size = n; + up->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(peer, "jupiter_recv: bad header checksum!\n"); + /* This is drastic but checksum errors should be rare */ + up->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(peer, + "jupiter_recv: bad payload checksum!\n"); + /* This is drastic but checksum errors should be rare */ + up->ssize = 0; + return (0); + } + cc += n; + } + return (cc); +} + +static int +jupiter_ttyinit(register struct peer *peer, register int fd) +{ + struct termios termios; + + memset((char *)&termios, 0, sizeof(termios)); + if (cfsetispeed(&termios, B9600) < 0 || + cfsetospeed(&termios, B9600) < 0) { + jupiter_debug(peer, + "jupiter_ttyinit: cfsetispeed/cfgetospeed: %s\n", + strerror(errno)); + return (0); + } +#ifdef HAVE_CFMAKERAW + cfmakeraw(&termios); +#else + termios.c_iflag &= ~(IMAXBEL | IXOFF | INPCK | BRKINT | PARMRK | + ISTRIP | INLCR | IGNCR | ICRNL | IXON | IGNPAR); + termios.c_iflag |= IGNBRK; + termios.c_oflag &= ~OPOST; + termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | ISIG | + IEXTEN | NOFLSH | TOSTOP | PENDIN); + termios.c_cflag &= ~(CSIZE | PARENB); + termios.c_cflag |= CS8 | CREAD; + termios.c_cc[VMIN] = 1; +#endif + termios.c_cflag |= CLOCAL; + if (tcsetattr(fd, TCSANOW, &termios) < 0) { + jupiter_debug(peer, "jupiter_ttyinit: tcsetattr: %s\n", + strerror(errno)); + return (0); + } + +#ifdef TIOCSPPS + if (ioctl(fd, TIOCSPPS, (char *)&fdpps) < 0) { + jupiter_debug(peer, "jupiter_ttyinit: TIOCSPPS: %s\n", + strerror(errno)); + return (0); + } +#endif +#ifdef I_PUSH + if (ioctl(fd, I_PUSH, "ppsclock") < 0) { + jupiter_debug(peer, "jupiter_ttyinit: push ppsclock: %s\n", + strerror(errno)); + return (0); + } +#endif + + return (1); +} + +#else /* not (REFCLOCK && CLOCK_JUPITER && PPS) */ +int refclock_jupiter_bs; +#endif /* not (REFCLOCK && CLOCK_JUPITER && PPS) */ diff --git a/contrib/ntp/ntpd/refclock_leitch.c b/contrib/ntp/ntpd/refclock_leitch.c new file mode 100644 index 0000000..edee3f8 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_leitch.c @@ -0,0 +1,619 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.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) */ +#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);} + +#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; + } +#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..12184db --- /dev/null +++ b/contrib/ntp/ntpd/refclock_local.c @@ -0,0 +1,258 @@ +/* wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT */ + +/* + * refclock_local - local pseudo-clock driver + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef REFCLOCK +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.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 3 or 4) 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 3 by default, but it can be changed + * by the fudge command and/or the ntpdc utility. The reference ID is + * "LCL" 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 REFID "LCL\0" /* reference ID */ +#define DESCRIPTION "Undisciplined local clock" /* WRU */ + +#define STRATUM 3 /* 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; + peer->stratum = STRATUM; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); +#if defined(VMS) && defined(VMS_LOCALUNIT) + /* provide a non-standard REFID */ + if(unit == VMS_LOCALUNIT) + memcpy((char *)&pp->refid,"LCLv",4); +#endif /* VMS && VMS_LOCALUNIT */ + poll_time = current_time; + return (1); +} + + +/* + * local_poll - called by the transmit procedure + */ +static void +local_poll( + int unit, + struct peer *peer + ) +{ + struct refclockproc *pp; +#if defined(KERNEL_PLL) && defined(STA_CLK) + struct timex ntv; + int retval; +#endif /* KERNEL_PLL STA_CLK */ + +#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); + pp->leap = LEAP_NOWARNING; + pp->disp = DISPERSION; + pp->variance = 0; +#if defined(KERNEL_PLL) && defined(STA_CLK) + + /* + * If the kernel pll code is up and running, somebody else + * may come diddle the clock. If so, they better use ntp_adjtime(), + * and set the STA_CLK bit in the status word. In this case, the + * performance information is read from the kernel and becomes the + * variables presented to the clock mitigation process. + */ + if (pll_control && kern_enable && (peer->flags & FLAG_PREFER)) { + memset((char *)&ntv, 0, sizeof ntv); + retval = ntp_adjtime(&ntv); + if (ntv.status & STA_CLK) { + ext_enable = 1; + switch(retval) { + + case TIME_OK: + pp->leap = LEAP_NOWARNING; + break; + + case TIME_INS: + pp->leap = LEAP_ADDSECOND; + break; + + case TIME_DEL: + pp->leap = LEAP_DELSECOND; + break; + + case TIME_ERROR: + pp->leap = LEAP_NOTINSYNC; + } + pp->disp = ntv.maxerror / 1e6; + pp->variance = SQUARE(ntv.esterror / 1e6); + } + } else { + ext_enable = 0; + } +#endif /* KERNEL_PLL STA_CLK */ + refclock_receive(peer); + pp->fudgetime1 = 0; +} +#endif /* REFCLOCK */ diff --git a/contrib/ntp/ntpd/refclock_msfees.c b/contrib/ntp/ntpd/refclock_msfees.c new file mode 100644 index 0000000..01fe27f --- /dev/null +++ b/contrib/ntp/ntpd/refclock_msfees.c @@ -0,0 +1,1444 @@ +/* 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 <ctype.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.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" + +/* + fudgefactor = fudgetime1; + os_delay = fudgetime2; + offset_fudge = os_delay + fudgefactor + inherent_delay; + stratumtouse = fudgeval1 & 0xf + debug = 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 loose 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 }; */ + +#ifdef DEBUG +static int debug; +#endif + +#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 int offcompare P((l_fp *a, l_fp *b)); +static void ees_process P((struct eesunit *ees)); + +/* + * 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 ((debug & 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 (debug & DB_LOG_AWAITMORE) + msyslog(LOG_INFO, + "I: ees clock %d: %x == %x: 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 (debug & 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 && !(debug & 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 (debug & 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, + (debug & DB_NO_PPS) ? " [no PPS]" : ""); + + if (ees->nsamples++ == NCODES-1) ees_process(ees); + + /* Done! */ +} + + +/* offcompare - auxiliary comparison routine for offset sort */ + +static int +offcompare( + l_fp *a, + l_fp *b + ) +{ + return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1); +} + + +/* 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((char *) coffs, (u_int)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 (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog( + (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, + "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d", + debug & (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 & (1 << 31)) != + (((long) offset.l_uf) & ( 1 << 31))) + { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d", + new & (1 << 31), + ((long) offset.l_uf) & ( 1 << 31), + new, (long) offset.l_uf, + (new < 0) ? -1 : 1); + offset.l_ui += (new < 0) ? -1 : 1; + } + dispersion /= 4; + if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog( + (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] Smooth data: %ld -> %ld, dispersion now %f", + debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE), + ((long) offset.l_uf) / 4295, new / 4295, + (dispersion * 1526) / 100); + offset.l_uf = (unsigned long) new; + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "[%x] No smooth as delta not %d < %ld < %d", + debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), + - FRACT_SEC(100), diff, FRACT_SEC(100)); + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)", + debug & (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..28f563f --- /dev/null +++ b/contrib/ntp/ntpd/refclock_mx4200.c @@ -0,0 +1,1774 @@ +/* + * 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(PPS) + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" + +#include "mx4200.h" + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#ifdef HAVE_SYS_TERMIOS_H +# include <sys/termios.h> +#endif +#ifdef HAVE_SYS_PPSCLOCK_H +# include <sys/ppsclock.h> +#endif + +#ifndef HAVE_STRUCT_PPSCLOCKEV +struct ppsclockev { +# ifdef HAVE_TIMESPEC + struct timespec tv; +# else + struct timeval tv; +# endif + u_int serial; +}; +#endif /* ! HAVE_STRUCT_PPSCLOCKEV */ + +/* + * 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_RIGHT_NOW 1998 + +/* + * 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. + * Reference: Dr. Thomas A. Clark's Totally Accurate Clock (TAC) files at + * ftp://aleph.gsfc.nasa.gov/GPS/totally.accurate.clock/ + * For a 6-channel Motorola Oncore, he indicates that good nominal + * HDOP and VDOP are 1.50 and 2.00 respectively. Given the relationship + * HDOP^2 = NDOP^2 + EDOP^2 and assuming EDOP and NDOP are equal, we + * have a nominal NDOP = EDOP = sqrt((HDOP*HDOP)/2). An 8-channel + * Oncore does well with HDOP=1.20 and VDOP=1.70. + */ +#define INTERVAL 1 /* Interval between position measurements (s) */ +#define AVGING_TIME 24 /* Number of hours to average */ +#define USUAL_EDOP 1.06066 /* used for normalizing EDOP */ +#define USUAL_NDOP 1.06066 /* used for normalizing NDOP */ +#define USUAL_VDOP 2.00 /* used for normalizing VDOP */ +#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 filt_lat; /* latitude filter length */ + double filt_lon; /* longitude filter length */ + double filt_alt; /* height filter length */ + double edop; /* EDOP (east DOP) */ + double ndop; /* NDOP (north DOP) */ + double vdop; /* VDOP (vertical DOP) */ + 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 */ +}; + +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 +#endif +#else +#ifndef __attribute__ +#define __attribute__(args) +#endif +#endif +/* 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_d 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 void 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]; + +#ifdef HAVE_TIOCGPPSEV +#ifdef HAVE_TERMIOS + struct termios ttyb; +#endif /* HAVE_TERMIOS */ +#ifdef HAVE_SYSV_TTYS + struct termio ttyb; +#endif /* HAVE_SYSV_TTYS */ +#ifdef HAVE_BSD_TTYS + struct sgttyb ttyb; +#endif /* HAVE_BSD_TTYS */ +#endif /* HAVE_TIOCGPPSEV */ + + /* + * Open serial port + */ + (void)sprintf(gpsdev, DEVICE, unit); + if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) { + return (0); + } +#ifdef HAVE_TIOCGPPSEV + if (fdpps > 0) { + /* + * Truly nasty hack in order to get this to work on Solaris 7. + * Really, refclock_open() should set the port properly, but + * it doesn't work (as of ntp-4.0.98a) - almost 99% dropped + * PPS signals with "Interrupted system call". Even this + * still gives a 5% error rate. + */ + ttyb.c_iflag = IGNCR; + ttyb.c_oflag = 0; + ttyb.c_cflag = CS8 | CREAD | CLOCAL; + ttyb.c_lflag = ICANON; + if (tcsetattr(fdpps, TCSAFLUSH, &ttyb) < 0) { + return (0); + } + } +#endif /* HAVE_TIOCGPPSEV */ + + /* + * Allocate unit structure + */ + if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) { + (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 */ + mx4200_config(peer); + return (1); +} + + +/* + * 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 void +mx4200_config( + struct peer *peer + ) +{ + char tr_mode; + int add_mode; + register struct mx4200unit *up; + struct refclockproc *pp; + + 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->filt_lat = 0.0; + up->filt_lon = 0.0; + up->filt_alt = 0.0; + up->edop = USUAL_EDOP; + up->ndop = USUAL_NDOP; + up->vdop = USUAL_VDOP; + up->last_leap = 0; /* LEAP_NOWARNING */ + up->clamp_time = current_time + (AVGING_TIME * 60 * 60); + up->log_time = current_time + SLEEPTIME; + + /* + * "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 "022" DOPs + */ + mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg, + PMVXG_S_PORTCONF, + PMVXG_D_DOPS, /* 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 */ + + + /* + * "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 */ +} + +/* + * 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 "022" DOPs + */ + mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg, + PMVXG_S_PORTCONF, + PMVXG_D_DOPS, /* 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 */ + + /* + * "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,", 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 */ + /* Altitude Reference */ + + 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); + + /* + * "000" Status message + */ + + if (sentence_type == 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); + return; + } + + /* + * "021" Position, Height, Velocity message, + * if we are still averaging our position + */ + if (sentence_type == PMVXG_D_PHV && !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 %.9f %.9f %.4f\n", + up->avg_lat, up->avg_lon, up->avg_alt); + mx4200_debug(peer, + "mx4200_receive: position len %.4f %.4f %.4f\n", + up->filt_lat, up->filt_lon, up->filt_alt); + mx4200_debug(peer, + "mx4200_receive: position dop %.1f %.1f %.1f\n", + up->ndop, up->edop, up->vdop); + /* + * 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); + } + return; + } + + /* + * "022" DOPs, if we are still averaging our position + */ + if (sentence_type == PMVXG_D_DOPS && !up->known) { + if ((cp = mx4200_parse_d(peer)) != NULL) { + mx4200_debug(peer, "mx4200_receive: dop: %s\n", cp); + return; + } + return; + } + + /* + * Print to the syslog: + * "004" Mode Data + * "030" Software Configuration + * "523" Time Recovery Parameters Currently in Use + */ + if (sentence_type == PMVXG_D_MODEDATA || + sentence_type == PMVXG_D_SOFTCONF || + sentence_type == PMVXG_D_TRECOVUSEAGE ) { + if ((cp = mx4200_parse_s(peer)) != NULL) { + mx4200_debug(peer, + "mx4200_receive: multi-record: %s\n", cp); + return; + } + return; + } + + /* + * "830" Time Recovery Results message + */ + if (sentence_type == 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); + + refclock_receive(peer); + + /* + * We have succeeded in answering the poll. + * Turn off the flag and return + */ + up->polled = 0; + return; + } + + /* + * Ignore all other sentence types + */ + 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 occurence, 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, 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_RIGHT_NOW) { + 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; + pp->msec = 0; + pp->usec = 0; + + /* + * 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, weight; + 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 weighted averages + */ + weight = USUAL_EDOP / up->edop; + weight *= weight; + up->avg_lon = (up->filt_lon * up->avg_lon) + (weight * lon); + up->filt_lon += weight; + up->avg_lon = up->avg_lon / up->filt_lon; + + weight = USUAL_NDOP / up->ndop; + weight *= weight; + up->avg_lat = (up->filt_lat * up->avg_lat) + (weight * lat); + up->filt_lat += weight; + up->avg_lat = up->avg_lat / up->filt_lat; + + weight = USUAL_VDOP / up->vdop; + weight *= weight; + up->avg_alt = (up->filt_alt * up->avg_alt) + (weight * alt); + up->filt_alt += weight; + up->avg_alt = up->avg_alt / up->filt_alt; + + mx4200_debug(peer, + "mx4200_receive: position rdg %.9f %.9f %.4f (CM=%.9f)\n", + lat, lon, alt, up->central_meridian); + + return (NULL); +} + +/* + * Parse a mx4200 DOP sentence. + * + * A typical message looks like this. Checksum has already been stripped. + * + * $PMVXG,022,SSSSSS.SSEE.E,NN.N,VV.V,XX,XX,XX,XX,XX,XX + * + * Field Field Contents + * ----- -------------- + * Block Label: $PMVXG + * Sentence Type: 022=DOPs. The DOP values in this sentence + * correspond to the satellites listed. The PRNs in + * the message are listed in receiver channel number order + * 1 UTC measurement time (seconds into week) + * 2 EDOP (east DOP) + * 3 NDOP (north DOP) + * 4 VDOP (vertical DOP) + * 5 PRN on channel 1 + * 6 PRN on channel 2 + * 7 PRN on channel 3 + * 8 PRN on channel 4 + * 9 PRN on channel 5 + * 10 PRN on channel 6 + * 11 PRN on channel 7 (12-channel receivers only) + * 12 PRN on channel 8 (12-channel receivers only) + * 13 PRN on channel 9 (12-channel receivers only) + * 14 PRN on channel 10 (12-channel receivers only) + * 15 PRN on channel 11 (12-channel receivers only) + * 16 PRN on channel 12 (12-channel receivers only) + */ +static char * +mx4200_parse_d( + struct peer *peer + ) +{ + struct refclockproc *pp; + struct mx4200unit *up; + int sentence_type; + double mtime, edop, ndop, vdop; + + pp = peer->procptr; + up = (struct mx4200unit *)pp->unitptr; + + /* Should never happen! */ + if (up->moving) return ("mobile platform - no dop!"); + + sscanf ( pp->a_lastcode, "$PMVXG,%d,%lf,%lf,%lf,%lf", + &sentence_type, &mtime, &edop, &ndop, &vdop); + + /* Sentence type */ + if (sentence_type != PMVXG_D_DOPS) + return ("wrong rec-type"); + + /* Update values */ + if (edop <= 0.0 || ndop <= 0.0 || vdop <= 0.0) + return ("nonpositive dop"); + up->edop = edop; + up->ndop = ndop; + up->vdop = vdop; + + 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, returning a timestamp. + */ +static int +mx4200_pps( + struct peer *peer + ) +{ + int temp_serial; + struct refclockproc *pp; + struct mx4200unit *up; + + int request; +#ifdef HAVE_CIOGETEV + request = CIOGETEV; +#endif +#ifdef HAVE_TIOCGPPSEV + request = TIOCGPPSEV; +#endif + + pp = peer->procptr; + up = (struct mx4200unit *)pp->unitptr; + + /* + * Grab the timestamp of the PPS signal. + */ + temp_serial = up->ppsev.serial; + if (ioctl(fdpps, request, (caddr_t)&up->ppsev) < 0) { + /* XXX Actually, if this fails, we're pretty much screwed */ + mx4200_debug(peer, + "mx4200_pps: CIOGETEV/TIOCGPPSEV: serial=%d, fdpps=%d, %s\n", + up->ppsev.serial, fdpps, strerror(errno)); + refclock_report(peer, CEVNT_FAULT); + return(1); + } + if (temp_serial == up->ppsev.serial) { + mx4200_debug(peer, + "mx4200_pps: ppsev serial not incrementing: %d\n", + up->ppsev.serial); + refclock_report(peer, CEVNT_FAULT); + return(1); + } + + /* + * Check pps serial number against last one + */ + if (up->lastserial + 1 != up->ppsev.serial && up->lastserial != 0) { + if (up->ppsev.serial == up->lastserial) + mx4200_debug(peer, "mx4200_pps: no new pps event\n"); + else + mx4200_debug(peer, "mx4200_pps: missed %d pps events\n", + up->ppsev.serial - up->lastserial - 1); + refclock_report(peer, CEVNT_FAULT); + } + up->lastserial = up->ppsev.serial; + + /* + * Return the timestamp in pp->lastrec + */ + up->ppsev.tv.tv_sec += (u_int32) JAN_1970; + TVTOTS(&up->ppsev.tv,&pp->lastrec); + + return(0); +} + +/* + * mx4200_debug - print debug messages + */ +#if __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 +{ + va_list ap; + struct refclockproc *pp; + struct mx4200unit *up; + + if (debug) { + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + 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); + } +} + +/* + * Send a character string to the receiver. Checksum is appended here. + */ +static void +#if __STDC__ +mx4200_send(struct peer *peer, char *fmt, ...) +#else + 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 __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif /* __STDC__ */ + + pp = peer->procptr; + up = (struct mx4200unit *)pp->unitptr; + + cp = buf; + *cp++ = '$'; +#ifdef notdef + /* BSD is rational */ + n = vsnprintf(cp, sizeof(buf) - 1, fmt, ap); +#else + /* SunOS sucks */ + (void)vsprintf(cp, fmt, ap); + n = strlen(cp); +#endif /* notdef */ + ck = mx4200_cksum(cp, n); + cp += n; + ++n; +#ifdef notdef + /* BSD is rational */ + n += snprintf(cp, sizeof(buf) - n - 5, "*%02X\r\n", ck); +#else + /* SunOS sucks */ + sprintf(cp, "*%02X\r\n", ck); + n += strlen(cp); +#endif /* notdef */ + + 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_nmea.c b/contrib/ntp/ntpd/refclock_nmea.c new file mode 100644 index 0000000..211d01e --- /dev/null +++ b/contrib/ntp/ntpd/refclock_nmea.c @@ -0,0 +1,411 @@ +/* + * 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(REFCLOCK) && defined(CLOCK_NMEA) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +/* + * 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. + */ + +/* + * Definitions + */ +#define DEVICE "/dev/gps%d" /* name of radio device */ +#define SPEED232 B4800 /* uart speed (4800 bps) */ +#define PRECISION (-9) /* precision assumed (about 2 ms) */ +#define DCD_PRECISION (-20) /* precision assumed (about 1 us) */ +#define REFID "GPS\0" /* reference id */ +#define DESCRIPTION "NMEA GPS Clock" /* who we are */ + +#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 */ +}; + +/* + * Function prototypes + */ +static int nmea_start P((int, struct peer *)); +static void nmea_shutdown P((int, struct peer *)); +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 */ + noentry, /* handle control */ + 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); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct nmeaunit *) + emalloc(sizeof(struct nmeaunit)))) { + (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 = DCD_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); + + return (1); +} + +/* + * 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; + io_closeclock(&pp->io); + free(up); +} + +/* + * 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; + l_fp trtmp; + int month, day; + int i; + char *cp, *dp; + int cmdtype; + + /* + * Initialize pointers and read the timecode and timestamp + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct nmeaunit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); + + /* + * There is a case that a <CR><LF> gives back a "blank" line + */ + if (pp->lencode == 0) + return; + + /* + * We get a buffer and timestamp for each <cr>. + */ + pp->lastrec = up->tstamp = trtmp; + up->pollcnt = 2; +#ifdef DEBUG + if (debug) + printf("nmea: timecode %d %s\n", pp->lencode, + pp->a_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 + * $GPGGA,162617.0,4548.339,N,00837.719,E,1,07,0.97,00262,M,048,M,,*5D + */ +#define GPRMC 0 +#define GPXXX 1 +#define GPGCA 2 + cp = pp->a_lastcode; + cmdtype=0; + if(strncmp(cp,"$GPRMC",6)==0) { + cmdtype=GPRMC; + } + else if(strncmp(cp,"$GPGGA",6)==0) { + cmdtype=GPGCA; + } + else if(strncmp(cp,"$GPXXX",6)==0) { + cmdtype=GPXXX; + } + else + return; + + switch( cmdtype ) { + case GPRMC: + case GPGCA: + /* + * Check time code format of NMEA + */ + + dp = field_parse(cp,1); + 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; + } + + /* + * Test for synchronization. Check for quality byte. + */ + dp = field_parse(cp,2); + if( dp[0] != 'A') { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + break; + case GPXXX: + return; + default: + return; + + } + + if (cmdtype ==GPGCA) { + /* 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; + } else { + dp = field_parse(cp,9); + /* + * Convert date and check values. + */ + 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'; + } + + 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; + + dp = field_parse(cp,1); + /* + * 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'; + pp->msec = 0; + + if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + /* + * 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; + + refclock_receive(peer); + + 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..9ff3d23 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_oncore.c @@ -0,0 +1,1660 @@ +/* + * ---------------------------------------------------------------------------- + * "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. + * + * 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 + * + * -------------------------------------------------------------------------- + * 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 (where n and N are the unit number, viz 127.127.30.N) + * or /etc/ntp.oncore + * -------------------------------------------------------------------------- + * Reg.Clemens <reg@dwf.com> Sep98. + * Original code written for FreeBSD. + * With these mods it works on SunOS, Solaris (untested) and Linux + * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + changes). + * + * 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 */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_ONCORE) + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +#ifdef HAVE_PPSAPI +# ifdef HAVE_TIMEPPS_H +# include <timepps.h> +# else +# ifdef HAVE_SYS_TIMEPPS_H +# include <sys/timepps.h> +# endif +# endif +#endif + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +#ifdef HAVE_SYS_TERMIOS_H +#include <sys/termios.h> +#endif + +#ifdef HAVE_SYS_PPSCLOCK_H +# include <sys/ppsclock.h> +#endif + +#ifndef HAVE_STRUCT_PPSCLOCKEV +struct ppsclockev { +# ifdef HAVE_TIMESPEC + struct timespec tv; +# else + struct timeval tv; +# endif + u_int serial; +}; +#endif /* not HAVE_STRUCT_PPSCLOCKEV */ + +enum receive_state { + ONCORE_NO_IDEA, + ONCORE_RESET_SENT, + ONCORE_TEST_SENT, + ONCORE_ID_SENT, + ONCORE_ALMANAC, + ONCORE_RUN +}; + +enum site_survey_state { + ONCORE_SS_UNKNOWN, + ONCORE_SS_HW, + ONCORE_SS_SW, + ONCORE_SS_DONE +}; + +struct instance { + int unit; /* 127.127.30.unit */ + int ttyfd; /* TTY file descriptor */ + int ppsfd; /* PPS file descriptor */ +#ifdef HAVE_PPSAPI + pps_handle_t pps_h; + pps_params_t pps_p; +#endif + enum receive_state o_state; /* Receive state */ + + enum site_survey_state site_survey; /* Site Survey state */ + + struct refclockproc *pp; + struct peer *peer; + + int Bj_day; + int assert; + + long delay; /* ns */ + long offset; /* ns */ + + double ss_lat; + double ss_long; + double ss_ht; + int ss_count; + u_char ss_ht_type; + int posn_set; + + int printed; + int pollcnt; + int polled; + u_int ev_serial; + int Rcvptr; + u_char Rcvbuf[500]; + u_char Ea[77]; + u_char En[70]; + u_char Cj[300]; + u_char As; + u_char Ay; + u_char Az; + u_char init_type; + s_char saw_tooth; +}; + +#define rcvbuf instance->Rcvbuf +#define rcvptr instance->Rcvptr + +static void oncore_consume P((struct instance *)); +static void oncore_poll P((int, struct peer *)); +static void oncore_read_config P((struct instance *)); +static void oncore_receive P((struct recvbuf *)); +static void oncore_sendmsg P((int fd, u_char *, u_int)); +static void oncore_shutdown P((int, struct peer *)); +static int oncore_start P((int, struct peer *)); +static void oncore_stats P((struct instance *)); + +static void oncore_msg_any P((struct instance *, u_char *, u_int, int)); +static void oncore_msg_As P((struct instance *, u_char *, u_int)); +static void oncore_msg_At P((struct instance *, u_char *, u_int)); +static void oncore_msg_Ay P((struct instance *, u_char *, u_int)); +static void oncore_msg_Az P((struct instance *, u_char *, u_int)); +static void oncore_msg_Bj P((struct instance *, u_char *, u_int)); +static void oncore_msg_Cf P((struct instance *, u_char *, u_int)); +static void oncore_msg_Cj P((struct instance *, u_char *, u_int)); +static void oncore_msg_Ea P((struct instance *, u_char *, u_int)); +static void oncore_msg_En P((struct instance *, u_char *, u_int)); +static void oncore_msg_Fa P((struct instance *, u_char *, u_int)); + +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 UT or VP Oncore. + */ + +static struct { + const char flag[3]; + const int len; + void (*handler) P((struct instance *, u_char *, u_int)); + const char *fmt; +} oncore_messages[] = { + /* Ea and En first since they're most common */ + { "Ea", 76, oncore_msg_Ea, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" }, + { "En", 69, oncore_msg_En, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" }, + { "Ab", 10, 0, "" }, + { "Ac", 11, 0, "" }, + { "Ad", 11, 0, "" }, + { "Ae", 11, 0, "" }, + { "Af", 15, 0, "" }, + { "As", 20, oncore_msg_As, "" }, + { "At", 8, oncore_msg_At, "" }, + { "Aw", 8, 0, "" }, + { "Ay", 11, oncore_msg_Ay, "" }, + { "Az", 11, oncore_msg_Az, "" }, + { "AB", 8, 0, "" }, + { "Bb", 92, 0, "" }, + { "Bj", 8, oncore_msg_Bj, "" }, + { "Cb", 33, 0, "" }, + { "Cf", 7, oncore_msg_Cf, "" }, + { "Cg", 8, 0, "" }, + { "Ch", 9, 0, "" }, + { "Cj", 294, oncore_msg_Cj, "" }, + { "Ek", 71, 0, "" }, + { "Fa", 9, oncore_msg_Fa, "" }, + { "Sz", 8, 0, "" }, + { {0}, 7, 0, ""} +}; + + +/* + * Position Set. + */ +u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; +u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; +u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; + +/* + * Position-Hold Mode + * Start automatic site survey + */ +static u_char oncore_cmd_At[] = { 'A', 't', 2 }; + +/* + * Position-Hold Position + */ +u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; +u_char oncore_cmd_Asx[]= { 'A', 's', 0x7f, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, 0xff }; + +/* + * Set to UTC time (not GPS). + */ +u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; + +/* + * Read back PPS Offset for Output + */ +u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; +u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; + +/* + * Read back Cable Delay for Output + */ +u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; +u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; + +/* + * Application type = static. + */ +u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; + +/* + * Visible Satellite Status Msg. + */ +u_char oncore_cmd_Bb[] = { 'B', 'b', 0 }; /* just turn off */ + +/* + * Leap Second Pending Message + * Request message once + */ +u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; + +/* + * Set to Defaults + */ +static u_char oncore_cmd_Cf[] = { 'C', 'f' }; + +/* + * Set to Position Fix mode (only needed on VP). + */ +u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; + +/* + * Receiver Id + */ +static u_char oncore_cmd_Cj[] = { 'C', 'j' }; + +/* + * Position/Status/Data message + * Send once per second + */ +static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; + +/* + * Position/Status Extension Msg + */ +u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ + +/* + * Time Raim Setup & Status Message + * Send once per second + * Time-RAIM on + * Alarm limit 1us + * PPS on when we have the first sat + */ +static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* + * Self-test + */ +static u_char oncore_cmd_Fa[] = { 'F', 'a' }; + +#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */ +#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */ +#define INIT_FILE "/etc/ntp.oncore" /* optional init file */ + +#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) { unsigned 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 + ) +{ + register struct instance *instance; + struct refclockproc *pp; + int fd1, fd2, mode; + char device1[30], device2[30]; + const char *cp; + struct stat stat1, stat2; + + /* 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. + */ + + (void)sprintf(device1, DEVICE1, unit); + (void)sprintf(device2, DEVICE2, unit); + + if (stat(device1, &stat1)) { + perror("ONCORE: stat fd1"); + exit(1); + } + + if (stat(device2, &stat2)) { + perror("ONCORE: stat fd2"); + exit(1); + } + + if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) { + /* same device here */ + if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW +#ifdef HAVE_PPSAPI +#else + | LDISC_PPS +#endif + ))) { + perror("ONCORE: fd1"); + exit(1); + } + fd2 = fd1; + } else { /* different devices here */ + if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) { + perror("ONCORE: fd1"); + exit(1); + } + if ((fd2=open(device2, O_RDWR)) < 0) { + perror("ONCORE: fd2"); + exit(1); + } + } + + /* Devices now open, initialize instance structure */ + + if (!(instance = (struct instance *)emalloc(sizeof *instance))) { + perror("malloc"); + close(fd1); + return (0); + } + memset((char *) instance, 0, sizeof *instance); + pp = peer->procptr; + pp->unitptr = (caddr_t)instance; + instance->unit = unit; + instance->ttyfd = fd1; + instance->ppsfd = fd2; + + instance->Bj_day = -1; + instance->assert = 1; + + /* go read any input data in /etc/ntp.oncoreX */ + + oncore_read_config(instance); + +#ifdef HAVE_PPSAPI + if (time_pps_create(fd2, &instance->pps_h) < 0) { + perror("time_pps_create"); + exit(1); + } + + if (instance->assert) { + instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; + instance->pps_p.assert_offset.tv_sec = 0; + instance->pps_p.assert_offset.tv_nsec = 0; + } else { + instance->pps_p.mode = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; + instance->pps_p.clear_offset.tv_sec = 0; + instance->pps_p.clear_offset.tv_nsec = 0; + } + instance->pps_p.mode |= PPS_TSFMT_TSPEC; + if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) { + perror("time_pps_setparams"); + exit(1); + } + if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, + instance->pps_p.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR), + PPS_TSFMT_TSPEC) < 0) { + perror("time_pps_kcbind"); + } +#endif + instance->pp = pp; + instance->peer = peer; + instance->o_state = ONCORE_NO_IDEA; + cp = "state = ONCORE_NO_IDEA"; + record_clock_stats(&(instance->peer->srcadr), cp); + + /* + * Initialize miscellaneous variables + */ + + peer->precision = -26; + peer->minpoll = 4; + peer->maxpoll = 4; + pp->clockdesc = "Motorola UT/VP Oncore GPS Receiver"; + memcpy((char *)&pp->refid, "GPS\0", 4); + + 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)) { + perror("io_addclock"); + (void) close(fd1); + free(instance); + return (0); + } + pp->unitptr = (caddr_t)instance; + + /* + * This will start the Oncore receiver. + * We send info from config to Oncore later. + */ + + mode = instance->init_type; + if (mode == 3 || mode == 4) { + oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof oncore_cmd_Cf); + instance->o_state = ONCORE_RESET_SENT; + cp = "state = ONCORE_RESET_SENT"; + record_clock_stats(&(instance->peer->srcadr), cp); + } else { + oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa); + instance->o_state = ONCORE_TEST_SENT; + cp = "state = ONCORE_TEST_SENT"; + record_clock_stats(&(instance->peer->srcadr), cp); + } + + instance->pollcnt = 2; + + return (1); +} + +/* + * Read Input file if it exists. + */ +static void +oncore_read_config( + struct instance *instance + ) +{ +/* + * First we try to open the configuration file /etc/ntp.oncoreN, where + * N is the unit number viz 127.127.30.N. + * If we don't find it, then we try the file /etc/ntp.oncore. + * + * If we find NEITHER 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 + * 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' (or 'HTMSL' or 'HTGPS') 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 same as HTGPS. + * HTMSL = HT above mean_sea_level, + * HTGPS = HT above GPS ellipse. + * + * There are two optional lines, starting with DELAY and 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. + * DELAY is cable delay, typically a few tens of ns. + * 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 another 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. + * + * 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, line[100], units[2], device[20]; + int i, j, sign, lat_flg, long_flg, ht_flg, mode; + double f1, f2, f3; + + + sprintf(device, "%s%d", INIT_FILE, instance->unit); + if ((fd=fopen(device, "r")) == NULL) + if ((fd=fopen(INIT_FILE, "r")) == NULL) { + instance->init_type = 4; + return; + } + + mode = 0; + lat_flg = long_flg = ht_flg = 0; + while (fgets(line, 100, fd)) { + if ((cp=strchr(line, '#'))) + *cp = '\0'; + i = strlen(line); + for (j=0; j<i; j++) /* just in case lower case */ + if (islower((int)line[j])) + line[j] = toupper(line[j]); + for (j=0; j<i; j++) /* let them use `=' between terms */ + if (line[j] == '=') + line[j] = ' '; + for (j=0; j<i; j++) + if (line[j] != ' ') + break; + if (!strncmp(&line[j], "LAT", 3)) { + j += 3; + f1 = f2 = f3 = 0; + sscanf(&line[j], "%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(&line[j], "LON", 3)) { + j += 3; + f1 = f2 = f3 = 0; + sscanf(&line[j], "%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(&line[j], "HT", 2)) { + instance->ss_ht_type = 0; + if (!strncmp(&line[j], "HTGPS", 5)) { + instance->ss_ht_type = 0; + j +=3; + } + if (!strncmp(&line[j], "HTMSL", 5)) { + instance->ss_ht_type = 1; + j +=3; + } + j += 2; + f1 = 0; + units[0] = '\0'; + sscanf(&line[j], "%lf %1s", &f1, units); + if (units[0] == 'F') + f1 = 0.3048 * f1; + instance->ss_ht = 100 * f1; /* cm */ + ht_flg++; + } else if (!strncmp(&line[j], "DELAY", 5)) { + j += 5; + f1 = 0; + units[0] = '\0'; + sscanf(&line[j], "%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; + instance->delay = f1; /* delay in ns */ + } else if (!strncmp(&line[j], "OFFSET", 6)) { + j += 6; + f1 = 0; + units[0] = '\0'; + sscanf(&line[j], "%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; + instance->offset = f1; /* offset in ns */ + } else if (!strncmp(&line[j], "MODE", 4)) { + j += 4; + sscanf(&line[j], "%d", &mode); + if (mode < 0 || mode > 4) + mode = 4; + instance->init_type = mode; + } else if (!strncmp(&line[j], "ASSERT", 6)) { + instance->assert = 1; + } else if (!strncmp(&line[j], "CLEAR", 5)) { + instance->assert = 0; + } + } + 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) && !(lat_flg * long_flg * ht_flg)) { + printf("ONCORE: incomplete data on %s\n", INIT_FILE); + instance->posn_set = 0; + if (mode == 1 || mode == 3) + instance->init_type++; + } +} + + + +/* + * 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; + 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->pollcnt) + refclock_report(peer, CEVNT_TIMEOUT); + else + instance->pollcnt--; + peer->procptr->polls++; + instance->polled = 1; +} + + + +/* + * move dta from NTP to buffer (toss in unlikely case it wont fit) + */ +static void +oncore_receive( + struct recvbuf *rbufp + ) +{ + u_int 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, l, j, m; + + 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; + if (debug > 4) + printf("ONCORE: >>> skipping %d chars\n", i); + if (i != rcvptr) + memcpy(rcvbuf, rcvbuf+i, (unsigned)(rcvptr-i)); + rcvptr -= i; + } + + /* 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), 2)) + break; + l = oncore_messages[m].len; +#if 0 + if (debug > 3) + printf("ONCORE: GOT: %c%c %d of %d entry %d\n", rcvbuf[2], rcvbuf[3], rcvptr, l, m); +#endif + /* Got the entire message ? */ + + if (rcvptr < l) + return; + + /* Check the checksum */ + + j = 0; + for (i = 2; i < l-3; i++) + j ^= rcvbuf[i]; + if (j == rcvbuf[l-3]) { + oncore_msg_any(instance, rcvbuf, (unsigned) (l-3), m); + if (oncore_messages[m].handler) + oncore_messages[m].handler(instance, rcvbuf, (unsigned) (l-3)); + } else if (debug) { + printf("ONCORE: Checksum mismatch! calc %o is %o\n", j, rcvbuf[l-3]); + printf("ONCORE: @@%c%c ", rcvbuf[2], rcvbuf[3]); + for (i=4; i<l; i++) + printf("%03o ", rcvbuf[i]); + printf("\n"); + } + + if (l != rcvptr) + memcpy(rcvbuf, rcvbuf+l, (unsigned) (rcvptr-l)); + rcvptr -= l; + } +} + + + +/* + * write message to Oncore. + */ +static void +oncore_sendmsg( + int fd, + u_char *ptr, + u_int len + ) +{ + u_char cs = 0; + + printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], len); + write(fd, "@@", 2); + write(fd, ptr, len); + while (len--) + cs ^= *ptr++; + write(fd, &cs, 1); + write(fd, "\r\n", 2); +} + + + +static void +oncore_msg_any( + struct instance *instance, + u_char *buf, + u_int len, + int idx + ) +{ + int i; + const char *fmt = oncore_messages[idx].fmt; + const char *p; + struct timeval tv; + + if (debug > 3) { + GETTIMEOFDAY(&tv, 0); + printf("ONCORE: %ld.%06ld\n", (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"); + } + } +} + + + +/* + * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup + * not so for VP (eeprom) or UT with battery + */ +static void +oncore_msg_Cf( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + const char *cp; + + if (instance->o_state == ONCORE_RESET_SENT) { + oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa); + instance->o_state = ONCORE_TEST_SENT; + cp = "state = ONCORE_TEST_SENT"; + record_clock_stats(&(instance->peer->srcadr), cp); + } +} + + + +static void +oncore_msg_Fa( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + const char *cp; + + if (instance->o_state == ONCORE_TEST_SENT) { + if (debug > 2) + printf("ONCORE: >>@@Fa %x %x\n", buf[4], buf[5]); + if (buf[4] || buf[5]) { + printf("ONCORE: SELF TEST FAILED\n"); + exit(1); + } + + /* sometimes the @@Cj request does not produce any output + PERHAPS the ONCORE is still busy from the selftest??? + try a 2 second sleep here to see if it makes any difference + */ + + sleep(2); + oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof oncore_cmd_Cj); + instance->o_state = ONCORE_ID_SENT; + cp = "state = ONCORE_ID_SENT"; + record_clock_stats(&(instance->peer->srcadr), cp); + } +} + + + +/* + * preliminaries out of the way, this is the REAL start of initialization + */ +static void +oncore_msg_Cj( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + const char *cp; + int mode; + + if (instance->o_state != ONCORE_ID_SENT) + return; + + memcpy(instance->Cj, buf, len); + + oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof oncore_cmd_Cg); /* Set Posn Fix mode (not Idle (VP)) */ + oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof oncore_cmd_Bb); /* turn off */ + oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof oncore_cmd_Ek); /* turn off */ + oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof oncore_cmd_Aw); /* UTC time */ + oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof oncore_cmd_AB); /* Appl type static */ + + mode = instance->init_type; + if (debug) + printf("ONCORE: INIT mode = %d\n", mode); + + /* 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 Site Survey. + */ + + switch (mode) { + case 0: /* NO initialization, don't change anything */ + instance->site_survey = ONCORE_SS_DONE; + break; + + case 1: + case 3: + w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat); + w32_buf(&oncore_cmd_As[6], (int) instance->ss_long); + w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht); + oncore_cmd_As[14] = instance->ss_ht_type; + oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As); + + instance->site_survey = ONCORE_SS_DONE; + oncore_cmd_At[2] = 1; + oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At); + record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode"); + break; + + case 2: + case 4: + if (instance->posn_set) { + w32_buf(&oncore_cmd_Ad[2], (int) instance->ss_lat); + w32_buf(&oncore_cmd_Ae[2], (int) instance->ss_long); + w32_buf(&oncore_cmd_Af[2], (int) instance->ss_ht); + oncore_cmd_Af[6] = instance->ss_ht_type; + oncore_sendmsg(instance->ttyfd, oncore_cmd_Ad, sizeof oncore_cmd_Ad); + oncore_sendmsg(instance->ttyfd, oncore_cmd_Ae, sizeof oncore_cmd_Ae); + oncore_sendmsg(instance->ttyfd, oncore_cmd_Af, sizeof oncore_cmd_Af); + } + instance->site_survey = ONCORE_SS_UNKNOWN; + oncore_cmd_At[2] = 2; + oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At); + break; + } + + if (mode != 0) { + /* cable delay in ns */ + w32_buf(&oncore_cmd_Az[2], instance->delay); + oncore_sendmsg(instance->ttyfd, oncore_cmd_Az, sizeof oncore_cmd_Az); + + /* PPS offset in ns */ + w32_buf(&oncore_cmd_Ay[2], instance->offset); + oncore_sendmsg(instance->ttyfd, oncore_cmd_Ay, sizeof oncore_cmd_Ay); + } + + /* 8chan - Position/Status/Data Output Message, 1/s */ + + oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof oncore_cmd_Ea); + + instance->o_state = ONCORE_ALMANAC; + cp = "state = ONCORE_ALMANAC"; + record_clock_stats(&(instance->peer->srcadr), cp); +} + + + +static void +oncore_msg_Ea( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + const char *cp; + + if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN) + return; + + memcpy(instance->Ea, buf, len); + + /* When we have an almanac, start the En messages */ + + if (instance->o_state == ONCORE_ALMANAC) { + if ((instance->Ea[72] & 1)) { + if (debug) + printf("ONCORE: waiting for almanac\n"); + return; + } else { + oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof oncore_cmd_En); + instance->o_state = ONCORE_RUN; + cp = "state = ONCORE_RUN"; + record_clock_stats(&(instance->peer->srcadr), cp); + } + } + + /* must be ONCORE_RUN if we are here */ + /* First check if Hardware SiteSurvey has Finished */ + + if ((instance->site_survey == ONCORE_SS_HW) && !(instance->Ea[37] & 0x20)) { + instance->site_survey = ONCORE_SS_DONE; + record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode"); + } + + if (!instance->printed && instance->site_survey == ONCORE_SS_DONE) { /* will print to clockstat when all */ + instance->printed = 1; /* three messages respond */ + /* Read back Position Hold Params */ + oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof oncore_cmd_Asx); + /* Read back PPS Offset for Output */ + 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); + } + + /* Check the leap second status once per day */ + + /* + * The following additional check, checking for June/December, is a + * workaround for incorrect ONCORE firmware. The oncore starts + * reporting the leap second when the GPS satellite data message + * (page 18, subframe 4) is updated to a date in the future, which + * which can be several months before the leap second. WWV and other + * services seem to wait until the month of the event to turn + * on their indicators (which are usually a single bit). + */ + + if ((buf[4] == 6) || (buf[4] == 12)) { + if (instance->Bj_day != buf[5]) { /* do this 1/day */ + instance->Bj_day = buf[5]; + oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof oncore_cmd_Bj); + } + } + 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]; + + if (instance->site_survey != ONCORE_SS_SW) + return; + + /* + * We have to average our own position for the Position Hold Mode + */ + + /* We only take PDOP/3D fixes */ + + if (instance->Ea[37] & 1) + return; + + /* Not if poor geometry or less than 3 sats */ + + if (instance->Ea[72] & 0x52) + return; + + /* Only 3D fix */ + + if (!(instance->Ea[72] & 0x20)) + return; + + instance->ss_lat += buf_w32(&instance->Ea[15]); + instance->ss_long += buf_w32(&instance->Ea[19]); + instance->ss_ht += buf_w32(&instance->Ea[23]); /* GPS ellipse */ + instance->ss_count++; + + if (debug) + printf("ONCORE: AVG %d %d %d %d\n", + instance->ss_count, + (unsigned) (instance->ss_lat / instance->ss_count), + (unsigned) (instance->ss_long / instance->ss_count), + (unsigned) (instance->ss_ht / 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; + + w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat); + w32_buf(&oncore_cmd_As[6], (int) instance->ss_long); + w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht); + oncore_cmd_As[14] = 0; + oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As); + + oncore_cmd_At[2] = 1; + oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At); + record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode"); + instance->site_survey = ONCORE_SS_DONE; +} + + + +static void +oncore_msg_En( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + int j; + l_fp ts, ts_tmp; + double dmy; +#ifdef HAVE_TIMESPEC + struct timespec *tsp = 0; +#else + struct timeval *tsp = 0; +#endif +#ifdef HAVE_PPSAPI + struct timespec timeout; + pps_info_t pps_i; +#else /* ! HAVE_PPSAPI */ +#ifdef HAVE_CIOGETEV + struct ppsclockev ev; + int r = CIOGETEV; +#endif +#ifdef HAVE_TIOCGPPSEV + struct ppsclockev ev; + int r = TIOCGPPSEV; +#endif +#endif /* ! HAVE_PPS_API */ + + if (instance->o_state != ONCORE_RUN) + return; + + memcpy(instance->En, buf, len); + + /* Don't do anything without an almanac to define the GPS->UTC delta */ + + if (instance->Ea[72] & 1) + return; + + /* If Time RAIM doesn't like it, don't trust it */ + + if (instance->En[21]) + return; + +#ifdef HAVE_PPSAPI + 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; + + if (debug > 2) + printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n", + pps_i.assert_sequence, j, tsp->tv_sec, tsp->tv_nsec); + + if (pps_i.assert_sequence == j) { + printf("ONCORE: oncore_msg_En, error serial pps\n"); + return; + } + instance->ev_serial = pps_i.assert_sequence; + } else { + tsp = &pps_i.clear_timestamp; + + if (debug > 2) + printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n", + pps_i.clear_sequence, j, tsp->tv_sec, tsp->tv_nsec); + + if (pps_i.clear_sequence == j) { + printf("ONCORE: oncore_msg_En, 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 */ +#else /* ! HAVE_PPSAPI */ + j = instance->ev_serial; + if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) { + perror("ONCORE: IOCTL:"); + return; + } + + tsp = &ev.tv; + + if (debug > 2) + printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n", + ev.serial, j, tsp->tv_sec, tsp->tv_usec); + + if (ev.serial == j) { + printf("ONCORE: oncore_msg_En, error serial pps\n"); + return; + } + instance->ev_serial = ev.serial; + + /* convert timeval -> ntp l_fp */ + + TVTOTS(tsp, &ts); +#endif + /* now have timestamp in ts */ + /* add in saw_tooth and offset */ + + /* 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 */ + + j = instance->saw_tooth + instance->offset; + instance->saw_tooth = (s_char) buf[25]; /* update for next time */ +#ifdef HAVE_PPSAPI + /* must hand this offset 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 = + -(instance->saw_tooth + instance->offset); + } else { + instance->pps_p.clear_offset.tv_nsec = + -(instance->saw_tooth + instance->offset); + } + + if (time_pps_setparams(instance->pps_h, &instance->pps_p)) + perror("time_pps_setparams"); +#else + /* if not PPSAPI, no way to inform kernel of OFFSET, just do it */ + + dmy = -1.0e-9*j; + DTOLFP(dmy, &ts_tmp); + L_ADD(&ts, &ts_tmp); +#endif + /* have time from UNIX origin, convert to NTP origin. */ + + ts.l_ui += JAN_1970; + instance->pp->lastrec = ts; + instance->pp->msec = 0; + + 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 */ + sprintf(instance->pp->a_lastcode, + "%u.%09u %d %d %2d %2d %2d %2ld rstat %02x dop %d nsat %2d,%d raim %d sigma %d neg-sawtooth %3d 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, + + instance->Ea[72], instance->Ea[37], instance->Ea[38], instance->Ea[39], instance->En[21], + /*rstat dop nsat visible, nsat tracked, raim */ + instance->En[23]*256+instance->En[24], (s_char) buf[25], + /* sigma neg-sawtooth */ + /*sat*/ instance->Ea[41], instance->Ea[45], instance->Ea[49], instance->Ea[53], + instance->Ea[57], instance->Ea[61], instance->Ea[65], instance->Ea[69] + ); + + if (debug > 2) { + int i; + i = strlen(instance->pp->a_lastcode); + printf("ONCORE: len = %d %s\n", i, instance->pp->a_lastcode); + } + + 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; +*/ + refclock_receive(instance->peer); + } +} + + + +/* + * 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, + u_int len + ) +{ + + if (instance->site_survey != ONCORE_SS_UNKNOWN) + return; + + if (buf[4] == 2) + instance->site_survey = ONCORE_SS_HW; + else { + instance->site_survey = ONCORE_SS_SW; + + /* + * Probably a VP or an older UT which can't do site-survey. + * We will have to do it ourselves + */ + oncore_cmd_At[2] = 0; + instance->ss_lat = instance->ss_long = instance->ss_ht = 0; + oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At); + } +} + + + +/* 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. + * Until this firmware bug is fixed, @@Bj is only called in June/December. + */ + +static void +oncore_msg_Bj( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + const char *cp; + + switch(buf[4]) { + case 1: + instance->peer->leap = LEAP_ADDSECOND; + cp = "Set peer.leap to LEAP_ADDSECOND"; + break; + case 2: + instance->peer->leap = LEAP_DELSECOND; + cp = "Set peer.leap to LEAP_DELSECOND"; + break; + case 0: + default: + instance->peer->leap = LEAP_NOWARNING; + cp = "Set peer.leap to LEAP_NOWARNING"; + break; + } + record_clock_stats(&(instance->peer->srcadr), cp); +} + + + +/* + * get Position hold position + */ +static void +oncore_msg_As( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + int lat, lon, ht; + + if (!instance->printed || instance->As) + return; + + instance->As = 1; + + lat = buf_w32(&buf[4]); + instance->ss_lat = lat; + + lon = buf_w32(&buf[8]); + instance->ss_long = lon; + + ht = buf_w32(&buf[12]); + instance->ss_ht = ht; + + instance->ss_ht_type = buf[16]; + + if (instance->Ay && instance->Az) + oncore_stats(instance); +} + + + +/* + * get PPS Offset + */ +static void +oncore_msg_Ay( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + if (!instance->printed || instance->Ay) + return; + + instance->Ay = 1; + + instance->offset = buf_w32(&buf[4]); + + if (instance->As && instance->Az) + oncore_stats(instance); +} + + + +/* + * get Cable Delay + */ +static void +oncore_msg_Az( + struct instance *instance, + u_char *buf, + u_int len + ) +{ + if (!instance->printed || instance->Az) + return; + + instance->Az = 1; + + instance->delay = buf_w32(&buf[4]); + + if (instance->As && instance->Ay) + oncore_stats(instance); +} + + + +/* + * print init data in ONCORE to clockstats file + */ +static void +oncore_stats( + struct instance *instance + ) +{ + char Msg[120], ew, ns, *cp, *cp1; + const char *Ht; + double xd, xm, xs, yd, ym, ys, hm, hft; + int idx, idy, is, imx, imy; + long lat, lon; + + /* First, Receiver ID */ + + 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 Position */ + + 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; + Ht = instance->ss_ht_type ? "MSL" : "GPS"; + + 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) %s", ns, xd, ew, yd, hm, hft, Ht); + 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 = %5.2fm (%5.2fft) %s", ns, idx, xm, ew, idy, ym, hm, hft, Ht); + 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 = %5.2fm (%5.2fft) %s", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft, Ht); + record_clock_stats(&(instance->peer->srcadr), Msg); + + /* finally, cable delay and PPS offset */ + + sprintf(Msg, "Cable delay is set to %ld ns", instance->delay); + record_clock_stats(&(instance->peer->srcadr), Msg); + + sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset); + record_clock_stats(&(instance->peer->srcadr), Msg); + +#ifdef HAVE_PPSAPI + if (instance->assert) + cp = "Timing on Assert."; + else + cp = "Timing on Clear."; + record_clock_stats(&(instance->peer->srcadr), cp); +#endif +} + +#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..b9a0e2a --- /dev/null +++ b/contrib/ntp/ntpd/refclock_palisade.c @@ -0,0 +1,880 @@ +/* + * This software was developed by the Software and Component Technologies + * group of Trimble Navigation, Ltd. + * + * Copyright (c) 1997, 1998, 1999 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(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 */ +}; + + +/* + * 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)); + + 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; + } + + if (up->rpt_buf[0] == (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->usec = (long) (secfrac * 1000000); + + 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->usec, 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->usec = (long) (getdbl((u_char *) &mb(3)) * 1000000); + + 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->usec, 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->usec); +#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->usec); + 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 + + 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 (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; + + 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)); +} + +#endif /* REFCLOCK */ diff --git a/contrib/ntp/ntpd/refclock_palisade.h b/contrib/ntp/ntpd/refclock_palisade.h new file mode 100644 index 0000000..b78d988 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_palisade.h @@ -0,0 +1,167 @@ +/* + * This software was developed by the Software and Component Technologies + * group of Trimble Navigation, Ltd. + * + * Copyright (c) 1997, 1998, 1999 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> +#define TIOCMSET MCSETA +#define TIOCMGET MCGETA +#define TIOCM_RTS MRTS +#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 5 /* 16 seconds */ +#define TRMB_MAXPOLL 7 /* 64 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 */ +}; + +/* + * 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..6771a13 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_parse.c @@ -0,0 +1,5340 @@ +/* + * /src/NTP/ntp-4/ntpd/refclock_parse.c,v 4.29 1999/02/28 19:58:23 kardel RELEASE_19990228_A + * + * refclock_parse.c,v 4.29 1999/02/28 19:58:23 kardel RELEASE_19990228_A + * + * generic reference clock driver for receivers + * + * optionally make use of a STREAMS module for input processing where + * available and configured. Currently the STREAMS module + * is only available for Suns running SunOS 4.x and SunOS5.x + * + * the STREAMS module is not required for operation and may be omitted + * at the cost of reduced accuracy. As new kernel interfaces emerger this + * restriction may be lifted in future. + * + * Copyright (c) 1995-1999 by Frank Kardel <kardel@acm.org> + * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany + * + * This software may not be sold for profit without a written consent + * from the author. + * + * 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_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) + * + * - Meinberg GPS166/GPS167 (GPS) + * - Trimble (TSIP and TAIP protocol) (GPS) + * + * - RCC8000 MSF Receiver (MSF) + * - WHARTON 400A Series clock (DCF) + * - 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 <stdio.h> +#include <ctype.h> +#ifndef TM_IN_SYS_TIME +# include <time.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 PPS +#ifdef HAVE_SYS_PPSCLOCK_H +#include <sys/ppsclock.h> +#endif +#ifdef HAVE_TIO_SERIAL_STUFF +#include <linux/serial.h> +#endif +#endif + +#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" + +static char rcsid[]="refclock_parse.c,v 4.29 1999/02/28 19:58:23 kardel RELEASE_19990228_A"; + +/**=========================================================================== + ** external interface to ntp mechanism + **/ + +static void parse_init P((void)); +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 *)); + +#define parse_buginfo noentry + +struct refclock refclock_parse = { + parse_start, + parse_shutdown, + parse_poll, + parse_control, + parse_init, + parse_buginfo, + NOFLAGS +}; + +/* + * Definitions + */ +#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */ +#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */ + +#undef ABS +#define ABS(_X_) (((_X_) < 0) ? -(_X_) : (_X_)) + +/**=========================================================================== + ** 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_char pollneeddata; /* 1 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 lastmissed; /* time (ntp) when poll didn't get data (powerup heuristic) */ + u_long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */ + parsetime_t time; /* 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_DATA 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_DATA +#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) +#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)" + +/* + * 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 rawdcfdtr_init P((struct parseunit *)); +#define RAWDCFDTR_DESCRIPTION "RAW DCF77 CODE (DTR OPTION)" +#define RAWDCFDTR_INIT rawdcfdtr_init + +/* + * RAWDCF receivers that need to be powered from RTS + */ +static int rawdcfrts_init P((struct parseunit *)); +#define RAWDCFRTS_DESCRIPTION "RAW DCF77 CODE (RTS OPTION)" +#define RAWDCFRTS_INIT rawdcfrts_init + +/* + * 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 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 loosing 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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_DATA, + 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, + RAWDCFDTR_INIT, + NO_EVENT, + NO_END, + NO_MESSAGE, + NO_DATA, + RAWDCF_ROOTDELAY, + RAWDCF_BASEDELAY, + DCF_A_ID, + RAWDCFDTR_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_DATA, /* 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 loosing 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 */ + VARITEXT_FLAGS, + NO_POLL, + NO_INIT, + NO_EVENT, + NO_END, + NO_MESSAGE, + NO_DATA, + 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 17 */ + RAWDCF_FLAGS, + NO_POLL, + RAWDCFRTS_INIT, + NO_EVENT, + NO_END, + NO_MESSAGE, + NO_DATA, + RAWDCF_ROOTDELAY, + RAWDCF_BASEDELAY, + DCF_A_ID, + RAWDCFRTS_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + RAWDCF_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 struct parseunit *parseunits[MAXUNITS]; + +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->generic->io.fd, I_PUSH, (caddr_t)m1) == -1 && + ioctl(parse->generic->io.fd, 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->generic->io.fd, 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->generic->baddata++; + 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, + (long)parsetime.parse_time.tv.tv_sec, + (long)parsetime.parse_time.tv.tv_usec, + (long)parsetime.parse_stime.tv.tv_sec, + (long)parsetime.parse_stime.tv.tv_usec, + (long)parsetime.parse_ptime.tv.tv_sec, + (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 TIOCDCDTIMESTAMP + struct timeval dcd_time; + + if (ioctl(rbufp->fd, 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", + rbufp->fd, + 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->generic->io.fd, CIOGETEV, (caddr_t)&ev) == 0) +#endif +#ifdef HAVE_TIOCGPPSEV + if (ioctl(parse->generic->io.fd, 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 */ + } + if (count) + { /* simulate receive */ + memmove((caddr_t)buf.recv_buffer, + (caddr_t)&parse->parseio.parse_dtime, + sizeof(parsetime_t)); + parse_iodone(&parse->parseio); + buf.recv_length = sizeof(parsetime_t); + buf.recv_time = rbufp->recv_time; + buf.srcadr = rbufp->srcadr; + buf.dstadr = rbufp->dstadr; + buf.fd = rbufp->fd; + buf.next = 0; + buf.X_from_where = rbufp->X_from_where; + rbufp->receiver(&buf); + } + 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->generic->baddata++; + 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, + (long)parsetime.parse_time.tv.tv_sec, + (long)parsetime.parse_time.tv.tv_usec, + (long)parsetime.parse_stime.tv.tv_sec, + (long)parsetime.parse_stime.tv.tv_usec, + (long)parsetime.parse_ptime.tv.tv_sec, + (long)parsetime.parse_ptime.tv.tv_usec); + } +#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 + ) +{ + 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; + + *buffer = '\0'; + + i = 0; + while (flagstrings[i].bit) + { + if (flagstrings[i].bit & lstate) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + if (lstate & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION)) + { + char *s, *t; + + if (buffer[0]) + strcat(buffer, "; "); + + strcat(buffer, "("); + + t = s = buffer + strlen(buffer); + + i = 0; + while (sflagstrings[i].bit) + { + if (sflagstrings[i].bit & lstate) + { + if (t != s) + { + strcpy(t, "; "); + t += 2; + } + + strcpy(t, sflagstrings[i].name); + t += strlen(t); + } + i++; + } + strcpy(t, ")"); + } + return buffer; +} + +/*-------------------------------------------------- + * convert a status flag field to a string + */ +static char * +parsestatus( + u_long lstate, + char *buffer + ) +{ + 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]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + 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++; + } + + sprintf(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]; + + buffer[0] = '\0'; + + if ((tmp = delta / (60*60*24)) != 0) + { + sprintf(buffer, "%ldd+", (u_long)tmp); + delta -= tmp * 60*60*24; + } + + s = delta % 60; + delta /= 60; + m = delta % 60; + delta /= 60; + + sprintf(buffer+strlen(buffer), "%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( + register struct parseunit *parse + ) +{ + if (parse->laststatistic + PARSESTATISTICS < current_time) + parse_statistics(parse); + parse->laststatistic = current_time; +} + +/**=========================================================================== + ** ntp interface routines + **/ + +/*-------------------------------------------------- + * parse_init - initialize internal parse driver data + */ +static void +parse_init(void) +{ + memset((caddr_t)parseunits, 0, sizeof parseunits); +} + + +/*-------------------------------------------------- + * parse_shutdown - shut down a PARSE clock + */ +static void +parse_shutdown( + int unit, + struct peer *peer + ) +{ + struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr; + + if (parse && !parse->peer) + { + msyslog(LOG_ERR, + "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit not in use", unit); + return; + } + + /* + * print statistics a last time and + * stop statistics machine + */ + parse_statistics(parse); + + if (parse->parse_type->cl_end) + { + parse->parse_type->cl_end(parse); + } + + 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 */ + free(parse); +} + +/*-------------------------------------------------- + * 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]; + parsectl_t tmp_ctl; + u_int type; + + 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) sprintf(parsedev, PARSEDEVICE, 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->generic->currentstatus = CEVNT_TIMEOUT; /* expect the worst */ + + parse->flags = 0; + parse->pollneeddata = 0; + parse->laststatistic = current_time; + parse->lastformat = (unsigned short)~0; /* assume no format known */ + parse->time.parse_status = (unsigned short)~0; /* be sure to mark initial status change */ + parse->lastmissed = 0; /* assume got everything */ + parse->ppsserial = 0; + 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->generic->fudgetime1 = parse->parse_type->cl_basedelay; + + parse->generic->fudgetime2 = 0.0; + + 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->burst = NTP_SHIFT; + peer->flags |= FLAG_BURST; + + 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 + +#if defined(HAVE_TIO_SERIAL_STUFF) /* Linux hack: define PPS interface */ + { + struct serial_struct ss; + if (ioctl(fd232, TIOCGSERIAL, &ss) < 0 || + ( +#ifdef ASYNC_LOW_LATENCY + ss.flags |= ASYNC_LOW_LATENCY, +#endif +#ifdef ASYNC_PPS_CD_NEG + ss.flags |= ASYNC_PPS_CD_NEG, +#endif + ioctl(fd232, TIOCSSERIAL, &ss)) < 0) { + msyslog(LOG_NOTICE, "refclock_parse: TIOCSSERIAL fd %d, %m", fd232); + msyslog(LOG_NOTICE, + "refclock_parse: optional PPS processing not available"); + } else { + parse->flags |= PARSE_PPSCLOCK; + msyslog(LOG_INFO, + "refclock_parse: PPS detection on"); + } + } +#endif +#ifdef HAVE_TIOCSPPS /* SUN PPS support */ + if (CLK_PPS(parse->peer)) + { + int i = 1; + + if (ioctl(fd232, TIOCSPPS, (caddr_t)&i) == 0) + { + parse->flags |= PARSE_PPSCLOCK; + } + } +#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; + } + } + + /* + * Insert in async io device list. + */ + parse->generic->io.srcclock = (caddr_t)parse; + parse->generic->io.datalen = 0; + + 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; + } + + parse->binding = init_iobinding(parse); + 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 */ + + 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 */ + } + + /* + * 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 */ + } + + strcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format); + 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 +#ifdef TCFLSH + { +#ifndef TCIOFLUSH +#define TCIOFLUSH 2 +#endif + 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 */ + } + } + + /* + * 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-1999, Frank Kardel"); + notice = 1; + } + + /* + * print out configuration + */ + NLOG(NLOG_CLOCKINFO) + { + /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added", + CLK_UNIT(parse->peer), + parse->parse_type->cl_description, parsedev); + + msyslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d", + CLK_UNIT(parse->peer), + parse->peer->stratum, CLK_PPS(parse->peer) ? "" : "no ", + l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision); + + msyslog(LOG_INFO, "PARSE receiver #%d: rootdelay %.6f s, phaseadjust %.6f s, %s IO handling", + CLK_UNIT(parse->peer), + parse->parse_type->cl_rootdelay, + parse->generic->fudgetime1, + parse->binding->bd_description); + + msyslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CLK_UNIT(parse->peer), + parse->parse_type->cl_format); +#ifdef PPS + msyslog(LOG_INFO, "PARSE receiver #%d: %sPPS ioctl support", CLK_UNIT(parse->peer), + (parse->flags & PARSE_PPSCLOCK) ? "" : "NO "); +#endif + } + + return 1; +} + +/*-------------------------------------------------- + * 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) + { + /* + * bad news - didn't get a response last time + */ + parse->generic->noreply++; + 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 / cableling)", CLK_UNIT(parse->peer)); + } + + /* + * we just mark that we want the next sample for the clock filter + */ + parse->pollneeddata = 1; + + 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 + ) +{ + register 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); + + if (in) + { + if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) + { + parse->flags = in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4); + } + } + + if (out) + { + u_long sum = 0; + char *t, *tt, *start; + int i; + + outstatus[0] = '\0'; + + out->type = REFCLK_PARSE; + out->haveflags |= CLK_HAVETIME2; + + /* + * figure out skew between PPS and RS232 - just for informational + * purposes - returned in time2 value + */ + if (PARSE_SYNC(parse->time.parse_state)) + { + if (PARSE_PPS(parse->time.parse_state) && PARSE_TIMECODE(parse->time.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->time.parse_stime.fp; + L_SUB(&off, &parse->time.parse_ptime.fp); /* true offset */ + tt = add_var(&out->kv_list, 80, RO); + sprintf(tt, "refclock_ppsskew=%s", lfptoms(&off, 6)); + } + } + + if (PARSE_PPS(parse->time.parse_state)) + { + tt = add_var(&out->kv_list, 80, RO|DEF); + sprintf(tt, "refclock_ppstime=\"%s\"", gmprettydate(&parse->time.parse_ptime.fp)); + } + + tt = add_var(&out->kv_list, 128, RO|DEF); + sprintf(tt, "refclock_time=\""); + tt += strlen(tt); + + if (parse->time.parse_time.fp.l_ui == 0) + { + strcpy(tt, "<UNDEFINED>\""); + } + else + { + sprintf(tt, "%s\"", gmprettydate(&parse->time.parse_time.fp)); + t = tt + strlen(tt); + } + + if (!PARSE_GETTIMECODE(parse, &tmpctl)) + { + ERR(ERR_INTERNAL) + msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit); + } + else + { + tt = add_var(&out->kv_list, 512, RO|DEF); + sprintf(tt, "refclock_status=\""); + tt += strlen(tt); + + /* + * copy PPS flags from last read transaction (informational only) + */ + tmpctl.parsegettc.parse_state |= parse->time.parse_state & + (PARSEB_PPS|PARSEB_S_PPS); + + (void) parsestate(tmpctl.parsegettc.parse_state, tt); + + strcat(tt, "\""); + + if (tmpctl.parsegettc.parse_count) + mkascii(outstatus+strlen(outstatus), (int)(sizeof(outstatus)- strlen(outstatus) - 1), + tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1)); + + parse->generic->badformat += tmpctl.parsegettc.parse_badformat; + } + + 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); + sprintf(tt, "refclock_format=\""); + + strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count); + strcat(tt,"\""); + } + + /* + * gather state statistics + */ + + start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF); + strcpy(tt, "refclock_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; + + sprintf(item, "%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))) + { + strcpy(tt, item); + tt += count; + } + sum += s_time; + } + } + + sprintf(tt, "; running time: %s\"", l_mktime(sum)); + + tt = add_var(&out->kv_list, 32, RO); + sprintf(tt, "refclock_id=\"%s\"", parse->parse_type->cl_id); + + tt = add_var(&out->kv_list, 80, RO); + sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description); + + tt = add_var(&out->kv_list, 128, RO); + sprintf(tt, "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 + */ +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; + + parse->generic->currentstatus = (u_char)event; + + if (parse->parse_type->cl_event) + parse->parse_type->cl_event(parse, event); + + if (event != CEVNT_NOMINAL) + { + parse->generic->lastevent = parse->generic->currentstatus; + } + else + { + NLOG(NLOG_CLOCKSTATUS) + msyslog(LOG_INFO, "PARSE receiver #%d: SYNCHRONIZED", + CLK_UNIT(parse->peer)); + } + + if (event == CEVNT_FAULT) + { + NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */ + ERR(ERR_BADEVENT) + msyslog(LOG_ERR, + "clock %s fault '%s' (0x%02x)", refnumtoa(parse->peer->srcadr.sin_addr.s_addr), ceventstr(event), + (u_int)event); + } + else + { + NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */ + if (event == CEVNT_NOMINAL || list_err(parse, ERR_BADEVENT)) + msyslog(LOG_INFO, + "clock %s event '%s' (0x%02x)", refnumtoa(parse->peer->srcadr.sin_addr.s_addr), ceventstr(event), + (u_int)event); + } + + report_event(EVNT_PEERCLOCK, parse->peer); + report_event(EVNT_CLOCKEXCPT, parse->peer); + } +} + +/*-------------------------------------------------- + * 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->time.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)); + + 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 / cableling)", + CLK_UNIT(parse->peer), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1))); + parse->generic->badformat += tmpctl.parsegettc.parse_badformat; + } + } + } + + /* + * 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); + break; /* well, still waiting - timeout is handled at higher levels */ + + case CVT_FAIL: + parse->generic->badformat++; + 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->time.parse_state != parsetime->parse_state) + { + char tmp1[200]; + char tmp2[200]; + /* + * something happend + */ + + (void) parsestate(parsetime->parse_state, tmp1); + (void) parsestate(parse->time.parse_state, 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); + } + + /* + * remember for future + */ + parse->time = *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; + + /* + * 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 = parse->generic->fudgetime2; /* 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 = parse->generic->fudgetime2; /* 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); + +#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 + + refclock_process_offset(parse->generic, reftime, rectime, fudge); + if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer)) + { + (void) pps_sample(&parse->time.parse_ptime.fp); + } + + + /* + * 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 + * see the clock states section above for more reasoning + */ + if (((current_time - parse->lastsync) > parse->parse_type->cl_maxunsync) || + (parse->lastsync <= parse->lastmissed)) + { + parse->generic->leap = LEAP_NOTINSYNC; + } + 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; + } + } + + /* + * ready, unless the machine wants a sample + */ + if (!parse->pollneeddata) + return; + + parse->pollneeddata = 0; + + 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 + ) +{ + l_fp leapdate; + + sprintf(t, "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)) + { + sprintf(t, ", next correction %d sec on %s, new GPS-UTC offset %d", + dtlsf - dtls, gmprettydate(&leapdate), dtlsf); + } + else + { + sprintf(t, ", 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->time.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); + sprintf(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, 64, 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); + sprintf(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++ = ' '; + } + + strcat(p, (const char *)s->string); + } + s++; + } + + *p++ = '"'; + *p = '\0'; + } + else + { + strcat(buffer, "<OK>\""); + } + + set_var(&parse->kv, buffer, 64, RO|DEF); + } + break; + + case GPS_POS_XYZ: + { + XYZ xyz; + char buffer[256]; + + get_mbg_xyz(&bufp, xyz); + sprintf(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); + + sprintf(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); + sprintf((char *)buffer, "meinberg_antenna_status=\""); + p = buffer + strlen(buffer); + + switch (antinfo.status) + { + case ANT_INVALID: + strcat(p, "<OK>"); + p += strlen(p); + break; + + case ANT_DISCONN: + strcat(p, "DISCONNECTED since "); + 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((unsigned char **)&p, &antinfo.tm_disconn); + *p = '\0'; + break; + + case ANT_RECONN: + strcat(p, "RECONNECTED on "); + p += strlen(p); + mbg_tm_str((unsigned char **)&p, &antinfo.tm_reconn); + sprintf(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((unsigned char **)&p, &antinfo.tm_disconn); + *p = '\0'; + break; + + default: + sprintf(p, "bad status 0x%04x", antinfo.status); + p += strlen(p); + break; + } + + *p++ = '"'; + *p = '\0'; + + set_var(&parse->kv, buffer, sizeof(buffer), 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; + strcpy(buffer, "gps_tot_51=\""); + p += strlen(p); + mbg_tgps_str((unsigned char **)&p, &cfgh.tot_51); + *p++ = '"'; + *p = '\0'; + set_var(&parse->kv, buffer, sizeof(buffer), RO); + + p = buffer; + strcpy(buffer, "gps_tot_63=\""); + p += strlen(p); + mbg_tgps_str((unsigned char **)&p, &cfgh.tot_63); + *p++ = '"'; + *p = '\0'; + set_var(&parse->kv, buffer, sizeof(buffer), RO); + + p = buffer; + strcpy(buffer, "gps_t0a=\""); + p += strlen(p); + mbg_tgps_str((unsigned char **)&p, &cfgh.t0a); + *p++ = '"'; + *p = '\0'; + set_var(&parse->kv, buffer, sizeof(buffer), RO); + + for (i = MIN_SVNO; i <= MAX_SVNO; i++) + { + p = buffer; + sprintf(p, "gps_cfg[%d]=\"[0x%x] ", i, cfgh.cfg[i]); + p += strlen(p); + switch (cfgh.cfg[i] & 0x7) + { + case 0: + strcpy(p, "BLOCK I"); + break; + case 1: + strcpy(p, "BLOCK II"); + break; + default: + sprintf(p, "bad CFG"); + break; + } + strcat(p, "\""); + set_var(&parse->kv, buffer, sizeof(buffer), RO); + + p = buffer; + sprintf(p, "gps_health[%d]=\"[0x%x] ", i, cfgh.health[i]); + p += strlen(p); + switch ((cfgh.health[i] >> 5) & 0x7 ) + { + case 0: + strcpy(p, "OK;"); + break; + case 1: + strcpy(p, "PARITY;"); + break; + case 2: + strcpy(p, "TLM/HOW;"); + break; + case 3: + strcpy(p, "Z-COUNT;"); + break; + case 4: + strcpy(p, "SUBFRAME 1,2,3;"); + break; + case 5: + strcpy(p, "SUBFRAME 4,5;"); + break; + case 6: + strcpy(p, "UPLOAD BAD;"); + break; + case 7: + strcpy(p, "DATA BAD;"); + break; + } + + p += strlen(p); + + switch (cfgh.health[i] & 0x1F) + { + case 0: + strcpy(p, "SIGNAL OK"); + break; + case 0x1C: + strcpy(p, "SV TEMP OUT"); + break; + case 0x1D: + strcpy(p, "SV WILL BE TEMP OUT"); + break; + case 0x1E: + break; + case 0x1F: + strcpy(p, "MULTIPLE ERRS"); + break; + default: + strcpy(p, "TRANSMISSION PROBLEMS"); + break; + } + + strcat(p, "\""); + set_var(&parse->kv, buffer, sizeof(buffer), 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) + { + strcpy(p, "gps_utc_correction=\""); + p += strlen(p); + mk_utcinfo(p, utc.t0t.wn, utc.WNlsf, utc.DNt, utc.delta_tls, utc.delta_tlsf); + strcat(p, "\""); + } + else + { + strcpy(p, "gps_utc_correction=\"<NO UTC DATA>\""); + } + set_var(&parse->kv, buffer, sizeof(buffer), 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); + + sprintf(buffer, "gps_message=\"%s\"", buffer1); + } + else + strcpy(buffer, "gps_message=<NONE>"); + + set_var(&parse->kv, buffer, 128, 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_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( + struct txbuf *buf, + int c + ) +{ + buf->txt[0] = DLE; + buf->txt[1] = (u_char)c; + buf->idx = 2; +} + +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; + + 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, 0); /* automatic */ + 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->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) + { + sprintf(t, "%s=\"", s->varname); + } + else + { + printf("TRIMBLE unknown command 0x%02x\n", cmd); + return; + } + + var_flag = s->varmode; + + t += strlen(t); + + switch(cmd) + { + case CMD_RCURTIME: + sprintf(t, "%f, %d, %f", + getflt((unsigned char *)&mb(0)), getshort((unsigned char *)&mb(4)), + getflt((unsigned char *)&mb(6))); + break; + + case CMD_RBEST4: + strcpy(t, "mode: "); + t += strlen(t); + switch (mb(0) & 0xF) + { + default: + sprintf(t, "0x%x", mb(0) & 0x7); + break; + + case 1: + strcat(t, "0D"); + break; + + case 3: + strcat(t, "2D"); + break; + + case 4: + strcat(t, "3D"); + break; + } + t += strlen(t); + if (mb(0) & 0x10) + strcpy(t, "-MANUAL, "); + else + strcpy(t, "-AUTO, "); + t += strlen(t); + + sprintf(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: + sprintf(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: + sprintf(t, "illegal value 0x%02x", mb(0) & 0xFF); + break; + case 0x00: + strcpy(t, "doing position fixes"); + break; + case 0x01: + strcpy(t, "no GPS time yet"); + break; + case 0x03: + strcpy(t, "PDOP too high"); + break; + case 0x08: + strcpy(t, "no usable satellites"); + break; + case 0x09: + strcpy(t, "only ONE usable satellite"); + break; + case 0x0A: + strcpy(t, "only TWO usable satellites"); + break; + case 0x0B: + strcpy(t, "only THREE usable satellites"); + break; + case 0x0C: + strcpy(t, "the chosen satellite is unusable"); + break; + } + + t += strlen(t); + + bits = mb(1) & 0xFF; + + for (i = 0; i < 8; i++) + if (bits & (0x1<<i)) + { + sprintf(t, ", %s", msgs[i]); + t += strlen(t); + } + } + break; + + case CMD_RMESSAGE: + mkreadable(t, (int)(sizeof(pbuffer) - (t - pbuffer)), (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; + + sprintf(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)) + { + sprintf(t, ", %s", msgs[i]); + t += strlen(t); + } + + sprintf(t, ", Superpackets %ssupported", (mb(2) & 0xFF) ? "" :"un" ); + } + break; + + case CMD_ROPERPARAM: + sprintf(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); + } + else + { + strcpy(t, "<NO UTC DATA>"); + } + } + break; + + case CMD_RSAT1BIAS: + sprintf(t, "%.1fm %.2fm/s at %.1fs", + getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8))); + break; + + case CMD_RIOOPTIONS: + { + sprintf(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) + sprintf(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) + sprintf(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)); + sprintf(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)); + sprintf(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; + + strcpy(t, "mode: "); + t += strlen(t); + switch (mb(0) & 0x7) + { + default: + sprintf(t, "0x%x", mb(0) & 0x7); + break; + + case 3: + strcat(t, "2D"); + break; + + case 4: + strcat(t, "3D"); + break; + } + t += strlen(t); + if (mb(0) & 0x8) + strcpy(t, "-MANUAL, "); + else + strcpy(t, "-AUTO, "); + t += strlen(t); + + sats = (mb(0)>>4) & 0xF; + + sprintf(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++) + { + sprintf(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: + { + sprintf(t-2, "[%02d]=\"", mb(0)); /* add index to var name */ + t += strlen(t); + + if (getflt((unsigned char *)&mb(4)) < 0.0) + { + strcpy(t, "<NO MEASUREMENTS>"); + var_flag &= ~DEF; + } + else + { + sprintf(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; + strcpy(t, ", OLD"); + } + t += strlen(t); + if (mb(22)) + { + if (mb(22) == 1) + strcpy(t, ", BAD PARITY"); + else + if (mb(22) == 2) + strcpy(t, ", BAD EPH HEALTH"); + } + t += strlen(t); + if (mb(23)) + strcpy(t, ", collecting data"); + } + } + break; + + default: + strcpy(t, "<UNDECODED>"); + break; + } + strcat(t,"\""); + set_var(&parse->kv, pbuffer, sizeof(pbuffer), var_flag); + } +} + + +/**============================================================ + ** RAWDCF support + **/ + +/*-------------------------------------------------- + * rawdcfdtr_init - set up modem lines for RAWDCF receivers + */ +#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR)) +static int +rawdcfdtr_init( + struct parseunit *parse + ) +{ + /* + * 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. + */ + +#ifdef TIOCM_DTR + int sl232 = TIOCM_DTR; /* turn on DTR for power supply */ +#else + int sl232 = CIOCM_DTR; /* turn on DTR for power supply */ +#endif + + if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) + { + msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer)); + } + return 0; +} +#else +static int +rawdcfdtr_init( + struct parseunit *parse + ) +{ + msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer)); + return 0; +} +#endif /* DTR initialisation type */ + +/*-------------------------------------------------- + * rawdcfrts_init - set up modem lines for RAWDCF receivers + */ +#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) +static int +rawdcfrts_init( + struct parseunit *parse + ) +{ + /* + * You can use the RS232 to supply the power for a DCF77 receiver. + * Here a voltage between the RTS and the DTR line is used. + */ + +#ifdef TIOCM_RTS + int sl232 = TIOCM_RTS; /* turn on RTS for power supply */ +#else + int sl232 = CIOCM_RTS; /* turn on RTS for power supply */ +#endif + + if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) + { + msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer)); + } + return 0; +} +#else +static int +rawdcfrts_init( + struct parseunit *parse + ) +{ + msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: OS interface incapable of setting RTS to power DCF modules", CLK_UNIT(parse->peer)); + return 0; +} +#endif /* RTS initialisation type */ + +#else /* defined(REFCLOCK) && defined(PARSE) */ +int refclock_parse_bs; +#endif /* defined(REFCLOCK) && defined(PARSE) */ + +/* + * History: + * + * refclock_parse.c,v + * 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_pst.c b/contrib/ntp/ntpd/refclock_pst.c new file mode 100644 index 0000000..6af4382 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_pst.c @@ -0,0 +1,318 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.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/pst%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 { + u_char 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; + peer->burst = NSTAGE; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, WWVREFID, 4); + 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) + pp->lastrec = trtmp; + up->tcswitch++; + pp->lencode = up->lastptr - pp->a_lastcode; + if (up->tcswitch < 3) + return; +#ifdef DEBUG + if (debug) + printf("pst: 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 < 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.%3d%c %9s%3d%13s%4ld", + &mchar, &pp->hour, &pp->minute, &pp->second, + &pp->msec, &daychar, junque, &pp->day, + info, <emp) != 10) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * 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; + pp->disp = PST_PHI * ltemp; + + /* + * Process the new sample in the median filter and determine the + * timecode timestamp. + */ + if (!refclock_process(pp)) + refclock_report(peer, CEVNT_BADTIME); + +} + + +/* + * 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. If nothing is heard from the clock for two polls, + * 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); + else + pp->polls++; + 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; +} + +#else +int refclock_pst_int; +#endif /* REFCLOCK */ diff --git a/contrib/ntp/ntpd/refclock_ptbacts.c b/contrib/ntp/ntpd/refclock_ptbacts.c new file mode 100644 index 0000000..09d7bf4 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_ptbacts.c @@ -0,0 +1,16 @@ +/* + * crude hack to avoid hard links in distribution + * and keep only one ACTS type source for different + * ACTS refclocks + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_PTBACTS) +# define KEEPPTBACTS +# include "refclock_acts.c" +#else /* not (REFCLOCK && CLOCK_PTBACTS) */ +int refclock_ptbacts_bs; +#endif /* not (REFCLOCK && CLOCK_PTBACTS) */ diff --git a/contrib/ntp/ntpd/refclock_shm.c b/contrib/ntp/ntpd/refclock_shm.c new file mode 100644 index 0000000..fe05b02 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_shm.c @@ -0,0 +1,320 @@ +/* + * 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) + +#undef fileno +#include <ctype.h> +#undef fileno +#include <sys/time.h> +#undef fileno + +#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" + +#ifndef SYS_WINNT +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <assert.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.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 unit) { +#ifndef SYS_WINNT + extern char *sys_errlist[ ]; + extern int sys_nerr; + 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 */ + char buf[20]; + char *pe=buf; + if (errno<sys_nerr) + pe=sys_errlist[errno]; + else { + sprintf (buf,"errno=%d",errno); + } + msyslog(LOG_ERR,"SHM shmget (unit %d): %s",unit,pe); + return 0; + } + else { /* no error */ + struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0); + if ((int)(long)p==-1) { /* error */ + char buf[20]; + char *pe=buf; + if (errno<sys_nerr) + pe=sys_errlist[errno]; + else { + sprintf (buf,"errno=%d",errno); + } + msyslog(LOG_ERR,"SHM shmat (unit %d): %s",unit,pe); + 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 + return 0; +} +/* + * 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 + 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; + switch (up->mode) { + case 0: { + tvr.tv_sec=up->receiveTimeStampSec-172800; + 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-172800; + 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); + } + /*msyslog(LOG_NOTICE,"poll2a tvr.s %d tvr.u %d tvt.s %d tvt.u %d",tvr.tv_sec,tvr.tv_usec,tvt.tv_sec,tvt.tv_usec);*/ + up->valid=0; + if (ok) { + TVTOTS(&tvr,&pp->lastrec); + /* pp->lasttime = current_time; */ + pp->polls++; + t=gmtime (&tvt.tv_sec); + pp->day=t->tm_yday;/*+2; */ + pp->hour=t->tm_hour; + pp->minute=t->tm_min; + pp->second=t->tm_sec; + pp->usec=tvt.tv_usec; + 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; + } + 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..a29383a --- /dev/null +++ b/contrib/ntp/ntpd/refclock_tpro.c @@ -0,0 +1,209 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "sys/tpro.h" +#include "ntp_stdlib.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. + */ + 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->usec) + != 5) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + 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; + } + 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..e7833af --- /dev/null +++ b/contrib/ntp/ntpd/refclock_trak.c @@ -0,0 +1,361 @@ +/* + * 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) + +#include <stdio.h> +#include <ctype.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" +#include "ntp_unixtime.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; + } + 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..b841f72 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_true.c @@ -0,0 +1,852 @@ +/* + * 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 <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.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: + * ? +/- 500 milliseconds # +/- 50 milliseconds + * * +/- 5 milliseconds . +/- 1 millisecond + * space less than 1 millisecond + * OM-DC OMEGA Receiver: + * > >+- 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[20]; + + sprintf(filename, "/tmp/true%d.debug", up->unit); + up->debug = fopen(filename, "w"); + if (up->debug) { +#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[20]; + int fd; + + /* + * Open serial port + */ + (void)sprintf(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 */ + + /* + * 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. + */ + 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; + pp->a_lastcode[pp->lencode] = '\0'; + 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] == '?') { + 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" + * (from a TM/TMD clock during initialization.) + */ + if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0) { + true_doevent(peer, e_F18); + NLOG(NLOG_CLOCKSTATUS) { + msyslog(LOG_INFO, "TM/TMD: %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 + */ + if (synced != ' ' && synced != '.' && synced != '*') + pp->leap = LEAP_NOTINSYNC; + + 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; + } +#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; + } + refclock_receive(peer); + + /* + * 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_ulink.c b/contrib/ntp/ntpd/refclock_ulink.c new file mode 100644 index 0000000..347040d --- /dev/null +++ b/contrib/ntp/ntpd/refclock_ulink.c @@ -0,0 +1,319 @@ +/* + * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers + * By Dave Strout <dstrout@linuxfoundary.com> + * + * Latest version is always on www.linuxfoundary.com + * + * Based on the Spectracom driver + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_ULINK) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the Ultralink Model 320 WWVB receiver. The Model 320 is + * an RS-232 powered unit which consists of two parts: a DB-25 shell that contains + * a microprocessor, and an approx 2"x4" plastic box that contains the antenna. + * The two are connected by a 6-wire RJ-25 cable of length up to 1000'. The + * microprocessor steals power from the RS-232 port, which means that the port must + * be kept open all of the time. The unit also has an internal clock for loss of signal + * periods. Claimed accuracy is 0.1 sec. + * + * 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, else '?' + * 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' = inset, 'D'=delete + * T = DST <-> STD transition indicators + * + * Note that this driver does not do anything with the L or T flags. + * + * The M320 also has a 'U' command which returns UT1 correction information. It + * is not used in this driver. + * + */ + +/* + * Interface definitions + */ +#define DEVICE "/dev/ulink%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-13) /* precision assumed (about 100 us) */ +#define REFID "M320" /* reference ID */ +#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */ + +#define LENWWVB0 28 /* format 0 timecode length */ +#define LENWWVB2 24 /* format 2 timecode length */ +#define LENWWVB3 29 /* format 3 timecode length */ + +#define MONLIN 15 /* number of monitoring lines */ + +/* + * ULINK unit control structure + */ +struct ulinkunit { + 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 */ +}; + +/* + * 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 *)); +static int fd; /* We need to keep the serial port open to power the ULM320 */ + +/* + * Transfer vector + */ +struct refclock refclock_ulink = { + ulink_start, /* start up driver */ + ulink_shutdown, /* shut down driver */ + ulink_poll, /* transmit poll message */ + noentry, /* not used (old wwvb_control) */ + noentry, /* initialize driver (not used) */ + noentry, /* not used (old wwvb_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * 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; + char device[20]; + fprintf(stderr, "Starting Ulink driver\n"); + /* + * 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->flags |= FLAG_BURST; + 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); + close(fd); +} + + +/* + * 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 */ + char syncchar; /* synchronization indicator */ + char qualchar; /* quality indicator */ + char modechar; /* Modes: 'R'=rx, 'N'=noise, ' '=standby */ + char leapchar; /* leap indicator */ + int temp; /* int temp */ + + /* + * 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. 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) { + 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. This code uses the timecode length to determine + * whether format 0 or format 2. If the timecode has invalid + * length or is not in proper format, we declare bad format and + * exit. + */ + syncchar = qualchar = leapchar = ' '; + pp->msec = 0; + + /* + * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT + */ + sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d", + &syncchar, &qualchar, &modechar, &pp->year, &pp->day, + &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec); + + pp->msec *= 10; /* M320 returns 10's of msecs */ + qualchar = ' '; + + /* + * 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. + */ + pp->disp = .001; + 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 + ) +{ + register struct ulinkunit *up; + struct refclockproc *pp; + char pollchar; + + pp = peer->procptr; + up = (struct ulinkunit *)pp->unitptr; + pollchar = 'T'; + if (write(pp->io.fd, &pollchar, 1) != 1) + refclock_report(peer, CEVNT_FAULT); + else + pp->polls++; + 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; + + /* + * 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; +} + +#else +int refclock_ulink_bs; +#endif /* REFCLOCK */ diff --git a/contrib/ntp/ntpd/refclock_usno.c b/contrib/ntp/ntpd/refclock_usno.c new file mode 100644 index 0000000..cf404e7 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_usno.c @@ -0,0 +1,674 @@ +/* + * refclock_usno - clock driver for the Naval Observatory dialup + * Michael Shields <shields@tembel.org> 1995/02/25 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_USNO) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif /* HAVE_SYS_IOCTL_H */ + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" +#include "ntp_control.h" + +/* + * This driver supports the Naval Observatory dialup at +1 202 653 0351. + * It is a hacked-up version of the ACTS driver. + * + * This driver does not support the `phone' configuration because that + * is needlessly global; it would clash with the ACTS driver. + * + * The Naval Observatory does not support the echo-delay measurement scheme. + * + * However, this driver *does* support UUCP port locking, allowing the + * line to be shared with other processes when not actually dialing + * for time. + */ + +/* + * Interface definitions + */ + +#define DEVICE "/dev/cua%d" /* device name and unit */ +#define LOCKFILE "/var/lock/LCK..cua%d" +/* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */ + +#define PHONE "atdt 202 653 0351" +/* #define PHONE "atdt 1 202 653 0351" */ + +#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "USNO" /* reference ID */ +#define DESCRIPTION "Naval Observatory dialup" + +#define MODE_AUTO 0 /* automatic mode */ +#define MODE_BACKUP 1 /* backup mode */ +#define MODE_MANUAL 2 /* manual mode */ + +#define MSGCNT 10 /* we need this many time messages */ +#define SMAX 80 /* max token string length */ +#define LENCODE 20 /* length of valid timecode string */ +#define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */ +#define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */ +#define MAXOUTAGE 3600 /* max before USNO kicks in (s) */ + +/* + * Modem control strings. These may have to be changed for some modems. + * + * AT command prefix + * B1 initiate call negotiation using Bell 212A + * &C1 enable 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 untill carrier detect + * Q0 return result codes + * V1 return result codes as English words + */ +#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */ +#define MODEM_HANGUP "ATH" /* modem disconnect */ + +/* + * Timeouts + */ +#define IDLE 60 /* idle timeout (s) */ +#define WAIT 2 /* wait timeout (s) */ +#define ANSWER 30 /* answer timeout (s) */ +#define CONNECT 10 /* connect timeout (s) */ +#define TIMECODE (MSGCNT+16) /* timecode timeout (s) */ + +/* + * Unit control structure + */ +struct usnounit { + int pollcnt; /* poll message counter */ + + int state; /* the first one was Delaware */ + int run; /* call program run switch */ + int msgcnt; /* count of time messages received */ + long redial; /* interval to next automatic call */ + int unit; /* unit number (= port) */ +}; + +/* + * Function prototypes + */ +static int usno_start P((int, struct peer *)); +static void usno_shutdown P((int, struct peer *)); +static void usno_poll P((int, struct peer *)); +static void usno_disc P((struct peer *)); +#if 0 +static void usno_timeout P((struct peer *)); +static void usno_receive P((struct recvbuf *)); +static int usno_write P((struct peer *, const char *)); +#endif /* 0 */ + +/* + * Transfer vector + */ +struct refclock refclock_usno = { + usno_start, /* start up driver */ + usno_shutdown, /* shut down driver */ + usno_poll, /* transmit poll message */ + noentry, /* not used (usno_control) */ + noentry, /* not used (usno_init) */ + noentry, /* not used (usno_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * usno_start - open the devices and initialize data for processing + */ +static int +usno_start( + int unit, + struct peer *peer + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + + /* + * Initialize miscellaneous variables + */ + pp = peer->procptr; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + peer->minpoll = USNO_MINPOLL; + peer->maxpoll = USNO_MAXPOLL; + peer->sstclktype = CTL_SST_TS_TELEPHONE; + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct usnounit *) + emalloc(sizeof(struct usnounit)))) + return (0); + memset((char *)up, 0, sizeof(struct usnounit)); + up->unit = unit; + pp->unitptr = (caddr_t)up; + + /* + * Set up the driver timeout + */ + peer->nextdate = current_time + WAIT; + return (1); +} + + +/* + * usno_shutdown - shut down the clock + */ +static void +usno_shutdown( + int unit, + struct peer *peer + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + +#ifdef DEBUG + if (debug) + printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr)); +#endif + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + usno_disc(peer); + free(up); +} + + +#if 0 +/* + * usno_receive - receive data from the serial interface + */ +static void +usno_receive( + struct recvbuf *rbufp + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + struct peer *peer; + char str[SMAX]; + u_long mjd; /* Modified Julian Day */ + static int day, hour, minute, second; + + /* + * Initialize pointers and read the timecode and timestamp. If + * the OK modem status code, leave it where folks can find it. + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, + &pp->lastrec); + if (pp->lencode == 0) { + if (strcmp(pp->a_lastcode, "OK") == 0) + pp->lencode = 2; + return; + } +#ifdef DEBUG + if (debug) + printf("usno: timecode %d %s\n", pp->lencode, + pp->a_lastcode); +#endif + + switch (up->state) { + + case 0: + + /* + * State 0. We are not expecting anything. Probably + * modem disconnect noise. Go back to sleep. + */ + return; + + case 1: + + /* + * State 1. We are about to dial. Just drop it. + */ + return; + + case 2: + + /* + * State 2. We are waiting for the call to be answered. + * All we care about here is CONNECT as the first token + * in the string. If the modem signals BUSY, ERROR, NO + * ANSWER, NO CARRIER or NO DIALTONE, we immediately + * hang up the phone. If CONNECT doesn't happen after + * ANSWER seconds, hang up the phone. If everything is + * okay, start the connect timeout and slide into state + * 3. + */ + (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX); + if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") == + 0 || strcmp(str, "NO") == 0) { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s USNO modem status %s", + ntoa(&peer->srcadr), pp->a_lastcode); + usno_disc(peer); + } else if (strcmp(str, "CONNECT") == 0) { + peer->nextdate = current_time + CONNECT; + up->msgcnt = 0; + up->state++; + } else { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_WARNING, + "clock %s USNO unknown modem status %s", + ntoa(&peer->srcadr), pp->a_lastcode); + } + return; + + case 3: + + /* + * State 3. The call has been answered and we are + * waiting for the first message. If this doesn't + * happen within the timecode timeout, hang up the + * phone. We probably got a wrong number or they are + * down. + */ + peer->nextdate = current_time + TIMECODE; + up->state++; + return; + + case 4: + + /* + * State 4. We are reading a timecode. It's an actual + * timecode, or it's the `*' OTM. + * + * jjjjj nnn hhmmss UTC + */ + if (pp->lencode == LENCODE) { + if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC", + &mjd, &day, &hour, &minute, &second) != 5) { +#ifdef DEBUG + if (debug) + printf("usno: bad timecode format\n"); +#endif + refclock_report(peer, CEVNT_BADREPLY); + } else + up->msgcnt++; + return; + } else if (pp->lencode != 1 || !up->msgcnt) + return; + /* else, OTM; drop out of switch */ + } + + pp->leap = LEAP_NOWARNING; + pp->day = day; + pp->hour = hour; + pp->minute = minute; + pp->second = second; + + /* + * Colossal hack here. We process each sample in a trimmed-mean + * filter and determine the reference clock offset and + * dispersion. The fudge time1 value is added to each sample as + * received. + */ + if (!refclock_process(pp)) { +#ifdef DEBUG + if (debug) + printf("usno: time rejected\n"); +#endif + refclock_report(peer, CEVNT_BADTIME); + return; + } else if (up->msgcnt < MSGCNT) + return; + + /* + * We have a filtered sample offset ready for peer processing. + * 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. Finaly, we unhook the + * timeout, arm for the next call, fold the tent and go home. + */ + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + pp->sloppyclockflag &= ~CLK_FLAG1; + up->pollcnt = 0; + up->state = 0; + usno_disc(peer); +} +#endif /* 0 */ + + +/* + * usno_poll - called by the transmit routine + */ +static void +usno_poll( + int unit, + struct peer *peer + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + + /* + * If the driver is running, we set the enable flag (fudge + * flag1), which causes the driver timeout routine to initiate a + * call. If not, the enable flag can be set using + * ntpdc. If this is the sustem peer, then follow the system + * poll interval. + */ + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + if (up->run) { + pp->sloppyclockflag |= CLK_FLAG1; + if (peer == sys_peer) + peer->hpoll = sys_poll; + else + peer->hpoll = peer->minpoll; + } +} + + +#if 0 +/* + * usno_timeout - called by the timer interrupt + */ +static void +usno_timeout( + struct peer *peer + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + int fd; + char device[20]; + char lockfile[128], pidbuf[8]; + int dtr = TIOCM_DTR; + + /* + * If a timeout occurs in other than state 0, the call has + * failed. If in state 0, we just see if there is other work to + * do. + */ + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + if (up->state) { + if (up->state != 1) { + usno_disc(peer); + return; + } + /* + * Call, and start the answer timeout. We think it + * strange if the OK status has not been received from + * the modem, but plow ahead anyway. + * + * This code is *here* because we had to stick in a brief + * delay to let the modem settle down after raising DTR, + * and for the OK to be received. State machines are + * contorted. + */ + if (strcmp(pp->a_lastcode, "OK") != 0) + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "clock %s USNO no modem status", + ntoa(&peer->srcadr)); + (void)usno_write(peer, PHONE); + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "clock %s USNO calling %s\n", + ntoa(&peer->srcadr), PHONE); + up->state = 2; + up->pollcnt++; + pp->polls++; + peer->nextdate = current_time + ANSWER; + return; + } + 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: + up->run = 0; + break; + + /* + * In automatic mode the calling program runs + * continuously at intervals determined by the sys_poll + * variable. + */ + case MODE_AUTO: + if (!up->run) + pp->sloppyclockflag |= CLK_FLAG1; + up->run = 1; + break; + + /* + * In backup mode the calling program is disabled, + * unless no system peer has been selected for MAXOUTAGE + * (3600 s). Once enabled, it runs until some other NTP + * peer shows up. + */ + case MODE_BACKUP: + if (!up->run && sys_peer == 0) { + if (current_time - last_time > MAXOUTAGE) { + up->run = 1; + peer->hpoll = peer->minpoll; + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s USNO backup started ", + ntoa(&peer->srcadr)); + } + } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) { + peer->hpoll = peer->minpoll; + up->run = 0; + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s USNO backup stopped", + ntoa(&peer->srcadr)); + } + break; + + default: + msyslog(LOG_ERR, + "clock %s USNO invalid mode", ntoa(&peer->srcadr)); + + } + + /* + * The fudge flag1 is used as an enable/disable; if set either + * by the code or via ntpdc, the calling program is + * started; if reset, the phones stop ringing. + */ + if (!(pp->sloppyclockflag & CLK_FLAG1)) { + up->pollcnt = 0; + peer->nextdate = current_time + IDLE; + return; + } + + /* + * Lock the port. + */ + (void)sprintf(lockfile, LOCKFILE, up->unit); + fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644); + if (fd < 0) { + msyslog(LOG_ERR, "clock %s USNO port busy", + ntoa(&peer->srcadr)); + return; + } + sprintf(pidbuf, "%d\n", (int) getpid()); + write(fd, pidbuf, strlen(pidbuf)); + close(fd); + + /* + * Open serial port. Use ACTS line discipline, if available. It + * pumps a timestamp into the data stream at every on-time + * character '*' found. Note: the port must have modem control + * or deep pockets for the phone bill. HP-UX 9.03 users should + * have very deep pockets. + */ + (void)sprintf(device, DEVICE, up->unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) { + unlink(lockfile); + return; + } + if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) + msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m", + ntoa(&peer->srcadr)); + + pp->io.clock_recv = usno_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + unlink(lockfile); + free(up); + return; + } + + /* + * Initialize modem and kill DTR. We skedaddle if this comes + * bum. + */ + if (!usno_write(peer, MODEM_SETUP)) { + msyslog(LOG_ERR, "clock %s USNO couldn't write", + ntoa(&peer->srcadr)); + io_closeclock(&pp->io); + unlink(lockfile); + free(up); + return; + } + + /* + * Initiate a call to the Observatory. If we wind up here in + * other than state 0, a successful call could not be completed + * within minpoll seconds. + */ + if (up->pollcnt) { + refclock_report(peer, CEVNT_TIMEOUT); + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, + "clock %s USNO calling program terminated", + ntoa(&peer->srcadr)); + pp->sloppyclockflag &= ~CLK_FLAG1; + up->pollcnt = 0; +#ifdef DEBUG + if (debug) + printf("usno: calling program terminated\n"); +#endif + usno_disc(peer); + return; + } + + /* + * Raise DTR, and let the modem settle. Then we'll dial. + */ + if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1) + msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m", + ntoa(&peer->srcadr)); + up->state = 1; + peer->nextdate = current_time + WAIT; +} +#endif /* 0 */ + + +/* + * usno_disc - disconnect the call and wait for the ruckus to cool + */ +static void +usno_disc( + struct peer *peer + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + int dtr = TIOCM_DTR; + char lockfile[128]; + + /* + * We should never get here other than in state 0, unless a call + * has timed out. We drop DTR, which will reliably get the modem + * off the air, even while the modem is hammering away full tilt. + */ + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + + if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0) + msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m", + ntoa(&peer->srcadr)); + + if (up->state > 0) { + up->state = 0; + msyslog(LOG_NOTICE, "clock %s USNO call failed %d", + ntoa(&peer->srcadr), up->state); +#ifdef DEBUG + if (debug) + printf("usno: call failed %d\n", up->state); +#endif + } + + io_closeclock(&pp->io); + sprintf(lockfile, LOCKFILE, up->unit); + unlink(lockfile); + + peer->nextdate = current_time + WAIT; +} + + +#if 0 +/* + * usno_write - write a message to the serial port + */ +static int +usno_write( + struct peer *peer, + const char *str + ) +{ + register struct usnounit *up; + struct refclockproc *pp; + int len; + int code; + char cr = '\r'; + + /* + * Not much to do here, other than send the message, handle + * debug and report faults. + */ + pp = peer->procptr; + up = (struct usnounit *)pp->unitptr; + len = strlen(str); +#ifdef DEBUG + if (debug) + printf("usno: state %d send %d %s\n", up->state, len, + str); +#endif + code = write(pp->io.fd, str, (unsigned)len) == len; + code |= write(pp->io.fd, &cr, 1) == 1; + if (!code) + refclock_report(peer, CEVNT_FAULT); + return (code); +} +#endif /* 0 */ + +#else +int refclock_usno_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..9d3b3d6 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_wwvb.c @@ -0,0 +1,440 @@ +/* + * refclock_wwvb - clock driver for Spectracom WWVB receivers + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_WWVB) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.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 on when the first timecode + * message of a new day is received. + */ + +/* + * 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 Receivers" /* WRU */ + +#define LENWWVB0 22 /* format 0 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 { + 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 */ +}; + +/* + * 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 *)); + +/* + * 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) */ + NOFLAGS /* not used */ +}; + + +/* + * 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. + */ + (void)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)))) { + (void) 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)) { + (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); +} + + +/* + * 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 */ + + /* + * 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) { + 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("wwvb: 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 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; + pp->msec = 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 %cTZ=%2d", + &syncchar, &pp->day, &pp->hour, &pp->minute, + &pp->second, &dstchar, &tz) == 7) + 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.%3d %c", + &syncchar, &qualchar, &pp->year, &pp->day, + &pp->hour, &pp->minute, &pp->second, &pp->msec, + &leapchar) == 9) + 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); + 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; + 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); +} + + +/* + * wwvb_poll - called by the transmit procedure + */ +static void +wwvb_poll( + 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. If the clock becomes + * unreachable, declare a timeout and keep going. + */ + 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); + else + pp->polls++; + 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; + + /* + * 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; +} + +#else +int refclock_wwvb_bs; +#endif /* REFCLOCK */ |