--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+The software is distributed as source code which has to be compiled.
+
+PARTS OF THE SOFTWARE ARE HIGHLY SYSTEM-SPECIFIC AND NON-PORTABLE.
+UNLESS YOU ARE RUNNING A SUPPORTED SYSTEM, BE PREPARED FOR SOME
+PROGRAMMING!
+
+After unpacking the source code, change directory into it, and type
+
+ ./configure
+
+This is a shell script that automatically determines the system type.
+There is a single optional parameter, --prefix which indicates the
+directory tree where the software should be installed. For example,
+
+ ./configure --prefix=/opt/free
+
+will install the chronyd daemon into /opt/free/sbin and the chronyc
+control program into /opt/free/bin. The default value for the prefix
+is /usr/local.
+
+The configure script assumes you want to use gcc as your compiler.
+If you want to use a different compiler, you can configure this way:
+
+ CC=cc CFLAGS=-O ./configure --prefix=/opt/free
+
+for Bourne-family shells, or
+
+ setenv CC cc
+ setenv CFLAGS -O
+ ./configure --prefix=/opt/free
+
+for C-family shells.
+
+If the software cannot (yet) be built on your system, an error message
+will be shown. Otherwise, the files `options.h' and `Makefile' will
+be generated.
+
+By default, chronyc will be built to make use of the readline library. If you
+don't want this, specify the --disable-readline flag to configure. If you have
+readline and/or ncurses installed in a non-standard location, please refer to
+the chrony.txt file for information.
+
+Now type
+
+ make
+
+to build the programs.
+
+If you want to build the manual in plain text, HTML and info versions, type
+
+ make docs
+
+Once the programs have been successfully compiled, they need to be
+installed in their target locations. This step normally needs to be
+performed by the superuser, and requires the following command to be
+entered.
+
+ make install
+
+This will install the binaries, plain text manual and manpages.
+
+To install the HTML and info versions of the manual as well, enter the command
+
+ make install-docs
+
+If you want chrony to appear in the top level info directory listing, you need
+to run the install-info command manually after this step. install-info takes 2
+arguments. The first is the path to the chrony.info file you have just
+installed. This will be the argument you gave to --prefix when you configured
+(/usr/local by default), with /info/chrony.info on the end. The second
+argument is the location of the file called 'dir'. This will typically be
+/usr/info/dir. So the typical command line would be
+
+ install-info /usr/local/info/chrony.info /usr/info/dir
+
+Now that the software is successfully installed, the next step is to
+set up a configuration file. The contents of this depend on the
+network environment in which the computer operates. Typical scenarios
+are described in the manual. The simplest case is for a computer with
+a permanent Internet connection - suppose you want to use the NTP
+server ntp1.foobar.com as your time reference. You would create an
+/etc/chrony.conf file containing
+
+ server ntp1.foobar.com
+ driftfile /etc/chrony.drift
+
+and then run /usr/local/sbin/chronyd.
+
+
--- /dev/null
+##################################################
+#
+# $Header: /cvs/src/chrony/Makefile.in,v 1.47 2003/04/07 21:43:54 richard Exp $
+#
+# =======================================================================
+#
+# chronyd/chronyc - Programs for keeping computer clocks accurate.
+#
+# Copyright (C) Richard P. Curnow 1997-2003
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+#
+# =======================================================================
+#
+# Makefile template
+
+INSTALL_PREFIX=@INSTALL_PREFIX@
+MANDIR=@MANDIR@
+INFODIR=@INFODIR@
+
+CC = @CC@
+CCWARNFLAGS = @CCWARNFLAGS@
+OPTFLAGS = @CFLAGS@ @EXTRA_DEFS@
+
+DESTDIR=
+
+OBJS = util.o sched.o regress.o local.o \
+ sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \
+ sources.o sourcestats.o reference.o \
+ logging.o conf.o cmdmon.o md5.o keys.o \
+ nameserv.o acquire.o manual.o addrfilt.o \
+ cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
+ broadcast.o
+
+EXTRA_OBJS=@EXTRA_OBJECTS@
+
+CLI_OBJS = client.o md5.o nameserv.o getdate.o cmdparse.o \
+ pktlength.o
+
+SRCS = $(patsubst %.o,%.c,$(OBJS))
+EXTRA_SRCS = $(patsubst %.o,%.c,$(EXTRA_OBJS))
+
+CLI_SRCS = $(patsubst %.o,%.c,$(CLI_OBJS))
+
+LIBS = @LIBS@
+
+EXTRA_LIBS=@EXTRA_LIBS@
+EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
+
+DEFS=@SYSDEFS@
+
+CFLAGS = $(CCWARNFLAGS) $(OPTFLAGS)
+
+# Until we have a main procedure we can link, just build object files
+# to test compilation
+
+all : chronyd chronyc
+
+chronyd : $(OBJS) $(EXTRA_OBJS)
+ $(CC) $(OPTFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LIBS) $(EXTRA_LIBS)
+
+chronyc : $(CLI_OBJS)
+ $(CC) $(OPTFLAGS) -o chronyc $(CLI_OBJS) @READLINE_LINK@ $(LIBS) $(EXTRA_CLI_LIBS)
+
+client.o : client.c
+ $(CC) $(CFLAGS) $(DEFS) @READLINE_COMPILE@ -c $<
+
+.depend :
+ gcc -MM $(SRCS) $(EXTRA_SRCS) > .depend
+
+distclean :
+ -rm -f *.o *.s chronyc chronyd core options.h Makefile *~
+
+clean :
+ -rm -f *.o *.s chronyc chronyd core *~
+
+version.h : version.txt
+ sed -e 's/[$$]Name: \(.*\) [$$]/\1/;' < version.txt > version.h
+
+
+# For install, don't use the install command, because its switches
+# seem to vary between systems.
+
+install: chronyd chronyc
+ [ -d $(DESTDIR)$(INSTALL_PREFIX) ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)
+ [ -d $(DESTDIR)$(INSTALL_PREFIX)/sbin ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/sbin
+ [ -d $(DESTDIR)$(INSTALL_PREFIX)/bin ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/bin
+ [ -d $(DESTDIR)$(INSTALL_PREFIX)/doc ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/doc
+ [ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1
+ [ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5
+ [ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+ [ -d $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony
+ cp chronyd $(DESTDIR)$(INSTALL_PREFIX)/sbin/chronyd
+ chmod 555 $(DESTDIR)$(INSTALL_PREFIX)/sbin/chronyd
+ cp chronyc $(DESTDIR)$(INSTALL_PREFIX)/bin/chronyc
+ chmod 555 $(DESTDIR)$(INSTALL_PREFIX)/bin/chronyc
+ cp chrony.txt $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.txt
+ chmod 444 $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.txt
+ cp COPYING $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/COPYING
+ chmod 444 $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/COPYING
+ cp README $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/README
+ chmod 444 $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/README
+ cp chrony.1 $(DESTDIR)$(MANDIR)/man1
+ chmod 444 $(DESTDIR)$(MANDIR)/man1/chrony.1
+ cp chronyc.1 $(DESTDIR)$(MANDIR)/man1
+ chmod 444 $(DESTDIR)$(MANDIR)/man1/chronyc.1
+ cp chronyd.8 $(DESTDIR)$(MANDIR)/man8
+ chmod 444 $(DESTDIR)$(MANDIR)/man8/chronyd.8
+ cp chrony.conf.5 $(DESTDIR)$(MANDIR)/man5
+ chmod 444 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
+
+%.o : %.c
+ $(CC) $(CFLAGS) $(DEFS) -c $<
+
+%.s : %.c
+ $(CC) $(CFLAGS) $(DEFS) -S $<
+
+main.o logging.o client.o : version.h
+
+# makeinfo v4 required to generate plain text and html
+MAKEINFO:=makeinfo
+
+install-docs : docs
+ [ -d $(DESTDIR)$(INSTALL_PREFIX)/doc ] || mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/doc
+ cp chrony.txt $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.txt
+ chown root $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.txt
+ chmod 444 $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.txt
+ cp chrony.html $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.html
+ chown root $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.html
+ chmod 444 $(DESTDIR)$(INSTALL_PREFIX)/doc/chrony/chrony.html
+ [ -d $(DESTDIR)$(INFODIR) ] || mkdir -p $(DESTDIR)$(INFODIR)
+ cp chrony.info* $(DESTDIR)$(INFODIR)
+ chown root $(DESTDIR)$(INFODIR)/chrony.info*
+ chmod 444 $(DESTDIR)$(INFODIR)/chrony.info*
+
+docs : chrony.txt chrony.html chrony.info
+
+chrony.txt : chrony.texi
+ $(MAKEINFO) --no-headers --number-sections -o chrony.txt chrony.texi
+
+chrony.html : chrony.texi
+ $(MAKEINFO) --no-split --html --number-sections -o chrony.html chrony.texi
+
+chrony.info : chrony.texi
+ $(MAKEINFO) chrony.texi
+
+# This is only relevant if you're maintaining the website!
+faq.php : faq.txt faqgen.pl
+ perl faqgen.pl < faq.txt > faq.php
+
--- /dev/null
+New in version 1.20
+===================
+
+* Many small tidy-ups and security improvements
+* Improve documentation (RTC support in post 2.0 kernels)
+* Remove trailing \n from syslog messages
+* Syslog messages now include IP and port number when packet cannot be sent.
+* Added the "acquisitionport" directive. (Kalle Olavi Niemitalo)
+* Use uname(2) instead of /proc/version to get kernel version.
+* Merge support for Linux on Alpha
+* Merge support for 64bit architectures
+* Don't link -lm if it's not needed
+* Fix Solaris build (broken by 64bit change)
+* Fix for Linux on SH-5 (floating point denorm handling).
+* Add detection of Linux 2.5
+* Allow arbitrary value of HZ in Linux kernel
+
+New in version 1.19
+===================
+
+* Auto-detect kernel's timer interrupt rate (so-called 'HZ') when chronyd
+ starts instead of relying on compiled-in value.
+* Fix 2 bugs in function that creates the directory for the log and dump files.
+* Amended webpage URL and contact details.
+* Generate more informative syslog messages before exiting on failed
+ assertions.
+* Fix bugs in clamping code for the tick value used when slewing a large
+ offset.
+* Don't chown files to root during install (should be pointless, and makes RPM
+ building awkward as ordinary user.)
+* Include chrony.spec file for building RPMs
+
+New in version 1.18
+===================
+* Amend homepage and mailing list information to chrony.sunsite.dk
+* Delete pidfile on exit from chronyd.
+* Improvements to readline interface to chronyc
+* Only generate syslog message when synchronisation is initially lost (instead
+ of on every failed synchronisation attempt)
+* Use double fork approach when initialising daemon.
+* More things in contrib directory.
+* New options to help package builders: --infodir/--mandir for configure, and
+ DESTDIR=xxx for make. (See section 2.2 of chrony.txt for details).
+* Changed the wording of the messages generated by mailonchange and logchange
+ directives.
+
+New in version 1.17
+===================
+* Port to NetBSD
+* Configuration supports Linux on PPC
+* Fix compilation warnings
+* Several documentation improvements
+* Bundled manpages (taken from the 'missing manpages project')
+* Cope with lack of bzero function for Solaris 2.3 systems
+* Store chronyd's pid in a file (default /var/run/chronyd.pid) and check if
+ chronyd may already be running when starting up. New pidfile directive in
+ configuration file.
+* Any size subnet is now allowed in allow and deny commands. (Example:
+ 6.7.8/20 or 6.7.8.x/20 (any x) mean a 20 bit subnet).
+* The environment variables CC and CFLAGS passed to configure can now be used
+ to select the compiler and optimisation/debug options to use
+* Write syslog messages when chronyd loses synchronisation.
+* Print GPL text when chronyc is run.
+* Add NTP broadcast server capability (new broadcast directive).
+* Add 'auto_offline' option to server/peer (conf file) or add server/peer (via
+ chronyc).
+* Add 'activity' command to chronyc, to report how many servers/peers are
+ currently online/offline.
+* Fix long-standing bug with how the system time quantum was calculated.
+* Include support for systems with HZ!=100 (HZ is the timer interrupt
+ frequency).
+* Include example chrony.conf and chrony.keys files (examples subdirectory).
+* Include support for readline in chronyc.
+
+New in version 1.16.1
+=====================
+* Fix compilation problem on Linux 2.4.13 (spinlock.h / spinlock_t)
+
+New in version 1.16
+===================
+* More informative captions for 'sources' and 'sourcestats' commands in chronyc
+ (use 'sources -v' and 'sourcestats -v' to get them).
+* Correct behaviour for Solaris versions>=2.6 (dosynctodr not required on these
+ versions.)
+* Remove some compiler warnings (Solaris)
+* If last line of keys file doesn't have end-of-line, don't truncate final
+ character of that key.
+* Change timestamp format used in logfiles to make it fully numeric (to aid
+ importing data into spreadsheets etc)
+* Minor documentation updates and improvements.
+
+New in version 1.15
+===================
+* Add contributed change to 'configure' to support Solaris 2.8 on x86
+* Workaround for assertion failure that arises if two received packets occur
+ close together. (Still need to find out why this happens at all.)
+* Hopefully fix problem where fast slewing was incompatible with machines
+ that have a large background drift rate (=> tick value went out of range
+ for adjtimex() on Linux.)
+* Fix rtc_linux.c compile problems with 2.4.x kernel include files.
+* Include support for RTC device not being at /dev/rtc (new rtcdevice directive
+ in configuration file).
+* Include support for restricting network interfaces for commands (new
+ bindcmdaddress directive in configuration file)
+* Fix potential linking fault in pktlength.c (use of CROAK macro replaced by
+ normal assert).
+* Add some material on bug reporting + contributing to the chrony.texi file
+* Made the chrony.texi file "Vim6-friendly" (removed xrefs on @node lines,
+ added folding markers to chapters + sections.)
+* Switched over to GPL for the licence
+
+New in version 1.14
+===================
+* Fix compilation for certain other Linux distributions (including Mandrake
+ 7.1)
+
+New in version 1.13
+===================
+* Fixed compilation problems on Redhat/SuSE installations with recent 2.2.x
+ kernels.
+* Minor tidy-ups and documentation enhancements.
+* Add support for Linux 2.4 kernels
+
+New in version 1.12
+===================
+
+* Trial fix for long-standing bug in Linux RTC estimator when system time is
+ slewed.
+* Fix bug in chronyc if -h is specified without a hostname
+* Fixes to logging various error conditions when operating in daemon mode.
+* More stuff under contrib/
+* Changes to README file (e.g. about the new chrony-users mailing list)
+
+New in version 1.11a
+====================
+
+* Minor changes to contact details
+* Minor changes to installation details (chrony subdirectory under doc/)
+
+New in version 1.11
+===================
+
+* Improve robustness of installation procedure
+* Tidy up documenation and contact details
+* Distribute manual as .txt rather than as .ps
+* Add -n option to chronyc to work with numeric IP addresses rather than
+ names.
+* Add material in contrib subdirectory
+* Improve robustness of handling drift file and RTC coefficients file
+* Improve robustness of regression algorithm
+
+New in version 1.1
+==================
+
+Bug fixes
+---------
+
+* Made linear regression more resistant to rounding errors (old one
+ occasionally generated negative variances which made everything go
+ haywire). Trap infinite or 'not-a-number' values being used to
+ alter system clock to increase robustness further.
+
+Other changes/Enhancements
+--------------------------
+
+* Support for Linux 2.1 and 2.2 kernels
+
+* New command 'makestep' in chronyc to immediately jump the system
+ time to match the NTP estimated time (Linux only) - a response to
+ systems booting an hour wrong after summertime/wintertime changes,
+ due to RTCs running on local time. Needs extending to Sun driver
+ files too.
+
+* New directives 'logchange' and 'mailonchange' to log to syslog or
+ email to a specific address respectively if chronyd detects a clock
+ offset exceeding a defined threshold.
+
+* Added capability to log all client/peer NTP accesses and command
+ accesses (can be turned off with conf file directive 'noclientlog').
+ Added 'clients' command to chronyc to display this data.
+
+* Improved manual mode to use robust regression rather than 2 point
+ fit.
+
+* Added 'manual list' and 'manual delete' commands to chronyc to
+ allow display of entered timestamps and discretionary deletion of
+ outliers.
+
+* If host goes unsynchronised the dummy IP address 0.0.0.0 is detected
+ to avoid attempting a reverse name lookup (to stop dial on demand IP
+ links from being started)
+
+* Changed chronyc/chronyd protocol so messages are now all variable
+ length. Saves on network bandwidth particularly for large replies
+ from chronyd to chronyc (to support the clients command).
+
+* Added bindaddress directive to configuration file, to give
+ additional control over limiting which hosts can access the local
+ server.
+
+* Groundwork done for a port to Windows NT to compile with Cygwin
+ toolkit. chronyc works (to monitor another host). sys_winnt.c
+ needs finishing to use NT clock control API. Program structure
+ needs adapting to use Windows NT service functions, so it can be
+ started at boot time. Hopefully a Windows NT / Cygwin guru with
+ some spare time can take this port over :-)
+
+New in version 1.02
+===================
+
+Bug fixes
+---------
+
+* Fix error messages in chronyc if daemon is not reachable.
+
+* Fix config file problem for 'allow all' and 'deny all' without a
+ trailing machine address.
+
+* Remove fatal failed assertion if command socket cannot be read from
+ in daemon.
+
+* Rewrote timezone handling for Linux real time clock, following
+ various reported problems related to daylight saving.
+
+Other changes/Enhancements
+--------------------------
+
+* Configure script recognizes BSD/386 and uses SunOS 4.1 driver for
+ it.
+
+* Log files now print date as day-month-year rather than as a day
+ number. Milliseconds removed from timestamps of logged data.
+ Banners included in file to give meanings of columns.
+
+* Only do 1 initial step (followed by a trimming slew) when
+ initialising from RTC on Linux (previously did 2 steps).
+
+New in version 1.01
+===================
+
+Bug fixes
+---------
+
+* Handle timezone of RTC correctly with respect to daylight saving
+ time
+
+* Syntax check the chronyc 'local' command properly
+
+* Fixed assertion failed fault in median finder (used by RTC
+ regression fitting)
+
+Other changes/Enhancements
+--------------------------
+
+* Log selection of new NTP reference source to syslog.
+
+* Don't zero-pad IP address fields
+
+* Add new command to chronyc to allow logfiles to be cycled.
+
+* Extend allow/deny directive syntax in configuration file to so
+ directive can apply to all hosts on the Internet.
+
+* Tidy up printout of timestamps to make it clear they are in UTC
+
+* Make 'configure' check the processor type as well as the operating
+ system.
--- /dev/null
+This is the README for chrony.
+
+What is chrony?
+===============
+Chrony is a pair of programs for maintaining the accuracy of computer
+clocks.
+
+chronyd is a (background) daemon program that can be started at boot
+time. This does most of the work.
+
+chronyc is a command-line interface program which can be used to
+monitor chronyd's performance and to change various operating
+parateters whilst it is running.
+
+chronyd's main function is to obtain measurements of the true (UTC)
+time from one of several sources, and correct the system clock
+accordingly. It also works out the rate at which the system clock
+gains or loses time and uses this information to keep it accurate
+between measurements from the reference.
+
+The reference time can be derived from either Network Time Protocol
+(NTP) servers (preferred), or wristwatch-and-keyboard (via chronyc).
+The main source of information about the Network Time Protocol is
+http://www.eecis.udel.edu/~ntp.
+
+It is designed so that it can work on computers which only have
+intermittent access to reference sources, for example computers which
+use a dial-up account to access the Internet. Of course, it will work
+on computers with permanent connections too.
+
+In addition, the Linux 2.0.x (for x >= 32), 2.2.x and 2.3.x versions
+can monitor the system's real time clock performance, so the system
+can maintain accurate time even across reboots.
+
+Typical accuracies available between 2 machines are
+
+On an ethernet LAN : 100-200 microseconds, often much better
+On a V32bis dial-up modem connection : 10's of milliseconds (from one
+session to the next)
+
+chronyd can also operate as an RFC1305-compatible NTP server and peer.
+
+
+What will chrony run on?
+========================
+
+Chrony can be successfully built and run on
+
+1. Linux v1.2.13, v2.0.x, 2.1.x (partially), 2.2.x, 2.3.x, 2.4.x (i386).
+Real time clock support is limited to 2.0.32 onwards and to 2.2, 2.3 and
+2.4 series only. PowerPC is also known to be supported.
+
+2. Solaris 2.5/2.5.1/2.6/2.7/2.8 (various platforms)
+
+3. SunOS 4.1.4 (Sparc 2 and Sparc 20)
+
+4. BSD/386 v1.1 has been reported to work using the SunOS 4.1 driver.
+
+5. NetBSD.
+
+Any other system will require a porting exercise. You would need to
+start from one of the existing system-specific drivers and look into
+the quirks of certain system calls and the kernel on your target
+system. (This is described in the manual).
+
+How do I set it up?
+===================
+
+The file INSTALL gives instructions. On supported systems the
+compilation process should be automatic.
+
+You will need an ANSI C compiler -- gcc is recommended. Versions
+2.7.2/2.7.2.2 are known to work.
+
+The manual (in texinfo and text formats) describes how to set the
+software up for the less straightforward cases.
+
+What documentation is there?
+============================
+
+A manual is supplied in Texinfo format (chrony.texi) and
+ready-formatted plain text (chrony.txt) in the distribution.
+
+There is also information available on the chrony web pages, accessible
+through the URL
+
+ http://chrony.sunsite.dk/
+
+What can chrony not do?
+=======================
+
+Compared to the `reference' RFC1305 implementation xntpd, chronyd does
+not support hardware reference clocks, leap seconds or broadcast
+modes.
+
+Where are new versions announced?
+=================================
+
+There is a low volume mailing list where new versions and other
+important news relating to chrony is announced. You can join this list
+by sending mail to
+
+chrony-announce-subscribe@sunsite.dk
+
+These messages will be copied to chrony-users (see below). I also try
+to announce new versions on Freshmeat (http://freshmeat.net/).
+
+I don't reliably announce via news any more - I don't tend to keep up
+with news as I haven't enough time.
+
+How can I get support for chrony?
+and where can I discuss new features, possible bugs etc?
+========================================================
+
+There are 3 mailing lists relating to chrony. chrony-announce was
+mentioned above. chrony-users is a users' discussion list, e.g. for
+general questions and answers about using chrony. chrony-dev is a more
+technical list, e.g. for discussing how new features should be
+implemented, exchange of information between developers etc. To
+subscribe to either of these lists, send an empty message to
+
+chrony-users-subscribe@sunsite.dk
+or
+chrony-dev-subscribe@sunsite.dk
+
+as applicable.
+
+Note that due to family commitments (a 3 year-old and a 1 year-old), I
+no longer have the time to give to supporting chrony that I once had.
+Therefore, the chrony-users list should be your main route for support,
+rather than mailing me directly. Even if it's me that responds to your
+question on the list, at least *ALL* subscribers then benefit from
+seeing the discussion, rather than me taking up lots of time on
+supporting people on a one-to-one basis. If you do mail me directly,
+don't be surprised if I cc: the response to the mailing list.
+
+But how can I contact the author if I need to?
+==============================================
+
+You can email me at <rc@rc0.org.uk>. If that fails, you could try to
+find me through one of the mailing lists. It would be nice if:
+
+- you include the word 'chrony' in the subject line (so my mail reader
+can sort my mail by topic)
+
+- you don't send complete log files, encoded binaries etc, without
+editing such material down to just the relevant bits - a few tens of
+lines at most. (My dial-up connection handles large messages rather
+slowly ...).
+
+Acknowledgements
+================
+
+The following people have provided patches and other major contributions
+to the program :
+
+Andrew Bishop <amb@gedanken.demon.co.uk>
+ Fixes for bugs in logging when in daemon mode
+ Fixes for compiler warnings
+ Robustness improvements for drift file
+ Improve installation (directory checking etc)
+ Entries in contrib directory
+ Improvements to 'sources' and 'sourcestats' output from chronyc
+ Improvements to documentation
+ Investigation of required dosynctodr behaviour for various Solaris
+ versions.
+
+Stephan I. Boettcher <stephan@nevis1.columbia.edu>
+ Entries in contrib directory
+
+Erik Bryer <ebryer@spots.ab.ca>
+ Entries in contrib directory
+
+Paul Elliott <pelliott@io.com>
+ DNSchrony (in contrib directory), a tool for handling NTP servers
+ with variable IP addresses.
+
+Mike Fleetwood <mike@rockover.demon.co.uk>
+ Fixes for compiler warnings
+
+Alexander Gretencord <arutha@gmx.de>
+ Changes to installation directory system to make it easier for
+ package builders.
+
+Walter Haidinger <walter.haidinger@gmx.at>
+ Providing me with login access to a Linux installation where v1.12
+ wouldn't compile, so I could develop the fixes for v1.13. Also, for
+ providing the disc space so I can keep an independent backup of the
+ sources.
+
+Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
+ Port to NetBSD
+
+John Hasler <john@dhh.gt.org>
+ Changes to support 64 bit machines (i.e. those where
+ sizeof(unsigned long) > 4)
+
+Liam Hatton <me@liamhatton.com>
+ Advice on configuring for Linux on PPC
+
+Jachym Holecek <jakym@volny.cz>
+ Patch to make Linux real time clock work with devfs
+
+Jim Knoble <jmknoble@pobox.com>
+ Fixes for compiler warnings
+
+Antti Jrvinen <costello@iki.fi>
+ Advice on configuring for BSD/386
+
+Victor Moroz <vim@prv.adlum.ru>
+ Patch to support Linux with HZ!=100
+
+Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
+ acquisitionport support
+
+Frank Otto <sandwichmacher@web.de>
+ Handling arbitrary HZ values
+
+Andreas Piesk <apiesk@virbus.de>
+ Patch to make chronyc use the readline library if available
+
+Wolfgang Weisselberg <weissel@netcologne.de>
+ Entries in contrib directory
+
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+ Many robustness and security improvements
+
+Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
+ Providing me with information about the Linux 2.2 kernel
+ functionality compared to 2.0.
+
+Doug Woodward <dougw@whistler.com>
+ Advice on configuring for Solaris 2.8 on x86
+
+Many other people have contributed bug reports and suggestions. I'm
+sorry I can't identify all of you individually.
+
+Version control information
+===========================
+
+$Header: /cvs/src/chrony/README,v 1.27 2003/07/01 20:56:23 richard Exp $
+
+vim:tw=72
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/acquire.c,v 1.23 2003/03/27 23:45:47 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Processing to perform the equivalent of what ntpdate does. That is,
+ make a rapid-fire set of measurements to a designated set of
+ sources, and step or slew the local clock to bring it into line with
+ the result.
+
+ This is kept completely separate of the main chronyd processing, by
+ using a separate socket for sending/receiving the measurement
+ packets. That way, ntp_core.c can be kept completely independent of
+ this functionality.
+
+ A few of the finer points of how to construct valid RFC1305 packets
+ and validate responses for this case have been cribbed from the
+ ntpdate source.
+
+ */
+
+#include "sysincl.h"
+
+#include "acquire.h"
+#include "memory.h"
+#include "sched.h"
+#include "local.h"
+#include "logging.h"
+#include "ntp.h"
+#include "util.h"
+#include "main.h"
+#include "conf.h"
+
+/* ================================================== */
+
+/* Interval between firing off the first sample to successive sources */
+#define INTER_SOURCE_START (0.2)
+
+#define MAX_SAMPLES 8
+
+#define MAX_DEAD_PROBES 4
+#define N_GOOD_SAMPLES 4
+
+#define RETRANSMISSION_TIMEOUT (1.0)
+
+typedef struct { unsigned long ip_addr;
+ int sanity; /* Flag indicating whether source
+ looks sane or not */
+ int n_dead_probes; /* Number of probes sent to the server
+ since a good one */
+ int n_samples; /* Number of samples accumulated */
+ int n_total_samples; /* Total number of samples received
+ including useless ones */
+ double offsets[MAX_SAMPLES]; /* In seconds, positive means local
+ clock is fast of reference */
+ double root_distances[MAX_SAMPLES]; /* in seconds */
+ double inter_lo; /* Low end of estimated range of offset */
+ double inter_hi; /* High end of estimated range of offset */
+
+ NTP_int64 last_tx; /* Transmit timestamp in last packet
+ transmitted to source. */
+
+ int timer_running;
+ SCH_TimeoutID timeout_id;
+} SourceRecord;
+
+static SourceRecord *sources;
+static int n_sources;
+static int n_started_sources;
+static int n_completed_sources;
+
+static int init_slew_threshold = -1;
+
+static int sock_fd = -1;
+
+/* ================================================== */
+
+static void (*saved_after_hook)(void *) = NULL;
+static void *saved_after_hook_anything = NULL;
+
+/* ================================================== */
+
+typedef struct {
+ double offset;
+ enum {LO, HIGH} type;
+ int index;
+} Endpoint;
+
+typedef struct {
+ double lo;
+ double hi;
+} Interval;
+
+/* ================================================== */
+
+static void read_from_socket(void *anything);
+static void transmit_timeout(void *x);
+static void wind_up_acquisition(void);
+static void start_source_timeout_handler(void *not_used);
+
+/* ================================================== */
+
+static SCH_TimeoutID source_start_timeout_id;
+
+/* ================================================== */
+
+void
+ACQ_Initialise(void)
+{
+ return;
+}
+
+
+/* ================================================== */
+
+void
+ACQ_Finalise(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+static void
+initialise_io(void)
+{
+ unsigned short port_number = CNF_GetAcquisitionPort();
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sock_fd < 0) {
+ LOG_FATAL(LOGF_Acquire, "Could not open socket : %s", strerror(errno));
+ }
+
+ if (port_number == 0) {
+ /* Don't bother binding this socket - we're not fussed what port
+ number it gets */
+ } else {
+ struct sockaddr_in my_addr;
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port_number);
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ LOG(LOGS_ERR, LOGF_Acquire, "Could not bind socket : %s\n", strerror(errno));
+ /* but keep running */
+ }
+ }
+
+ SCH_AddInputFileHandler(sock_fd, read_from_socket, NULL);
+
+}
+
+/* ================================================== */
+
+static void
+finalise_io(void)
+{
+ if (sock_fd >= 0) {
+ SCH_RemoveInputFileHandler(sock_fd);
+ close(sock_fd);
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+probe_source(SourceRecord *src)
+{
+ NTP_Packet pkt;
+ int version = 3;
+ NTP_Mode my_mode = MODE_CLIENT;
+ struct timeval cooked;
+ double local_time_err;
+ struct sockaddr_in his_addr;
+
+#if 0
+ printf("Sending probe to %08lx sent=%d samples=%d\n", src->ip_addr, src->n_probes_sent, src->n_samples);
+#endif
+
+ pkt.lvm = (((LEAP_Unsynchronised << 6) & 0xc0) |
+ ((version << 3) & 0x38) |
+ ((my_mode) & 0x7));
+
+ pkt.stratum = 0;
+ pkt.poll = 4;
+ pkt.precision = -6; /* as ntpdate */
+ pkt.root_delay = double_to_int32(1.0); /* 1 second */
+ pkt.root_dispersion = double_to_int32(1.0); /* likewise */
+ pkt.reference_id = 0UL;
+ pkt.reference_ts.hi = 0; /* Set to 0 */
+ pkt.reference_ts.lo = 0; /* Set to 0 */
+ pkt.originate_ts.hi = 0; /* Set to 0 */
+ pkt.originate_ts.lo = 0; /* Set to 0 */
+ pkt.receive_ts.hi = 0; /* Set to 0 */
+ pkt.receive_ts.lo = 0; /* Set to 0 */
+
+ /* And do transmission */
+ his_addr.sin_addr.s_addr = htonl(src->ip_addr);
+ his_addr.sin_port = htons(123); /* Fixed for now */
+ his_addr.sin_family = AF_INET;
+
+ LCL_ReadCookedTime(&cooked, &local_time_err);
+ UTI_TimevalToInt64(&cooked, &pkt.transmit_ts);
+
+ if (sendto(sock_fd, (void *) &pkt, NTP_NORMAL_PACKET_SIZE,
+ 0,
+ (struct sockaddr *) &his_addr, sizeof(his_addr)) < 0) {
+ LOG(LOGS_WARN, LOGF_Acquire, "Could not send to %s : %s",
+ UTI_IPToDottedQuad(src->ip_addr),
+ strerror(errno));
+ }
+
+ src->last_tx = pkt.transmit_ts;
+
+ ++(src->n_dead_probes);
+ src->timer_running = 1;
+ src->timeout_id = SCH_AddTimeoutByDelay(RETRANSMISSION_TIMEOUT, transmit_timeout, (void *) src);
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+transmit_timeout(void *x)
+{
+ SourceRecord *src = (SourceRecord *) x;
+
+ src->timer_running = 0;
+
+#if 0
+ printf("Timeout expired for server %08lx\n", src->ip_addr);
+#endif
+
+ if (src->n_dead_probes < MAX_DEAD_PROBES) {
+ probe_source(src);
+ } else {
+ /* Source has croaked or is taking too long to respond */
+ ++n_completed_sources;
+ if (n_completed_sources == n_sources) {
+ wind_up_acquisition();
+ }
+ }
+}
+
+/* ================================================== */
+
+#define MAX_STRATUM 15
+
+static void
+process_receive(NTP_Packet *msg, SourceRecord *src, struct timeval *now)
+{
+
+ unsigned long lvm;
+ int leap, version, mode;
+ double root_delay, root_dispersion;
+ double total_root_delay, total_root_dispersion, total_root_distance;
+
+ struct timeval local_orig, local_average, remote_rx, remote_tx, remote_average;
+ double remote_interval, local_interval;
+ double delta, theta, epsilon;
+ int n;
+
+ /* Most of the checks are from ntpdate */
+
+ /* Need to do something about authentication */
+
+ lvm = msg->lvm;
+ leap = (lvm >> 6) & 0x3;
+ version = (lvm >> 3) & 0x7;
+ mode = lvm & 0x7;
+
+ if ((leap == LEAP_Unsynchronised) ||
+ (version != 3) ||
+ (mode != MODE_SERVER && mode != MODE_PASSIVE)) {
+ return;
+ }
+
+ if (msg->stratum > MAX_STRATUM) {
+ return;
+ }
+
+ /* Check whether server is responding to our last request */
+ if ((msg->originate_ts.hi != src->last_tx.hi) ||
+ (msg->originate_ts.lo != src->last_tx.lo)) {
+ return;
+ }
+
+ /* Check that the server is sane */
+ if (((msg->originate_ts.hi == 0) && (msg->originate_ts.lo == 0)) ||
+ ((msg->receive_ts.hi == 0) && (msg->receive_ts.lo) == 0)) {
+ return;
+ }
+
+ root_delay = int32_to_double(msg->root_delay);
+ root_dispersion = int32_to_double(msg->root_dispersion);
+
+ UTI_Int64ToTimeval(&src->last_tx, &local_orig);
+ UTI_Int64ToTimeval(&msg->receive_ts, &remote_rx);
+ UTI_Int64ToTimeval(&msg->transmit_ts, &remote_tx);
+ UTI_AverageDiffTimevals(&remote_rx, &remote_tx, &remote_average, &remote_interval);
+ UTI_AverageDiffTimevals(&local_orig, now, &local_average, &local_interval);
+
+ delta = local_interval - remote_interval;
+
+ /* Defined as positive if we are fast. Note this sign convention is
+ opposite to that used in ntp_core.c */
+
+ UTI_DiffTimevalsToDouble(&theta, &local_average, &remote_average);
+
+ /* Could work out epsilon - leave till later */
+ epsilon = 0.0;
+
+ total_root_delay = fabs(delta) + root_delay;
+ total_root_dispersion = epsilon + root_dispersion;
+ total_root_distance = 0.5 * fabs(total_root_delay) + total_root_dispersion;
+
+ n = src->n_samples;
+#if 0
+ printf("Sample %d theta=%.6f delta=%.6f root_del=%.6f root_disp=%.6f root_dist=%.6f\n",
+ n, theta, delta, total_root_delay, total_root_dispersion, total_root_distance);
+#endif
+ src->offsets[n] = theta;
+ src->root_distances[n] = total_root_distance;
+ ++(src->n_samples);
+
+}
+
+/* ================================================== */
+
+static void
+read_from_socket(void *anything)
+{
+ int status;
+ ReceiveBuffer msg;
+ struct sockaddr_in his_addr;
+ int his_addr_len;
+ int flags;
+ int message_length;
+ unsigned long remote_ip;
+ int i, ok;
+ struct timeval now;
+ double local_time_err;
+ SourceRecord *src;
+
+ flags = 0;
+ message_length = sizeof(msg);
+ his_addr_len = sizeof(his_addr);
+
+ /* Get timestamp */
+ LCL_ReadCookedTime(&now, &local_time_err);
+
+ status = recvfrom (sock_fd, (char *)&msg, message_length, flags,
+ (struct sockaddr *) &his_addr, &his_addr_len);
+
+ if (status < 0) {
+ LOG(LOGS_WARN, LOGF_Acquire, "Error reading from socket, %s", strerror(errno));
+ return;
+ }
+
+ remote_ip = ntohl(his_addr.sin_addr.s_addr);
+
+#if 0
+ printf("Got message from %08lx\n", remote_ip);
+#endif
+
+ /* Find matching host */
+ ok = 0;
+ for (i=0; i<n_sources; i++) {
+ if (remote_ip == sources[i].ip_addr) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (ok) {
+
+ src = sources + i;
+ ++src->n_total_samples;
+
+ src->n_dead_probes = 0; /* reset this when we actually receive something */
+
+ /* If we got into this function, we know the retransmission timeout has not
+ expired for the source */
+ if (src->timer_running) {
+ SCH_RemoveTimeout(src->timeout_id);
+ src->timer_running = 0;
+ }
+
+ process_receive(&msg.ntp_pkt, src, &now);
+
+ /* Check if server done and requeue timeout */
+ if ((src->n_samples >= N_GOOD_SAMPLES) ||
+ (src->n_total_samples >= MAX_SAMPLES)) {
+ ++n_completed_sources;
+#if 0
+ printf("Source %08lx completed\n", src->ip_addr);
+#endif
+ if (n_completed_sources == n_sources) {
+ wind_up_acquisition();
+ }
+ } else {
+
+ /* Send the next probe */
+ probe_source(src);
+
+ }
+ }
+
+}
+
+/* ================================================== */
+
+static void
+start_next_source(void)
+{
+ probe_source(sources + n_started_sources);
+#if 0
+ printf("Trying to start source %08lx\n", sources[n_started_sources].ip_addr);
+#endif
+ n_started_sources++;
+
+ if (n_started_sources < n_sources) {
+ source_start_timeout_id = SCH_AddTimeoutByDelay(INTER_SOURCE_START, start_source_timeout_handler, NULL);
+ }
+}
+
+/* ================================================== */
+
+static int
+endpoint_compare(const void *a, const void *b)
+{
+ const Endpoint *aa = (const Endpoint *) a;
+ const Endpoint *bb = (const Endpoint *) b;
+
+ if (aa->offset < bb->offset) {
+ return -1;
+ } else if (aa->offset > bb->offset) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+process_measurements(void)
+{
+
+ SourceRecord *s;
+ Endpoint *eps;
+ int i, j;
+ int n_sane_sources;
+ double lo, hi;
+ double inter_lo, inter_hi;
+
+ int depth;
+ int best_depth;
+ int n_at_best_depth;
+ Interval *intervals;
+ double estimated_offset;
+ int index1, index2;
+
+ n_sane_sources = 0;
+
+ /* First, get a consistent interval for each source. Those for
+ which this is not possible are considered to be insane. */
+
+ for (i=0; i<n_sources; i++) {
+ s = sources + i;
+ /* If we got no measurements, the source is insane */
+ if (s->n_samples == 0) {
+ s->sanity = 0;
+ } else {
+ s->sanity = 1; /* so far ... */
+ lo = s->offsets[0] - s->root_distances[0];
+ hi = s->offsets[0] + s->root_distances[0];
+ inter_lo = lo;
+ inter_hi = hi;
+ for (j=1; j<s->n_samples; j++) {
+ lo = s->offsets[j] - s->root_distances[j];
+ hi = s->offsets[j] + s->root_distances[j];
+ if ((inter_hi <= lo) || (inter_lo >= hi)) {
+ /* Oh dear, we won't get an interval for this source */
+ s->sanity = 0;
+ break;
+ } else {
+ inter_lo = (lo < inter_lo) ? inter_lo : lo;
+ inter_hi = (hi > inter_hi) ? inter_hi : hi;
+ }
+ }
+ if (s->sanity) {
+ s->inter_lo = inter_lo;
+ s->inter_hi = inter_hi;
+ }
+ }
+
+ if (s->sanity) {
+ ++n_sane_sources;
+ }
+
+ }
+
+ /* Now build the endpoint list, similar to the RFC1305 clock
+ selection algorithm. */
+ eps = MallocArray(Endpoint, 2*n_sane_sources);
+ intervals = MallocArray(Interval, n_sane_sources);
+
+ j = 0;
+ for (i=0; i<n_sources; i++) {
+ s = sources + i;
+ if (s->sanity) {
+ eps[j].offset = s->inter_lo;
+ eps[j].type = LO;
+ eps[j].index = i;
+ eps[j+1].offset = s->inter_hi;
+ eps[j+1].type = HIGH;
+ eps[j+1].index = i;
+ j += 2;
+ }
+ }
+
+ qsort(eps, 2*n_sane_sources, sizeof(Endpoint), endpoint_compare);
+
+ /* Now do depth searching algorithm */
+ n_at_best_depth = best_depth = depth = 0;
+ for (i=0; i<2*n_sane_sources; i++) {
+
+#if 0
+ fprintf(stderr, "Endpoint type %s source index %d [ip=%08lx] offset=%.6f\n",
+ (eps[i].type == LO) ? "LO" : "HIGH",
+ eps[i].index,
+ sources[eps[i].index].ip_addr,
+ eps[i].offset);
+#endif
+
+ switch (eps[i].type) {
+ case LO:
+ depth++;
+ if (depth > best_depth) {
+ best_depth = depth;
+ n_at_best_depth = 0;
+ intervals[0].lo = eps[i].offset;
+ } else if (depth == best_depth) {
+ intervals[n_at_best_depth].lo = eps[i].offset;
+ } else {
+ /* Nothing to do */
+ }
+
+ break;
+
+ case HIGH:
+ if (depth == best_depth) {
+ intervals[n_at_best_depth].hi = eps[i].offset;
+ n_at_best_depth++;
+ }
+
+ depth--;
+
+ break;
+
+ }
+ }
+
+ if (best_depth > 0) {
+ if ((n_at_best_depth % 2) == 1) {
+ index1 = (n_at_best_depth - 1) / 2;
+ estimated_offset = 0.5 * (intervals[index1].lo + intervals[index1].hi);
+ } else {
+ index2 = (n_at_best_depth / 2);
+ index1 = index2 - 1;
+ estimated_offset = 0.5 * (intervals[index1].lo + intervals[index2].hi);
+ }
+
+
+ /* Apply a step change to the system clock. As per sign
+ convention in local.c and its children, a positive offset means
+ the system clock is fast of the reference, i.e. it needs to be
+ stepped backwards. */
+
+ if (fabs(estimated_offset) > (double) init_slew_threshold) {
+ LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (step)",
+ fabs(estimated_offset),
+ (estimated_offset >= 0) ? "fast" : "slow");
+ LCL_ApplyStepOffset(estimated_offset);
+ } else {
+ LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (slew)",
+ fabs(estimated_offset),
+ (estimated_offset >= 0) ? "fast" : "slow");
+ LCL_AccumulateOffset(estimated_offset);
+ }
+
+ } else {
+ LOG(LOGS_WARN, LOGF_Acquire, "No intersecting endpoints found");
+ }
+
+ Free(intervals);
+ Free(eps);
+
+}
+
+/* ================================================== */
+
+static void
+wind_up_acquisition(void)
+{
+
+ /* Now process measurements */
+ process_measurements();
+
+ Free(sources);
+
+ finalise_io();
+
+ if (saved_after_hook) {
+ (saved_after_hook)(saved_after_hook_anything);
+ }
+
+}
+
+/* ================================================== */
+
+static void
+start_source_timeout_handler(void *not_used)
+{
+
+ start_next_source();
+}
+
+/* ================================================== */
+
+void
+ACQ_StartAcquisition(int n, unsigned long *ip_addrs, int threshold, void (*after_hook)(void *), void *anything)
+{
+
+ int i;
+
+ saved_after_hook = after_hook;
+ saved_after_hook_anything = anything;
+
+ init_slew_threshold = threshold;
+
+ n_started_sources = 0;
+ n_completed_sources = 0;
+ n_sources = n;
+ sources = MallocArray(SourceRecord, n);
+
+ for (i=0; i<n; i++) {
+ sources[i].ip_addr = ip_addrs[i];
+ sources[i].n_samples = 0;
+ sources[i].n_total_samples = 0;
+ sources[i].n_dead_probes = 0;
+ }
+
+ initialise_io();
+
+ /* Start sampling first source */
+ start_next_source();
+
+ return;
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/acquire.h,v 1.9 2002/02/28 23:27:07 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for acquisition module
+ */
+
+#ifndef GOT_ACQUIRE_H
+#define GOT_ACQUIRE_H
+
+typedef struct ACQ_SourceRecord *ACQ_Source;
+
+extern void ACQ_Initialise(void);
+
+extern void ACQ_Finalise(void);
+
+extern void ACQ_StartAcquisition(int n, unsigned long *ip_addrs, int init_slew_threshold,
+ void (*after_hook)(void *), void *anything);
+
+extern void ACQ_AccumulateSample(ACQ_Source acq_source, double offset, double root_distance);
+
+extern void ACQ_MissedSample(ACQ_Source acq_source);
+
+#endif /* GOT_ACQUIRE_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/addressing.h,v 1.7 2002/02/28 23:27:08 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Types used for addressing sources etc
+ */
+
+#ifndef GOT_ADDRESSING_H
+#define GOT_ADDRESSING_H
+
+/* This type is used to represent an IPv4 address and port
+ number. Both parts are in HOST order, NOT network order. */
+typedef struct {
+ unsigned long ip_addr;
+ unsigned short port;
+} NTP_Remote_Address;
+
+#if 0
+unsigned long NTP_IP_Address;
+#endif
+
+#endif /* GOT_ADDRESSING_H */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/addrfilt.c,v 1.8 2002/02/28 23:27:08 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module provides a set of routines for checking IP addresses
+ against a set of rules and deciding whether they are allowed or
+ disallowed.
+
+ */
+
+#include "sysincl.h"
+
+#include "addrfilt.h"
+#include "memory.h"
+
+/* Define the number of bits which are stripped off per level of
+ indirection in the tables */
+#define NBITS 4
+
+/* Define the table size */
+#define TABLE_SIZE (1UL<<NBITS)
+
+struct _TableNode;
+
+typedef struct _TableNode ExtendedTable[TABLE_SIZE];
+
+typedef enum {DENY, ALLOW, AS_PARENT} State;
+
+typedef struct _TableNode {
+ State state;
+ ExtendedTable *extended;
+} TableNode;
+
+struct ADF_AuthTableInst {
+ TableNode base;
+};
+
+/* ================================================== */
+
+inline static unsigned long
+get_subnet(unsigned long addr)
+{
+ return (addr >> (32-NBITS)) & ((1UL<<NBITS) - 1);
+}
+
+/* ================================================== */
+
+inline static unsigned long
+get_residual(unsigned long addr)
+{
+ return (addr << NBITS);
+}
+
+/* ================================================== */
+
+ADF_AuthTable
+ADF_CreateTable(void)
+{
+ ADF_AuthTable result;
+ result = MallocNew(struct ADF_AuthTableInst);
+
+ /* Default is that nothing is allowed */
+ result->base.state = DENY;
+ result->base.extended = NULL;
+
+ return result;
+}
+
+/* ================================================== */
+/* This function deletes all definitions of child nodes, in effect
+ pruning a whole subnet definition back to a single parent
+ record. */
+static void
+close_node(TableNode *node)
+{
+ int i;
+ TableNode *child_node;
+
+ if (node->extended != NULL) {
+ for (i=0; i<TABLE_SIZE; i++) {
+ child_node = &((*(node->extended))[i]);
+ close_node(child_node);
+ }
+ Free(node->extended);
+ node->extended = NULL;
+ }
+
+ return;
+}
+
+
+/* ================================================== */
+/* Allocate the extension field in a node, and set all the children's
+ states to default to that of the node being extended */
+
+static void
+open_node(TableNode *node)
+{
+ int i;
+ TableNode *child_node;
+
+ if (node->extended == NULL) {
+
+ node->extended = MallocNew(ExtendedTable);
+
+ for (i=0; i<TABLE_SIZE; i++) {
+ child_node = &((*(node->extended))[i]);
+ child_node->state = AS_PARENT;
+ child_node->extended = NULL;
+ }
+ }
+ return;
+}
+
+/* ================================================== */
+
+static ADF_Status
+set_subnet(TableNode *start_node,
+ unsigned long ip,
+ int subnet_bits,
+ State new_state,
+ int delete_children)
+{
+ int bits_to_go;
+ unsigned long residual;
+ unsigned long subnet;
+ TableNode *node;
+
+ bits_to_go = subnet_bits;
+ residual = ip;
+ node = start_node;
+
+ if ((subnet_bits < 0) ||
+ (subnet_bits > 32)) {
+
+ return ADF_BADSUBNET;
+
+ } else {
+
+ if ((bits_to_go & (NBITS-1)) == 0) {
+
+ while (bits_to_go > 0) {
+ subnet = get_subnet(residual);
+ residual = get_residual(residual);
+ if (!(node->extended)) {
+ open_node(node);
+ }
+ node = &((*(node->extended))[subnet]);
+ bits_to_go -= NBITS;
+ }
+
+ if (delete_children) {
+ close_node(node);
+ }
+ node->state = new_state;
+
+ } else { /* Have to set multiple entries */
+ int N, i, j;
+ TableNode *this_node;
+
+ while (bits_to_go >= NBITS) {
+ subnet = get_subnet(residual);
+ residual = get_residual(residual);
+ if (!(node->extended)) {
+ open_node(node);
+ }
+ node = &((*(node->extended))[subnet]);
+ bits_to_go -= NBITS;
+ }
+
+ /* How many subnet entries to set : 1->8, 2->4, 3->2 */
+ N = 1 << (NBITS-bits_to_go);
+ subnet = get_subnet(residual);
+ if (!(node->extended)) {
+ open_node(node);
+ }
+
+ for (i=subnet, j=0; j<N; i++, j++) {
+ this_node = &((*(node->extended))[i]);
+ if (delete_children) {
+ close_node(this_node);
+ }
+ this_node->state = new_state;
+ }
+ }
+
+ return ADF_SUCCESS;
+ }
+
+}
+
+/* ================================================== */
+
+ADF_Status
+ADF_Allow(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits)
+{
+ return set_subnet(&(table->base), ip, subnet_bits, ALLOW, 0);
+}
+
+/* ================================================== */
+
+
+ADF_Status
+ADF_AllowAll(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits)
+{
+ return set_subnet(&(table->base), ip, subnet_bits, ALLOW, 1);
+}
+
+/* ================================================== */
+
+ADF_Status
+ADF_Deny(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits)
+{
+ return set_subnet(&(table->base), ip, subnet_bits, DENY, 0);
+}
+
+/* ================================================== */
+
+ADF_Status
+ADF_DenyAll(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits)
+{
+ return set_subnet(&(table->base), ip, subnet_bits, DENY, 1);
+}
+
+/* ================================================== */
+
+void
+ADF_DestroyTable(ADF_AuthTable table)
+{
+ close_node(&(table->base));
+ Free(table);
+}
+
+/* ================================================== */
+
+static int
+check_ip_in_node(TableNode *start_node, unsigned long ip)
+{
+ unsigned long residual, subnet;
+ int result = 0;
+ int finished = 0;
+ TableNode *node;
+ State state=DENY;
+
+ node = start_node;
+ residual = ip;
+
+ do {
+ if (node->state != AS_PARENT) {
+ state = node->state;
+ }
+ if (node->extended) {
+ subnet = get_subnet(residual);
+ residual = get_residual(residual);
+ node = &((*(node->extended))[subnet]);
+ } else {
+ /* Make decision on this node */
+ finished = 1;
+ }
+ } while (!finished);
+
+ switch (state) {
+ case ALLOW:
+ result = 1;
+ break;
+ case DENY:
+ result = 0;
+ break;
+ case AS_PARENT:
+ assert(0);
+ break;
+ }
+
+ return result;
+}
+
+
+/* ================================================== */
+
+int
+ADF_IsAllowed(ADF_AuthTable table,
+ unsigned long ip)
+{
+
+ return check_ip_in_node(&(table->base), ip);
+
+}
+
+/* ================================================== */
+
+#if defined TEST
+
+static void print_node(TableNode *node, unsigned long addr, int shift, int subnet_bits)
+{
+ unsigned long new_addr;
+ int i;
+ TableNode *sub_node;
+
+ for (i=0; i<subnet_bits; i++) putchar(' ');
+
+ printf("%d.%d.%d.%d/%d : %s\n",
+ ((addr >> 24) & 255),
+ ((addr >> 16) & 255),
+ ((addr >> 8) & 255),
+ ((addr ) & 255),
+ subnet_bits,
+ (node->state == ALLOW) ? "allow" :
+ (node->state == DENY) ? "deny" : "as parent");
+ if (node->extended) {
+ for (i=0; i<16; i++) {
+ sub_node = &((*(node->extended))[i]);
+ new_addr = addr | ((unsigned long) i << shift);
+ print_node(sub_node, new_addr, shift - 4, subnet_bits + 4);
+ }
+ }
+ return;
+}
+
+
+static void print_table(ADF_AuthTable table)
+{
+ unsigned long addr = 0;
+ int shift = 28;
+ int subnet_bits = 0;
+
+ print_node(&table->base, addr, shift, subnet_bits);
+ return;
+}
+
+/* ================================================== */
+
+int main (int argc, char **argv)
+{
+ ADF_AuthTable table;
+ table = ADF_CreateTable();
+
+ ADF_Allow(table, 0x7e800000, 9);
+ ADF_Deny(table, 0x7ecc0000, 14);
+ /* ADF_Deny(table, 0x7f000001, 32); */
+ /* ADF_Allow(table, 0x7f000000, 8); */
+
+ print_table(table);
+
+ ADF_DestroyTable(table);
+ return 0;
+}
+
+
+
+#endif /* defined TEST */
+
+
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/addrfilt.h,v 1.6 2002/02/28 23:27:08 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module for providing an authorisation filter on IP addresses
+ */
+
+#ifndef GOT_ADDRFILT_H
+#define GOT_ADDRFILT_H
+
+typedef struct ADF_AuthTableInst *ADF_AuthTable;
+
+typedef enum {
+ ADF_SUCCESS,
+ ADF_BADSUBNET
+} ADF_Status;
+
+
+/* Create a new table. The default rule is deny for everything */
+extern ADF_AuthTable ADF_CreateTable(void);
+
+/* Allow anything in the supplied subnet, EXCEPT for any more specific
+ subnets that are already defined */
+extern ADF_Status ADF_Allow(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits);
+
+/* Allow anything in the supplied subnet, overwriting existing
+ definitions for any more specific subnets */
+extern ADF_Status ADF_AllowAll(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits);
+
+/* Deny anything in the supplied subnet, EXCEPT for any more specific
+ subnets that are already defined */
+extern ADF_Status ADF_Deny(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits);
+
+/* Deny anything in the supplied subnet, overwriting existing
+ definitions for any more specific subnets */
+extern ADF_Status ADF_DenyAll(ADF_AuthTable table,
+ unsigned long ip,
+ int subnet_bits);
+
+/* Clear up the table */
+extern void ADF_DestroyTable(ADF_AuthTable table);
+
+/* Check whether a given IP address is allowed by the rules in
+ the table */
+extern int ADF_IsAllowed(ADF_AuthTable table,
+ unsigned long ip);
+
+#endif /* GOT_ADDRFILT_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/broadcast.c,v 1.3 2002/02/28 23:27:08 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Deal with broadcast server functions.
+ */
+
+#include "sysincl.h"
+#include "memory.h"
+
+#include "addressing.h"
+#include "broadcast.h"
+#include "sched.h"
+#include "ntp.h"
+#include "local.h"
+#include "reference.h"
+#include "util.h"
+#include "ntp_io.h"
+
+typedef struct {
+ NTP_Remote_Address addr;
+ int interval;
+} Destination;
+static Destination *destinations = 0;
+static int n_destinations = 0;
+static int max_destinations = 0;
+
+void
+BRD_Initialise(void)
+{
+ return; /* Nothing to do */
+}
+
+/* ================================================== */
+
+void
+BRD_Finalise(void)
+{
+ return; /* Nothing to do */
+}
+
+/* ================================================== */
+/* This is a cut-down version of what transmit_packet in ntp_core.c does */
+
+static void
+timeout_handler(void *arbitrary)
+{
+ Destination *d = (Destination *) arbitrary;
+ NTP_Packet message;
+ /* Parameters read from reference module */
+ int version;
+ int leap;
+ int are_we_synchronised, our_stratum;
+ NTP_Leap leap_status;
+ unsigned long our_ref_id;
+ struct timeval our_ref_time;
+ double our_root_delay, our_root_dispersion;
+ double local_time_err;
+ struct timeval local_transmit;
+
+ version = 3;
+
+ LCL_ReadCookedTime(&local_transmit, &local_time_err);
+ REF_GetReferenceParams(&local_transmit,
+ &are_we_synchronised, &leap_status,
+ &our_stratum,
+ &our_ref_id, &our_ref_time,
+ &our_root_delay, &our_root_dispersion);
+
+
+ if (are_we_synchronised) {
+ leap = (int) leap_status;
+ } else {
+ leap = 3;
+ }
+
+ message.lvm = ((leap << 6) &0xc0) | ((version << 3) & 0x38) | (MODE_BROADCAST & 0x07);
+ message.stratum = our_stratum;
+ message.poll = 6; /* FIXME: what should this be? */
+ message.precision = LCL_GetSysPrecisionAsLog();
+
+ /* If we're sending a client mode packet and we aren't synchronized yet,
+ we might have to set up artificial values for some of these parameters */
+ message.root_delay = double_to_int32(our_root_delay);
+ message.root_dispersion = double_to_int32(our_root_dispersion);
+
+ message.reference_id = htonl((NTP_int32) our_ref_id);
+
+ /* Now fill in timestamps */
+ UTI_TimevalToInt64(&our_ref_time, &message.reference_ts);
+ message.originate_ts.hi = 0UL;
+ message.originate_ts.lo = 0UL;
+ message.receive_ts.hi = 0UL;
+ message.receive_ts.lo = 0UL;
+
+ LCL_ReadCookedTime(&local_transmit, &local_time_err);
+ UTI_TimevalToInt64(&local_transmit, &message.transmit_ts);
+ NIO_SendNormalPacket(&message, &d->addr);
+
+ /* Requeue timeout. Don't care if interval drifts gradually, so just do it
+ * at the end. */
+ SCH_AddTimeoutInClass((double) d->interval, 1.0,
+ SCH_NtpBroadcastClass,
+ timeout_handler, (void *) d);
+
+
+}
+
+/* ================================================== */
+
+void
+BRD_AddDestination(unsigned long addr, unsigned short port, int interval)
+{
+ if (max_destinations == n_destinations) {
+ /* Expand array */
+ max_destinations += 8;
+ if (destinations) {
+ destinations = ReallocArray(Destination, max_destinations, destinations);
+ } else {
+ destinations = MallocArray(Destination, max_destinations);
+ }
+ }
+
+ destinations[n_destinations].addr.ip_addr = addr;
+ destinations[n_destinations].addr.port = port;
+ destinations[n_destinations].interval = interval;
+
+ SCH_AddTimeoutInClass((double) interval, 1.0,
+ SCH_NtpBroadcastClass,
+ timeout_handler, (void *)(destinations + n_destinations));
+
+ ++n_destinations;
+
+}
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/broadcast.h,v 1.2 2002/02/28 23:27:08 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Deal with broadcast server functions.
+ */
+
+#ifndef GOT_BROADCAST_H
+#define GOT_BROADCAST_H
+
+extern void BRD_Initialise(void);
+extern void BRD_Finalise(void);
+extern void BRD_AddDestination(unsigned long addr, unsigned short port, int interval);
+
+#endif /* GOT_BROADCAST_H */
+
--- /dev/null
+#!/usr/bin/env perl
+# $Header: /cvs/src/chrony/build_kit,v 1.13 2003/01/12 23:50:54 richard Exp $
+# Perl script for building a release
+
+chmod 0755, "configure";
+
+# Construct chrony.spec file
+$version = $ARGV[0] || die "No version on command line";
+open (IN, "<chrony.spec.sample");
+open (OUT, ">chrony.spec");
+while (<IN>) {
+ s/\@\@VERSION\@\@/$version/;
+ print OUT;
+}
+close (IN);
+close (OUT);
+
+unlink "chrony.spec.sample";
+
+# Requires the makeinfo from texinfo v4
+system("makeinfo --no-headers --number-sections -o chrony.txt chrony.texi");
+unlink("build_kit");
+unlink("LICINS");
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/candm.h,v 1.39 2003/04/01 20:54:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Definitions for the network protocol used for command and monitoring
+ of the timeserver.
+
+ */
+
+#ifndef GOT_CANDM_H
+#define GOT_CANDM_H
+
+#include "sysincl.h"
+
+/* This is the default port to use for CANDM, if no alternative is
+ defined */
+#define DEFAULT_CANDM_PORT 323
+
+/* Request codes */
+#define REQ_NULL 0
+#define REQ_ONLINE 1
+#define REQ_OFFLINE 2
+#define REQ_BURST 3
+#define REQ_MODIFY_MINPOLL 4
+#define REQ_MODIFY_MAXPOLL 5
+#define REQ_DUMP 6
+#define REQ_MODIFY_MAXDELAY 7
+#define REQ_MODIFY_MAXDELAYRATIO 8
+#define REQ_MODIFY_MAXUPDATESKEW 9
+#define REQ_LOGON 10
+#define REQ_SETTIME 11
+#define REQ_LOCAL 12
+#define REQ_MANUAL 13
+#define REQ_N_SOURCES 14
+#define REQ_SOURCE_DATA 15
+#define REQ_REKEY 16
+#define REQ_ALLOW 17
+#define REQ_ALLOWALL 18
+#define REQ_DENY 19
+#define REQ_DENYALL 20
+#define REQ_CMDALLOW 21
+#define REQ_CMDALLOWALL 22
+#define REQ_CMDDENY 23
+#define REQ_CMDDENYALL 24
+#define REQ_ACCHECK 25
+#define REQ_CMDACCHECK 26
+#define REQ_ADD_SERVER 27
+#define REQ_ADD_PEER 28
+#define REQ_DEL_SOURCE 29
+#define REQ_WRITERTC 30
+#define REQ_DFREQ 31
+#define REQ_DOFFSET 32
+#define REQ_TRACKING 33
+#define REQ_SOURCESTATS 34
+#define REQ_RTCREPORT 35
+#define REQ_TRIMRTC 36
+#define REQ_CYCLELOGS 37
+#define REQ_SUBNETS_ACCESSED 38
+#define REQ_CLIENT_ACCESSES 39
+#define REQ_CLIENT_ACCESSES_BY_INDEX 40
+#define REQ_MANUAL_LIST 41
+#define REQ_MANUAL_DELETE 42
+#define REQ_MAKESTEP 43
+#define REQ_ACTIVITY 44
+#define N_REQUEST_TYPES 45
+
+/* Special utoken value used to log on with first exchange being the
+ password. (This time value has long since gone by) */
+#define SPECIAL_UTOKEN 0x10101010
+
+/* The EOR (end of record) fields are used by the offsetof operator in
+ pktlength.c, to get the number of bytes that ought to be
+ transmitted for each packet type. */
+
+typedef struct {
+ uint32_t mask;
+ uint32_t address;
+ int32_t EOR;
+} REQ_Online;
+
+typedef struct {
+ uint32_t mask;
+ uint32_t address;
+ int32_t EOR;
+} REQ_Offline;
+
+typedef struct {
+ uint32_t mask;
+ uint32_t address;
+ int32_t n_good_samples;
+ int32_t n_total_samples;
+ int32_t EOR;
+} REQ_Burst;
+
+typedef struct {
+ uint32_t address;
+ int32_t new_minpoll;
+ int32_t EOR;
+} REQ_Modify_Minpoll;
+
+typedef struct {
+ uint32_t address;
+ int32_t new_maxpoll;
+ int32_t EOR;
+} REQ_Modify_Maxpoll;
+
+typedef struct {
+ int32_t pad;
+ int32_t EOR;
+} REQ_Dump;
+
+typedef struct {
+ uint32_t address;
+ int32_t new_max_delay;
+ int32_t EOR;
+} REQ_Modify_Maxdelay;
+
+typedef struct {
+ uint32_t address;
+ int32_t new_max_delay_ratio;
+ int32_t EOR;
+} REQ_Modify_Maxdelayratio;
+
+typedef struct {
+ int32_t new_max_update_skew;
+ int32_t EOR;
+} REQ_Modify_Maxupdateskew;
+
+typedef struct {
+ struct timeval ts;
+ int32_t EOR;
+} REQ_Logon;
+
+typedef struct {
+ struct timeval ts;
+ int32_t EOR;
+} REQ_Settime;
+
+typedef struct {
+ int32_t on_off;
+ int32_t stratum;
+ int32_t EOR;
+} REQ_Local;
+
+typedef struct {
+ int32_t option;
+ int32_t EOR;
+} REQ_Manual;
+
+typedef struct {
+ int32_t EOR;
+} REQ_N_Sources;
+
+typedef struct {
+ int32_t index;
+ int32_t EOR;
+} REQ_Source_Data;
+
+typedef struct {
+ int32_t EOR;
+} REQ_Rekey;
+
+typedef struct {
+ uint32_t ip;
+ int32_t subnet_bits;
+ int32_t EOR;
+} REQ_Allow_Deny;
+
+typedef struct {
+ uint32_t ip;
+ int32_t EOR;
+} REQ_Ac_Check;
+
+typedef struct {
+ uint32_t ip_addr;
+ uint32_t port;
+ int32_t minpoll;
+ int32_t maxpoll;
+ int32_t presend_minpoll;
+ int32_t online;
+ int32_t auto_offline;
+ uint32_t authkey;
+ int32_t max_delay;
+ int32_t max_delay_ratio;
+ int32_t EOR;
+} REQ_NTP_Source;
+
+typedef struct {
+ uint32_t ip_addr;
+ int32_t EOR;
+} REQ_Del_Source;
+
+typedef struct {
+ int32_t EOR;
+} REQ_WriteRtc;
+
+typedef struct {
+ int32_t dfreq;
+ int32_t EOR;
+} REQ_Dfreq;
+
+typedef struct {
+ int32_t sec;
+ int32_t usec;
+ int32_t EOR;
+} REQ_Doffset;
+
+typedef struct {
+ int32_t EOR;
+} REQ_Tracking;
+
+typedef struct {
+ uint32_t index;
+ int32_t EOR;
+} REQ_Sourcestats;
+
+typedef struct {
+ int32_t EOR;
+} REQ_RTCReport;
+
+typedef struct {
+ int32_t EOR;
+} REQ_TrimRTC;
+
+typedef struct {
+ int32_t EOR;
+} REQ_CycleLogs;
+
+typedef struct {
+ uint32_t ip;
+ uint32_t bits_specd;
+} REQ_SubnetsAccessed_Subnet;
+
+#define MAX_SUBNETS_ACCESSED 8
+
+typedef struct {
+ uint32_t n_subnets;
+ REQ_SubnetsAccessed_Subnet subnets[MAX_SUBNETS_ACCESSED];
+} REQ_SubnetsAccessed;
+
+/* This is based on the response size rather than the
+ request size */
+#define MAX_CLIENT_ACCESSES 16
+
+typedef struct {
+ uint32_t n_clients;
+ uint32_t client_ips[MAX_CLIENT_ACCESSES];
+} REQ_ClientAccesses;
+
+typedef struct {
+ uint32_t first_index;
+ uint32_t n_indices;
+ int32_t EOR;
+} REQ_ClientAccessesByIndex;
+
+typedef struct {
+ int32_t EOR;
+} REQ_ManualList;
+
+typedef struct {
+ int32_t index;
+ int32_t EOR;
+} REQ_ManualDelete;
+
+typedef struct {
+ int32_t EOR;
+} REQ_MakeStep;
+
+typedef struct {
+ int32_t EOR;
+} REQ_Activity;
+
+/* ================================================== */
+
+#define PKT_TYPE_CMD_REQUEST 1
+#define PKT_TYPE_CMD_REPLY 2
+
+/* This version number needs to be incremented whenever the packet
+ size and/or the format of any of the existing messages is changed.
+ Other changes, e.g. new command types, should be handled cleanly by
+ client.c and cmdmon.c anyway, so the version can stay the same.
+
+ Version 1 : original version with fixed size packets
+
+ Version 2 : both command and reply packet sizes made capable of
+ being variable length.
+
+ Version 3 : NTP_Source message lengthened (auto_offline)
+
+ */
+
+#define PROTO_VERSION_NUMBER 3
+
+/* ================================================== */
+
+typedef struct {
+ uint8_t version; /* Protocol version */
+ uint8_t pkt_type; /* What sort of packet this is */
+ uint8_t res1;
+ uint8_t res2;
+ uint16_t command; /* Which command is being issued */
+ uint16_t attempt; /* How many resends the client has done
+ (count up from zero for same sequence
+ number) */
+ uint32_t sequence; /* Client's sequence number */
+ uint32_t utoken; /* Unique token per incarnation of daemon */
+ uint32_t token; /* Command token (to prevent replay attack) */
+ uint32_t auth[4]; /* MD5 authentication of the packet */
+
+ union {
+ REQ_Online online;
+ REQ_Offline offline;
+ REQ_Burst burst;
+ REQ_Modify_Minpoll modify_minpoll;
+ REQ_Modify_Maxpoll modify_maxpoll;
+ REQ_Dump dump;
+ REQ_Modify_Maxdelay modify_maxdelay;
+ REQ_Modify_Maxdelayratio modify_maxdelayratio;
+ REQ_Modify_Maxupdateskew modify_maxupdateskew;
+ REQ_Logon logon;
+ REQ_Settime settime;
+ REQ_Local local;
+ REQ_Manual manual;
+ REQ_N_Sources n_sources;
+ REQ_Source_Data source_data;
+ REQ_Rekey rekey;
+ REQ_Allow_Deny allow_deny;
+ REQ_Ac_Check ac_check;
+ REQ_NTP_Source ntp_source;
+ REQ_Del_Source del_source;
+ REQ_WriteRtc writertc;
+ REQ_Dfreq dfreq;
+ REQ_Doffset doffset;
+ REQ_Tracking tracking;
+ REQ_Sourcestats sourcestats;
+ REQ_RTCReport rtcreport;
+ REQ_TrimRTC trimrtc;
+ REQ_CycleLogs cyclelogs;
+ REQ_SubnetsAccessed subnets_accessed;
+ REQ_ClientAccesses client_accesses;
+ REQ_ClientAccessesByIndex client_accesses_by_index;
+ REQ_ManualList manual_list;
+ REQ_ManualDelete manual_delete;
+ REQ_MakeStep make_step;
+ REQ_Activity activity;
+ } data; /* Command specific parameters */
+
+} CMD_Request;
+
+/* ================================================== */
+/* Authority codes for command types */
+
+#define PERMIT_OPEN 0
+#define PERMIT_LOCAL 1
+#define PERMIT_AUTH 2
+
+/* ================================================== */
+/* These conversion utilities are used to convert between the internal
+ and the 'wire' representation of real quantities */
+
+#define WIRE2REAL(x) ((double) ((int32_t) ntohl(x)) / 65536.0)
+#define REAL2WIRE(x) (htonl((int32_t)(0.5 + 65536.0 * (x))))
+
+/* ================================================== */
+
+/* Reply codes */
+#define RPY_NULL 1
+#define RPY_N_SOURCES 2
+#define RPY_SOURCE_DATA 3
+#define RPY_MANUAL_TIMESTAMP 4
+#define RPY_TRACKING 5
+#define RPY_SOURCESTATS 6
+#define RPY_RTC 7
+#define RPY_SUBNETS_ACCESSED 8
+#define RPY_CLIENT_ACCESSES 9
+#define RPY_CLIENT_ACCESSES_BY_INDEX 10
+#define RPY_MANUAL_LIST 11
+#define RPY_ACTIVITY 12
+#define N_REPLY_TYPES 13
+
+/* Status codes */
+#define STT_SUCCESS 0
+#define STT_FAILED 1
+#define STT_UNAUTH 2
+#define STT_INVALID 3
+#define STT_NOSUCHSOURCE 4
+#define STT_INVALIDTS 5
+#define STT_NOTENABLED 6
+#define STT_BADSUBNET 7
+#define STT_ACCESSALLOWED 8
+#define STT_ACCESSDENIED 9
+#define STT_NOHOSTACCESS 10
+#define STT_SOURCEALREADYKNOWN 11
+#define STT_TOOMANYSOURCES 12
+#define STT_NORTC 13
+#define STT_BADRTCFILE 14
+#define STT_INACTIVE 15
+#define STT_BADSAMPLE 16
+
+typedef struct {
+ int32_t EOR;
+} RPY_Null;
+
+typedef struct {
+ uint32_t n_sources;
+ int32_t EOR;
+} RPY_N_Sources;
+
+#define RPY_SD_MD_CLIENT 0
+#define RPY_SD_MD_PEER 1
+#define RPY_SD_MD_REF 2
+
+#define RPY_SD_ST_SYNC 0
+#define RPY_SD_ST_UNREACH 1
+#define RPY_SD_ST_FALSETICKER 2
+#define RPY_SD_ST_JITTERY 3
+#define RPY_SD_ST_OTHER 4
+
+typedef struct {
+ uint32_t ip_addr;
+ uint16_t poll;
+ uint16_t stratum;
+ uint16_t state;
+ uint16_t mode;
+ uint32_t since_sample;
+ int32_t orig_latest_meas;
+ int32_t latest_meas;
+ uint32_t latest_meas_err;
+ int32_t est_offset;
+ uint32_t est_offset_err;
+ int32_t resid_freq;
+ uint32_t resid_skew;
+ int32_t EOR;
+} RPY_Source_Data;
+
+typedef struct {
+ uint32_t ref_id;
+ uint32_t stratum;
+ uint32_t ref_time_s;
+ uint32_t ref_time_us;
+ uint32_t current_correction_s;
+ uint32_t current_correction_us;
+ int32_t freq_ppm;
+ int32_t resid_freq_ppm;
+ int32_t skew_ppm;
+ int32_t root_delay;
+ int32_t root_dispersion;
+ int32_t EOR;
+} RPY_Tracking;
+
+typedef struct {
+ uint32_t ip_addr;
+ uint32_t n_samples;
+ uint32_t n_runs;
+ uint32_t span_seconds;
+ uint32_t sd_us;
+ int32_t resid_freq_ppm;
+ int32_t skew_ppm;
+ int32_t EOR;
+} RPY_Sourcestats;
+
+typedef struct {
+ uint32_t ref_time;
+ uint16_t n_samples;
+ uint16_t n_runs;
+ uint32_t span_seconds;
+ int32_t rtc_seconds_fast;
+ int32_t rtc_gain_rate_ppm;
+ int32_t EOR;
+} RPY_Rtc;
+
+typedef struct {
+ uint32_t centiseconds;
+ int32_t dfreq_ppm;
+ int32_t new_afreq_ppm;
+ int32_t EOR;
+} RPY_ManualTimestamp;
+
+typedef struct {
+ uint32_t ip;
+ uint32_t bits_specd;
+ uint32_t bitmap[8];
+} RPY_SubnetsAccessed_Subnet;
+
+typedef struct {
+ uint32_t n_subnets;
+ RPY_SubnetsAccessed_Subnet subnets[MAX_SUBNETS_ACCESSED];
+} RPY_SubnetsAccessed;
+
+typedef struct {
+ uint32_t ip;
+ uint32_t client_hits;
+ uint32_t peer_hits;
+ uint32_t cmd_hits_auth;
+ uint32_t cmd_hits_normal;
+ uint32_t cmd_hits_bad;
+ uint32_t last_ntp_hit_ago;
+ uint32_t last_cmd_hit_ago;
+} RPY_ClientAccesses_Client;
+
+typedef struct {
+ uint32_t n_clients;
+ RPY_ClientAccesses_Client clients[MAX_CLIENT_ACCESSES];
+} RPY_ClientAccesses;
+
+typedef struct {
+ uint32_t n_indices; /* how many indices there are in the server's table */
+ uint32_t next_index; /* the index 1 beyond those processed on this call */
+ uint32_t n_clients; /* the number of valid entries in the following array */
+ RPY_ClientAccesses_Client clients[MAX_CLIENT_ACCESSES];
+} RPY_ClientAccessesByIndex;
+
+#define MAX_MANUAL_LIST_SAMPLES 32
+
+typedef struct {
+ uint32_t when;
+ int32_t slewed_offset;
+ int32_t orig_offset;
+ int32_t residual;
+} RPY_ManualListSample;
+
+typedef struct {
+ uint32_t n_samples;
+ RPY_ManualListSample samples[MAX_MANUAL_LIST_SAMPLES];
+} RPY_ManualList;
+
+typedef struct {
+ int32_t online;
+ int32_t offline;
+ int32_t burst_online;
+ int32_t burst_offline;
+ int32_t EOR;
+} RPY_Activity;
+
+typedef struct {
+ uint8_t version;
+ uint8_t pkt_type;
+ uint8_t res1;
+ uint8_t res2;
+ uint16_t command; /* Which command is being replied to */
+ uint16_t reply; /* Which format of reply this is */
+ uint16_t status; /* Status of command processing */
+ uint16_t number; /* Which packet this is in reply sequence */
+ uint16_t total; /* Number of replies to expect in this sequence */
+ uint16_t pad1; /* Get up to 4 byte alignment */
+ uint32_t sequence; /* Echo of client's sequence number */
+ uint32_t utoken; /* Unique token per incarnation of daemon */
+ uint32_t token; /* New command token (only if command was successfully
+ authenticated) */
+ uint32_t auth[4]; /* MD5 authentication of the packet */
+
+ union {
+ RPY_Null null;
+ RPY_N_Sources n_sources;
+ RPY_Source_Data source_data;
+ RPY_ManualTimestamp manual_timestamp;
+ RPY_Tracking tracking;
+ RPY_Sourcestats sourcestats;
+ RPY_Rtc rtc;
+ RPY_SubnetsAccessed subnets_accessed;
+ RPY_ClientAccesses client_accesses;
+ RPY_ClientAccessesByIndex client_accesses_by_index;
+ RPY_ManualList manual_list;
+ RPY_Activity activity;
+ } data; /* Reply specific parameters */
+
+} CMD_Reply;
+
+/* ================================================== */
+
+#endif /* GOT_CANDM_H */
--- /dev/null
+.TH CHRONY 1 "August 10, 2001" chrony "User's Manual"
+.SH NAME
+chrony \- programs for keeping computer clocks accurate
+
+.SH SYNOPSIS
+\fBchronyc\fR [\fIOPTIONS\fR]
+
+\fBchronyd\fR [\fIOPTIONS\fR]
+
+.SH DESCRIPTION
+\fBchrony\fR is a pair of programs for keeping computer clocks accurate.
+\fIchronyd\fR is a background (daemon) program and \fIchronyc\fR is a
+command-line interface to it. Time reference sources for chronyd can be
+RFC1305 NTP servers, human (via keyboard and \fIchronyc\fR), or the computer's
+real-time clock at boot time (Linux only). chronyd can determine the rate at
+which the computer gains or loses time and compensate for it while no external
+reference is present. Its use of NTP servers can be switched on and off
+(through \fIchronyc\fR) to support computers with dial-up/intermittent access
+to the Internet, and it can also act as an RFC1305-compatible NTP server.
+
+.SH USAGE
+\fIchronyc\fR is a command-line interface program which can be used to
+monitor \fIchronyd\fR's performance and to change various operating
+parateters whilst it is running.
+
+\fIchronyd\fR's main function is to obtain measurements of the true (UTC)
+time from one of several sources, and correct the system clock
+accordingly. It also works out the rate at which the system clock
+gains or loses time and uses this information to keep it accurate
+between measurements from the reference.
+
+The reference time can be derived from either Network Time Protocol
+(NTP) servers (preferred), or wristwatch-and-keyboard (via \fIchronyc\fR).
+The main source of information about the Network Time Protocol is
+\fIhttp://www.eecis.udel.edu/~ntp\fR.
+
+It is designed so that it can work on computers which only have
+intermittent access to reference sources, for example computers which
+use a dial-up account to access the Internet. Of course, it will work
+on computers with permanent connections too.
+
+In addition, for Linux 2.0.x (for x >= 32) or 2.2 onwards, chronyd can monitor
+the system's real time clock performance, so the system can maintain accurate
+time even across reboots.
+
+Typical accuracies available between 2 machines are
+
+On an ethernet LAN : 100-200 microseconds, often much better
+On a V32bis dial-up modem connection : 10's of milliseconds (from one
+session to the next)
+
+\fIchronyd\fR can also operate as an RFC1305-compatible NTP server and peer.
+
+.SH "SEE ALSO"
+.BR chronyc(1),
+.BR chrony(1)
+
+.I http://chrony.sunsite.dk/
+
+.SH AUTHOR
+Richard Curnow <rc@rc0.org.uk>
+
+This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part
+of "The Missing Man Pages Project". Please see
+\fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details.
--- /dev/null
+.TH chrony.conf 5 "August 10, 2001" chrony "Configuration Files"
+.SH NAME
+chrony.conf \- chronyd configuration file
+
+.SH SYNOPSIS
+.B /etc/chrony.conf
+
+.SH DESCRIPTION
+\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
+clocks. \fIchronyd\fR is a background daemon program that can be started at
+boot time.
+
+Assuming that you have found some servers, you need to set up a
+configuration file to run \fIchrony\fR. The (compiled-in) default location
+for this file is \fB/etc/chrony.conf\fR. Assuming that your ntp servers
+are called `a.b.c' and `d.e.f', your \fBchrony.conf\fR file could contain
+as a minimum
+
+ server a.b.c
+ server d.e.f
+ server g.h.i
+
+However, you will probably want to include some of the other directives
+described in detail in the documentation supplied with the distribution
+(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives will be
+particularly useful : `driftfile', `commandkey', `keyfile'. The smallest
+useful configuration file would look something like
+
+ server a.b.c
+ server d.e.f
+ server g.h.i
+ keyfile /etc/chrony.keys
+ commandkey 1
+ driftfile /etc/chrony.drift
+
+
+.SH "SEE ALSO"
+.BR chrony(1),
+.BR chronyc(1),
+.BR chronyd(1)
+
+.I http://chrony.sunsite.dk/
+
+.SH AUTHOR
+Richard Curnow <rc@rc0.org.uk>
+
+This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
+Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
+for details.
--- /dev/null
+Begin3
+Title: chrony
+Version: 1.18
+Entered-date: 01APR02
+Description: A pair of programs for keeping computer clocks accurate.
+ chronyd is a background (daemon) program and chronyc is a
+ command-line interface to it. Time reference sources for
+ chronyd can be RFC1305 NTP servers, human (via keyboard and
+ chronyc), and the computer's real-time clock at boot time
+ (Linux only). chronyd can determine the rate at which the
+ computer gains or loses time and compensate for it whilst no
+ external reference is present. chronyd's use of NTP servers
+ can be switched on and off (through chronyc) to support
+ computers with dial-up/intermittent access to the
+ Internet. chronyd can also act as an RFC1305-compatible NTP
+ server.
+Keywords: time NTP RFC1305 RTC adjtime
+Author: rc@rc0.org.uk (Richard Curnow)
+Maintained-by: rc@rc0.org.uk (Richard Curnow)
+Primary-site: sunsite.unc.edu /pub/Linux/system/admin/time
+ 295k chrony-1.18.tar.gz
+ 2k chrony.lsm
+Platforms: Linux 2.0/2.1/2.2/2.3/2.4 (x86, powerpc)
+ Solaris 2.5/6/7/8, SunOS 4.1.4. (Sparc)
+ BSDI/386.
+ NetBSD
+ Solaris 2.8 (x86)
+Copying-policy: GPL
+End
--- /dev/null
+Summary: An NTP client/server
+Name: chrony
+Version: @@VERSION@@
+Release: 1
+Source: chrony-%{version}.tar.gz
+Copyright: GPL
+Group: Applications/Utilities
+Packager: Richard P. Curnow <rc@rc0.org.uk>
+BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
+Requires: info
+
+%description
+A pair of programs for keeping computer clocks accurate. chronyd is a
+background (daemon) program and chronyc is a command-line interface to it.
+Time reference sources for chronyd can be RFC1305 NTP servers, human (via
+keyboard and chronyc), and the computer's real-time clock at boot time (Linux
+only). chronyd can determine the rate at which the computer gains or loses
+time and compensate for it whilst no external reference is present. chronyd's
+use of NTP servers can be switched on and off (through chronyc) to support
+computers with dial-up/intermittent access to the Internet. chronyd can also
+act as an RFC1305-compatible NTP server.
+
+%prep
+%setup
+
+%build
+./configure --prefix=%{_prefix}
+make CC=gcc CFLAGS=-O2 prefix=%{_prefix}
+make chrony.txt prefix=%{_prefix}
+make chrony.info prefix=%{_prefix}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+cd $RPM_BUILD_DIR/chrony-%{version}
+make install DESTDIR=$RPM_BUILD_ROOT prefix=%{_prefix}
+mkdir -p $RPM_BUILD_ROOT%{_infodir}
+cp chrony.info* $RPM_BUILD_ROOT%{_infodir}
+
+%files
+%{_sbindir}/chronyd
+%{_bindir}/chronyc
+%{_infodir}/chrony.info*
+%{_mandir}/man1/chrony.1.gz
+%{_mandir}/man1/chronyc.1.gz
+%{_mandir}/man5/chrony.conf.5.gz
+%{_mandir}/man8/chronyd.8.gz
+%doc README
+%doc chrony.txt
+%doc COPYING
+%doc examples/chrony.conf.example
+%doc examples/chrony.keys.example
+
--- /dev/null
+\input texinfo
+@c {{{ Main header stuff
+@afourwide
+@paragraphindent 0
+@setfilename chrony.info
+@settitle User guide for the chrony suite
+@c @setchapternewpage off
+
+@ifinfo
+@dircategory Net Utilities
+@direntry
+* chrony: (chrony). How to use chronyd and chronyc
+* chronyd: (chrony)Starting chronyd. Reference for chronyd
+* chronyc: (chrony)Running chronyc. Reference for chronyc
+@end direntry
+@end ifinfo
+
+@titlepage
+@sp 10
+@title The chrony suite
+@subtitle This manual describes how to use
+@subtitle the programs chronyd and chronyc
+@author Richard P. Curnow
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1997-1999 Richard P. Curnow
+@end titlepage
+@c }}}
+@c {{{ Top node
+@node Top
+@top
+@menu
+* Introduction:: What the chrony suite does
+* Installation:: How to compile and install the software
+* Typical scenarios:: How to configure the software for some common cases
+* Usage reference:: Reference manual
+* Porting guide:: Hints to help with porting the software
+* GPL:: The GNU General Public License
+@end menu
+@c }}}
+@c {{{ Ch:Introduction
+@c {{{ Chapter top
+@node Introduction
+@chapter Introduction
+@menu
+* Overview:: What the programs do
+* Acknowledgements:: Credit where credit is due
+* Availability:: Where to get the software
+* Other time synchronisation packages:: Comparision with other software
+* Distribution and warranty:: There is no warranty
+* Bug reporting:: How to report bugs and make suggestions
+* Contributing:: Areas where contributions are particularly welcome
+@end menu
+@c }}}
+@c {{{ S:Overview
+@node Overview
+@section Overview
+Chrony is a software package for maintaining the accuracy of computer
+system clocks. It consists of a pair of programs :
+
+@itemize @bullet
+@item @code{chronyd}. This is a daemon which runs in background on the
+system. It obtains measurements (e.g. via the network) of the system's
+offset relative to other systems, and adjusts the system time
+accordingly. For isolated systems, the user can periodically enter the
+correct time by hand (using @code{chronyc}). In either case,
+@code{chronyd} determines the rate at which the computer gains or loses
+time, and compensates for this.
+
+@code{chronyd} can also act as an NTP server, and provide a time-of-day service
+to other computers. A typical set-up is to run @code{chronyd} on a gateway
+computer that has a dial-up link to the Internet, and use it to serve time to
+computers on a private LAN sitting behind the gateway. The IP addresses that
+can act as clients of @code{chronyd} can be tightly controlled. The default is
+no client access.
+
+@item @code{chronyc}. This is a command-line driven control and
+monitoring program. An administrator can use this to fine-tune various
+parameters within the daemon, add or delete servers etc whilst the
+daemon is running.
+
+The IP addresses from which @code{chronyc} clients may connect can be tightly
+controlled. The default is just the computer that @code{chronyd} itself is
+running on.
+@end itemize
+@c }}}
+@c {{{ S:Acknowledgments
+@node Acknowledgements
+@section Acknowledgements
+
+The @code{chrony} suite makes use of the algorithm known as @emph{RSA
+Data Security, Inc. MD5 Message-Digest Algorithm} for authenticating
+messages between different machines on the network.
+
+In writing the @code{chronyd} program, extensive use has been made of
+RFC1305, written by David Mills. I have occasionally referred to the
+@code{xntp} suite's source code to check details of the protocol that
+the RFC did not make absolutely clear. The core algorithms in
+@code{chronyd} are all completely distinct from @code{xntp}, however.
+@c }}}
+@c {{{ S:Availability
+@node Availability
+@section Availability
+@menu
+* Getting the software:: Where can I get the software from?
+* Platforms:: Which platforms will it run on?
+@end menu
+
+
+@node Getting the software
+@subsection Getting the software
+Links on @uref{http://chrony.sunsite.dk/download.php, the
+chrony home page} describe how to obtain the software.
+
+
+@node Platforms
+@subsection Platforms
+Although most of the program is portable between
+Unix-like systems, there are parts that have to be tailored to each
+specific vendor's system. These are the parts that interface with the
+operating system's facilities for adjusting the system clock;
+different operating systems may provide different function calls to
+achieve this, and even where the same function is used it may have
+different quirks in its behaviour.
+
+The software is known to work in the following environments:
+@itemize @bullet
+@item Linux/i386 and Linux/ppc. The software is known to work on Linux 2.0.x,
+2.2.x and 2.4.x. Prior to 2.0.31, the real time clock can't be used.
+
+@item NetBSD
+@item BSD/386
+
+@item Solaris 2.3/2.5/2.5.1/2.6/2.7/2.8 on Sparc (Sparc 20, Ultrasparc) and
+i386
+
+@item SunOS 4.1.4 on Sparc 2 and Sparc20.
+@end itemize
+
+Closely related systems may work too, but they have not been tested.
+
+Porting the software to other system (particularly to those supporting
+an @code{adjtime} system call) should not be difficult, however it
+requires access to such systems to test out the driver.
+@c }}}
+@c {{{ S:Other programs
+@node Other time synchronisation packages
+@section Relationship to other software packages
+@menu
+* Comparison with xntpd::
+* Comparison with timed::
+@end menu
+
+@node Comparison with xntpd
+@subsection xntpd
+The `reference' implementation of the Network Time Protocol is the
+program @code{xntpd}, available via
+@uref{http://www.eecis.udel.edu/~ntp, The NTP home page}.
+
+@code{xntpd} is designed to support all the operating modes defined by
+RFC1305, and has driver support for a large number of reference clocks
+(such as GPS receivers) that can be connected directly to a computer,
+thereby providing a so-called 'stratum 1' server.
+
+Things @code{chronyd} can do that @code{xntpd} can't:
+
+@itemize @bullet
+@item
+@code{chronyd} can perform usefully in an environment where access to
+the time reference is intermittent. @code{chronyd} estimates
+@emph{both} the current time offset @emph{and} the rate at which the
+computer's clock gains or loses time, and can use that rate estimate to
+trim the clock after the reference disappears. @code{xntpd} corrects
+any time offset by speeding up and slowing down the computer clock, and
+so could be left with a significant rate error if the reference
+disappears whilst it is trying to correct a big offset.
+
+@item
+@code{chronyd} provides support for isolated networks whether the only
+method of time correction is manual entry (e.g. by the administrator
+looking at a clock). @code{chronyd} can look at the errors corrected at
+different updates to work out the rate at which the computer gains or
+loses time, and use this estimate to trim the computer clock
+subsequently.
+
+@item
+@code{chronyd} provides support to work out the gain or loss rate of the
+`real-time clock', i.e. the clock that maintains the time when the
+computer is turned off. It can use this data when the system boots to
+set the system time from a corrected version of the real-time clock.
+These real-time clock facilities are only available on certain releases
+of Linux, so far.
+
+@item
+The @code{xntpd} program is supported by other programs to carry out
+certain functions. @code{ntpdate} is used to provide an initial
+correction to the system clock based on a `one-shot' sampling of other
+NTP servers. @code{tickadj} is used to adjust certain operating system
+parameters to make @code{xntpd} work better. All this functionality is
+integrated into @code{chronyd}.
+@end itemize
+
+Things @code{xntpd} can do that @code{chronyd} can't:
+
+@itemize @bullet
+@item
+@code{xntpd} supports a range of different hardware reference clocks
+(GPS, atomic etc) that can be connected to a computer to provide a
+`stratum-1' server. @code{chronyd} does not support any such hardware
+@emph{yet}; I don't have access to any to do any development work.
+However, the software architecture should allow such equipment to be
+interfaced at a later date.
+
+@item
+@code{xntpd} supports effectively all of RFC1305, including broadcast /
+multicast clients, leap seconds, and extra encryption schemes for
+authenticating data packets.
+
+@item
+@code{xntpd} has been ported to more types of computer / operating
+system (so far).
+
+@item
+xntpd is designed to work solely with integer arithmetic (i.e. does not
+require floating point support from its host).
+@end itemize
+
+@node Comparison with timed
+@subsection timed
+@code{timed} is a program that is part of the BSD networking suite. It
+uses broadcast packets to find all machines running the daemon within a
+subnet. The machines elect a master which periodically measures the
+system clock offsets of the other computers using ICMP timestamps.
+Corrections are sent to each member as a result of this process.
+
+Problems that may arise with @code{timed} are :
+
+@itemize @bullet
+@item
+Because it uses broadcasts, it is not possible to isolate its
+functionality to a particular group of computers; there is a risk of
+upsetting other computers on the same network (e.g. where a whole
+company is on the same subnet but different departments are independent
+from the point of view of administering their computers.)
+@item
+The update period appears to be 10 minutes. Computers can build up
+significant offsets relative to each other in that time. If a
+computer can estimate its rate of drift it can keep itself closer to
+the other computers between updates by adjusting its clock every few
+seconds. @code{timed} does not seem to do this.
+@item
+@code{timed} does not have any integrated capability for feeding
+real-time into its estimates, or for estimating the average rate of time
+loss/gain of the machines relative to real-time (unless one of the
+computers in the group has access to an external reference and is always
+appointed as the `master').
+@end itemize
+
+@code{timed} does have the benefit over @code{chronyd} that for isolated
+networks of computers, they will track the `majority vote' time. For
+such isolated networks, @code{chronyd} requires one computer to be the
+`master' with the others slaved to it. If the master has a particular
+defective clock, the whole set of computers will tend to slip relative
+to real time (but they @emph{will} stay accurate relative to one
+another).
+@c }}}
+@c {{{ S:Rights + warranty
+@node Distribution and warranty
+@section Distribution rights and (lack of) warranty
+
+Chrony may be distributed in accordance with the GNU General Public License
+version 2, reproduced in @xref{GPL}.
+
+@c }}}
+@c {{{ S:Bug reporting + suggestions
+@node Bug reporting
+@section Bug reporting and suggestions
+
+If you think you've found a bug in chrony, or have a suggestion, please let me
+know. My primary current email address is @email{rc@@rc0.org.uk}. If that
+fails, you could try finding me through one of the chrony mailing lists, or by
+looking up my name on a search engine.
+
+I can't promise a timescale to fix a bug; it depends a lot on the how complex
+the bug is to track down, as I have a lot of other calls on my time : 2 young
+children, my job, and indeed other free/open source software projects.
+However, I do intend to look into problems when time allows.
+
+Another source of information to try is the chrony users mailing list. You can
+join this by sending an empty message to
+@email{chrony-users-subscribe@@sunsite.dk}. Only subscribers can post to
+the list.
+
+When you are reporting a bug, please send me all the information you can.
+Unfortunately, chrony has proven to be one of those programs where it is very
+difficult to reproduce bugs in a different environment. So I may have to
+interact with you quite a lot to obtain enough extra logging and tracing to
+pin-point the problem in some cases. Please be patient and plan for this!
+
+Of course, if you can debug the problem yourself and send me a source code
+patch to fix it, I will be very grateful!
+
+@c }}}
+@c {{{ S:Contributions
+@node Contributing
+@section Contributions
+
+Although chrony is now a fairly mature and established project, there are still
+areas that could be improved. If you can program in C and have some expertise
+in these areas, you might be able to fill the gaps.
+
+Particular areas I know need addressing are :
+
+@enumerate
+@item Porting to other Unices
+
+This involves creating equivalents of sys_solaris.c, sys_linux.c etc for the
+new system. Note, the Linux driver has been reported as working on a range of
+different architectures (Alpha, Sparc, MIPS as well as x86 of course).
+
+@item Porting to Windows NT
+
+I did a small amount of work on this under Cygwin. Only the sorting out of the
+include files has really been achieved so far. The two main areas still to
+address are
+
+@enumerate
+@item The system clock driver.
+@item How to make chronyd into an NT service (i.e. what to replace fork(),
+setsid() etc with so that chronyd can be automatically started in the system
+bootstrap.
+@end enumerate
+
+@item Hardware clock support
+
+@item Automation of the trimrtc and writertc mechanisms
+
+Currently, the RTC trimming mechanism is a manual operation, because there has
+to be a reasonable guarantee that the system will stay up for a reasonable
+length of time afterwards. (If it is shut down too soon, a poor
+characterisation of the RTC drift rate will be stored on disc, giving a bad
+system clock error when the system is next booted.)
+
+To make chrony more automated for the non-expert user, it would be useful if
+this problem could be avoided so that trimrtc could be done automatically (e.g.
+in a crontab, or as part of the ip-up or ip-down scripts.)
+
+@end enumerate
+@c }}}
+@c }}}
+@c {{{ Ch:Installation
+@node Installation
+@chapter Installation
+
+@c {{{ main introduction text
+The software is distributed as source code which has to be compiled.
+The source code is supplied in the form of a gzipped tar file, which
+unpacks to a subdirectory identifying the name and version of the
+program.
+
+After unpacking the source code, change directory into it, and type
+
+@example
+./configure
+@end example
+
+This is a shell script that automatically determines the system type.
+There is a single optional parameter, @code{--prefix} which indicates
+the directory tree where the software should be installed. For example,
+
+@example
+./configure --prefix=/opt/free
+@end example
+
+will install the @code{chronyd} daemon into /opt/free/sbin and the
+chronyc control program into /opt/free/bin. The default value for the
+prefix is /usr/local.
+
+The configure script assumes you want to use gcc as your compiler.
+If you want to use a different compiler, you can configure this way:
+
+@example
+CC=cc CFLAGS=-O ./configure --prefix=/opt/free
+@end example
+
+for Bourne-family shells, or
+
+@example
+setenv CC cc
+setenv CFLAGS -O
+./configure --prefix=/opt/free
+@end example
+
+for C-family shells.
+
+If the software cannot (yet) be built on your system, an error message
+will be shown. Otherwise, the files @file{options.h} and
+@file{Makefile} will be generated.
+
+By default, chronyc will be built to make use of the readline library. If you
+don't want this, specify the --disable-readline flag to configure. If you have
+readline and/or ncurses installed in a non-standard location, please refer to
+@pxref{readline support} for information.
+
+Now type
+
+@example
+make
+@end example
+
+to build the programs.
+
+If you want to build the manual in plain text, HTML and info versions, type
+
+@example
+make docs
+@end example
+
+Once the programs have been successfully compiled, they need to be
+installed in their target locations. This step normally needs to be
+performed by the superuser, and requires the following command to be
+entered.
+
+@example
+make install
+@end example
+
+This will install the binaries, plain text manual and manpages.
+
+To install the HTML and info versions of the manual as well, enter the command
+
+@example
+make install-docs
+@end example
+
+If you want chrony to appear in the top level info directory listing, you need
+to run the @command{install-info} command manually after this step.
+@command{install-info} takes 2 arguments. The first is the path to the
+@file{chrony.info} file you have just installed. This will be the argument you
+gave to --prefix when you configured (@file{/usr/local} by default), with
+@file{/info/chrony.info} on the end. The second argument is the location of
+the file called @file{dir}. This will typically be @file{/usr/info/dir}. So
+the typical command line would be
+
+@example
+install-info /usr/local/info/chrony.info /usr/info/dir
+@end example
+
+Now that the software is successfully installed, the next step is to
+set up a configuration file. The contents of this depend on the
+network environment in which the computer operates. Typical scenarios
+are described in the following section of the document.
+@c }}}
+@menu
+* readline support:: If readline or ncurses in in a non-standard place
+* package builders:: Extra options useful to package builders
+@end menu
+@c {{{ readline support
+@node readline support
+@section Support for the readline library
+By default, chronyc is built to make use of the readline library. This allows
+you to use the cursor keys to replay and edit old commands. If you don't want
+to use readline (in which case chronyc will use a minimal command line
+interface), invoke configure like this:
+
+@example
+./configure --disable-readline other-options...
+@end example
+
+If you have readline and/or ncurses installed in locations that aren't normally searched by the compiler and linker, you need extra options if you want readline to be used:
+
+@table @samp
+@item --with-readline-includes=directory_name
+This defines the name of the directory above the one where @file{readline.h}
+is. @file{readline.h} is assumed to be in a @file{readline} subdirectory of
+the named directory.
+
+@item --with-readline-library=directory_name
+This defines the directory containing the @file{libreadline.a} or
+@file{libreadline.so} file.
+
+@item --with-ncurses-library=directory_name
+This defines the directory containing the @file{libncurses.a} or
+@file{libncurses.so} file.
+@end table
+
+@c }}}
+@c {{{
+@node package builders
+@section Extra options for package builders
+The configure and make procedures have some extra options that may be useful if
+you are building a distribution package for chrony.
+
+The --infodir=DIR option to configure specifies a different install directory
+for the info files. This overrides the @file{info} subdirectory of the
+argument to the --prefix option. For example, you might use
+
+@example
+./configure --prefix=/usr --infodir=/usr/share/info
+@end example
+
+The --mandir=DIR option to configure specifies a different install directory
+for the man pages. This overrides the @file{man} subdirectory of the
+argument to the --prefix option.
+
+@example
+./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man
+@end example
+
+to set both options together.
+
+The final option is the DESTDIR option to the make command. For example, you
+could use the commands
+
+@example
+./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man
+make all docs
+make install DESTDIR=./tmp
+cd tmp
+tar cvf - . | gzip -9 > chrony.tar.gz
+@end example
+
+to build a package. When untarred within the root directory, this will install
+the files to the intended final locations.
+
+@c }}}
+
+@c }}}
+@c {{{ Ch:Typical operating scenarios
+@c {{{ Chapter top
+@node Typical scenarios
+@chapter Typical operating scenarios
+@menu
+* Computers on the net:: Your computer is permanently on the Internet (or on
+ a private network with NTP servers)
+* Infrequent connection:: You connect to the Internet sometimes (e.g. via a modem)
+* Isolated networks:: You have an isolated network with no reference clocks
+* Dial-up home PCs:: Additional considerations if you turn your computer off
+ when it's not in use.
+* Configuration options overview:: Overview of some configuration options.
+@end menu
+@c }}}
+@c {{{ S:Permanent connection
+@node Computers on the net
+@section Computers connected to the internet
+In this section we discuss how to configure chrony for computers that
+have permanent connections to the internet (or to any network
+containing true NTP servers which ultimately derive their time from a
+reference clock).
+
+To operate in this mode, you will need to know the names of the NTP
+server machines you wish to use. You may be able to find names of
+suitable servers by one of the following methods:
+
+@itemize @bullet
+@item Your institution may already operate servers on its network.
+Contact your system administrator to find out.
+
+@item Your ISP probably has one or more NTP servers available for its
+customers.
+
+@item Somewhere under the NTP homepage there is a list of public
+stratum 1 and stratum 2 servers. You should find one or more servers
+that are near to you --- check that their access policy allows you to
+use their facilities.
+@end itemize
+
+Assuming that you have found some servers, you need to set up a
+configuration file to run chrony. The (compiled-in) default location
+for this file is @file{/etc/chrony.conf}. Assuming that your ntp
+servers are called @code{a.b.c} and @code{d.e.f}, your
+@file{chrony.conf} file could contain as a minimum
+
+@example
+server a.b.c
+server d.e.f
+server g.h.i
+@end example
+
+However, you will probably want to include some of the other directives
+described later. The following directives will be particularly useful :
+@code{driftfile}, @code{commandkey}, @code{keyfile}. The smallest
+useful configuration file would look something like
+
+@example
+server a.b.c
+server d.e.f
+server g.h.i
+keyfile /etc/chrony.keys
+commandkey 1
+driftfile /etc/chrony.drift
+@end example
+@c }}}
+@c {{{ S:Infrequent connection
+@node Infrequent connection
+@section Infrequent connection to true NTP servers
+In this section we discuss how to configure chrony for computers that
+have occasional connections to the internet.
+
+@menu
+* Configuration for infrequent connections:: How to set up the @code{/etc/chrony} file
+* Advising chronyd of internet availability:: How to tell chronyd when the link is available
+@end menu
+
+@node Configuration for infrequent connections
+@subsection Setting up the configuration file for infrequent connections
+As in the previous section, you will need access to NTP servers on the
+internet. The same remarks apply for how to find them.
+
+In this case, you will need some additional configuration to tell
+@code{chronyd} when the connection to the internet goes up and down.
+This saves the program from continuously trying to poll the servers when
+they are inaccessible.
+
+Again, assuming that your ntp servers are called @code{a.b.c} and
+@code{d.e.f}, your @file{chrony.conf} file would need to contain
+something like
+
+@example
+server a.b.c
+server d.e.f
+server g.h.i
+@end example
+
+However, the following issues need to be addressed:
+
+@enumerate 1
+@item
+Your computer probably doesn't have DNS access whilst offline to turn
+the machine names into IP addresses.
+@item
+Your computer will keep trying to contact the servers to obtain
+timestamps, even whilst offline. If you operate a dial-on-demand
+system, things are even worse, because the link to the internet will
+keep getting established.
+@end enumerate
+
+For this reason, it would be better to specify this part of your
+configuration file in the following way:
+
+@example
+server 1.2.3.4 offline
+server 5.6.7.8 offline
+server 9.10.11.12 offline
+@end example
+
+Because numeric IP addresses have been used, the first problem is
+overcome. The @code{offline} keyword indicates that the servers start
+in an offline state, and that they should not be contacted until @code{chronyd}
+receives notification that the link to the internet is present.
+
+An alternative is to use the names of the NTP servers, and put entries for them
+into your @file{/etc/hosts} file. This will be OK as long as @samp{files}
+comes before @samp{dns} in the @samp{hosts} line of the
+@file{/etc/nsswitch.conf} file.
+
+In order to notify @code{chronyd} of the presence of the link, you will need to
+be able to log in to it with the program chronyc. To do this, @code{chronyd}
+needs to be configured with an administrator password. To set up an
+administrator password, you can create a file @file{/etc/chrony.keys}
+containing a single line
+
+@example
+1 xyzzy
+@end example
+
+and add the following line to @file{/etc/chrony.conf} (the order of the
+lines does not matter)
+
+@example
+commandkey 1
+@end example
+
+The smallest useful configuration file would look something like
+
+@example
+server 1.2.3.4 offline
+server 5.6.7.8 offline
+server 9.10.11.12 offline
+keyfile /etc/chrony.keys
+commandkey 1
+driftfile /etc/chrony.drift
+@end example
+
+The next section describes how to tell @code{chronyd} when the internet link
+goes up and down.
+
+@node Advising chronyd of internet availability
+@subsection How to tell chronyd when the internet link is available.
+To use this option, you will need to configure a command key in
+@code{chronyd's} configuration file @file{/etc/chrony.conf}, as described in
+the previous section.
+
+To tell @code{chronyd} when to start and finish sampling the servers, the
+@code{online} and @code{offline} commands of chronyc need to be used.
+To give an example of their use, we assume that @code{pppd} is the
+program being used to connect to the internet, and that chronyc has been
+installed at its default location @file{/usr/local/bin/chronyc}. We
+also assume that the command key has been set up as described in the
+previous section.
+
+In the file @file{/etc/ppp/ip-up} we add the command sequence
+
+@example
+/usr/local/bin/chronyc <<EOF
+password xyzzy
+online
+EOF
+@end example
+
+and in the file @file{/etc/ppp/ip-down} we add the sequence
+
+@example
+/usr/local/bin/chronyc <<EOF
+password xyzzy
+offline
+EOF
+@end example
+
+@code{chronyd's} polling of the servers will now only occur whilst the
+machine is actually connected to the Internet.
+@c }}}
+@c {{{ S:Isolated networks
+@node Isolated networks
+@section Isolated networks
+In this section we discuss how to configure chrony for computers that
+never have network conectivity to any computer which ultimately derives
+its time from a reference clock.
+
+In this situation, one computer is selected to be the master timeserver.
+The other computers are either direct clients of the master, or clients
+of clients.
+
+The rate value in the master's drift file needs to be set to the average
+rate at which the master gains or loses time. @code{chronyd} includes
+support for this, in the form of the @code{manual} directive in the
+configuration file and the @code{settime} command in the @code{chronyc}
+program.
+
+If the master is rebooted, @code{chronyd} can re-read the drift rate
+from the drift file. However, the master has no accurate estimate of
+the current time. To get around this, the system can be configured so
+that the master can initially set itself to a `majority-vote' of
+selected clients' times; this allows the clients to `flywheel' the
+master across its outage.
+
+A typical configuration file for the master (called @code{master}) might
+be (assuming the clients are in the 192.168.165.x subnet and that the
+master's address is 192.168.169.170)
+
+@example
+driftfile /etc/chrony.drift
+commandkey 25
+keyfile /etc/chrony.keys
+initstepslew 10 client1 client3 client6
+local stratum 8
+manual
+allow 192.168.165
+@end example
+
+For the clients that have to resynchronise the master when it restarts,
+the configuration file might be
+
+@example
+server master
+driftfile /etc/chrony.drift
+logdir /var/log/chrony
+log measurements statistics tracking
+keyfile /etc/chrony.keys
+commandkey 24
+local stratum 10
+initstepslew 20 master
+allow 192.168.169.170
+@end example
+
+The rest of the clients would be the same, except that the @code{local}
+and @code{allow} directives are not required.
+@c }}}
+@c {{{ S:Dial-up home PCs
+@node Dial-up home PCs
+@section The home PC with a dial-up connection
+
+@menu
+* Dial-up overview:: General discussion of how the software operates in this mode
+* Dial-up configuration:: Typical configuration files
+@end menu
+
+@node Dial-up overview
+@subsection Assumptions/how the software works
+This section considers the home computer which has a dial-up connection.
+It assumes that Linux is run exclusively on the computer. Dual-boot
+systems may work; it depends what (if anything) the other system does to
+the system's real-time clock.
+
+Much of the configuration for this case is discussed earlier
+(@pxref{Infrequent connection}). This section addresses specifically
+the case of a computer which is turned off between 'sessions'.
+
+In this case, @code{chronyd} relies on the computer's real-time clock
+(RTC) to maintain the time between the periods when it is powered up.
+The arrangement is shown in the figure below.
+
+@example
+@group
+ trim if required PSTN
+ +---------------------------+ +----------+
+ | | | |
+ v | | |
++---------+ +-------+ +-----+ +---+
+| System's| measure error/ |chronyd| |modem| |ISP|
+|real-time|------------------->| |-------| | | |
+| clock | drift rate +-------+ +-----+ +---+
++---------+ ^ |
+ | | |
+ +---------------------------+ --o-----o---
+ set time at boot up |
+ +----------+
+ |NTP server|
+ +----------+
+@end group
+@end example
+
+When the computer is connected to the Internet (via the modem),
+@code{chronyd} has access to external NTP servers which it makes
+measurements from. These measurements are saved, and straight-line fits
+are performed on them to provide an estimate of the computer's time
+error and rate of gaining/losing time.
+
+When the computer is taken offline from the Internet, the best estimate
+of the gain/loss rate is used to free-run the computer until it next
+goes online.
+
+Whilst the computer is running, @code{chronyd} makes measurements of the
+real-time clock (RTC) (via the @file{/dev/rtc} interface, which must be
+compiled into the kernel). An estimate is made of the RTC error at a
+particular RTC second, and the rate at which the RTC gains or loses time
+relative to true time.
+
+The RTC is fully supported in 2.2 and 2.4 kernels.
+
+For kernels in the 2.0 series prior to 2.0.32, the kernel was set up to
+trim the RTC every 11 minutes. This would be disasterous for
+@code{chronyd} -- there is no reliable way of synchronising with this
+trimming. For this reason, @code{chronyd} only supports the RTC in 2.0
+kernels from v2.0.32 onwards.
+
+When the computer is powered down, the measurement histories for all the
+NTP servers are saved to files (if the @code{dumponexit} directive is
+specified in the configuration file), and the RTC tracking information
+is also saved to a file (if the @code{rtcfile} directive has been
+specified). These pieces of information are also saved if the
+@code{dump} and @code{writertc} commands respectively are issued through
+@code{chronyc}.
+
+When the computer is rebooted, @code{chronyd} reads the current RTC time
+and the RTC information saved at the last shutdown. This information is
+used to set the system clock to the best estimate of what its time would
+have been now, had it been left running continuously. The measurement
+histories for the servers are then reloaded.
+
+The next time the computer goes online, the previous sessions'
+measurements can contribute to the line-fitting process, which gives a
+much better estimate of the computer's gain/loss rate.
+
+One problem with saving the measurements and RTC data when the machine
+is shut down is what happens if there is a power failure; the most
+recent data will not be saved. Although @code{chronyd} is robust enough
+to cope with this, some performance may be lost. (The main danger
+arises if the RTC has been changed during the session, with the
+@code{trimrtc} command in @code{chronyc}. Because of this,
+@code{trimrtc} will make sure that a meaningful RTC file is saved out
+after the change is completed).
+
+The easiest protection against power failure is to put the @code{dump}
+and @code{writertc} commands in the same place as the @code{offline}
+command is issued to take @code{chronyd} offline; because @code{chronyd}
+free-runs between online sessions, no parameters will change
+significantly between going offline from the Internet and any power
+failure.
+
+A final point regards home computers which are left running for extended
+periods and where it is desired to spin down the hard disc when it is
+not in use (e.g. when not accessed for 15 minutes). @code{chronyd} has
+been planned so it supports such operation; this is the reason why the
+RTC tracking parameters are not saved to disc after every update, but
+only when the user requests such a write, or during the shutdown
+sequence. The only other facility that will generate periodic writes to
+the disc is the @code{log rtc} facility in the configuration file; this
+option should not be used if you want your disc to spin down.
+
+@node Dial-up configuration
+@subsection Typical configuration files.
+
+To illustrate how a dial-up home computer might be configured, example
+configuration files are shown in this section.
+
+For the @file{/etc/chrony.conf} file, the following can be used as an
+example. @emph{NOTE : The @code{server} directives are only applicable
+to customers of Demon Internet; users of other ISPs will need to use
+their own ISP's NTP servers or public NTP servers.}
+
+@example
+server 158.152.1.65 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+server 158.152.1.76 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+server 194.159.253.2 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+logdir /var/log/chrony
+log statistics measurements tracking
+driftfile /etc/chrony.drift
+keyfile /etc/chrony.keys
+commandkey 25
+maxupdateskew 100.0
+dumponexit
+dumpdir /var/log/chrony
+rtcfile /etc/chrony.rtc
+@end example
+
+With Freeserve as the ISP, I use the following server lines :
+
+@example
+server 194.152.64.68 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+server 194.152.64.35 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+server 194.152.64.34 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+@end example
+
+I use @code{pppd} for connecting to my ISP. This runs two scripts
+@file{/etc/ppp/ip-up} and @file{/etc/ppp/ip-down} when the link goes
+online and offline respectively.
+
+The relevant part of the @file{/etc/ppp/ip-up} file is (with a dummy
+password)
+
+@example
+/usr/local/bin/chronyc <<EOF
+password xxxxxxxx
+online
+EOF
+@end example
+
+and the relevant part of the @file{/etc/ppp/ip-down} script is
+
+@example
+/usr/local/bin/chronyc <<EOF
+password xxxxxxxx
+offline
+dump
+writertc
+EOF
+@end example
+
+(Because they have to contain the administrator password, it would be
+desirable to make the files readable only by root on a multiuser
+machine).
+
+To start @code{chronyd} during the boot sequence, I have the following
+in @file{/etc/rc.d/rc.local} (this is a Slackware system)
+
+@example
+if [ -f /usr/local/sbin/chronyd -a -f /etc/chrony.conf ]; then
+ /usr/local/sbin/chronyd -r -s
+ echo "Start chronyd"
+fi
+@end example
+
+The placement of this command may be important on some systems. In
+particular, @code{chronyd} may need to be started several seconds (about
+10 as a minimum) before any software that depends on the system clock
+not jumping or moving backwards, depending on the directives in
+@code{chronyd's} configuration file.
+
+For the system shutdown, @code{chronyd} should receive a SIGTERM several
+seconds before the final SIGKILL; the SIGTERM causes the measurement
+histories and RTC information to be saved out. There should be no need
+to add anything to the shutdown sequence, unless (as my system had)
+there is no pause between the SIGTERM and SIGKILL being delivered to the
+remaining processes. So if you find something like
+
+@example
+killall5 -15
+killall5 -9
+@end example
+
+in your @code{/etc/rc.d/rc.0} script, you will need to insert a sleep, e.g.
+
+@example
+killall5 -15
+sleep 5
+killall5 -9
+@end example
+
+Otherwise, @code{chronyd} will not always save information on shutdown,
+which could be a problem if you don't use @code{dump} and
+@code{writertc} when you go offline.
+@c }}}
+@c {{{ S:Other config options
+@node Configuration options overview
+@section Other important configuration options
+The most common option to include in the configuration file is the
+@code{driftfile} option. One of the major tasks of @code{chronyd} is to
+work out how fast or how slow the system clock runs relative to real
+time - e.g. in terms of seconds gained or lost per day. Measurements
+over a long period are usually required to refine this estimate to an
+acceptable degree of accuracy. Therefore, it would be bad if
+@code{chronyd} had to work the value out each time it is restarted,
+because the system clock would not run so accurately whilst the
+determination is taking place.
+
+To avoid this problem, @code{chronyd} allows the gain or loss rate to be
+stored in a file, which can be read back in when the program is
+restarted. This file is called the drift file, and might typically be
+stored in @file{/etc/chrony.drift}. By specifying an option like the
+following
+
+@example
+driftfile /etc/chrony.drift
+@end example
+
+in the configuration file (@file{/etc/chrony.conf}), the drift file
+facility will be activated.
+@c }}}
+@c }}}
+@c {{{ Ch:Usage reference
+@node Usage reference
+@chapter Usage reference
+
+@c {{{ Chapter top
+@menu
+* Starting chronyd:: Command line options for the daemon
+* Configuration file:: Format of the configuration file
+* Running chronyc:: The run-time configuration program
+@end menu
+@c }}}
+@c {{{ S:Starting chronyd
+@node Starting chronyd
+@section Starting chronyd
+If @code{chronyd} has been installed to its default location
+@file{/usr/local/sbin/chronyd}, starting it is simply a matter of
+entering the command
+
+@example
+/usr/local/sbin/chronyd
+@end example
+
+Information messages and warnings will be logged to syslog.
+
+The command line options supported are as follows:
+
+@table @code
+@item -d
+When run in this mode, the program will not detach itself from the
+terminal, and all messages will be sent to the terminal instead of to
+syslog.
+@item -f <conf-file>
+This option can be used to specify an alternate location for the
+configuration file (default @file{/etc/chrony.conf}).
+@item -r
+This option will reload sample histories for each of the servers being
+used. These histories are created by using the @code{dump} command in
+@code{chronyc}, or by setting the @code{dumponexit} directive in the
+configuration file. This option is useful if you want to stop and
+restart @code{chronyd} briefly for any reason, e.g. to install a new
+version. However, it only makes sense on systems where the kernel can
+maintain clock compensation whilst not under @code{chronyd's} control.
+The only version where this happens so far is Linux. On systems where
+this is not the case, e.g. Solaris and SunOS the option should not be
+used.
+@item -s
+This option will set the system clock from the computer's real-time
+clock. This is analogous to supplying the `-s' flag to the
+@file{/sbin/clock} program during the Linux boot sequence.
+
+Support for real-time clocks is limited at present - the criteria are
+described in the section on the @code{rtcfile} directive (@pxref{rtcfile
+directive}).
+
+If @code{chronyd} cannot support the real time clock on your computer,
+this option cannot be used and a warning message will be logged to the
+syslog.
+
+If used in conjunction with the `-r' flag, @code{chronyd} will attempt
+to preserve the old samples after setting the system clock from the real
+time clock. This can be used to allow @code{chronyd} to perform long
+term averaging of the gain or loss rate across system reboots, and is
+useful for dial-up systems that are shut down when not in use. For this
+to work well, it relies on @code{chronyd} having been able to determine
+accurate statistics for the difference between the real time clock and
+system clock last time the computer was on.
+
+@item -v
+This option displays @code{chronyd's} version number to the terminal and
+exits.
+@end table
+
+On systems that support an @file{/etc/rc.local} file for starting
+programs at boot time, @code{chronyd} can be started from there.
+
+On systems with a System V style initialisation (e.g. Solaris), a
+suitable start/stop script might be as shown below. This might be
+placed in the file @file{/etc/rc2.d/S83chrony}.
+
+@example
+@group
+#!/bin/sh
+# This file should have uid root, gid sys and chmod 744
+#
+
+killproc() @{ # kill the named process(es)
+ pid=`/usr/bin/ps -e |
+ /usr/bin/grep -w $1 |
+ /usr/bin/sed -e 's/^ *//' -e 's/ .*//'`
+ [ "$pid" != "" ] && kill $pid
+@}
+
+case "$1" in
+
+'start')
+ if [ -f /opt/free/sbin/chronyd -a -f /etc/chrony.conf ]; then
+ /opt/free/sbin/chronyd
+ fi
+ ;;
+'stop')
+ killproc chronyd
+ ;;
+*)
+ echo "Usage: /etc/rc2.d/S83chrony @{ start | stop @}"
+ ;;
+esac
+@end group
+@end example
+
+(In both cases, you may want to bear in mind that @code{chronyd} can
+step the time when it starts. There may be other programs started at
+boot time that could be upset by this, so you may need to consider the
+ordering carefully. However, @code{chronyd} will need to start after
+daemons providing services that it may require, e.g. the domain name
+service.)
+@c }}}
+@c {{{ S:chronyd configuration file
+@node Configuration file
+@section The chronyd configuration file
+@c {{{ section top
+The configuration file is normally called @file{/etc/chrony.conf}; in
+fact, this is the compiled-in default. However, other locations can be
+specified with a command line option.
+
+Each command in the configuration file is placed on a separate line.
+The following sections describe each of the commands in turn. The
+directives can occur in any order in the file.
+
+@menu
+* comments in config file:: How to write a comment
+* acquisitionport directive:: Set port to use for initial time probes
+* allow directive:: Give access to NTP clients
+* bindaddress directive:: Limit the network interface that is used for NTP
+* bindcmdaddress directive:: Limit the network interface that is used for commands
+* broadcast directive:: Make chronyd act as an NTP broadcast server
+* cmdallow directive:: Give control access to chronyc on other computers
+* cmddeny directive:: Deny control access to chronyc on other computers
+* commandkey directive:: Set runtime command key
+* cmdport directive:: Set port to use for runtime commanding
+* deny directive:: Deny access to NTP clients
+* driftfile directive:: Specify location of file containing drift data
+* dumpdir directive:: Specify directory for dumping measurements
+* dumponexit directive:: Dump measurements when daemon exits
+* initstepslew directive:: Trim the system clock on boot-up.
+* keyfile directive:: Specify location of file containing keys
+* linux_hz directive:: Define a non-standard value of the kernel HZ constant
+* linux_freq_scale directive:: Define a non-standard value to compensate the kernel frequency bias
+* local directive:: Allow unsynchronised machine to act as server
+* log directive:: Make daemon log certain sets of information
+* logchange directive:: Generate syslog messages if large offsets occur
+* logdir directive:: Specify directory for logging
+* mailonchange directive:: Send email if a clock correction above a threshold occurs
+* manual directive:: Allow manual entry using chronyc's settime cmd.
+* maxupdateskew directive:: Stop bad estimates upsetting machine clock
+* noclientlog directive:: Prevent chronyd from gathering data about clients
+* peer directive:: Specify an NTP peer
+* pidfile directive:: Specify the file where chronyd's pid is written
+* port directive:: Set port to use for NTP packets
+* rtcdevice directive:: Specify name of enhanced RTC device (if not /dev/rtc)
+* rtcfile directive:: Specify the file where real-time clock data is stored
+* rtconutc directive:: Specify that the real time clock keeps UTC not local time
+* server directive:: Specify an NTP server
+@end menu
+@c }}}
+@c {{{ comments in config file
+@node comments in config file
+@subsection Comments in the configuration file
+The configuration file may contain comment lines. A comment line is any line
+that starts with zero or more spaces followed by any one of the following
+characters:
+@itemize
+@item !
+@item ;
+@item #
+@item %
+@end itemize
+Any line with this format will be ignored.
+@c }}}
+@c {{{ acquisitionport directive
+@node acquisitionport directive
+@subsection acquisitionport
+@code{chronyd} uses a separate client-side port for the rapid-fire
+measurements requested with the @code{initstepslew} directive
+(@pxref{initstepslew directive}). Normally, that port is chosen
+arbitrarily by the operating system. However, you can use
+@code{acquisitionport} to explicitly specify a port. This may be useful
+for getting through firewalls.
+
+Do not make acquisition and regular NTP service (@pxref{port directive})
+use the same port.
+
+An example of the @code{acquisitionport} command is
+
+@example
+acquisitionport 1123
+@end example
+
+This would change the port used for rapid queries to udp/1123. You
+could then persuade the firewall administrator to let that port through.
+@c }}}
+@c {{{ allow
+@node allow directive
+@subsection allow
+The @code{allow} command is used to designate a particular subnet from
+which NTP clients are allowed to access the computer as an NTP server.
+
+The default is that no clients are allowed access, i.e. @code{chronyd}
+operates purely as an NTP client. If the @code{allow} directive is
+used, @code{chronyd} will be both a client of its servers, and a server
+to other clients.
+
+Examples of use of the command are as follows:
+
+@example
+allow foo.bar.com
+allow 1.2
+allow 3.4.5
+allow 6.7.8/22
+allow 6.7.8.9/22
+allow
+@end example
+
+The first command allows the named node to be an NTP client of this computer.
+The second command allows any node with an IP address of the form 1.2.x.y (with
+x and y arbitrary) to be an NTP client of this computer. Likewise, the third
+command allows any node with an IP address of the form 3.4.5.x to have client
+NTP access. The fourth and fifth forms allow access from any node with an IP
+address of the form 6.7.8.x, 6.7.9.x, 6.7.10.x or 6.7.11.x (with x arbitrary),
+i.e. the value 22 is the number of bits defining the specified subnet. (In the
+fifth form, the final byte is ignored). The sixth form allows access by any
+node on the entire Internet.
+
+A second form of the directive, @code{allow all}, has a greater effect,
+depending on the ordering of directives in the configuration file. To
+illustrate the effect, consider the two examples
+
+@example
+allow 1.2.3.4
+deny 1.2.3
+allow 1.2
+@end example
+
+and
+
+@example
+allow 1.2.3.4
+deny 1.2.3
+allow all 1.2
+@end example
+
+In the first example, the effect is the same regardles of what order the
+three directives are given in. So the 1.2.x.y subnet is allowed access,
+except for the 1.2.3.x subnet, which is denied access, however the host
+1.2.3.4 is allowed access.
+
+In the second example, the @code{allow all 1.2} directives overrides the
+effect of @emph{any} previous directive relating to a subnet within the
+specified subnet. Within a configuration file this capability is
+probably rather moot; however, it is of greater use for reconfiguration
+at run-time via @code{chronyc} (@pxref{allow all command}).
+
+Note, if the @code{initstepslew} directive (@pxref{initstepslew
+directive}) is used in the configuration file, each of the computers
+listed in that directive must allow client access by this computer for
+it to work.
+@c }}}
+@c {{{ bindaddress
+@node bindaddress directive
+@subsection bindaddress
+The bindaddress allows you to restrict the network interface to which
+chronyd will listen for NTP packets. This provides an additional level of
+access restriction above that available through the 'deny' mechanism.
+
+Suppose you have a local ethernet with addresses in the 192.168.1.0
+subnet together with a dial-up connection. The ethernet interface's IP
+address is 192.168.1.1. Suppose (for some reason) you want to block all
+access through the dialup connection (note, this will even block replies
+from servers on the dialup side, so you will not be able to synchronise
+to an external source). You could add the line
+
+@example
+bindaddress 192.168.1.1
+@end example
+
+to the configuration file.
+
+This directive affects NTP (UDP port 123) packets. If no @code{bindcmdaddress}
+directive is present, the address supplied by @code{bindaddress} will be used
+to control binding of the command socket (UDP port 323) as well.
+
+The @code{bindaddress} directive has been found to cause problems when used on
+computers that need to pass NTP traffic over multiple network interfaces (e.g.
+firewalls). It is, therefore, not particularly useful. Use of the
+@code{allow} and @code{deny} directives together with a network firewall is
+more likely to be successful.
+
+@c }}}
+@c {{{ bindcmdaddress
+@node bindcmdaddress directive
+@subsection bindcmdaddress
+The bindcmdaddress allows you to restrict the network interface to which
+chronyd will listen for command packets (issued by chronyc).
+
+Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet
+together with a dial-up connection. The ethernet interface's IP address is
+192.168.1.1. Suppose you want to block all access through the dialup
+connection. You could add the line
+
+@example
+bindcmdaddress 192.168.1.1
+@end example
+
+to the configuration file.
+
+The @code{bindcmdaddress} directive has been found to cause problems when used
+on computers that need to pass command traffic over multiple network
+interfaces. It is, therefore, not particularly useful. Use of the
+@code{cmdallow} and @code{cmddeny} directives together with a network firewall
+is more likely to be successful.
+
+@c }}}
+@c {{{ broadcast directive
+@node broadcast directive
+@subsection broadcast
+The @code{broadcast} directive is used to declare a broadcast address to which
+chronyd should send packets in NTP broadcast mode (i.e. make chronyd act as a
+broadcast server). Broadcast clients on that subnet will be able to
+synchronise.
+
+The syntax is as follows
+
+@example
+broadcast 30 192.168.1.255
+broadcast 60 192.168.2.255 12123
+@end example
+
+In the first example, the destination port defaults to 123/udp (the normal NTP
+port). In the second example, the destionation port is specified as 12123.
+The first parameter in each case (30 or 60 respectively) is the interval in
+seconds between broadcast packets being sent. The second parameter in each
+case is the broadcast address to send the packet to. This should correspond to
+the broadcast address of one of the network interfaces on the computer where
+chronyd is running.
+
+You can have more than 1 @code{broadcast} directive if you have more than 1
+network interface onto which you wish to send NTP broadcast packets.
+
+Chronyd itself cannot currently act as a broadcast client; it must always be
+configured as a point-to-point client by defining specific NTP servers and
+peers. This broadcast server feature is intended for providing a time source
+to other NTP software (e.g. various MS Windows clients).
+
+If xntpd is used as the broadcast client, it will try to use a point-to-point
+client/server NTP access to measure the round-trip delay. Thus, the broadcast
+subnet should also be the subject of an @code{allow} directive (@pxref{allow
+directive}).
+@c }}}
+@c {{{ cmdallow
+@node cmdallow directive
+@subsection cmdallow
+
+This is similar to the @code{allow} directive (@pxref{allow directive}), except
+that it allows control access (rather than NTP client access) to a particular
+subnet or host. (By 'control access' is meant that chronyc can be run on those
+hosts and successfully connect to chronyd on this computer.)
+
+The syntax is identical to the @code{allow} directive.
+
+There is also a @code{cmdallow all} directive with similar behaviour to the
+@code{allow all} directive (but applying to control access in this case, of
+course).
+@c }}}
+@c {{{ cmddeny
+@node cmddeny directive
+@subsection cmddeny
+
+This is similar to the @code{cmdallow} directive (@pxref{cmdallow directive}),
+except that it denies control access to a particular subnet or host,
+rather than allowing it.
+
+The syntax is identical.
+
+There is also a @code{cmddeny all} directive with similar behaviour to the
+@code{cmdallow all} directive.
+@c }}}
+@c {{{ commandkey
+@node commandkey directive
+@subsection commandkey
+The commandkey command is used to set the key number used for
+authenticating user commands via the chronyc program at run time.
+This allows certain actions of the chronyc program to be restricted to
+administrators.
+
+An example of the commandkey command is
+
+@example
+commandkey 20
+@end example
+
+In the key file (see the keyfile command) there should be a line of
+the form
+
+@example
+20 foobar
+@end example
+
+When running the chronyc program to perform run-time configuration,
+the command
+
+@example
+password foobar
+@end example
+
+must be entered before any commands affecting the operation of the
+daemon can be entered.
+@c }}}
+@c {{{ cmdport
+@node cmdport directive
+@subsection cmdport
+
+The @code{cmdport} directive allows the port that is used for run-time
+command and monitoring (via the program @code{chronyc}) to be altered
+from its default (323/udp).
+
+An example shows the syntax
+
+@example
+cmdport 257
+@end example
+
+This would make @code{chronyd} use 257/udp as its command port.
+(@code{chronyc} would need to be run with the @code{-p 257} switch to
+inter-operate correctly).
+@c }}}
+@c {{{ deny
+@node deny directive
+@subsection deny
+
+This is similar to the @code{allow} directive (@pxref{allow directive}),
+except that it denies NTP client access to a particular subnet or host,
+rather than allowing it.
+
+The syntax is identical.
+
+There is also a @code{deny all} directive with similar behaviour to the
+@code{allow all} directive.
+@c }}}
+@c {{{ driftfile
+@node driftfile directive
+@subsection driftfile
+One of the main activities of the @code{chronyd} program is to work out
+the rate at which the system clock gains or loses time relative to real
+time.
+
+Whenever @code{chronyd} computes a new value of the gain/loss rate, it
+is desirable to record it somewhere. This allows @code{chronyd} to
+begin compensating the system clock at that rate whenever it is
+restarted, even before it has had a chance to obtain an equally good
+estimate of the rate during the new run. (This process may take many
+minutes, at least).
+
+The driftfile command allows a file to be specified into which
+@code{chronyd} can store the rate information. Two parameters are
+recorded in the file. The first is the rate at which the system clock
+gains or loses time, expressed in parts per million, with gains
+positive. Therefore, a value of 100.0 indicates that when the system
+clock has advanced by a second, it has gained 100 microseconds on
+reality (so the true time has only advanced by 999900 microseconds).
+The second is an estimate of the error bound around the first value in
+which the true rate actually lies.
+
+An example of the driftfile command is
+
+@example
+driftfile /etc/chrony.drift
+@end example
+@c }}}
+@c {{{ dumpdir
+@node dumpdir directive
+@subsection dumpdir
+To compute the rate of gain or loss of time, @code{chronyd} has to store
+a measurement history for each of the time sources it uses.
+
+Certain systems (so far only Linux) have operating system support for
+setting the rate of gain or loss to compensate for known errors. (On
+other systems, @code{chronyd} must simulate such a capability by
+periodically slewing the system clock forwards or backwards by a
+suitable amount to compensate for the error built up since the previous
+slew).
+
+For such systems, it is possible to save the measurement history across
+restarts of @code{chronyd} (assuming no changes are made to the system
+clock behaviour whilst it is not running). If this capability is to be
+used (via the dumponexit command in the configuration file, or the dump
+command in chronyc), the dumpdir command should be used to define the
+directory where the measurement histories are saved.
+
+An example of the command is
+
+@example
+dumpdir /var/log/chrony
+@end example
+
+A source whose IP address is 1.2.3.4 would have its measurement
+history saved in the file @file{/var/log/chrony/1.2.3.4.dat}.
+@c }}}
+@c {{{ dumponexit
+@node dumponexit directive
+@subsection dumponexit
+If this command is present, it indicates that @code{chronyd} should save
+the measurement history for each of its time sources recorded whenever
+the program exits. (See the dumpdir command above).
+@c }}}
+@c {{{ initstepslew
+@node initstepslew directive
+@subsection initstepslew
+In normal operation, @code{chronyd} always slews the time when it needs to
+adjust the system clock. For example, to correct a system clock which
+is 1 second slow, @code{chronyd} slightly increases the amount by which the
+system clock is advanced on each clock interrupt, until the error is
+removed. (Actually, this is done by calling the @code{adjtime()} or
+similar system function which does it for us.) Note that at no time
+does time run backwards with this method.
+
+On most Unix systems it is not desirable to step the system clock,
+because many programs rely on time advancing monotonically forwards.
+
+When the @code{chronyd} daemon is initially started, it is possible that the
+system clock is considerably in error. Attempting to correct such an
+error by slewing may not be sensible, since it may take several hours
+to correct the error by this means.
+
+The purpose of the @code{initstepslew} directive is to allow @code{chronyd} to
+make a rapid measurement of the system clock error at boot time, and to
+correct the system clock by stepping before normal operation begins.
+Since this would normally be performed only at an appropriate point in
+the system boot sequence, no other software should be adversely affected
+by the step.
+
+If the correction required is less than a specified threshold, a slew is
+used instead. This makes it easier to restart @code{chronyd} whilst the
+system is in normal operation.
+
+The @code{initstepslew} directive takes a threshold and a list of NTP
+servers as arguments. A maximum of 8 will be used. Each of the servers
+is rapidly polled several times, and a majority voting mechanism used to
+find the most likely range of system clock error that is present. A
+step (or slew) is applied to the system clock to correct this error.
+@code{chronyd} then enters its normal operating mode (where only slews are
+used).
+
+An example of use of the command is
+
+@example
+initstepslew 30 foo.bar.com baz.quz.com
+@end example
+
+where 2 NTP servers are used to make the measurement. The @code{30}
+indicates that if the system's error is found to be 30 seconds or less,
+a slew will be used to correct it; if the error is above 30 seconds, a
+step will be used.
+
+The @code{initstepslew} directive can also be used in an isolated LAN
+environment, where the clocks are set manually. The most stable
+computer is chosen as the master, and the other computers are slaved to
+it. If each of the slaves is configured with the local option (see
+below), the master can be set up with an @code{initstepslew} directive
+which references some or all of the slaves. Then, if the master machine
+has to be rebooted, the slaves can be relied on to 'flywheel' the time
+for the master.
+@c }}}
+@c {{{ keyfile
+@node keyfile directive
+@subsection keyfile
+This command is used to specify the location of the file containing
+ID/key pairs for the following 2 uses:
+
+@itemize @bullet
+@item Authentication of NTP packets.
+@item Authentication of administrator commands entered via chronyc.
+@end itemize
+
+The format of the command is shown in the example below
+
+@example
+keyfile /etc/chrony.keys
+@end example
+
+The argument is simply the name of the file containing the ID/key
+pairs. The format of the file is shown below
+
+@example
+10 tulip
+11 hyacinth
+20 crocus
+25 iris
+ ...
+@end example
+
+Each line consists of an ID and a password. The ID can be any
+unsigned integer in the range 0 through 2**32-1. The password can be
+any string of characters not containing a space.
+
+For NTP use, the MD5 authentication scheme is always used. This must be
+borne in mind if @code{chronyd} is to inter-operate in authenticated
+mode with @code{xntpd} running on other computers.
+
+The ID for the chronyc authentication key is specified with the
+commandkey command (see earlier).
+@c }}}
+@c {{{ local
+@node local directive
+@subsection local
+The local keyword is used to allow @code{chronyd} to appear synchronised
+to real time (from the viewpoint of clients polling it), even if it has
+no current synchronisation source.
+
+This option is normally used on computers in an isolated network,
+where several computers are required to synchronise to one other, this
+being the "master" which is kept vaguely in line with real time by
+manual input.
+
+An example of the command is
+
+@example
+local stratum 10
+@end example
+
+The value 10 may be substituted with other values in the range 1
+through 15. Stratum 1 indicates a computer that has a true real-time
+reference directly connected to it (e.g. GPS, atomic clock etc)
+– such computers are expected to be very close to real time.
+Stratum 2 computers are those which have a stratum 1 server; stratum 3
+computers have a stratum 2 server and so on.
+
+A large value of 10 indicates that the clock is so many hops away from
+a reference clock that its time is fairly unreliable. Put another
+way, if the computer ever has access to another computer which is
+ultimately synchronised to a reference clock, it will almost certainly
+be at a stratum less than 10. Therefore, the choice of a high value
+like 10 for the local command prevents the machine's own time from
+ever being confused with real time, were it ever to leak out to
+clients that have visibility of real servers.
+@c }}}
+@c {{{ linux_hz
+@node linux_hz directive
+@subsection linux_hz
+(This option only applies to Linux).
+
+By default, chronyd will find the value of @code{HZ} from a kernel header file
+at compile time. @code{HZ} is the nominal number of timer interrupts per
+second. If you're running chronyd on the system where it was built, the value
+it has should be right, and you don't need to worry about this option.
+
+This option is provided for people who move a pre-built chronyd onto a system
+where the value of HZ in the kernel headers has been changed from the default
+value.
+
+An example of the command is
+
+@example
+linux_hz 100
+@end example
+@c }}}
+@c {{{ linux_freq_scale
+@node linux_freq_scale directive
+@subsection linux_freq_scale
+(This option only applies to Linux).
+
+By default, chronyd will find the value of @code{HZ} and @code{SHIFT_HZ} from
+kernel header files at compile time. An internal value called
+@code{freq_scale} is calculated from this. By default it is (1<<SHIFT_HZ)/HZ,
+except for the case HZ=100, when special case code is used which leads to the
+value 128/128.125. If you're running chronyd on the system where it was built,
+the value it has should be right, and you don't need to worry about this
+option.
+
+This option is provided for people who move a pre-built chronyd onto a system
+where the method by which the kernel computes the reciprocal of this value has been changed or where the HZ and SHIFT_HZ constants differ from those on the system where chronyd was built.
+
+An example of the command is
+
+@example
+linux_freq_scale 0.99902439
+@end example
+@c }}}
+@c {{{ log
+@node log directive
+@subsection log
+@c {{{ section top
+The log command indicates that certain information is to be logged.
+
+@table @code
+@item measurements
+This option logs the raw NTP measurements and related information to a
+file called measurements.log.
+
+@item statistics
+This option logs information about the regression processing to a file
+called statistics.log.
+
+@item tracking
+This option logs changes to the estimate of the system's gain or loss
+rate, and any slews made, to a file called tracking.log.
+
+@item rtc
+This option logs information about the system's real-time clock.
+@end table
+
+The files are written to the directory specified by the logdir
+command.
+
+An example of the command is
+
+@example
+log measurements statistics tracking
+@end example
+
+@menu
+* measurements log:: The format of the measurements log
+* statistics log:: The format of the statistics log
+* tracking log:: The format of the tracking log
+* RTC log:: The format of the RTC log
+@end menu
+@c }}}
+@c {{{ measurements.log
+@node measurements log
+@subsubsection Measurements log file format
+
+An example line (which actually appears as a single line in the file)
+from the measurements log file is shown below.
+
+@example
+1998-07-22 05:40:50 158.152.1.76 N 8 1111 11 1111 10 10 1 \
+ -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03
+@end example
+
+The columns are as follows (the quantities in square brackets are the
+values from the example line above) :
+
+@enumerate 1
+@item
+Date [1998-07-22]
+@item
+Hour:Minute:Second [05:40:50]. Note that the date/time pair is
+expressed in UTC, not the local time zone.
+@item
+IP address of server/peer from which measurement comes [158.152.1.76]
+@item
+Leap status (@code{N} means normal, @code{-} means that the last minute
+of today has 61 seconds, @code{+} means that the last minute of the day
+has 59 seconds, @code{?} means the remote computer is not currently
+synchronised.) [N]
+@item
+Stratum of remote computer. [2]
+@item
+RFC1305 tests 1 through 4 (1=pass, 0=fail) [1111]
+@item
+Tests for maximum delay and maximum delay ratio, against user defined
+parameters (1=pass, 0=fail) [11]
+@item
+RFC1305 tests 5 through 8 (1=pass, 0=fail) [1111]
+@item
+Local poll [10]
+@item
+Remote poll [10]
+@item
+`Score' (an internal score within each polling level used to decide when
+to increase or decrease the polling level. This is adjusted based on
+changes to the variance of the measurements obtained from the source). [1]
+@item
+The estimated local clock error (`theta' in RFC1305). Positive indicates that the local clock is slow. [-4.966e-03].
+@item
+The peer delay (`delta' in RFC1305). [2.296e-01]
+@item
+The peer dispersion (`epsilon' in RFC1305). [1.577e-05]
+@item
+The root delay (`Delta' in RFC1305). [1.615e-01]
+@item
+The root dispersion (`E' in RFC1305). [7.446e-03]
+@end enumerate
+
+A banner is periodically written to the log file to indicate the
+meanings of the columns.
+@c }}}
+@c {{{ statistics.log
+@node statistics log
+@subsubsection Statistics log file format
+
+An example line (which actually appears as a single line in the file)
+from the measurements log file is shown below.
+
+@example
+1998-07-22 05:40:50 158.152.1.76 6.261e-03 -3.247e-03 \
+ 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8
+@end example
+
+The columns are as follows (the quantities in square brackets are the
+values from the example line above) :
+
+@enumerate 1
+@item
+Date [1998-07-22]
+@item
+Hour:Minute:Second [05:40:50]. Note that the date/time pair is
+expressed in UTC, not the local time zone.
+@item
+IP address of server/peer from which measurement comes [158.152.1.76]
+@item
+The estimated standard deviation of the measurements from the source (in
+seconds). [6.261e-03]
+@item
+The estimated offset of the source (in seconds, positive means the local
+clock is estimated to be fast, in this case). [-3.247e-03]
+@item
+The estimated standard deviation of the offset estimate (in
+seconds). [2.220e-03]
+@item
+The estimated rate at which the local clock is gaining or losing time
+relative to the source (in seconds per second, positive means the local
+clock is gaining). This is relative to the compensation currently being
+applied to the local clock, @emph{not} to the local clock without any
+compensation. [1.874e-06]
+@item
+The estimated error in the rate value (in seconds per
+second). [1.080e-06].
+@item
+The ration of |old_rate - new_rate| / old_rate_error. Large values
+indicate the statistics are not modelling the source very well. [7.8e-02]
+@item
+The number of measurements currently being used for the regression
+algorithm. [16]
+@item
+The new starting index (the oldest sample has index 0; this is the
+method used to prune old samples when it no longer looks like the
+measurements fit a linear model). [0, i.e. no samples discarded this
+time]
+@item
+The number of runs. The number of runs of regression residuals with the
+same sign is computed. If this is too small it indicates that the
+measurements are no longer represented well by a linear model and that
+some older samples need to be discarded. The number of runs for the
+data that is being retained is tabulated. Values of approximately half
+the number of samples are expected. [8]
+@end enumerate
+
+A banner is periodically written to the log file to indicate the
+meanings of the columns.
+@c }}}
+@c {{{ tracking.log
+@node tracking log
+@subsubsection Tracking log file format
+
+An example line (which actually appears as a single line in the file)
+from the measurements log file is shown below.
+
+@example
+1998-07-22 05:40:50 158.152.1.76 3 340.529 1.606 1.046e-03
+@end example
+
+The columns are as follows (the quantities in square brackets are the
+values from the example line above) :
+
+@enumerate 1
+@item
+Date [1998-07-22]
+@item
+Hour:Minute:Second [05:40:50]. Note that the date/time pair is
+expressed in UTC, not the local time zone.
+@item
+The IP address of the server/peer to which the local system is
+synchronised. [158.152.1.76]
+@item
+The stratum of the local system. [3]
+@item
+The local system frequency (in ppm, positive means the local system runs
+fast of UTC). [340.529]
+@item
+The error bounds on the frequency (in ppm) [1.606]
+@item
+The estimated local offset at the epoch (which is rapidly corrected by
+slewing the local clock. (In seconds, positive indicates the local
+system is fast of UTC). [1.046e-3]
+@end enumerate
+
+A banner is periodically written to the log file to indicate the
+meanings of the columns.
+@c }}}
+@c {{{ rtc.log
+@node RTC log
+@subsubsection Real-time clock log file format
+
+An example line (which actually appears as a single line in the file)
+from the measurements log file is shown below.
+
+@example
+1998-07-22 05:40:50 -0.037360 1 -0.037434\
+ -37.948 12 5 120
+@end example
+
+The columns are as follows (the quantities in square brackets are the
+values from the example line above) :
+
+@enumerate 1
+@item
+Date [1998-07-22]
+@item
+Hour:Minute:Second [05:40:50]. Note that the date/time pair is
+expressed in UTC, not the local time zone.
+@item
+The measured offset between the system's real time clock and the system
+(@code{gettimeofday()}) time. In seconds, positive indicates that the
+RTC is fast of the system time. [-0.037360].
+@item
+Flag indicating whether the regression has produced valid
+coefficients. (1 for yes, 0 for no). [1]
+@item
+Offset at the current time predicted by the regression process. A large
+difference between this value and the measured offset tends to indicate
+that the measurement is an outlier with a serious measurement
+error. [-0.037434].
+@item
+The rate at which the RTC is losing or gaining time relative to the
+system clock. In ppm, with positive indicating that the RTC is gaining
+time. [-37.948]
+@item
+The number of measurements used in the regression. [12]
+@item
+The number of runs of regression residuals of the same sign. Low values
+indicate that a straight line is no longer a good model of the measured
+data and that older measurements should be discarded. [5]
+@item
+The measurement interval used prior to the measurement being made (in
+seconds). [120]
+@end enumerate
+
+A banner is periodically written to the log file to indicate the
+meanings of the columns.
+@c }}}
+@c }}}
+@c {{{ logchange
+@node logchange directive
+@subsection logchange
+This directive forces @code{chronyd} to send a message to syslog if it
+makes a system clock adjustment larger than a threshold value. An
+example of use is
+
+@example
+logchange 0.5
+@end example
+
+which would cause a syslog message to be generated a system clock error
+of over 0.5 seconds starts to be compensated.
+
+Clock errors detected either via NTP packets or via timestamps entered
+via the @code{settime} command of @code{chronyc} are logged.
+
+This directive assumes that syslog messages are appearing where somebody
+can see them. This allows that person to see if a large error has
+arisen, e.g. because of a fault, or because of faulty timezone handling,
+for example when summer time (daylight saving) starts or ends.
+@c }}}
+@c {{{ logdir
+@node logdir directive
+@subsection logdir
+This directive allows the directory where log files are written to be
+specified.
+
+An example of the use of this directive is
+
+@example
+logdir /var/log/chrony
+@end example
+@c }}}
+@c {{{ mailonchange
+@node mailonchange directive
+@subsection mailonchange
+This directive defines an email address to which mail should be sent if
+chronyd applies a correction exceeding a particular threshold to the
+system clock.
+
+An example of use of this directive is
+
+@example
+mailonchange root@@localhost 0.5
+@end example
+
+This would send a mail message to root if a change of more than 0.5
+seconds were applied to the system clock.
+@c }}}
+@c {{{ manual
+@node manual directive
+@subsection manual
+The @code{manual} directive enables support at run-time for the
+@code{settime} command in chronyc (@pxref{settime command}). If no
+@code{manual} directive is included, any attempt to use the
+@code{settime} command in chronyc will be met with an error message.
+
+Note that the @code{settime} command can be enabled at run-time using
+the @code{manual} command in chronyc (@pxref{manual command}). (The
+idea of the two commands is that the @code{manual} command controls the
+manual clock driver's behaviour, whereas the @code{settime} command
+allows samples of manually entered time to be provided).
+@c }}}
+@c {{{ maxupdateskew
+@node maxupdateskew directive
+@subsection maxupdateskew
+One of @code{chronyd's} tasks is to work out how fast or slow the computer's
+clock runs relative to its reference sources. In addition, it computes
+an estimate of the error bounds around the estimated value.
+
+If the range of error is too large, it probably indicates that the
+measurements have not settled down yet, and that the estimated gain or
+loss rate is not very reliable.
+
+The @code{maxupdateskew} parameter allows the threshold for determining
+whether an estimate may be so unreliable that it should not be used.
+
+The syntax is
+
+@example
+maxupdateskew <skew-in-ppm>
+@end example
+
+Typical values for <skew-in-ppm> might be 100 for a dial-up connection
+to servers over a phone line, and 5 or 10 for a computer on a LAN.
+
+It should be noted that this is not the only means of protection against
+using unreliable estimates. At all times, @code{chronyd} keeps track of
+both the estimated gain or loss rate, and the error bound on the
+estimate. When a new estimate is generated following another
+measurement from one of the sources, a weighted combination algorithm is
+used to update the master estimate. So if @code{chronyd} has an existing
+highly-reliable master estimate and a new estimate is generated which
+has large error bounds, the existing master estimate will dominate in
+the new master estimate.
+@c }}}
+@c {{{ noclientlog
+@node noclientlog directive
+@subsection noclientlog
+This directive, which takes no arguments, specifies that client accesses
+are not to be logged. Normally they are logged, allowing statistics to
+be reported using the @code{clients} command in @code{chronyc}.
+@c }}}
+@c {{{ peer
+@node peer directive
+@subsection peer
+The syntax of this directive is identical to that for the @code{server}
+directive (@pxref{server directive}), except that it is used to specify
+an NTP peer rather than an NTP server.
+@c }}}
+@c {{{ pidfile
+@node pidfile directive
+@subsection pidfile
+chronyd always writes its process ID (pid) to a file, and checks this file on startup to see if another chronyd may already be running on the system. By default, the file used is @code{/var/run/chronyd.pid}. The @code{pidfile} directive allows the name to be changed, e.g.
+
+@example
+pidfile /var/tmp/chronyd.pid
+@end example
+@c }}}
+@c {{{ port
+@node port directive
+@subsection port
+This option allows you to configure the port used for the NTP service
+on your machine.
+
+The compiled in default is udp/123, the standard NTP port. It is
+unlikely that you would ever need to change this value. A possible
+exception would be if you wanted to operate strictly in client-only
+mode and never be available as a server to xntpd clients.
+
+An example of the port command is
+
+@example
+port 11123
+@end example
+
+This would change the NTP port served by chronyd on the computer to
+udp/11123.
+@c }}}
+@c {{{ rtcdevice
+@node rtcdevice directive
+@subsection rtcdevice
+The @code{rtcdevice} directive defines the name of the device file for
+accessing the real time clock. By default this is @code{/dev/rtc/}, unless the
+directive is used to set a different value. This applies to Linux systems with
+devfs. An example of use is
+
+@example
+rtcdevice /dev/misc/rtc
+@end example
+@c }}}
+@c {{{ rtcfile
+@node rtcfile directive
+@subsection rtcfile
+The @code{rtcfile} directive defines the name of the file in which
+@code{chronyd} can save parameters associated with tracking the accuracy
+of the system's real-time clock (RTC).
+
+The syntax is illustrated in the following example
+
+@example
+rtcfile /etc/chrony.rtc
+@end example
+
+@code{chronyd} saves information in this file when it exits and when the
+@code{writertc} command is issued in @code{chronyc}. The information
+saved is the RTC's error at some epoch, that epoch (in seconds since
+January 1 1970), and the rate at which the RTC gains or loses time.
+
+So far, the support for real-time clocks is limited - their code is even
+more system-specific than the rest of the software. You can only use
+the real time clock facilities (the @code{rtcfile} directive and the
+@code{-s} command line option to @code{chronyd}) if the following three
+conditions apply:
+
+@enumerate 1
+@item
+You are running Linux version 2.2.x or 2.4.x (for any value of x), or v2.0.x
+with x>=32.
+
+@item
+You have compiled the kernel with extended real-time clock support
+(i.e. the @file{/dev/rtc} device is capable of doing useful things).
+
+@item
+You don't have other applications that need to make use of
+@file{/dev/rtc} at all.
+
+@end enumerate
+@c }}}
+@c {{{ rtconutc
+@node rtconutc directive
+@subsection rtconutc
+
+@code{chronyd} assumes by default that the real time clock (RTC) keeps
+local time (including any daylight saving changes). This is convenient
+on PCs running Linux which are dual-booted with DOS or Windows.
+
+NOTE : IF YOU KEEP THE REAL TIME CLOCK ON LOCAL TIME AND YOUR COMPUTER
+IS OFF WHEN DAYLIGHT SAVING (SUMMER TIME) STARTS OR ENDS, THE COMPUTER'S
+SYSTEM TIME WILL BE ONE HOUR IN ERROR WHEN YOU NEXT BOOT AND START
+CHRONYD.
+
+An alternative is for the RTC to keep Universal Coordinated Time (UTC).
+This does not suffer from the 1 hour problem when daylight saving starts
+or ends.
+
+If the @code{rtconutc} directive appears, it means the RTC is required
+to keep UTC. The directive takes no arguments. It is equivalent to
+specifying the @code{-u} switch to the Linux @file{/sbin/clock} program.
+@c }}}
+@c {{{ server
+@node server directive
+@subsection server
+The @code{server} directive allows NTP servers to be specified. The
+client/server relationship is strictly hierarchical : a client may
+synchronise its system time to that of the server, but the server's
+system time will never be influenced by that of a client.
+
+The @code{server} directive is immediately followed by either the name
+of the server, or its IP address in dotted-quad notation. The server
+command also supports a number of subfields (which may be defined in any
+order):
+
+@table @code
+@item port
+This option allows the UDP port on which the server understands NTP
+requests to be specified. For normal servers this option should not be
+required (the default is 123, the standard NTP port).
+@item minpoll
+Although @code{chronyd} will trim the rate at which it samples the
+server during normal operation, the user may wish to constrain the
+minimum polling interval. This is always defined as a power of 2, so
+<tt/minpoll 5/ would mean that the polling interval cannot drop below 32
+seconds. The default is 6 (64 seconds).
+@item maxpoll
+In a similar way, the user may wish to constrain the maximum polling
+interval. Again this is specified as a power of 2, so <tt/maxpoll 9/
+indicates that the polling interval must stay at or below 512 seconds.
+The default is 10 (1024 seconds).
+@item maxdelay
+@code{chronyd} uses the network round-trip delay to the server to
+determine how accurate a particular measurement is likely to be. Long
+round-trip delays indicate that the request, or the response, or both
+were delayed. If only one of the messages was delayed the measurement
+error is likely to be substantial.
+
+For small variations in round trip delay, @code{chronyd} uses a
+weighting scheme when processing the measurements. However, beyond a
+certain level of delay the measurements are likely to be so corrupted as
+to be useless. (This is particularly so on dial-up or other slow links,
+where a long delay probably indicates a highly asymmetric delay caused
+by the response waiting behind a lot of packets related to a download of
+some sort).
+
+If the user knows that round trip delays above a certain level should
+cause the measurement to be ignored, this level can be defined with the
+maxdelay command. For example, <tt/maxdelay 0.3/ would indicate that
+measurements with a round-trip delay of 0.3 seconds or more should be
+ignored.
+
+@item maxdelayratio
+This option is similar to the maxdelay option above. @code{chronyd}
+keeps a record of the minimum round-trip delay amongst the previous
+measurements that it has buffered. If a measurement has a round trip
+delay that is greater than the maxdelayratio times the minimum delay, it
+will be rejected.
+
+@item presend
+If the timing measurements being made by @code{chronyd} are the only
+network data passing between two computers, you may find that some
+measurements are badly skewed due to either the client or the server
+having to do an ARP lookup on the other party prior to transmitting a
+packet. This is more of a problem with long sampling intervals, which
+may be similar in duration to the lifetime of entries in the ARP caches
+of the machines.
+
+In order to avoid this problem, the @code{presend} option may be used.
+It takes a single integer argument, which is the smallest polling
+interval for which a pair of packets will be exchanged between the
+client and the server prior to the actual measurement being initiated by
+the client. For example, with the following option included in a
+@code{server} directive :
+
+@example
+presend 9
+@end example
+
+when the polling interval is 512 seconds or more, a UDP echo datagram
+will be sent to the server a short time (currently 4 seconds) before the
+NTP client mode datagram.
+
+@item key
+The NTP protocol supports the inclusion of checksums in the packets, to
+prevent computers having their system time upset by rogue packets being
+sent to them. The checksums are generated as a function of a password,
+using the MD5 algorithm.
+
+The association between key numbers and passwords is contained in the
+keys file, defined by the keyfile command.
+
+If the key option is present, @code{chronyd} will attempt to use
+authenticated packets when communicating with this server. The key
+number used will be the single argument to the key option. The server
+must have the same password for this key number configured, otherwise no
+relationship between the computers will be possible.
+
+@item offline
+If the server will not be reachable when @code{chronyd} is started, the
+offline option may be specified. @code{chronyd} will not try to poll
+the server until it is enabled to do so (by using the online option of
+@code{chronyc}).
+
+@item auto_offline
+If this option is set, the server will be assumed to have gone offline when 2
+requests have been sent to it without receiving a response. This option avoids
+the need to run the @code{offline} (@pxref{offline command}) command from
+chrony when disconnecting the dial-up link. (It will still be necessary to use
+chronyc's @code{online} (@pxref{online command}) command when the link has been
+established, to enable measurements to start.)
+
+@end table
+@c }}}
+@c }}}
+@c {{{ S:Running chronyc
+@node Running chronyc
+@section Running chronyc
+@c {{{ Section top
+Chronyc is the program that can be used to reconfigure options within
+the @code{chronyd} program whilst it is running. Chronyc can also be
+used to generate status reports about the operation of @code{chronyd}.
+
+@menu
+* Chronyc basic use:: How to run chronyc
+* Chronyc command line options:: Chrony's command line options
+* Security with chronyc:: How chronyd restricts access
+* Chronyc command reference:: All the commands chronyc supports
+@end menu
+@c }}}
+@c {{{ SS:Chronyc basic use
+@node Chronyc basic use
+@subsection Basic use
+The program chronyc is run by entering
+
+@example
+chronyc
+@end example
+
+at the command line. The prompt @code{chronyc} is displayed whilst
+chronyc is expecting input from the user, when it is being run from a
+terminal. If chronyc's input or output are redirected from/to a file,
+the prompt is now shown.
+
+When you are finished entering commands, the commands @code{exit} or
+@code{quit} will terminate the program. (Entering @key{Control-D} will
+also terminate the program.)
+@c }}}
+@c {{{ SS:Command line options
+@node Chronyc command line options
+@subsection Command line options
+Chronyc supports the following command line options.
+
+@table @code
+@item -v
+Displays the version number of chronyc on the terminal, and exists.
+@item -h <host>
+This option allows the user to specify which host running the
+@code{chronyd} program is to be contacted. This allows for remote
+configuration, without having to telnet or rlogin to the other host
+first.
+
+The default is to contact @code{chronyd} running on the same host as
+that where chronyc is being run.
+@item -p <port>
+This option allows the user to specify the UDP port number which the
+target @code{chronyd} is using for its command & monitoring connections.
+This defaults to the compiled-in default; there would rarely be a need
+to change this.
+@end table
+@c }}}
+@c {{{ SS:Security with chronyc
+@node Security with chronyc
+@subsection Security with chronyc
+Many of the commands available through chronyc have a fair amount of
+power to reconfigure the run-time behaviour of @code{chronyd}. Consequently,
+@code{chronyc} is quite dangerous for the integrity of the target
+system's clock performance. Having access to @code{chronyd} via chronyc is
+more or less equivalent to being able to modify @code{chronyd's} configuration
+file (typically @file{/etc/chrony.conf}) and to restart @code{chronyd}.
+
+Chronyc also provides a number of monitoring (as opposed to commanding)
+commands, which will not affect the behaviour of @code{chronyd}. However, you
+may still want to restrict access to these commands.
+
+In view of this, access to some of the capabilities of chronyc will
+usually be tightly controlled. There are two mechanisms supported:
+
+@enumerate 1
+@item
+The set of hosts from which @code{chronyd} will accept commands can be
+restricted. By default, commands will only be accepted from the same
+host that @code{chronyd} is running on.
+@item
+Any command that actually reconfigures some aspect of @code{chronyd's}
+behaviour requires the user of chronyc to know a password. This
+password is specified in @code{chronyd's} keys file (@pxref{keyfile directive})
+and specified via the commandkey option in its configuration file
+(@pxref{commandkey directive}).
+@end enumerate
+
+Only the following commands can be used @emph{without} providing a
+password:
+
+@itemize @bullet
+@item @code{exit}
+@item @code{help}
+@item @code{password}
+@item @code{quit}
+@item @code{rtcdata}
+@item @code{sources}
+@item @code{sourcestats}
+@item @code{tracking}
+@end itemize
+
+All other commands require a password to have been specified previously,
+because they affect @code{chronyd's} operation.
+@c }}}
+@c {{{ SS:Chronyc command reference
+@node Chronyc command reference
+@subsection Command reference
+@c {{{ Top/menu
+This section describes each of the commands available within the chronyc
+program. Chronyc offers the user a simple command-line driven
+interface.
+
+@menu
+* accheck command:: Verifying NTP client access
+* activity command:: Check how many NTP servers/peers are online/offline
+* add peer command:: Add a new NTP peer
+* add server command:: Add a new NTP server
+* allow command:: Allowing NTP client access
+* allow all command:: Allowing NTP client access
+* burst command:: Initiating a rapid set of measurements
+* clients command:: Show clients that have accessed the server
+* cmdaccheck command:: Verifying command client access
+* cmdallow command:: Allowing command client access
+* cmdallow all command:: Allowing command client access
+* cmddeny command:: Denying command client access
+* cmddeny all command:: Denying command client access
+* cyclelogs command:: Close and re-open open log files
+* delete command:: Remove an NTP server or peer
+* deny command :: Denying NTP client access
+* deny all command:: Denying NTP client access
+* dump command:: Dump measurement histories to files
+* exit command:: Exit from chronyc
+* help command:: Generate help summary
+* local command:: Let computer be a server when it is unsynchronised
+* makestep command:: Immediately correct the system clock instead of slewing
+* manual command:: Enable/disable/configure options for settime
+* maxdelay command:: Set max measurement delay for a source
+* maxdelayratio command:: Set max measurement delay for a source as ratio
+* maxpoll command:: Set maximum polling interval for a source
+* maxupdateskew command:: Set safety threshold for clock gain/loss rate
+* minpoll command:: Set minimum polling interval for a source
+* offline command:: Warn that connectivity to a source will be lost
+* online command:: Warn that connectivity to a source has been restored
+* password command:: Provide password needed for most commands
+* quit command:: Exit from chronyc
+* rtcdata command:: Display RTC parameters
+* settime command:: Provide a manual input of the current time
+* sources command:: Display information about the current set of sources
+* sourcestats command:: Display the rate & offset estimation performance of sources
+* tracking command:: Display system clock performance
+* trimrtc command:: Correct the RTC time to the current system time
+* writertc command:: Write the RTC parameters to file.
+@end menu
+@c }}}
+@c {{{ accheck
+@node accheck command
+@subsubsection accheck
+This command allows you to check whether client NTP access is allowed
+from a particular host.
+
+Examples of use, showing a named host and a numeric IP address, are as
+follows:
+
+@example
+accheck a.b.c
+accheck 1.2.3.4
+@end example
+
+This command can be used to examine the effect of a series of
+@code{allow}, @code{allow all}, @code{deny} and @code{deny all} commands
+specified either via chronyc, or in @code{chronyd's} configuration file.
+@c }}}
+@c {{{ activity command
+@node activity command
+@subsubsection activity
+This command reports the number of servers/peers that are online and offline.
+If the auto_offline option is used in specifying some of the servers/peers, the
+@code{activity} command may be useful for detecting when all of them have
+entered the offline state after the PPP link has been disconnected.
+
+The report shows the number of servers/peers in 4 states:
+@itemize
+@item @code{online} : the server/peer is currently online (i.e. assumed by
+chronyd to be reachable)
+@item @code{offline} : the server/peer is currently offline (i.e. assumed by
+chronyd to be unreachable, and no measurements from it will be attempted.)
+@item @code{burst_online} : a burst command has been initiated for the
+server/peer and is being performed; after the burst is complete, the
+server/peer will be returned to the online state.
+@item @code{burst_offline} : a burst command has been initiated for the
+server/peer and is being performed; after the burst is complete, the
+server/peer will be returned to the offline state.
+@end itemize
+@c }}}
+@c {{{ add peer
+@node add peer command
+@subsubsection add peer
+The @code{add peer} command allows a new NTP peer to be added whilst
+@code{chronyd} is running.
+
+Following the words @code{add peer}, the syntax of the following
+parameters and options is identical to that for the @code{peer}
+directive in the configuration file (@pxref{peer directive}).
+
+An example of using this command is shown below.
+
+@example
+add peer foo.bar.com minpoll 6 maxpoll 10 authkey 25
+@end example
+@c }}}
+@c {{{ add server
+@node add server command
+@subsubsection add server
+The @code{add server} command allows a new NTP server to be added whilst
+@code{chronyd} is running.
+
+Following the words @code{add server}, the syntax of the following
+parameters and options is identical to that for the @code{server}
+directive in the configuration file (@pxref{server directive}).
+
+An example of using this command is shown below.
+
+@example
+add server foo.bar.com minpoll 6 maxpoll 10 authkey 25
+@end example
+@c }}}
+@c {{{ allow
+@node allow command
+@subsubsection allow
+The effect of the allow command is identical to the @code{allow} directive in
+the configuration file (@pxref{allow directive}).
+
+The syntax is illustrated in the following examples:
+
+@example
+allow foo.bar.com
+allow 1.2
+allow 3.4.5
+allow 6.7.8/22
+allow 6.7.8.9/22
+allow
+@end example
+
+The effect of each of these examples is the same as that of the @code{allow}
+directive in the configuration file.
+@c }}}
+@c {{{ allow all
+@node allow all command
+@subsubsection allow all
+The effect of the allow command is identical to the @code{allow all}
+directive in the configuration file (@pxref{allow directive}).
+@c }}}
+@c {{{ burst
+@node burst command
+@subsubsection burst
+The @code{burst} command tells @code{chronyd} to make a set of measurements to
+each of its sources over a short duration (rather than the usual
+periodic measurements that it makes). After such a burst, @code{chronyd} will
+revert to the previous state for each source. This might be either
+online, if the source was being periodically measured in the normal way,
+or offline, if the source had been indicated as being offline.
+(Switching a source between the online and offline states is described
+in @ref{online command}, @ref{offline command}).
+
+The syntax of the burst command is as follows
+
+@example
+burst <n-good-measurements>/<max-measurements> [<mask>/<masked-address>]
+@end example
+
+The mask and masked-address arguments are optional, in which case
+@code{chronyd} will initiate a burst for all of its currently defined sources.
+
+The arguments have the following meaning and format.
+
+@table @code
+@item n-good-measurements
+This defines the number of good measurements that @code{chronyd} will want to
+obtain from each source. A measurement is good if it passes certain
+tests, for example, the round trip time to the source must be
+acceptable. (This allows @code{chronyd} to reject measurements that are likely
+to be bogus.)
+
+@item max-measurements
+This defines the maximum number of measurements that @code{chronyd} will
+attempt to make, even if the required number of good measurements has
+not been obtained.
+
+@item mask
+This is a dotted quad argument (e.g. @code{255.255.255.0}) with which
+the IP address of each of @code{chronyd}'s sources is to be masked.
+
+@item masked-address
+This is a dotted quad argument (e.g. @code{1.2.3.0}). If the masked IP
+address of a source matches this value then the burst command is applied
+to that source.
+@end table
+
+If no mask or masked address arguments are provided, the default is
+@code{0.0.0.0} and @code{0.0.0.0} respectively, which will match every
+source.
+
+An example of the two-argument form of the command is
+
+@example
+burst 2/10
+@end example
+
+This will cause @code{chronyd} to attempt to get two good measurements from
+each source, stopping after two have been obtained, but in no event will
+it try more than ten probes to the source.
+
+An example of the four-argument form of the command is
+
+@example
+burst 2/10 255.255.0.0/1.2.0.0
+@end example
+
+In this case, the two out of ten sampling will only be applied to
+sources whose IP addresses are of the form @code{1.2.x.y}, where x and y
+are arbitrary.
+@c }}}
+@c {{{ clients
+@node clients command
+@comment node-name, next, previous, up
+@subsubsection clients
+This command shows a list of all clients that have accessed the server,
+through either the NTP or command/monitoring ports. There are no arguments.
+
+An example of the output is
+
+@example
+Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC
+========================= ====== ====== ====== ====== ====== ==== ====
+localhost 0 0 15 1 0 29y 0
+aardvark.xxx 4 0 0 0 0 49 29y
+badger.xxx 4 0 0 0 0 6 29y
+@end example
+
+Each row shows the data for a single host. Only hosts that have passed
+the host access checks (set with the @code{allow}, @code{deny},
+@code{cmdallow} and @code{cmddeny} commands or configuration file
+directives) are logged.
+
+The columns are as follows:
+
+@enumerate 1
+@item
+The hostname of the client
+@item
+The number of times the client has accessed the server using an NTP
+client mode packet.
+@item
+The number of times the client has accessed the server using an NTP
+symmetric active mode packet.
+@item
+The number of authenticated command packets that have been processed
+from the client (i.e. those following a successful @code{password}
+command).
+@item
+The number of unauthenticated command packets that have been processed
+from the client.
+@item
+The number of bad command packets received from the client (not all
+forms of bad packet are logged).
+@item
+Time since the last NTP packet was received
+@item
+Time since the last command packet was received
+@end enumerate
+
+The last two entries will be shown as the time since 1970 if no packet
+of that type has ever been received.
+@c }}}
+@c {{{ cmdaccheck
+@node cmdaccheck command
+@subsubsection cmdaccheck
+This command is similar to the @code{accheck} command, except that it is
+used to check whether command access is permitted from a named host.
+
+Examples of use are as follows:
+
+@example
+cmdaccheck a.b.c
+cmdaccheck 1.2.3.4
+@end example
+@c }}}
+@c {{{ cmdallow
+@node cmdallow command
+@subsubsection cmdallow
+This is similar to the @code{allow} command, except that it is used to
+allow particular hosts or subnets to use the chronyc program to interact
+with @code{chronyd} on the current host.
+@c }}}
+@c {{{ cmdallow all
+@node cmdallow all command
+@subsubsection cmdallow all
+This is similar to the @code{allow all} command, except that it is used to@c {{{
+allow particular hosts or subnets to use the chronyc program to interact@c }}}
+with @code{chronyd} on the current host.
+@c }}}
+@c {{{ cmddeny
+@node cmddeny command
+@subsubsection cmddeny
+This is similar to the @code{deny} command, except that it is used to
+allow particular hosts or subnets to use the chronyc program to interact
+with @code{chronyd} on the current host.
+@c }}}
+@c {{{ cmddeny all
+@node cmddeny all command
+@subsubsection cmddeny all
+This is similar to the @code{deny all} command, except that it is used
+to allow particular hosts or subnets to use the chronyc program to
+interact with @code{chronyd} on the current host.
+@c }}}
+@c {{{ cyclelogs
+@node cyclelogs command
+@subsubsection cyclelogs
+The @code{cyclelogs} command causes all of @code{chronyd's} open log files to
+be closed and re-opened. This allows them to be renamed so that they can be
+periodically purged. An example of how to do this is shown below.
+
+@example
+% mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
+% chronyc
+chronyc> password aardvark
+200 OK
+chronyc> cyclelogs
+200 OK
+chronyc> exit
+% ls -l /var/log/chrony
+-rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log
+-rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log
+% rm -f measurements1.log
+@end example
+@c }}}
+@c {{{ delete
+@node delete command
+@subsubsection delete
+The @code{delete} command allows an NTP server or peer to be removed
+from the current set of sources.
+
+The syntax is illustrated in the examples below.
+
+@example
+delete foo.bar.com
+delete 1.2.3.4
+@end example
+
+There is one parameter, the name or IP address of the server or peer to
+be deleted.
+@c }}}
+@c {{{ deny
+@node deny command
+@subsubsection deny
+The effect of the allow command is identical to the @code{deny}
+directive in the configuration file (@pxref{deny directive}).
+
+The syntax is illustrated in the following examples:
+
+@example
+deny foo.bar.com
+deny 1.2
+deny 3.4.5
+deny 6.7.8/22
+deny 6.7.8.9/22
+deny
+@end example
+@c }}}
+@c {{{ deny all
+@node deny all command
+@subsubsection deny all
+The effect of the allow command is identical to the @code{deny all}
+directive in the configuration file (@pxref{deny directive}).
+@c }}}
+@c {{{ dump
+@node dump command
+@subsubsection dump
+The @code{dump} command causes @code{chronyd} to write its current history of
+measurements for each of its sources to dump files, either for
+inspection or to support the @code{-r} option when @code{chronyd} is restarted.
+
+The @code{dump} command is somewhat equivalent to the @code{dumponexit}
+directive in the chrony configuration file. @xref{dumponexit directive}.
+
+To use the @code{dump}, you probably want to configure the name of the
+directory into which the dump files will be written. This can only be
+done in the configuration file, see @ref{dumpdir directive}.
+@c }}}
+@c {{{ exit
+@node exit command
+@subsubsection exit
+The exit command exits from chronyc and returns the user to the shell
+(same as the quit command).
+@c }}}
+@c {{{ help
+@node help command
+@subsubsection help
+The help command displays a summary of the commands and their arguments.
+@c }}}
+@c {{{ local
+@node local command
+@subsubsection local
+The @code{local} command allows @code{chronyd} to be told that it is to appear
+as a reference source, even if it is not itself properly synchronised to
+an external source. (This can be used on isolated networks, to allow
+one computer to be a master time server with the other computers slaving
+to it.) The @code{local} command is somewhat equivalent to the
+@code{local} directive in the configuration file, see @ref{local directive}.
+
+The syntax is as shown in the following examples.
+
+@example
+local stratum 10
+local off
+@end example
+
+The first example enables the local reference mode on the host, and sets
+the stratum at which it should claim to be synchronised.
+
+The second example disables the local reference mode.
+@c }}}
+@c {{{ makestep
+@node makestep command
+@subsubsection makestep
+Normally chronyd will cause the system to gradually correct any time
+offset, by slowing down or speeding up the clock as required. In
+certain situations, the system clock may be so far adrift that this
+slewing process would take a very long time to correct the system clock.
+
+The @code{makestep} command can be used in this situation. It cancels
+any remaining correction that was being slewed, and jumps the system
+clock by the equivalent amount, making it correct immediately.
+
+BE WARNED - certain software will be seriously affected by such jumps to
+the system time. (That is the reason why chronyd uses slewing
+normally.)
+
+The @code{makestep} command is currently only available on the Linux
+version of chrony.
+@c }}}
+@c {{{ manual
+@node manual command
+@subsubsection manual
+The manual command enables and disables use of the @code{settime}
+command (@pxref{settime command}), and is used to modify the behaviour
+of the manual clock driver.
+
+Examples of the command are shown below.
+
+@example
+manual on
+manual off
+manual delete 1
+manual list
+manual reset
+@end example
+
+The @code{on} form of the command enables use of the @code{settime}
+command.
+
+The @code{off} form of the command disables use of the @code{settime}
+command.
+
+The @code{list} form of the command lists all the samples currently
+stored in @code{chronyd}. The output is illustrated below.
+
+@example
+210 n_samples = 1
+# Date Time(UTC) Slewed Original Residual
+====================================================
+ 0 27Jan99 22:09:20 0.00 0.97 0.00
+@end example
+
+The columns as as follows :
+
+@enumerate 1
+@item
+The sample index (used for the @code{manual delete} command)
+@item
+The date and time of the sample
+@item
+The system clock error when the timestamp was entered, adjusted to allow
+for changes made to the system clock since.
+@item
+The system clock error when the timestamp was entered, as it originally
+was (without allowing for changes to the system clock since).
+@item
+The regression residual at this point, in seconds. This allows
+'outliers' to be easily spotted, so that they can be deleted using the
+@code{manual delete} command.
+@end enumerate
+
+The @code{delete} form of the command deletes a single sample. The
+parameter is the index of the sample, as shown in the first column of
+the output from @code{manual list}. Following deletion of the data
+point, the current error and drift rate are re-estimated from the
+remaining data points and the system clock trimmed if necessary. This
+option is intended to allow 'outliers' to be discarded, i.e. samples
+where the administrator realises he/she has entered a very poor
+timestamp.
+
+The @code{reset} form of the command deletes all samples at once. The
+system clock is left running as it was before the command was entered.
+@c }}}
+@c {{{ maxdelay
+@node maxdelay command
+@subsubsection maxdelay
+This allows the @code{maxdelay} option for one of the sources to be
+modified, in the same way as specifying the @code{maxdelay} option for
+the @code{server} directive in the configuration file (@pxref{server
+directive}).
+
+The following examples illustrate the syntax
+
+@example
+maxdelay foo.bar.com 0.3
+maxdelay 1.2.3.4 0.0015
+@end example
+
+The first example sets the maximum network delay allowed for a
+measurement to the host @code{foo.bar.com} to 0.3 seconds. The second
+example sets the maximum network delay for a measurement to the host
+with IP address @code{1.2.3.4} to 1.5 milliseconds.
+
+(Any measurement whose network delay exceeds the specified value is
+discarded.)
+@c }}}
+@c {{{ maxdelayratio
+@node maxdelayratio command
+@subsubsection maxdelayratio
+This allows the @code{maxdelayratio} option for one of the sources to be
+modified, in the same way as specifying the @code{maxdelayratio} option
+for the @code{server} directive in the configuration file (@pxref{server
+directive}).
+
+The following examples illustrate the syntax
+
+@example
+maxdelayratio foo.bar.com 1.5
+maxdelayratio 1.2.3.4 2.0
+@end example
+
+The first example sets the maximum network delay for a measurement to
+the host @code{foo.bar.com} to be 1.5 times the minimum delay found
+amongst the previous measurements that have been retained. The second
+example sets the maximum network delay for a measurement to the host
+with IP address @code{1.2.3.4} to be double the retained minimum.
+
+As for @code{maxdelay}, any measurement whose network delay is too large
+will be discarded.
+@c }}}
+@c {{{ maxpoll
+@node maxpoll command
+@subsubsection maxpoll
+The @code{maxpoll} command is used to modify the minimum polling
+interval for one of the current set of sources. It is equivalent to the
+@code{maxpoll} option in the @code{server} directive in the
+configuration file (@pxref{server directive}).
+
+The syntax is as follows
+
+@example
+maxpoll <host> <new-maxpoll>
+@end example
+
+where the host can be specified as either a machine name or dotted-quad
+IP address. The new minimum poll is specified as a base-2 logarithm of
+the number of seconds between polls (e.g. specify 6 for 64 second
+sampling).
+
+An example is
+
+@example
+maxpoll foo.bar.com 10
+@end example
+
+which sets the maximum polling interval for the host @code{foo.bar.com}
+to 1024 seconds.
+
+Note that the new maximum polling interval only takes effect after the
+next measurement has been made.
+@c }}}
+@c {{{ maxupdateskew
+@node maxupdateskew command
+@subsubsection maxupdateskew
+This command has the same effect as the @code{maxupdateskew} directive
+in the configuration file, see @ref{maxupdateskew directive}.
+@c }}}
+@c {{{ minpoll
+@node minpoll command
+@subsubsection minpoll
+The @code{minpoll} command is used to modify the minimum polling
+interval for one of the current set of sources. It is equivalent to the
+@code{minpoll} option in the @code{server} directive in the
+configuration file (@pxref{server directive}).
+
+The syntax is as follows
+
+@example
+minpoll <host> <new-minpoll>
+@end example
+
+where the host can be specified as either a machine name or dotted-quad
+IP address. The new minimum poll is specified as a base-2 logarithm of
+the number of seconds between polls (e.g. specify 6 for 64 second
+sampling).
+
+An example is
+
+@example
+minpoll foo.bar.com 5
+@end example
+
+which sets the minimum polling interval for the host @code{foo.bar.com}
+to 32 seconds.
+
+Note that the new minimum polling interval only takes effect after the
+next measurement has been made.
+@c }}}
+@c {{{ offline
+@node offline command
+@subsubsection offline
+The @code{offline} command is used to warn @code{chronyd} that the network
+connection to a particular host or hosts is about to be lost. It should
+be used on computers with a dial-up or similar connection to their time
+sources, to warn @code{chronyd} that the connection is about to be broken.
+
+An example of how to use @code{offline} in this case is shown in
+@ref{Advising chronyd of internet availability}.
+
+Another case where @code{offline} could be used is where a computer
+serves time to a local group of computers, and has a permanant
+connection to true time servers outside the organisation. However, the
+external connection is heavily loaded at certain times of the day and
+the measurements obtained are less reliable at those times. In this
+case, it is probably most useful to determine the gain/loss rate during
+the quiet periods and let the whole network coast through the loaded
+periods. The @code{offline} and @code{online} commands can be used to
+achieve this. The situation is shown in the figure below.
+
+@example
+@group
+ +----------+
+ |Ext source|
+ +----------+
+ |
+ |
+ |/| <-- Link with variable
+ | reliability
+ |
+ +-------------------+
+ |Local master server|
+ +-------------------+
+ |
+ +---+---+-----+-----+----+----+
+ | | | | | | |
+ Local clients
+@end group
+@end example
+
+
+
+If the source to which @code{chronyd} is currently synchronised is indicated
+offline in this way, @code{chronyd} will continue to treat it as the
+synchronisation source. If the network connection were broken without
+the @code{offline} command being used, @code{chronyd} would assume that the
+source had failed and would attempt to pick another synchronisation
+source.
+
+There are two forms of the @code{offline} command. The first form is a
+wildcard, meaning all sources. The second form allows a IP address mask
+and a masked address to be specified. These forms are illustrated below.
+
+@example
+offline
+offline 255.255.255.0/1.2.3.0
+@end example
+
+The second form means that the @code{offline} command is to be applied
+to any source whose IP address is in the 1.2.3 subnet. (The host's
+address is logically and-ed with the mask, and if the result matches the
+masked-address the host is processed).
+
+The wildcard form of the address is actually equivalent to
+
+@example
+offline 0.0.0.0/0.0.0.0
+@end example
+@c }}}
+@c {{{ online
+@node online command
+@subsubsection online
+The @code{online} command is opposite in function to the @code{offline}
+command. It is used to advise @code{chronyd} that network connectivity to a
+particular source or sources has been restored.
+
+The syntax is identical to that of the @code{offline} command, see
+@ref{offline command}.
+@c }}}
+@c {{{ password
+@node password command
+@subsubsection password
+The password command is used to allow chronyc to send privileged
+commands to @code{chronyd}. The password can either be entered on the command
+line, or can be entered without echoing. The syntax for entering the
+password on the command line is as follows
+
+@example
+password xyzzy
+@end example
+
+To enter the password without it being echoed, enter
+
+@example
+password
+@end example
+
+The computer will respond with a @samp{Password:} prompt, at which you
+should enter the password and press return. (Note that the no-echo mode
+is limited to 8 characters on SunOS 4.1 due to limitations in the system
+library. Other systems do not have this restriction.)
+
+The password is any string of characters not containing whitespace. It
+has to match @code{chronyd's} currently defined command key (@pxref{commandkey
+directive}).
+@c }}}
+@c {{{ quit
+@node quit command
+@subsubsection quit
+The quit command exits from chronyc and returns the user to the shell
+(same as the exit command).
+@c }}}
+@c {{{ rtcdata
+@node rtcdata command
+@subsubsection rtcdata
+The @code{rtcdata} command displays the current real time clock RTC parameters.
+
+An example output is shown below.
+
+@example
+RTC ref time (GMT) : Sat May 30 07:25:56 1998
+Number of samples : 10
+Number of runs : 5
+Sample span period : 549
+RTC is fast by : -1.632736 seconds
+RTC gains time at : -107.623 ppm
+@end example
+
+The fields have the following meaning
+
+@table @code
+@item RTC ref time (GMT)
+This is the RTC reading the last time its error was measured.
+@item Number of samples
+This is the number of previous measurements being used to determine the
+RTC gain/loss rate.
+@item Number of runs
+This is the number of runs of residuals of the same sign following the
+regression fit for (RTC error) versus (RTC time). A value which is
+small indicates that the measurements are not well approximated by a
+linear model, and that the algorithm will tend to delete the older
+measurements to improve the fit.
+@item Sample span period
+This is the period that the measurements span (from the oldest to the
+newest). Without a unit the value is in seconds; suffixes `m' for
+minutes, `h' for hours, `d' for days or `y' for years may be used.
+@item RTC is fast by
+This is the estimate of how many seconds fast the RTC when it thought
+the time was at the reference time (above). If this value is large, you
+may (or may not) want to use the @code{trimrtc} command to bring the RTC
+into line with the system clock. (Note, a large error will not affect
+@code{chronyd's} operation, unless it becomes so big as to start causing
+rounding errors.
+@item RTC gains time at
+This is the amount of time gained (positive) or lost (negative) by the
+real time clock for each second that it ticks. It is measured in parts
+per million. So if the value shown was +1, suppose the RTC was exactly
+right when it crosses a particular second boundary. Then it would be 1
+microsecond fast when it crosses its next second boundary.
+@end table
+@c }}}
+@c {{{ settime
+@node settime command
+@subsubsection settime
+The @code{settime} command allows the current time to be entered
+manually, if this option has been configured into @code{chronyd}. (It may be
+configured either with the @code{manual} directive in the configuration
+file (@pxref{manual directive}), or with the @code{manual} command of
+chronyc (@pxref{manual command}).
+
+It should be noted that the computer's sense of time will only be as
+accurate as the reference you use for providing this input (e.g. your
+watch), as well as how well you can time the press of the return key.
+When inputting time to an isolated network, I have a battery operated
+alarm clock that is synchronised to the Rugby MSF time signal in the UK.
+
+Providing your computer's time zone is set up properly, you will be able
+to enter a local time (rather than UTC).
+
+The response to a successful @code{settime} command indicates the amount
+that the computer's clock was wrong. It should be apparent from this if
+you have entered the time wrongly, e.g. with the wrong time zone.
+
+The rate of drift of the system clock is estimated by a regression
+process using the entered measurement and all previous measurements
+entered during the present run of @code{chronyd}. However, the entered
+measurement is used for adjusting the current clock offset (rather than
+the estimated intercept from the regression, which is ignored).
+Contrast what happens with the @code{manual delete} command, where the
+intercept is used to set the current offset (since there is no
+measurement that has just been typed in in that case).
+
+The time is parsed by the public domain @file{getdate} algorithm.
+Consequently, you can only specify time to the nearest second.
+
+Examples of inputs that are valid are shown below.
+
+@example
+settime 16:30
+settime 16:30:05
+settime Nov 21, 1997 16:30:05
+@end example
+
+For a full description of @code{getdate}, get hold of the getdate
+documentation (bundled, for example, with the source for GNU tar).
+@c }}}
+@c {{{ sources
+@node sources command
+@subsubsection sources
+This command displays information about the current time sources that
+@code{chronyd} is accessing.
+
+The optional argument @code{-v} can be specified, meaning @emph{verbose}. In
+this case, extra caption lines are shown as a reminder of the meanings of the
+columns.
+
+@example
+@group
+210 Number of sources = 3
+MS Name/IP address Stratum Poll LastRx Last sample
+=======================================================================
+^+ a.b.c 3 6 47m -9491us[-6983us] +/- 159ms
+^+ d.e.f 3 6 47m +32ms[ +35ms] +/- 274ms
+^* g.h.i 2 6 47m +8839us[ +11ms] +/- 214ms
+@end group
+@end example
+
+The columns are as follows:
+
+@table @code
+@item M
+This indicates the mode of the source. @code{^} means a server,
+@code{=} means a peer and @code{#} indicates a locally connected
+reference clock@footnote{In the current version this will never be
+shown, because @code{chronyd} has no support for reference clocks yet.}.
+
+@item S
+This column indicates the state of the sources. @code{*} indicates the
+source to which @code{chronyd} is current synchronised. @code{+} indicates
+other acceptable sources. @code{?} indicates sources to which
+connectivity has been lost. @code{x} indicates a clock which @code{chronyd}
+thinks is is a falseticker (i.e. its time is inconsistent with a
+majority of other sources). @code{~} indicates a source whose time
+appears to have too much variability. The @code{~} condition is also
+shown at start-up, until at least 3 samples have been gathered from it.
+
+@item Name/IP address
+This shows the name or the IP address of the source.
+
+@item Stratum
+This shows the stratum of the source, as reported in its most recently
+received sample. Stratum 1 indicates a computer with a locally attached
+reference clock. A computer that is synchronised to a stratum 1
+computer is at stratum 2. A computer that is synchronised to a stratum
+2 computer is at stratum 3, and so on.
+
+@item Poll
+This shows the rate at which the source is being polled, as a base-2
+logarithm of the interval in seconds. Thus, a value of 6 would indicate
+that a measurement is being made every 64 seconds.
+
+@code{chronyd} automatically varies the polling rate in response to prevailing
+conditions.
+
+@item LastRx
+This column shows how long ago the last sample was received from the
+source. This is normally in seconds. The letters @code{m}, @code{h},
+@code{d} or @code{y} indicate minutes, hours, days or years.
+
+@item Last sample
+This column shows the offset between the local clock and the source at
+the last measurement. The number in the square brackets shows the
+actual measured offset. This may be suffixed by @code{us} (indicating
+microseconds), @code{ms} (indicating milliseconds), or @code{s}
+(indicating seconds). The number to the left of the square brackets
+shows the original measurement, adjusted to allow for any slews applied
+to the local clock since. The number following the @code{+/-} indicator
+shows the margin of error in the measurement.
+
+Positive offsets indicate that the local clock is fast of the source.
+
+@end table
+@c }}}
+@c {{{ sourcestats
+@node sourcestats command
+@subsubsection sourcestats
+
+The @code{sourcestats} command displays information about the drift rate
+and offset estimatation process for each of the sources currently being
+examined by @code{chronyd}.
+
+The optional argument @code{-v} can be specified, meaning @emph{verbose}. In
+this case, extra caption lines are shown as a reminder of the meanings of the
+columns.
+
+An example report is
+
+@example
+@group
+210 Number of sources = 1
+Name/IP Address NP NR Span Frequency Freq Skew Std Dev
+========================================================================
+abc.def.ghi 11 5 46m -0.001 0.045 25us
+@end group
+@end example
+
+The columns are as follows
+
+@table @code
+@item Name/IP Address
+This is the name or dotted-quad IP address of the NTP server (or peer)
+to which the rest of the line relates.
+
+@item NP
+This is the number of sample points currently being retained for the
+server. The drift rate and current offset are estimated by performing a
+linear regression through these points.
+
+@item NR
+This is the number of runs of residuals having the same sign following
+the last regression. If this number starts to become too small relative
+to the number of samples, it indicates that a straight line is no longer
+a good fit to the data. If the number of runs is too low,
+@code{chronyd} discards older samples and re-runs the regression until
+the number of runs becomes acceptable.
+
+@item Span
+This is the interval between the oldest and newest samples. If no unit
+is shown the value is in seconds. In the example, the interval is 46
+minutes.
+
+@item Frequency
+This is the estimated residual frequency for the server, in parts per
+million. In this case, the computer's clock is estimated to be running
+1 part in 10**9 slow relative to the server.
+
+@item Freq Skew
+This is the estimated error bounds on @code{Freq} (again in parts per
+million).
+
+@item Std Dev
+This is the estimated sample standard deviation.
+
+@end table
+@c }}}
+@c {{{ tracking
+@node tracking command
+@subsubsection tracking
+The @code{tracking} command displays parameters about the system's clock
+performance. An example of the output is shown below.
+
+@example
+Reference ID : 1.2.3.4 (a.b.c)
+Stratum : 3
+Ref time (UTC) : Sun May 17 06:13:11 1998
+System time : 0.000000 seconds fast of NTP time
+Frequency : 331.898 ppm fast
+Residual freq : 0.004 ppm
+Skew : 0.154 ppm
+Root delay : 0.373169 seconds
+Root dispersion : 0.024780 seconds
+@end example
+
+The fields are explained as follows.
+
+@table @code
+@item Reference ID
+This is the IP address, and name if available, of the server to which
+the computer is currently synchronised. If this is @code{127.127.1.1}
+it means the computer is not synchronised to any external source and
+that you have the `local' mode operating (via the @code{local} command
+in @code{chronyc} (@pxref{local command}), or the @code{local} directive
+in the @file{/etc/chrony.conf} file (@pxref{local directive})).
+
+@item Stratum
+The stratum indicates how many hops away from a computer with an
+attached reference clock we are. Such a computer is a stratum-1
+computer, so the computer in the example is two hops away
+(i.e. @code{a.b.c} is a stratum-2 and is synchronised from a stratum-1).
+
+@item Ref time
+This is the time (GMT) at which the last measurement from the reference
+source was processed.
+
+@item System time
+In normal operation, @code{chronyd} @emph{never} steps the system clock,
+because any jump in the timescale can have adverse consequences for
+certain application programs. Instead, any error in the system clock is
+corrected by slightly speeding up or slowing down the system clock until
+the error has been removed, and then returning to the system clock's
+normal speed. A consequence of this is that there will be a period when
+the system clock (as read by other programs using the
+@code{gettimeofday()} system call, or by the @code{date} command in the
+shell) will be different from @code{chronyd's} estimate of the current
+true time (which it reports to NTP clients when it is operating in
+server mode). The value reported on this line is the difference due to
+this effect.
+
+On systems such as Solaris and SunOS, @code{chronyd} has no means to
+adjust the fundamental rate of the system clock, so keeps the system
+time correct by periodically making offsets to it as though an error had
+been measured. The build up of these offsets will be observed in this
+report. On systems such as Linux where @code{chronyd} can adjust the
+fundamental rate of the system clock, this value will show zero unless a
+very recent measurement has shown the system to be error.
+
+@item Frequency
+The `frequency' is the rate by which the system's clock would be would
+be wrong if @code{chronyd} was not correcting it. It is expressed in
+ppm (parts per million). For example, a value of 1ppm would mean that
+when the system's clock thinks it has advanced 1 second, it has actually
+advanced by 1.000001 seconds relative to true time.
+
+As you can see in the example, the clock in the computer I developed
+@code{chrony} on is not a very good one - it gains about 30 seconds per
+day! This was the reason I started to write @code{chrony} in the first
+place.
+
+@item Residual freq
+This shows the `residual frequency' for the currently selected reference
+source. This reflects any difference between what the measurements from
+the reference source indicate the frequency should be and the frequency
+currently being used.
+
+The reason this is not always zero is that a smoothing procedure is
+applied to the frequency. Each time a measurement from the reference
+source is obtained and a new residual frequency computed, the estimated
+accuracy of this residual is compared with the estimated accuracy (see
+`skew' next) of the existing frequency value. A weighted average is
+computed for the new frequency, with weights depending on these
+accuracies. If the measurements from the reference source follow a
+consistent trend, the residual will be driven to zero over time.
+
+@item Skew
+This is the estimated error bound on the the frequency.
+
+@item Root delay
+This is the total of the network path delays to the stratum-1 computer
+from which the computer is ultimately synchronised.
+
+In certain extreme situations, this value can be negative. (This can
+arise in a symmetric peer arrangement where the computers' frequencies
+are not tracking each other and the network delay is very short relative
+to the turn-around time at each computer.)
+
+@item Root dispersion
+This is the total dispersion accumulated through all the computers back
+to the stratum-1 computer from which the computer is ultimately
+synchronised. Dispersion is due to system clock resolution, statistical
+measurement variations etc.
+
+An absolute bound on the computer's clock accuracy (assuming the
+stratum-1 computer is correct) is given by
+
+@example
+clock_error <= root_dispersion + (0.5 * |root_delay|)
+@end example
+
+@end table
+@c }}}
+@c {{{ trimrtc
+@node trimrtc command
+@subsubsection trimrtc
+The @code{trimrtc} command is used to correct the system's real time
+clock (RTC) to the main system clock. It has no effect if the error
+between the two clocks is currently estimated at less than a second (the
+resolution of the RTC is only 1 second).
+
+The command takes no arguments. It performs the following steps (if the
+RTC is more than 1 second away from the system clock):
+
+@enumerate 1
+@item
+Remember the currently estimated gain/loss rate of the RTC and flush the
+previous measurements.
+@item
+Step the real time clock to bring it within a second of the system clock.
+@item
+Make several measurements to accurately determine the new offset between
+the RTC and the system clock (i.e. the remaining fraction of a second
+error)
+@item
+Save the RTC parameters to the RTC file (specified with the
+@code{rtcfile} directive in the configuration file (@pxref{rtcfile
+directive}).
+@end enumerate
+
+The last step is done as a precaution against the computer suffering a
+power failure before either the daemon exits or the @code{writertc}
+command is issued.
+
+@code{chronyd} will still work perfectly well both whilst operating and
+across machine reboots even if the @code{trimrtc} command is never used
+(and the RTC is allowed to drift away from true time). The
+@code{trimrtc} command is provided as a method by which it can be
+corrected, in a manner compatible with @code{chronyd} using it to
+maintain accurate time across machine reboots.
+@c }}}
+@c {{{ writertc
+@node writertc command
+@subsubsection writertc
+The @code{writertc} command writes the currently estimated error and
+gain/loss rate parameters for the RTC to the RTC file (specified with
+the @code{rtcfile} directive (@pxref{rtcfile directive})). This
+information is also written automatically when @code{chronyd} is killed
+(with SIGHUP, SIGINT, SIGQUIT or SIGTERM).
+@c }}}
+@c }}}
+@c }}}
+@c }}}
+@c {{{ apx: porting guide
+@node Porting guide
+@appendix Porting guide
+@c {{{ section top
+This appendix discusses issues that have arisen in writing the
+system-specific parts of the existing ports. This will provide useful
+information for those attempting to write ports to other systems.
+
+
+@menu
+* System driver files:: What needs to go in a driver file for a
+ particular type of system
+* Quirks of particular systems:: Problem areas that have been found on ports
+ already written.
+@end menu
+@c }}}
+@c {{{ S:system driver files
+@node System driver files
+@section System driver files
+The system specific parts of the software are contained in files with
+names like @code{sys_linux.c}.
+
+The following functions are required in a system driver file:
+
+@enumerate
+@item
+A function to read the current frequency
+@item
+A function to set the current frequency
+@item
+A function to slew the system time by a specified delta
+@item
+A function to step the system time by a specified delta
+@item
+A function to work out the error at a particular time between the
+system's clock and @code{chronyd's} estimate of real time. (This is required
+because some systems have to track real time by making the system time
+follow it in a 'sawtooth' fashion).
+@end enumerate
+
+The @dfn{frequency} is the rate at which the system gains or loses time,
+measured relative to the system when running uncompensated.
+@c }}}
+@c {{{ system quirks
+@node Quirks of particular systems
+@section Quirks of particular systems
+@c {{{ section top
+These sections describe quirks in each system type that needed to be
+investigated to port the software to each system type.
+
+@menu
+* Linux porting quirks::
+* Solaris 2.5 porting quirks::
+* SunOS 4.1.4 porting quirks::
+@end menu
+@c }}}
+@c {{{ linux
+@node Linux porting quirks
+@subsection Linux
+The following quirks have been found in developing the Linux port.
+
+@enumerate 1
+@item
+In order to avoid floating point arithmetic, the kernel uses shifting
+and adding to approximate a scaling of 100/128. This approximation
+implies that the frequency set via the @code{adjtimex()} system call is
+not the frequency that is actually obtained. The method of
+approximation varies between kernel versions and must be determined by
+examining the kernel source. An inverse factor must be included in the
+driver to compensate.
+@item
+In some kernel versions, an @code{adjtimex()} system call with the flags
+bits all zeroed will return the amount of offset still to be corrected.
+In others (e.g. the 2.0 series beyond 2.0.32), the offset must be
+changed in order to get the old offset returned (similar to
+@code{adjtime()} on other systems).
+
+@end enumerate
+@c }}}
+@c {{{ solaris 2.5
+@node Solaris 2.5 porting quirks
+@subsection Solaris 2.5
+
+The following quirks have been found in developing the Solaris port.
+
+@enumerate 1
+@item
+The @code{adjtime()} system call with a zero argument does not cancel an
+adjustment that is in progress - it just reports the remaining
+adjustment.
+@item
+The @code{settimeofday()} system call only observes the seconds part of
+the argument - any fractional seconds part is lost.
+second.
+@item
+The kernel variable @code{dosynctodr} has to be set to zero, otherwise
+the system clock is periodically reset to the real-time clock.
+@end enumerate
+@c }}}
+@c {{{ sunos 4.1.4
+@node SunOS 4.1.4 porting quirks
+@subsection SunOS 4.1.4
+The following quirks have been found in developing the SunOS port.
+
+@enumerate 1
+@item
+The @code{adjtime()} system call truncates its argument to a multiple of
+the system's @code{tickadj} variable. (@code{chronyd} sets that to 100,
+giving a 1 part in 100 slewing capability for correcting offsets.)
+@item
+The kernel variable @code{dosynctodr} has to be set to zero, otherwise
+the system clock is periodically reset to the real-time clock.
+@end enumerate
+@c }}}
+@c }}}
+@c }}}
+@c {{{ apx:GNU General Public License
+@node GPL
+@appendix GNU General Public License
+
+@center GNU GENERAL PUBLIC LICENSE
+@center Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+@c }}}
+@contents
+@bye
+@c vim:cms=@c\ %s:fdm=marker:fdc=5:syntax=off
+
--- /dev/null
+.TH CHRONYC 1 "August 10, 2001" chrony "User's Manual"
+.SH NAME
+chronyc \- command-line interface for chronyd
+
+.SH SYNOPSIS
+.B chronyc
+[\fIOPTIONS\fR]
+
+.SH DESCRIPTION
+\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
+clocks.
+
+\fBchronyc\fR is a command-line interface program which can be used to
+monitor \fIchronyd\fR's performance and to change various operating
+parateters whilst it is running.
+
+.SH USAGE
+A detailed description of all commands supported by \fBchronyc\fR is available
+via the documentation supplied with the distribution (\fIchrony.txt\fR and
+\fIchrony.texi\fR).
+
+.SH OPTIONS
+A summary of the options supported by \fBchronyc\fR is included below.
+
+.TP
+\fB\-h\fR \fIhostname\fR
+specify hostname
+.TP
+\fB\-p\fR \fIport-number\fR
+specify port-number
+.TP
+\fB\-n\fR
+display raw IP addresses (don't attempt to look up hostnames)
+.TP \fIcommand\fR
+specify command. If no command is given, chronyc will read commands
+interactively.
+
+
+.SH VERSION
+1.17
+
+.SH BUGS
+To report bugs, please contact the author and/or visit \fIhttp://go.to/chrony\fR
+
+.SH "SEE ALSO"
+.BR chronyd(1),
+.BR chrony(1)
+
+.I http://chrony.sunsite.dk/
+
+.SH AUTHOR
+Richard Curnow <rc@rc0.org.uk>
+
+This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
+Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
+for details.
--- /dev/null
+.TH CHRONYD 8 "August 10, 2001" chrony "System Administration"
+.SH NAME
+chronyd \- chrony background daemon
+
+.SH SYNOPSIS
+.B chronyd
+[\fIOPTIONS\fR]
+
+.SH DESCRIPTION
+\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
+clocks. \fBchronyd\fR is a background daemon program that can be started at boot
+time.
+
+\fBchronyd\fR is a daemon which runs in background on the
+system. It obtains measurements (e.g. via the network) of the
+system's offset relative to other systems, and adjusts the system
+time accordingly. For isolated systems, the user can periodically
+enter the correct time by hand (using \fIchronyc\fR). In either case,
+\fBchronyd\fR determines the rate at which the computer
+gains or loses time, and compensates for this.
+
+.SH USAGE
+\fBchronyd\fR is usually started at boot-time and requires superuser
+priviliges.
+
+If \fBchronyd\fR has been installed to its default location
+\fI/usr/local/sbin/chronyd\fR, starting it is simply a matter of entering the
+command:
+
+\fI/usr/local/sbin/chronyd\fR
+
+Information messages and warnings will be logged to syslog.
+
+
+.SH OPTIONS
+A summary of the options supported by \fBchronyd\fR is included below.
+
+.TP
+.B \-d
+When run in this mode, the program will not detach itself from the
+terminal, and all messages will be sent to the terminal instead of
+to syslog.
+.TP
+\fB\-f\fR \fIconf-file\fR
+This option can be used to specify an alternate location for the
+configuration file (default \fI/etc/chrony.conf\fR).
+.TP
+.B \-r
+This option will reload sample histories for each of the servers being used.
+These histories are created by using the \fIdump\fR command in \fIchronyc\fR,
+or by setting the \fIdumponexit\fR directive in the configuration file. This
+option is useful if you want to stop and restart \fBchronyd\fR briefly for any
+reason, e.g. to install a new version. However, it only makes sense on
+systems where the kernel can maintain clock compensation whilst not under
+\fBchronyd\fR's control. The only version where this happens so far is Linux.
+On systems where this is not the case, e.g. Solaris and SunOS the option
+should not be used.
+.TP
+.B \-s
+This option will set the system clock from the computer's real-time
+clock. This is analogous to supplying the \fI-s\fR flag to the
+\fI/sbin/clock\fR program during the Linux boot sequence.
+
+Support for real-time clocks is limited at present - the criteria
+are described in the section on the \fIrtcfile\fR directive in the
+documentation supplied with the distribution.
+
+If \fBchronyd\fR cannot support the real time clock on your computer,
+this option cannot be used and a warning message will be logged to
+the syslog.
+
+If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt
+to preserve the old samples after setting the system clock from
+the real time clock. This can be used to allow \fBchronyd\fR to
+perform long term averaging of the gain or loss rate across system
+reboots, and is useful for dial-up systems that are shut down when
+not in use. For this to work well, it relies on \fBchronyd\fR having
+been able to determine accurate statistics for the difference
+between the real time clock and system clock last time the
+computer was on.
+.TP
+.B \-v
+This option displays \fBchronyd\fR's version number to the terminal and exits
+
+.SH FILES
+\fI/etc/chrony.conf\fR
+
+.SH VERSION
+Version 1.17
+
+.SH BUGS
+To report bugs, please contact the author and/or visit \fIhttp://chrony.sunsite.dk/\fR
+
+.SH "SEE ALSO"
+\fBchronyd\fR is documented in detail in the documentation supplied with the
+distribution (\fIchrony.txt\fR and \fIchrony.texi\fR) and is also available
+from \fIhttp://go.to/chrony\fR
+
+.BR chrony(1),
+.BR chronyc(1),
+.BR chrony.conf(5),
+.BR clock(8),
+.BR xntpd(8),
+.BR ntpd(8)
+
+.SH AUTHOR
+Richard Curnow <rc@rc0.org.uk>
+
+This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part
+of "The Missing Man Pages Project". Please see
+\fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details.
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/client.c,v 1.66 2003/01/20 22:52:07 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Command line client for configuring the daemon and obtaining status
+ from it whilst running.
+ */
+
+#include "sysincl.h"
+
+#include "candm.h"
+#include "nameserv.h"
+#include "md5.h"
+#include "version.h"
+#include "getdate.h"
+#include "cmdparse.h"
+#include "pktlength.h"
+#include "memory.h"
+
+#ifdef FEAT_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+/* ================================================== */
+
+static int sock_fd;
+struct sockaddr_in his_addr;
+
+static int on_terminal = 0;
+
+static int no_dns = 0;
+
+/* ================================================== */
+/* Forward prototypes */
+
+static void process_cmd_manual_list(const char *line);
+static void process_cmd_manual_delete(const char *line);
+
+/* ================================================== */
+/* Ought to extract some code from util.c to make
+ a new set of utilities that can be linked into either
+ the daemon or the client. */
+
+static char *
+time_to_log_form(time_t t)
+{
+ struct tm stm;
+ static char buffer[64];
+ static const char *months[] = {"Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"};
+
+
+ stm = *gmtime(&t);
+ sprintf(buffer, "%2d%s%02d %02d:%02d:%02d",
+ stm.tm_mday, months[stm.tm_mon], stm.tm_year % 100,
+ stm.tm_hour, stm.tm_min, stm.tm_sec);
+
+ return buffer;
+}
+
+/* ================================================== */
+
+static char *
+UTI_IPToDottedQuad(unsigned long ip)
+{
+ unsigned long a, b, c, d;
+ static char result[64];
+ a = (ip>>24) & 0xff;
+ b = (ip>>16) & 0xff;
+ c = (ip>> 8) & 0xff;
+ d = (ip>> 0) & 0xff;
+ sprintf(result, "%ld.%ld.%ld.%ld", a, b, c, d);
+ return result;
+}
+
+/* ================================================== */
+/* Read a single line of commands from standard input. Eventually we
+ might want to use the GNU readline library. */
+
+static char *
+read_line(void)
+{
+ static char line[2048];
+ static const char *prompt = "chronyc> ";
+
+ if (on_terminal) {
+#ifdef FEAT_READLINE
+ char *cmd;
+
+ /* save line only if not empty */
+ cmd = readline(prompt);
+ if( cmd == NULL ) return( NULL );
+
+ /* user pressed return */
+ if( *cmd != '\0' ) {
+ strncpy(line, cmd, sizeof(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ add_history(cmd);
+ /* free the buffer allocated by readline */
+ free(cmd);
+ } else {
+ /* simulate the user has entered an empty line */
+ *line = '\0';
+ }
+ return( line );
+#else
+ printf(prompt);
+#endif
+ }
+ if (fgets(line, sizeof(line), stdin)) {
+ return line;
+ } else {
+ return NULL;
+ }
+
+}
+
+/* ================================================== */
+
+static unsigned long
+get_address(const char *hostname)
+{
+ unsigned char *address0;
+ struct hostent *host;
+ unsigned long result;
+
+ /* Note, this call could block for a while */
+ host = gethostbyname(hostname);
+ if (host == NULL) {
+ fprintf(stderr, "Could not get IP address for %s\n", hostname);
+ exit(1);
+ } else {
+ address0 = host->h_addr_list[0];
+ result = ((((unsigned long) address0[0]) << 24) |
+ (((unsigned long) address0[1]) << 16) |
+ (((unsigned long) address0[2]) << 8) |
+ (((unsigned long) address0[3])));
+ }
+
+ return result;
+
+}
+
+/* ================================================== */
+/* Initialise the socket used to talk to the daemon */
+
+static void
+open_io(const char *hostname, int port)
+{
+ struct sockaddr_in my_addr;
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0) {
+ perror("Can't create socket");
+ exit(1);
+ }
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(INADDR_ANY);
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if(bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ perror("Can't bind socket");
+ exit(1);
+ }
+
+ /* Build the socket address structure for sending packets */
+ his_addr.sin_family = AF_INET;
+ his_addr.sin_addr.s_addr = htonl(get_address(hostname));
+
+ /* Eventually the port number needs to be a command line param */
+ his_addr.sin_port = htons(port);
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+close_io(void)
+{
+
+ close(sock_fd);
+
+}
+
+/* ================================================== */
+
+static int
+read_mask_address(char *line, unsigned long *mask, unsigned long *address)
+{
+ unsigned int ma, mb, mc, md, aa, ab, ac, ad;
+ int ok = 0;
+ char *p;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!*p) {
+ *mask = *address = 0;
+ ok = 1;
+ } else {
+
+ if (sscanf(line, "%u.%u.%u.%u/%u.%u.%u.%u",
+ &ma, &mb, &mc, &md,
+ &aa, &ab, &ac, &ad) != 8) {
+ fprintf(stderr, "Invalid syntax for mask/address\n");
+ ok = 0;
+ } else {
+ *mask = (ma << 24) | (mb << 16) | (mc << 8) | md;
+ *address = (aa << 24) | (ab << 16) | (ac << 8) | ad;
+ ok = 1;
+ }
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_offline(CMD_Request *msg, char *line)
+{
+ unsigned long mask, address;
+ int ok;
+
+ if (read_mask_address(line, &mask, &address)) {
+ msg->data.offline.mask = htonl(mask);
+ msg->data.offline.address = htonl(address);
+ msg->command = htons(REQ_OFFLINE);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+
+static int
+process_cmd_online(CMD_Request *msg, char *line)
+{
+ unsigned long mask, address;
+ int ok;
+
+ if (read_mask_address(line, &mask, &address)) {
+ msg->data.online.mask = htonl(mask);
+ msg->data.online.address = htonl(address);
+ msg->command = htons(REQ_ONLINE);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+read_address_integer(char *line, unsigned long *address, int *value)
+{
+ char hostname[2048];
+ int ok = 0;
+
+ if (sscanf(line, "%2047s %d", hostname, value) != 2) {
+ fprintf(stderr, "Invalid syntax for address value\n");
+ ok = 0;
+ } else {
+ *address = DNS_Name2IPAddress(hostname);
+ if (*address == DNS_Failed_Address) {
+ fprintf(stderr, "Could not get address for hostname\n");
+ ok = 0;
+ } else {
+ ok = 1;
+ }
+ }
+
+ return ok;
+
+}
+
+
+/* ================================================== */
+
+static int
+read_address_double(char *line, unsigned long *address, double *value)
+{
+ char hostname[2048];
+ int ok = 0;
+
+ if (sscanf(line, "%2047s %lf", hostname, value) != 2) {
+ fprintf(stderr, "Invalid syntax for address value\n");
+ ok = 0;
+ } else {
+ *address = DNS_Name2IPAddress(hostname);
+ if (*address == DNS_Failed_Address) {
+ fprintf(stderr, "Could not get address for hostname\n");
+ ok = 0;
+ } else {
+ ok = 1;
+ }
+ }
+
+ return ok;
+
+}
+
+
+/* ================================================== */
+
+static int
+process_cmd_minpoll(CMD_Request *msg, char *line)
+{
+ unsigned long address;
+ int minpoll;
+ int ok;
+
+ if (read_address_integer(line, &address, &minpoll)) {
+ msg->data.modify_minpoll.address = htonl(address);
+ msg->data.modify_minpoll.new_minpoll = htonl(minpoll);
+ msg->command = htons(REQ_MODIFY_MINPOLL);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_maxpoll(CMD_Request *msg, char *line)
+{
+ unsigned long address;
+ int maxpoll;
+ int ok;
+
+ if (read_address_integer(line, &address, &maxpoll)) {
+ msg->data.modify_maxpoll.address = htonl(address);
+ msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll);
+ msg->command = htons(REQ_MODIFY_MAXPOLL);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_maxdelay(CMD_Request *msg, char *line)
+{
+ unsigned long address;
+ double max_delay;
+ int ok;
+
+ if (read_address_double(line, &address, &max_delay)) {
+ msg->data.modify_maxdelay.address = htonl(address);
+ msg->data.modify_maxdelay.new_max_delay = REAL2WIRE(max_delay);
+ msg->command = htons(REQ_MODIFY_MAXDELAY);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_maxdelayratio(CMD_Request *msg, char *line)
+{
+ unsigned long address;
+ double max_delay_ratio;
+ int ok;
+
+ if (read_address_double(line, &address, &max_delay_ratio)) {
+ msg->data.modify_maxdelayratio.address = htonl(address);
+ msg->data.modify_maxdelayratio.new_max_delay_ratio = REAL2WIRE(max_delay_ratio);
+ msg->command = htons(REQ_MODIFY_MAXDELAYRATIO);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_maxupdateskew(CMD_Request *msg, char *line)
+{
+ int ok;
+ double new_max_update_skew;
+
+ if (sscanf(line, "%lf", &new_max_update_skew) == 1) {
+ msg->data.modify_maxupdateskew.new_max_update_skew = REAL2WIRE(new_max_update_skew);
+ msg->command = htons(REQ_MODIFY_MAXUPDATESKEW);
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_dump(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_DUMP);
+}
+
+/* ================================================== */
+
+static void
+process_cmd_writertc(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_WRITERTC);
+}
+
+/* ================================================== */
+
+static void
+process_cmd_trimrtc(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_TRIMRTC);
+}
+
+/* ================================================== */
+
+static void
+process_cmd_cyclelogs(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_CYCLELOGS);
+}
+
+/* ================================================== */
+
+static int
+process_cmd_burst(CMD_Request *msg, char *line)
+{
+ int ok;
+ int n_good_samples, n_total_samples;
+ unsigned int ma, mb, mc, md, aa, ab, ac, ad;
+ int n_parsed;
+
+ n_parsed = sscanf(line, "%d/%d %u.%u.%u.%u/%u.%u.%u.%u",
+ &n_good_samples,
+ &n_total_samples,
+ &ma, &mb, &mc, &md,
+ &aa, &ab, &ac, &ad);
+
+ msg->command = htons(REQ_BURST);
+ msg->data.burst.n_good_samples = ntohl(n_good_samples);
+ msg->data.burst.n_total_samples = ntohl(n_total_samples);
+
+ if (n_parsed == 10) {
+ msg->data.burst.mask = htonl((ma << 24) | (mb << 16) | (mc << 8) | md);
+ msg->data.burst.address = htonl((aa << 24) | (ab << 16) | (ac << 8) | ad);
+ ok = 1;
+ } else if (n_parsed == 2) {
+ msg->data.burst.mask = 0;
+ msg->data.burst.address = 0;
+ ok = 1;
+ } else {
+ ok = 0;
+ fprintf(stderr, "Invalid syntax for burst command\n");
+ }
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int
+process_cmd_local(CMD_Request *msg, const char *line)
+{
+ const char *p;
+ int stratum;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ if (!*p) {
+ return 0;
+ } else if (!strncmp(p, "off", 3)) {
+ msg->data.local.on_off = htonl(0);
+ } else if (sscanf(p, "stratum%d", &stratum) == 1) {
+ msg->data.local.on_off = htonl(1);
+ msg->data.local.stratum = htonl(stratum);
+ } else {
+ fprintf(stderr, "Invalid syntax for local command\n");
+ return 0;
+ }
+
+ msg->command = htons(REQ_LOCAL);
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_manual(CMD_Request *msg, const char *line)
+{
+ const char *p;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ if (!*p) {
+ return 0;
+ } else if (!strncmp(p, "off", 3)) {
+ msg->data.manual.option = htonl(0);
+ } else if (!strncmp(p, "on", 2)) {
+ msg->data.manual.option = htonl(1);
+ } else if (!strncmp(p, "reset", 5)) {
+ msg->data.manual.option = htonl(2);
+ } else if (!strncmp(p, "list", 4)) {
+ process_cmd_manual_list(p);
+ return 0;
+ } else if (!strncmp(p, "delete", 6)) {
+ process_cmd_manual_delete(p+6);
+ return 0;
+ } else {
+ return 0;
+ }
+ msg->command = htons(REQ_MANUAL);
+
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+parse_allow_deny(CMD_Request *msg, char *line)
+{
+ unsigned long a, b, c, d, n, ip;
+ char *p, *q;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!*p) {
+ /* blank line - applies to all addresses */
+ msg->data.allow_deny.ip = htonl(0);
+ msg->data.allow_deny.subnet_bits = htonl(0);
+ } else {
+ char *slashpos;
+ slashpos = strchr(p, '/');
+ if (slashpos) *slashpos = 0;
+
+ n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
+
+ if (n == 0) {
+ /* Try to parse as the name of a machine */
+ q = p;
+ while (*q) {
+ if (*q == '\n') *q = 0;
+ q++;
+ }
+ ip = DNS_Name2IPAddress(p);
+ if (ip == DNS_Failed_Address) {
+ fprintf(stderr, "Could not read address\n");
+ return 0;
+ } else {
+ msg->data.allow_deny.ip = htonl(ip);
+ msg->data.allow_deny.subnet_bits = htonl(32);
+ }
+ } else {
+
+ a &= 0xff;
+ b &= 0xff;
+ c &= 0xff;
+ d &= 0xff;
+
+ switch (n) {
+ case 1:
+ msg->data.allow_deny.ip = htonl((a<<24));
+ msg->data.allow_deny.subnet_bits = htonl(8);
+ break;
+ case 2:
+ msg->data.allow_deny.ip = htonl((a<<24) | (b<<16));
+ msg->data.allow_deny.subnet_bits = htonl(16);
+ break;
+ case 3:
+ msg->data.allow_deny.ip = htonl((a<<24) | (b<<16) | (c<<8));
+ msg->data.allow_deny.subnet_bits = htonl(24);
+ break;
+ case 4:
+ msg->data.allow_deny.ip = htonl((a<<24) | (b<<16) | (c<<8) | d);
+ msg->data.allow_deny.subnet_bits = htonl(32);
+ break;
+ default:
+ assert(0);
+
+ }
+
+ if (slashpos) {
+ int specified_subnet_bits, n;
+ n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
+ if (n == 1) {
+ msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
+ } else {
+ fprintf(stderr, "Warning: badly formatted subnet size, using %ld\n", (long) ntohl(msg->data.allow_deny.subnet_bits));
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_allow(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_ALLOW);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_allowall(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_ALLOWALL);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_deny(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_DENY);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_denyall(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_DENYALL);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_cmdallow(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_CMDALLOW);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_cmdallowall(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_CMDALLOWALL);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_cmddeny(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_CMDDENY);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_cmddenyall(CMD_Request *msg, char *line)
+{
+ int status;
+ msg->command = htons(REQ_CMDDENYALL);
+ status = parse_allow_deny(msg, line);
+ return status;
+}
+
+/* ================================================== */
+
+static int
+accheck_getaddr(char *line, unsigned long *addr)
+{
+ unsigned long a, b, c, d, ip;
+ unsigned char *p, *q;
+ p = line;
+ while (*p && isspace(*p)) p++;
+ if (!*p) {
+ return 0;
+ } else {
+ if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) {
+ *addr = (a<<24) | (b<<16) | (c<<8) | d;
+ return 1;
+ } else {
+ q = p;
+ while (*q) {
+ if (*q == '\n') *q = 0;
+ q++;
+ }
+ ip = DNS_Name2IPAddress(p);
+ if (ip == DNS_Failed_Address) {
+ return 0;
+ } else {
+ *addr = ip;
+ return 1;
+ }
+ }
+ }
+}
+
+/* ================================================== */
+
+static int
+process_cmd_accheck(CMD_Request *msg, char *line)
+{
+ unsigned long ip;
+ msg->command = htons(REQ_ACCHECK);
+ if (accheck_getaddr(line, &ip)) {
+ msg->data.ac_check.ip = htonl(ip);
+ return 1;
+ } else {
+ fprintf(stderr, "Could not read address\n");
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static int
+process_cmd_cmdaccheck(CMD_Request *msg, char *line)
+{
+ unsigned long ip;
+ msg->command = htons(REQ_CMDACCHECK);
+ if (accheck_getaddr(line, &ip)) {
+ msg->data.ac_check.ip = htonl(ip);
+ return 1;
+ } else {
+ fprintf(stderr, "Could not read address\n");
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+process_cmd_dfreq(CMD_Request *msg, char *line)
+{
+ double dfreq;
+ msg->command = htons(REQ_DFREQ);
+ if (sscanf(line, "%lf", &dfreq) == 1) {
+ msg->data.dfreq.dfreq = REAL2WIRE(dfreq);
+ } else {
+ msg->data.dfreq.dfreq = REAL2WIRE(0.0);
+ }
+}
+
+/* ================================================== */
+
+static void
+cvt_to_sec_usec(double x, long *sec, long *usec) {
+ long s, us;
+ s = (long) x;
+ us = (long)(0.5 + 1.0e6 * (x - (double) s));
+ while (us >= 1000000) {
+ us -= 1000000;
+ s += 1;
+ }
+ while (us < 0) {
+ us += 1000000;
+ s -= 1;
+ }
+
+ *sec = s;
+ *usec = us;
+ return;
+}
+
+/* ================================================== */
+
+static void
+process_cmd_doffset(CMD_Request *msg, char *line)
+{
+ double doffset;
+ long sec, usec;
+ msg->command = htons(REQ_DOFFSET);
+ if (sscanf(line, "%lf", &doffset) == 1) {
+ cvt_to_sec_usec(doffset, &sec, &usec);
+ msg->data.doffset.sec = htonl(sec);
+ msg->data.doffset.usec = htonl(usec);
+ } else {
+ msg->data.doffset.sec = htonl(0);
+ msg->data.doffset.usec = htonl(0);
+ }
+}
+
+/* ================================================== */
+
+static int
+process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
+{
+ CPS_NTP_Source data;
+ CPS_Status status;
+ int result = 0;
+
+ status = CPS_ParseNTPSourceAdd(line, &data);
+ switch (status) {
+ case CPS_Success:
+ msg->data.ntp_source.port = htonl((unsigned long) data.port);
+ msg->data.ntp_source.ip_addr = htonl(data.ip_addr);
+ msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
+ msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll);
+ msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll);
+ msg->data.ntp_source.online = htonl(data.params.online);
+ msg->data.ntp_source.auto_offline = htonl(data.params.auto_offline);
+ msg->data.ntp_source.authkey = htonl(data.params.authkey);
+ msg->data.ntp_source.max_delay = REAL2WIRE(data.params.max_delay);
+ msg->data.ntp_source.max_delay_ratio = REAL2WIRE(data.params.max_delay_ratio);
+ result = 1;
+
+ break;
+ case CPS_BadOption:
+ fprintf(stderr, "Unrecognized subcommand\n");
+ break;
+ case CPS_BadHost:
+ fprintf(stderr, "Invalid host/IP address\n");
+ break;
+ case CPS_BadPort:
+ fprintf(stderr, "Unreadable port number\n");
+ break;
+ case CPS_BadMinpoll:
+ fprintf(stderr, "Unreadable minpoll value\n");
+ break;
+ case CPS_BadMaxpoll:
+ fprintf(stderr, "Unreadable maxpoll value\n");
+ break;
+ case CPS_BadPresend:
+ fprintf(stderr, "Unreadable presend value\n");
+ break;
+ case CPS_BadMaxdelayratio:
+ fprintf(stderr, "Unreadable max delay ratio value\n");
+ break;
+ case CPS_BadMaxdelay:
+ fprintf(stderr, "Unreadable max delay value\n");
+ break;
+ case CPS_BadKey:
+ fprintf(stderr, "Unreadable key value\n");
+ break;
+ }
+
+ return result;
+}
+
+/* ================================================== */
+
+static int
+process_cmd_add_server(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_ADD_SERVER);
+ return process_cmd_add_server_or_peer(msg, line);
+}
+
+/* ================================================== */
+
+static int
+process_cmd_add_peer(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_ADD_PEER);
+ return process_cmd_add_server_or_peer(msg, line);
+}
+
+/* ================================================== */
+
+static int
+process_cmd_delete(CMD_Request *msg, char *line)
+{
+ char hostname[2048];
+ int ok = 0;
+ unsigned long address = 0UL;
+
+ msg->command = htons(REQ_DEL_SOURCE);
+
+ if (sscanf(line, "%2047s", hostname) != 1) {
+ fprintf(stderr, "Invalid syntax for address\n");
+ ok = 0;
+ } else {
+ address = DNS_Name2IPAddress(hostname);
+ if (address == DNS_Failed_Address) {
+ fprintf(stderr, "Could not get address for hostname\n");
+ ok = 0;
+ } else {
+ ok = 1;
+ }
+ }
+
+ msg->data.del_source.ip_addr = htonl(address);
+
+ return ok;
+
+}
+
+/* ================================================== */
+
+static int password_seen = 0;
+static MD5_CTX md5_after_just_password;
+
+/* ================================================== */
+
+static int
+process_cmd_password(CMD_Request *msg, char *line)
+{
+ char *p, *q;
+ char *password;
+ struct timezone tz;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p))
+ p++;
+
+ /* Get rid of trailing newline */
+ for (q=p; *q; q++) {
+ if (isspace((unsigned char)*q)) *q = 0;
+ }
+
+ if (*p) {
+ password = p;
+ } else {
+ /* blank line, prompt for password */
+ password = getpass("Password: ");
+ }
+
+ if (!*password) {
+ password_seen = 0;
+ } else {
+ password_seen = 1;
+ }
+
+ /* Generate MD5 initial context */
+ MD5Init(&md5_after_just_password);
+ MD5Update(&md5_after_just_password, (unsigned char *) password, strlen(password));
+
+ /* Blank the password for security */
+ for (p = password; *p; p++) {
+ *p = 0;
+ }
+
+ if (gettimeofday(&msg->data.logon.ts, &tz) < 0) {
+ printf("500 - Could not read time of day\n");
+ return 0;
+ } else {
+ msg->command = htons(REQ_LOGON); /* Just force a round trip so that we get tokens etc */
+ msg->data.logon.ts.tv_sec = htonl(msg->data.logon.ts.tv_sec);
+ msg->data.logon.ts.tv_usec = htonl(msg->data.logon.ts.tv_usec);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+static void
+generate_auth(CMD_Request *msg)
+{
+ MD5_CTX ctx;
+ int pkt_len;
+
+ pkt_len = PKL_CommandLength(msg);
+ ctx = md5_after_just_password;
+ MD5Update(&ctx, (unsigned char *) msg, offsetof(CMD_Request, auth));
+ if (pkt_len > offsetof(CMD_Request, data)) {
+ MD5Update(&ctx, (unsigned char *) &(msg->data), pkt_len - offsetof(CMD_Request, data));
+ }
+ MD5Final(&ctx);
+ memcpy(&(msg->auth), &ctx.digest, 16);
+}
+
+/* ================================================== */
+
+static int
+check_reply_auth(CMD_Reply *msg)
+{
+ int pkt_len;
+ MD5_CTX ctx;
+
+ pkt_len = PKL_ReplyLength(msg);
+ ctx = md5_after_just_password;
+ MD5Update(&ctx, (unsigned char *) msg, offsetof(CMD_Request, auth));
+ if (pkt_len > offsetof(CMD_Reply, data)) {
+ MD5Update(&ctx, (unsigned char *) &(msg->data), pkt_len - offsetof(CMD_Reply, data));
+ }
+ MD5Final(&ctx);
+
+ if (!memcmp((void *) &ctx.digest, (void *) &(msg->auth), 16)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+give_help(void)
+{
+ printf("205 - Help follows\n");
+ printf("accheck <address> : Check whether NTP access is allowed to <address>\n");
+ printf("activity : Check how many NTP sources are online/offline\n");
+ printf("allow [<subnet-addr>] : Allow NTP access to that subnet as a default\n");
+ printf("allow all [<subnet-addr>] : Allow NTP access to that subnet and all children\n");
+ printf("burst <n-good>/<n-max> [<mask>/<masked-address>] : Start a rapid set of measurements\n");
+ printf("clients : Report on clients that have accessed the server\n");
+ printf("cmdaccheck <address> : Check whether command access is allowed to <address>\n");
+ printf("cmdallow [<subnet-addr>] : Allow command access to that subnet as a default\n");
+ printf("cmdallow all [<subnet-addr>] : Allow command access to that subnet and all children\n");
+ printf("cmddeny [<subnet-addr>] : Deny command access to that subnet as a default\n");
+ printf("cmddeny all [<subnet-addr>] : Deny command access to that subnet and all children\n");
+ printf("cyclelogs : Close and re-open logs files\n");
+ printf("deny [<subnet-addr>] : Deny NTP access to that subnet as a default\n");
+ printf("deny all [<subnet-addr>] : Deny NTP access to that subnet and all children\n");
+ printf("dump : Dump all measurements to save files\n");
+ printf("exit : Leave the program\n");
+ printf("help : Generate this help\n");
+ printf("local off : Disable server capability for unsynchronised clock\n");
+ printf("local stratum <stratum> : Enable server capability for unsynchronised clock\n");
+ printf("makestep : Jump the time to remove any correction being slewed\n");
+ printf("manual off|on|reset : Disable/enable/reset settime command and statistics\n");
+ printf("manual list : Show previous settime entries\n");
+ printf("minpoll <address> <new-minpoll> : Modify minimum polling interval of source\n");
+ printf("maxdelay <address> <new-max-delay> : Modify maximum round-trip valid sample delay for source\n");
+ printf("maxdelayratio <address> <new-max-ratio> : Modify max round-trip delay ratio for source\n");
+ printf("maxpoll <address> <new-minpoll> : Modify maximum polling interval of source\n");
+ printf("maxupdateskew <new-max-skew> : Modify maximum skew for a clock frequency update to be made\n");
+ printf("offline [<mask>/<masked-address>] : Set sources in subnet to offline status\n");
+ printf("online [<mask>/<masked-address>] : Set sources in subnet to online status\n");
+ printf("password [<new-password>] : Set command authentication password\n");
+ printf("quit : Leave the program\n");
+ printf("rtcdata : Print current RTC performance parameters\n");
+ printf("settime <date/time (e.g. Nov 21, 1997 16:30:05 or 16:30:05)> : Manually set the daemon time\n");
+ printf("sources [-v] : Display information about current sources\n");
+ printf("sourcestats [-v] : Display estimation information about current sources\n");
+ printf("tracking : Display system time information\n");
+ printf("trimrtc : Correct RTC relative to system clock\n");
+ printf("writertc : Save RTC parameters to file\n");
+ printf(".\n");
+}
+
+/* ================================================== */
+
+static unsigned long sequence = 0;
+static unsigned long utoken = 0;
+static unsigned long token = 0;
+
+#define MAX_ATTEMPTS 5
+
+
+/* This is the core protocol module. Complete particular fields in
+ the outgoing packet, send it, wait for a response, handle retries,
+ etc. Returns a Boolean indicating whether the protocol was
+ successful or not.*/
+
+static int
+submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
+{
+ unsigned long tx_sequence;
+ int where_from_len;
+ struct sockaddr_in where_from;
+ int bad_length, bad_sender, bad_sequence, bad_header;
+ int select_status;
+ int recvfrom_status;
+ int read_length;
+ int expected_length;
+ int command_length;
+ struct timeval timeout;
+ int timeout_seconds;
+ int n_attempts;
+ fd_set rdfd, wrfd, exfd;
+
+ request->version = PROTO_VERSION_NUMBER;
+ request->pkt_type = PKT_TYPE_CMD_REQUEST;
+ request->res1 = 0;
+ request->res2 = 0;
+ tx_sequence = sequence++;
+ request->sequence = htonl(tx_sequence);
+ request->attempt = 0;
+ request->utoken = htonl(utoken);
+ request->token = htonl(token);
+
+
+ timeout_seconds = 120;
+
+ n_attempts = 0;
+
+ do {
+
+ /* Decide whether to authenticate */
+ if (password_seen) {
+ if (!utoken || (request->command == htons(REQ_LOGON))) {
+ /* Otherwise, the daemon won't bother authenticating our
+ packet and we won't get a token back */
+ request->utoken = htonl(SPECIAL_UTOKEN);
+ }
+ generate_auth(request);
+ }
+
+ command_length = PKL_CommandLength(request);
+#if 0
+ printf("Sent command length=%d bytes\n", command_length);
+#endif
+
+ if (sendto(sock_fd, (void *) request, command_length, 0,
+ (struct sockaddr *) &his_addr, sizeof(his_addr)) < 0) {
+
+
+#if 0
+ perror("Could not send packet");
+#endif
+ return 0;
+ }
+
+ /* Increment this for next time */
+ ++ request->attempt;
+
+ timeout.tv_sec = timeout_seconds;
+ timeout.tv_usec = 0;
+
+ timeout_seconds += 1;
+ FD_ZERO(&rdfd);
+ FD_ZERO(&wrfd);
+ FD_ZERO(&exfd);
+
+ FD_SET(sock_fd, &rdfd);
+
+ select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &timeout);
+
+ if (select_status < 0) {
+#if 0
+ perror("Select returned negative status");
+#endif
+ } else if (select_status == 0) {
+ /* Timeout must have elapsed, try a resend? */
+ n_attempts ++;
+ if (n_attempts == MAX_ATTEMPTS) {
+ return 0;
+ }
+
+ /* Back to top of loop to do resend */
+ continue;
+
+ } else {
+
+ where_from_len = sizeof(where_from);
+ recvfrom_status = recvfrom(sock_fd, (void *) reply, sizeof(CMD_Reply), 0,
+ (struct sockaddr *) &where_from, &where_from_len);
+
+
+#if 0
+ printf("Received packet, status=%d\n", recvfrom_status);
+#endif
+
+ if (recvfrom_status < 0) {
+ /* If we get connrefused here, it suggests the sendto is
+ going to a dead port - but only if the daemon machine is
+ running Linux (Solaris doesn't return anything) */
+ n_attempts++;
+ if (n_attempts == MAX_ATTEMPTS) {
+ return 0;
+ }
+ } else {
+
+ read_length = recvfrom_status;
+ expected_length = PKL_ReplyLength(reply);
+
+ bad_length = (read_length != expected_length);
+ bad_sender = ((where_from.sin_addr.s_addr != his_addr.sin_addr.s_addr) ||
+ (where_from.sin_port != his_addr.sin_port));
+
+ if (!bad_length) {
+ bad_sequence = (ntohl(reply->sequence) != tx_sequence);
+ } else {
+ bad_sequence = 0;
+ }
+
+ if (bad_length || bad_sender || bad_sequence) {
+ n_attempts++;
+ if (n_attempts == MAX_ATTEMPTS) {
+ return 0;
+ }
+ continue;
+ }
+
+ bad_header = ((reply->version != PROTO_VERSION_NUMBER) ||
+ (reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
+ (reply->res1 != 0) ||
+ (reply->res2 != 0));
+
+ if (bad_header) {
+ n_attempts++;
+ if (n_attempts == MAX_ATTEMPTS) {
+ return 0;
+ }
+ continue;
+ }
+
+ /* Good packet received, print out results */
+#if 0
+ printf("Reply cmd=%d reply=%d stat=%d num=%d tot=%d seq=%d utok=%08lx tok=%d\n",
+ ntohs(reply->command), ntohs(reply->reply),
+ ntohs(reply->status), ntohs(reply->number),
+ ntohs(reply->total),
+ ntohl(reply->sequence),
+ ntohl(reply->utoken),
+ ntohl(reply->token));
+#endif
+
+ if (password_seen) {
+ *reply_auth_ok = check_reply_auth(reply);
+ } else {
+ /* Assume in this case that the reply is always considered
+ to be authentic */
+ *reply_auth_ok = 1;
+ }
+
+ utoken = ntohl(reply->utoken);
+
+ if (*reply_auth_ok) {
+ /* If we're in authenticated mode, only acquire the utoken
+ and new token values if the reply authenticated properly.
+ This protects against forged packets with bogus tokens
+ in. We won't accept a repeat of an old message with a
+ stale token in it, due to bad_sequence processing
+ earlier. */
+ utoken = ntohl(reply->utoken);
+ token = ntohl(reply->token);
+ }
+
+ break;
+
+ }
+ }
+ } while (1);
+
+ return 1;
+}
+
+/* ================================================== */
+
+static void
+print_seconds(unsigned long s)
+{
+ unsigned long d;
+ if (s <= 1024) {
+ printf("%4ld", s);
+ } else if (s < 36000) {
+ printf("%3ldm", s / 60);
+ } else if (s < 345600) {
+ printf("%3ldh", s / 3600);
+ } else {
+ d = s / 86400;
+ if (d > 999) {
+ printf("%3ldy", d / 365);
+ } else {
+ printf("%3ldd", d);
+ }
+ }
+}
+
+/* ================================================== */
+
+static void
+print_microseconds(unsigned long us)
+{
+ if (us <= 9999) {
+ printf("%4ldus", us);
+ } else if (us <= 9999999) {
+ printf("%4ldms", us / 1000);
+ } else if (us <= 999999999) {
+ printf("%3ld.%01lds", us / 1000000, (us/100000) % 10);
+ } else {
+ printf("%5lds", us / 1000000);
+ }
+}
+
+/* ================================================== */
+
+static void
+print_signed_microseconds(long us)
+{
+ long x = abs(us);
+ if (x <= 9999) {
+ printf("%+5ldus", us);
+ } else if (x <= 9999999) {
+ printf("%+5ldms", us / 1000);
+ } else {
+ printf("%+6lds", us / 1000000);
+ }
+}
+
+/* ================================================== */
+
+static int
+check_for_verbose_flag(char *line)
+{
+ char *p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!strncmp(p, "-v", 2)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+process_cmd_sources(char *line)
+{
+ int submit_ok;
+ int auth_ok;
+ CMD_Request request;
+ CMD_Reply reply;
+ int n_sources, i;
+ int verbose = 0;
+
+ long orig_latest_meas, latest_meas, est_offset;
+ unsigned long ip_addr;
+ unsigned long latest_meas_err, est_offset_err;
+ unsigned long latest_meas_ago;
+ unsigned short poll, stratum;
+ unsigned short state, mode;
+ double resid_freq, resid_skew;
+ const char *dns_lookup;
+ char hostname_buf[32];
+ unsigned short status;
+
+ /* Check whether to output verbose headers */
+ verbose = check_for_verbose_flag(line);
+
+ request.command = htons(REQ_N_SOURCES);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_INVALID:
+ printf("502 Invalid command\n");
+ return;
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ return;
+ break;
+ default:
+ break;
+ }
+
+ n_sources = ntohl(reply.data.n_sources.n_sources);
+ printf("210 Number of sources = %d\n", n_sources);
+ if (verbose) {
+ printf("\n");
+ printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n");
+ printf(" / .- Source state '*' = current synced, '+' = OK for sync, '?' = unreachable,\n");
+ printf("| / 'x' = time may be in error, '~' = time is too variable.\n");
+ printf("|| .- xxxx [ yyyy ] +/- zzzz\n");
+ printf("|| / xxxx = adjusted offset,\n");
+ printf("|| Log2(Polling interval) -. | yyyy = measured offset,\n");
+ printf("|| \\ | zzzz = estimated error.\n");
+ printf("|| | | \n");
+ }
+
+ printf("MS Name/IP address Stratum Poll LastRx Last sample\n");
+ printf("============================================================================\n");
+
+ /* "MS NNNNNNNNNNNNNNNNNNNNNNNNN SS PP RRRR SSSSSSS[SSSSSSS] +/- SSSSSS" */
+
+ for (i=0; i<n_sources; i++) {
+ request.command = htons(REQ_SOURCE_DATA);
+ request.data.source_data.index = htonl(i);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ if (ntohs(reply.status) == STT_SUCCESS) {
+ ip_addr = ntohl(reply.data.source_data.ip_addr);
+ poll = ntohs(reply.data.source_data.poll);
+ stratum = ntohs(reply.data.source_data.stratum);
+ state = ntohs(reply.data.source_data.state);
+ mode = ntohs(reply.data.source_data.mode);
+ latest_meas_ago = ntohl(reply.data.source_data.since_sample);
+ orig_latest_meas = ntohl(reply.data.source_data.orig_latest_meas);
+ latest_meas = ntohl(reply.data.source_data.latest_meas);
+ latest_meas_err = ntohl(reply.data.source_data.latest_meas_err);
+ est_offset = ntohl(reply.data.source_data.est_offset);
+ est_offset_err = ntohl(reply.data.source_data.est_offset_err);
+ resid_freq = (double) ((long) ntohl(reply.data.source_data.resid_freq)) * 1.0e-3;
+ resid_skew = (double) (ntohl(reply.data.source_data.resid_skew)) * 1.0e-3;
+
+ hostname_buf[25] = 0;
+ if (no_dns) {
+ sprintf(hostname_buf, "%s", UTI_IPToDottedQuad(ip_addr));
+ } else {
+ dns_lookup = DNS_IPAddress2Name(ip_addr);
+ strncpy(hostname_buf, dns_lookup, 25);
+ }
+
+ switch (mode) {
+ case RPY_SD_MD_CLIENT:
+ printf("^"); break;
+ case RPY_SD_MD_PEER:
+ printf("="); break;
+ case RPY_SD_MD_REF:
+ printf("#"); break;
+ }
+ switch (state) {
+ case RPY_SD_ST_SYNC:
+ printf("*"); break;
+ case RPY_SD_ST_UNREACH:
+ printf("?"); break;
+ case RPY_SD_ST_FALSETICKER:
+ printf("x"); break;
+ case RPY_SD_ST_JITTERY:
+ printf("~"); break;
+ case RPY_SD_ST_OTHER:
+ printf("+"); break;
+ }
+
+ printf(" %-25s %2d %2d ", hostname_buf, stratum, poll);
+ print_seconds(latest_meas_ago);
+ printf(" ");
+ print_signed_microseconds(latest_meas);
+ printf("[");
+ print_signed_microseconds(orig_latest_meas);
+ printf("]");
+ printf(" +/- ");
+ print_microseconds(latest_meas_err);
+ printf("\n");
+ }
+ }
+ }
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ }
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_sourcestats(char *line)
+{
+ int submit_ok;
+ int auth_ok;
+ CMD_Request request;
+ CMD_Reply reply;
+ int n_sources, i;
+ int verbose = 0;
+
+ const char *dns_lookup;
+ char hostname_buf[32];
+ unsigned long n_samples, n_runs, span_seconds;
+ double resid_freq_ppm, skew_ppm;
+ unsigned long sd_us;
+ unsigned long ip_addr;
+ unsigned short status;
+
+ verbose = check_for_verbose_flag(line);
+
+ request.command = htons(REQ_N_SOURCES);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_INVALID:
+ printf("502 Invalid command\n");
+ return;
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ return;
+ break;
+ default:
+ break;
+ }
+
+ n_sources = ntohl(reply.data.n_sources.n_sources);
+ printf("210 Number of sources = %d\n", n_sources);
+ if (verbose) {
+ printf(" .- Number of sample points in measurement set.\n");
+ printf(" / .- Number of residual runs with same sign.\n");
+ printf(" | / .- Length of measurement set (time).\n");
+ printf(" | | / .- Est. clock freq error (ppm).\n");
+ printf(" | | | / .- Est error in freq.\n");
+ printf(" | | | | / .- On the\n");
+ printf(" | | | | | / samples.\n");
+ printf(" | | | | | |\n");
+ }
+
+ printf("Name/IP Address NP NR Span Frequency Freq Skew Std Dev\n");
+ printf("========================================================================\n");
+
+ /* NNNNNNNNNNNNNNNNNNNNNNNNN NP NR SSSS FFFFFFFFFF SSSSSSSSSS SSSSSSS */
+
+ for (i=0; i<n_sources; i++) {
+ request.command = htons(REQ_SOURCESTATS);
+ request.data.source_data.index = htonl(i);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ if (ntohs(reply.status) == STT_SUCCESS) {
+
+ ip_addr = ntohl(reply.data.sourcestats.ip_addr);
+ n_samples = ntohl(reply.data.sourcestats.n_samples);
+ n_runs = ntohl(reply.data.sourcestats.n_runs);
+ span_seconds = ntohl(reply.data.sourcestats.span_seconds);
+ resid_freq_ppm = WIRE2REAL(reply.data.sourcestats.resid_freq_ppm);
+ skew_ppm = WIRE2REAL(reply.data.sourcestats.skew_ppm);
+ sd_us = ntohl(reply.data.sourcestats.sd_us);
+
+ hostname_buf[25] = 0;
+ if (no_dns) {
+ sprintf(hostname_buf, "%s", UTI_IPToDottedQuad(ip_addr));
+ } else {
+ dns_lookup = DNS_IPAddress2Name(ip_addr);
+ strncpy(hostname_buf, dns_lookup, 25);
+ }
+
+ printf("%-25s %2lu %2lu ", hostname_buf, n_samples, n_runs);
+ print_seconds(span_seconds);
+ printf(" %10.3f %10.3f ", resid_freq_ppm, skew_ppm);
+ print_microseconds(sd_us);
+ printf("\n");
+ }
+ }
+ }
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ }
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_tracking(char *line)
+{
+ int status;
+ int submit_ok;
+ int auth_ok;
+ CMD_Request request;
+ CMD_Reply reply;
+ unsigned long ref_id;
+ struct timeval ref_time;
+ struct tm ref_time_tm;
+ unsigned long a, b, c, d;
+ struct timeval correction_tv;
+ double correction;
+ double freq_ppm;
+ double resid_freq_ppm;
+ double skew_ppm;
+ double root_delay;
+ double root_dispersion;
+
+ request.command = htons(REQ_TRACKING);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_INVALID:
+ printf("502 Invalid command\n");
+ return;
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ return;
+ break;
+ default:
+ break;
+ }
+
+ ref_id = ntohl(reply.data.tracking.ref_id);
+ a = (ref_id >> 24);
+ b = (ref_id >> 16) & 0xff;
+ c = (ref_id >> 8) & 0xff;
+ d = (ref_id) & 0xff;
+ printf("Reference ID : %lu.%lu.%lu.%lu (%s)\n",
+ a, b, c, d,
+ (no_dns) ? UTI_IPToDottedQuad(ref_id) : DNS_IPAddress2Name(ref_id));
+ printf("Stratum : %lu\n", (unsigned long) ntohl(reply.data.tracking.stratum));
+ ref_time.tv_sec = ntohl(reply.data.tracking.ref_time_s);
+ ref_time.tv_usec = ntohl(reply.data.tracking.ref_time_us);
+ ref_time_tm = *gmtime((time_t *)&ref_time.tv_sec);
+ printf("Ref time (UTC) : %s", asctime(&ref_time_tm));
+ correction_tv.tv_sec = ntohl(reply.data.tracking.current_correction_s);
+ correction_tv.tv_usec = ntohl(reply.data.tracking.current_correction_us);
+ correction = (double) correction_tv.tv_sec + 1.0e-6 * correction_tv.tv_usec;
+ printf("System time : %.6f seconds %s of NTP time\n", fabs(correction),
+ (correction > 0.0) ? "slow" : "fast");
+ freq_ppm = WIRE2REAL(reply.data.tracking.freq_ppm);
+ resid_freq_ppm = WIRE2REAL(reply.data.tracking.resid_freq_ppm);
+ skew_ppm = WIRE2REAL(reply.data.tracking.skew_ppm);
+ root_delay = WIRE2REAL(reply.data.tracking.root_delay);
+ root_dispersion = WIRE2REAL(reply.data.tracking.root_dispersion);
+ printf("Frequency : %.3f ppm %s\n", fabs(freq_ppm), (freq_ppm < 0.0) ? "slow" : "fast");
+ printf("Residual freq : %.3f ppm\n", resid_freq_ppm);
+ printf("Skew : %.3f ppm\n", skew_ppm);
+ printf("Root delay : %.6f seconds\n", root_delay);
+ printf("Root dispersion : %.6f seconds\n", root_dispersion);
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ }
+}
+/* ================================================== */
+
+static void
+process_cmd_rtcreport(char *line)
+{
+ int status;
+ int submit_ok;
+ int auth_ok;
+ CMD_Request request;
+ CMD_Reply reply;
+ time_t ref_time;
+ struct tm ref_time_tm;
+ unsigned short n_samples;
+ unsigned short n_runs;
+ unsigned long span_seconds;
+ double coef_seconds_fast;
+ double coef_gain_rate_ppm;
+
+ request.command = htons(REQ_RTCREPORT);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_INVALID:
+ printf("502 Invalid command\n");
+ return;
+ break;
+ case STT_NORTC:
+ printf("513 No RTC driver\n");
+ return;
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ return;
+ break;
+ default:
+ break;
+ }
+ ref_time = (time_t) ntohl(reply.data.rtc.ref_time);
+ ref_time_tm = *gmtime(&ref_time);
+ n_samples = ntohs(reply.data.rtc.n_samples);
+ n_runs = ntohs(reply.data.rtc.n_runs);
+ span_seconds = ntohl(reply.data.rtc.span_seconds);
+ coef_seconds_fast = WIRE2REAL(reply.data.rtc.rtc_seconds_fast);
+ coef_gain_rate_ppm = WIRE2REAL(reply.data.rtc.rtc_gain_rate_ppm);
+ printf("RTC ref time (UTC) : %s", asctime(&ref_time_tm));
+ printf("Number of samples : %d\n", n_samples);
+ printf("Number of runs : %d\n", n_runs);
+ printf("Sample span period : ");
+ print_seconds(span_seconds);
+ printf("\n");
+ printf("RTC is fast by : %12.6f seconds\n", coef_seconds_fast);
+ printf("RTC gains time at : %9.3f ppm\n", coef_gain_rate_ppm);
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ }
+}
+
+/* ================================================== */
+
+#if 0
+
+/* This is a previous attempt at implementing the clients command. It
+ could be re-instated sometime as a way of looking at all clients in a
+ particular subnet. The problem with it is that is requires at least 5
+ round trips to the server even if the server only has one client to
+ report. */
+
+typedef struct XSubnetToDo {
+ struct XSubnetToDo *next;
+ unsigned long ip;
+ unsigned long bits;
+} SubnetToDo;
+
+static void
+process_cmd_clients(char *line)
+{
+ CMD_Request request;
+ CMD_Reply reply;
+ SubnetToDo *head, *todo, *tail, *p, *next_node, *new_node;
+ int submit_ok, auth_ok;
+ int status;
+ int i, j, nets_looked_up, clients_looked_up;
+ int word;
+ unsigned long mask;
+ unsigned long ip, bits;
+ unsigned long client_hits;
+ unsigned long peer_hits;
+ unsigned long cmd_hits_auth;
+ unsigned long cmd_hits_normal;
+ unsigned long cmd_hits_bad;
+ unsigned long last_ntp_hit_ago;
+ unsigned long last_cmd_hit_ago;
+ char hostname_buf[32];
+ const char *dns_lookup;
+
+ int n_replies;
+
+ head = todo = MallocNew(SubnetToDo);
+ todo->next = NULL;
+ /* Set up initial query = root subnet */
+ todo->ip = 0;
+ todo->bits = 0;
+ tail = todo;
+
+ do {
+
+ request.command = htons(REQ_SUBNETS_ACCESSED);
+ /* Build list of subnets to examine */
+ i=0;
+ p=todo;
+ while((i < MAX_SUBNETS_ACCESSED) &&
+ p &&
+ (p->bits < 32)) {
+
+ request.data.subnets_accessed.subnets[i].ip = htonl(p->ip);
+ request.data.subnets_accessed.subnets[i].bits_specd = htonl(p->bits);
+ p = p->next;
+ i++;
+ }
+
+ nets_looked_up = i;
+
+ if (nets_looked_up == 0) {
+ /* No subnets need examining */
+ break;
+ }
+
+ request.data.subnets_accessed.n_subnets = htonl(nets_looked_up);
+
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ n_replies = ntohl(reply.data.subnets_accessed.n_subnets);
+ for (j=0; j<n_replies; j++) {
+ ip = ntohl(reply.data.subnets_accessed.subnets[j].ip);
+ bits = ntohl(reply.data.subnets_accessed.subnets[j].bits_specd);
+ for (i=0; i<256; i++) {
+ word = i/32;
+ mask = 1UL << (i%32);
+ if (ntohl(reply.data.subnets_accessed.subnets[j].bitmap[word]) & mask) {
+ /* Add this subnet to the todo list */
+ new_node = MallocNew(SubnetToDo);
+ new_node->next = NULL;
+ new_node->bits = bits + 8;
+ new_node->ip = ip | (i << (24 - bits));
+ tail->next = new_node;
+ tail = new_node;
+#if 0
+ printf("%08lx %2d %3d %08lx\n", ip, bits, i, new_node->ip);
+#endif
+ }
+ }
+ }
+
+ /* Skip the todo pointer forwards by the number of nets looked
+ up. Can't do this earlier, because we might have to point
+ at the next layer of subnets that have only just been
+ concatenated to the linked list. */
+ for (i=0; i<nets_looked_up; i++) {
+ todo = todo->next;
+ }
+
+ break;
+ case STT_BADSUBNET:
+ /* We should never generate any bad subnet messages */
+ assert(0);
+ break;
+ case STT_INACTIVE:
+ printf("519 Client logging is not active in the daemon\n");
+ goto cleanup;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ goto cleanup;
+ default:
+ printf("520 Got unexpected error from daemon\n");
+ goto cleanup;
+ }
+
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ return;
+ }
+ } while (1); /* keep going until all subnets have been expanded,
+ down to single nodes */
+
+ /* Now the todo list consists of client records */
+ request.command = htons(REQ_CLIENT_ACCESSES);
+
+#if 0
+ printf("%d %d\n", sizeof (RPY_ClientAccesses_Client), offsetof(CMD_Reply, data.client_accesses.clients));
+#endif
+
+ printf("Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC\n"
+ "========================= ====== ====== ====== ====== ====== ==== ====\n");
+
+ do {
+
+ i = 0;
+ p = todo;
+ while ((i < MAX_CLIENT_ACCESSES) &&
+ p) {
+
+ request.data.client_accesses.client_ips[i] = htonl(p->ip);
+ p = p->next;
+ i++;
+ }
+
+ clients_looked_up = i;
+
+ if (clients_looked_up == 0) {
+ /* No more clients to do */
+ break;
+ }
+
+ request.data.client_accesses.n_clients = htonl(clients_looked_up);
+
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ n_replies = ntohl(reply.data.client_accesses.n_clients);
+ for (j=0; j<n_replies; j++) {
+ ip = ntohl(reply.data.client_accesses.clients[j].ip);
+ if (ip != 0UL) {
+ /* ip == 0 implies that the node could not be found in
+ the daemon's tables; we shouldn't ever generate this
+ case, but ignore it if we do. (In future there might
+ be a protocol to reset the client logging; if another
+ administrator runs that while we're doing the clients
+ command, there will be a race condition that could
+ cause this). */
+
+ client_hits = ntohl(reply.data.client_accesses.clients[j].client_hits);
+ peer_hits = ntohl(reply.data.client_accesses.clients[j].peer_hits);
+ cmd_hits_auth = ntohl(reply.data.client_accesses.clients[j].cmd_hits_auth);
+ cmd_hits_normal = ntohl(reply.data.client_accesses.clients[j].cmd_hits_normal);
+ cmd_hits_bad = ntohl(reply.data.client_accesses.clients[j].cmd_hits_bad);
+ last_ntp_hit_ago = ntohl(reply.data.client_accesses.clients[j].last_ntp_hit_ago);
+ last_cmd_hit_ago = ntohl(reply.data.client_accesses.clients[j].last_cmd_hit_ago);
+
+ if (no_dns) {
+ sprintf(hostname_buf, "%s", UTI_IPToDottedQuad(ip));
+ } else {
+ dns_lookup = DNS_IPAddress2Name(ip);
+ hostname_buf[25] = 0;
+ strncpy(hostname_buf, dns_lookup, 25);
+ }
+ printf("%-25s %6d %6d %6d %6d %6d ",
+ hostname_buf,
+ client_hits, peer_hits,
+ cmd_hits_auth, cmd_hits_normal, cmd_hits_bad);
+ print_seconds(last_ntp_hit_ago);
+ printf(" ");
+ print_seconds(last_cmd_hit_ago);
+ printf("\n");
+ }
+ }
+
+ /* Skip the todo pointer forwards by the number of nets looked
+ up. Can't do this earlier, because we might have to point
+ at the next layer of subnets that have only just been
+ concatenated to the linked list. */
+ for (i=0; i<clients_looked_up; i++) {
+ todo = todo->next;
+ }
+
+ break;
+
+ case STT_BADSUBNET:
+ /* We should never generate any bad subnet messages */
+ assert(0);
+ break;
+ case STT_INACTIVE:
+ printf("519 Client logging is not active in the daemon\n");
+ goto cleanup;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ goto cleanup;
+ default:
+ printf("520 Got unexpected error from daemon\n");
+ goto cleanup;
+ }
+
+ }
+
+ } while (1);
+
+cleanup:
+ for (p = head; p; ) {
+ next_node = p->next;
+ Free(p);
+ p = next_node;
+ }
+
+}
+
+#endif
+
+/* New implementation of clients command */
+
+static void
+process_cmd_clients(char *line)
+{
+ CMD_Request request;
+ CMD_Reply reply;
+ int submit_ok, auth_ok;
+ int status;
+ unsigned long next_index;
+ int j;
+ unsigned long ip;
+ unsigned long client_hits;
+ unsigned long peer_hits;
+ unsigned long cmd_hits_auth;
+ unsigned long cmd_hits_normal;
+ unsigned long cmd_hits_bad;
+ unsigned long last_ntp_hit_ago;
+ unsigned long last_cmd_hit_ago;
+ char hostname_buf[32];
+ const char *dns_lookup;
+
+ int n_replies;
+ int n_indices_in_table;
+
+ next_index = 0;
+
+ printf("Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC\n"
+ "========================= ====== ====== ====== ====== ====== ==== ====\n");
+
+ do {
+
+ request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX);
+ request.data.client_accesses_by_index.first_index = htonl(next_index);
+ request.data.client_accesses_by_index.n_indices = htonl(MAX_CLIENT_ACCESSES);
+
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ n_replies = ntohl(reply.data.client_accesses_by_index.n_clients);
+ n_indices_in_table = ntohl(reply.data.client_accesses_by_index.n_indices);
+ if (n_replies == 0) {
+ goto finished;
+ }
+ for (j=0; j<n_replies; j++) {
+ ip = ntohl(reply.data.client_accesses_by_index.clients[j].ip);
+ if (ip != 0UL) {
+ /* ip == 0 implies that the node could not be found in
+ the daemon's tables; we shouldn't ever generate this
+ case, but ignore it if we do. (In future there might
+ be a protocol to reset the client logging; if another
+ administrator runs that while we're doing the clients
+ command, there will be a race condition that could
+ cause this). */
+
+ client_hits = ntohl(reply.data.client_accesses_by_index.clients[j].client_hits);
+ peer_hits = ntohl(reply.data.client_accesses_by_index.clients[j].peer_hits);
+ cmd_hits_auth = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_auth);
+ cmd_hits_normal = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_normal);
+ cmd_hits_bad = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_bad);
+ last_ntp_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_ntp_hit_ago);
+ last_cmd_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_cmd_hit_ago);
+
+ if (no_dns) {
+ sprintf(hostname_buf, "%s", UTI_IPToDottedQuad(ip));
+ } else {
+ dns_lookup = DNS_IPAddress2Name(ip);
+ hostname_buf[25] = 0;
+ strncpy(hostname_buf, dns_lookup, 25);
+ }
+ printf("%-25s %6ld %6ld %6ld %6ld %6ld ",
+ hostname_buf,
+ client_hits, peer_hits,
+ cmd_hits_auth, cmd_hits_normal, cmd_hits_bad);
+ print_seconds(last_ntp_hit_ago);
+ printf(" ");
+ print_seconds(last_cmd_hit_ago);
+ printf("\n");
+ }
+ }
+
+ /* Set the next index to probe based on what the server tells us */
+ next_index = ntohl(reply.data.client_accesses_by_index.next_index);
+ if (next_index >= n_indices_in_table) {
+ goto finished;
+ }
+
+ break;
+ case STT_BADSUBNET:
+ /* We should never generate any bad subnet messages */
+ assert(0);
+ break;
+ case STT_INACTIVE:
+ printf("519 Client logging is not active in the daemon\n");
+ goto finished;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ goto finished;
+ default:
+ printf("520 Got unexpected error from daemon\n");
+ goto finished;
+ }
+
+ } else {
+ printf("506 Cannot talk to daemon\n");
+ return;
+ }
+ } while (1); /* keep going until all subnets have been expanded,
+ down to single nodes */
+
+finished:
+ return;
+
+}
+
+
+/* ================================================== */
+/* Process the manual list command */
+static void
+process_cmd_manual_list(const char *line)
+{
+ CMD_Request request;
+ CMD_Reply reply;
+ int submit_ok, auth_ok;
+ int status;
+ int n_samples;
+ RPY_ManualListSample *sample;
+ int i;
+ time_t when;
+ double slewed_offset, orig_offset, residual;
+
+ request.command = htons(REQ_MANUAL_LIST);
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ n_samples = ntohl(reply.data.manual_list.n_samples);
+ printf("210 n_samples = %d\n", n_samples);
+ printf("# Date Time(UTC) Slewed Original Residual\n"
+ "====================================================\n");
+ for (i=0; i<n_samples; i++) {
+ sample = &reply.data.manual_list.samples[i];
+ when = ntohl(sample->when);
+ slewed_offset = WIRE2REAL(sample->slewed_offset);
+ orig_offset = WIRE2REAL(sample->orig_offset);
+ residual = WIRE2REAL(sample->residual);
+ printf("%2d %s %10.2f %10.2f %10.2f\n", i, time_to_log_form(when), slewed_offset, orig_offset, residual);
+ }
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ break;
+ default:
+ printf("520 Got unexpected error from daemon\n");
+ break;
+ }
+ }
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_manual_delete(const char *line)
+{
+ int index;
+ CMD_Request request;
+ CMD_Reply reply;
+ int submit_ok, auth_ok;
+ int status;
+
+ if (sscanf(line, "%d", &index) != 1) {
+ fprintf(stderr, "Bad syntax for manual delete command\n");
+ return;
+
+ }
+
+ request.command = htons(REQ_MANUAL_DELETE);
+ request.data.manual_delete.index = htonl(index);
+
+ submit_ok = submit_request(&request, &reply, &auth_ok);
+
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ printf("200 OK\n");
+ break;
+ case STT_BADSAMPLE:
+ printf("516 Sample index out of range\n");
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host\n");
+ break;
+ default:
+ printf("520 Got unexpected error from daemon\n");
+ break;
+ }
+ }
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_settime(char *line)
+{
+ time_t now, new_time;
+ CMD_Request request;
+ CMD_Reply reply;
+ int submit_ok, reply_auth_ok;
+ long offset_cs;
+ double dfreq_ppm, new_afreq_ppm;
+ double offset;
+ int status;
+
+ now = time(NULL);
+ new_time = get_date(line, &now);
+
+ if (new_time == -1) {
+ printf("510 - Could not parse date string\n");
+ } else {
+ request.data.settime.ts.tv_sec = htonl(new_time);
+ request.data.settime.ts.tv_usec = htonl(0);
+ request.command = htons(REQ_SETTIME);
+ submit_ok = submit_request(&request, &reply, &reply_auth_ok);
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ offset_cs = ntohl(reply.data.manual_timestamp.centiseconds);
+ offset = 0.01 * (double) offset_cs;
+ dfreq_ppm = WIRE2REAL(reply.data.manual_timestamp.dfreq_ppm);
+ new_afreq_ppm = WIRE2REAL(reply.data.manual_timestamp.new_afreq_ppm);
+ printf("200 OK : Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm",
+ offset, dfreq_ppm, new_afreq_ppm);
+ break;
+ case STT_FAILED:
+ printf("500 Failure");
+ break;
+ case STT_UNAUTH:
+ printf("501 Not authorised");
+ break;
+ case STT_INVALID:
+ printf("502 Invalid command");
+ break;
+ case STT_NOTENABLED:
+ printf("505 Facility not enabled in daemon");
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host");
+ break;
+ }
+ if (reply_auth_ok) {
+ printf("\n");
+ } else {
+ printf(" --- Reply not authenticated\n");
+ }
+ } else {
+ printf("506 Could not submit settime command\n");
+ }
+ }
+
+}
+
+/* ================================================== */
+
+static void
+process_cmd_rekey(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_REKEY);
+}
+
+/* ================================================== */
+
+static void
+process_cmd_makestep(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_MAKESTEP);
+}
+
+/* ================================================== */
+
+static void
+process_cmd_activity(const char *line)
+{
+ CMD_Request request;
+ CMD_Reply reply;
+ int submit_ok, status, reply_auth_ok;
+ request.command = htons(REQ_ACTIVITY);
+ submit_ok = submit_request(&request, &reply, &reply_auth_ok);
+ if (submit_ok) {
+ status = ntohs(reply.status);
+ switch (status) {
+ case STT_SUCCESS:
+ printf("200 OK\n"
+ "%ld sources online\n"
+ "%ld sources offline\n"
+ "%ld sources doing burst (return to online)\n"
+ "%ld sources doing burst (return to offline)\n",
+ (long) ntohl(reply.data.activity.online),
+ (long) ntohl(reply.data.activity.offline),
+ (long) ntohl(reply.data.activity.burst_online),
+ (long) ntohl(reply.data.activity.burst_offline));
+ break;
+ default:
+ printf("Unexpected error returned\n");
+ break;
+ }
+ if (!reply_auth_ok) {
+ printf(" --- Reply not authenticated\n");
+ }
+ } else {
+ printf("506 Could not submit activity command\n");
+ }
+}
+
+/* ================================================== */
+
+static int
+process_line(char *line)
+{
+ char *p;
+ int quit;
+ int do_normal_submit;
+ CMD_Request tx_message;
+ CMD_Reply rx_message;
+ int reply_auth_ok, request_submitted_ok;
+
+ quit = 0;
+
+ do_normal_submit = 1;
+
+ /* Check for line being blank */
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!*p) return quit;
+
+ if (!strncmp(p, "offline", 7)) {
+ do_normal_submit = process_cmd_offline(&tx_message, p+7);
+ } else if (!strncmp(p, "online", 6)) {
+ do_normal_submit = process_cmd_online(&tx_message, p+6);
+ } else if (!strncmp(p, "burst", 5)) {
+ do_normal_submit = process_cmd_burst(&tx_message, p+5);
+ } else if (!strncmp(p, "password", 8)) {
+ do_normal_submit = process_cmd_password(&tx_message, p+8);
+ } else if (!strncmp(p, "minpoll", 7)) {
+ do_normal_submit = process_cmd_minpoll(&tx_message, p+7);
+ } else if (!strncmp(p, "maxpoll", 7)) {
+ do_normal_submit = process_cmd_maxpoll(&tx_message, p+7);
+ } else if (!strncmp(p, "dump", 4)) {
+ process_cmd_dump(&tx_message, p+4);
+ } else if (!strncmp(p, "maxdelayratio", 13)) {
+ do_normal_submit = process_cmd_maxdelayratio(&tx_message, p+13);
+ } else if (!strncmp(p, "maxdelay", 8)) {
+ do_normal_submit = process_cmd_maxdelay(&tx_message, p+8);
+ } else if (!strncmp(p, "maxupdateskew", 13)) {
+ do_normal_submit = process_cmd_maxupdateskew(&tx_message, p+13);
+ } else if (!strncmp(p, "settime", 7)) {
+ do_normal_submit = 0;
+ process_cmd_settime(p+7);
+ } else if (!strncmp(p, "local", 5)) {
+ do_normal_submit = process_cmd_local(&tx_message, p+5);
+ } else if (!strncmp(p, "manual", 6)) {
+ do_normal_submit = process_cmd_manual(&tx_message, p+6);
+ } else if (!strncmp(p, "sourcestats", 11)) {
+ do_normal_submit = 0;
+ process_cmd_sourcestats(p+11);
+ } else if (!strncmp(p, "sources", 7)) {
+ do_normal_submit = 0;
+ process_cmd_sources(p+7);
+ } else if (!strncmp(p, "rekey", 5)) {
+ process_cmd_rekey(&tx_message, p+5);
+ } else if (!strncmp(p, "allow all", 9)) {
+ do_normal_submit = process_cmd_allowall(&tx_message, p+9);
+ } else if (!strncmp(p, "allow", 5)) {
+ do_normal_submit = process_cmd_allow(&tx_message, p+5);
+ } else if (!strncmp(p, "deny all", 8)) {
+ do_normal_submit = process_cmd_denyall(&tx_message, p+8);
+ } else if (!strncmp(p, "deny", 4)) {
+ do_normal_submit = process_cmd_deny(&tx_message, p+4);
+ } else if (!strncmp(p, "cmdallow all", 12)) {
+ do_normal_submit = process_cmd_cmdallowall(&tx_message, p+12);
+ } else if (!strncmp(p, "cmdallow", 8)) {
+ do_normal_submit = process_cmd_cmdallow(&tx_message, p+8);
+ } else if (!strncmp(p, "cmddeny all", 11)) {
+ do_normal_submit = process_cmd_cmddenyall(&tx_message, p+11);
+ } else if (!strncmp(p, "cmddeny", 7)) {
+ do_normal_submit = process_cmd_cmddeny(&tx_message, p+7);
+ } else if (!strncmp(p, "accheck", 7)) {
+ do_normal_submit = process_cmd_accheck(&tx_message, p+7);
+ } else if (!strncmp(p, "cmdaccheck", 10)) {
+ do_normal_submit = process_cmd_cmdaccheck(&tx_message, p+10);
+ } else if (!strncmp(p, "add server", 10)) {
+ do_normal_submit = process_cmd_add_server(&tx_message, p+10);
+ } else if (!strncmp(p, "add peer", 8)) {
+ do_normal_submit = process_cmd_add_peer(&tx_message, p+8);
+ } else if (!strncmp(p, "delete", 6)) {
+ do_normal_submit = process_cmd_delete(&tx_message, p+6);
+ } else if (!strncmp(p, "writertc", 7)) {
+ process_cmd_writertc(&tx_message, p+7);
+ } else if (!strncmp(p, "rtcdata", 7)) {
+ do_normal_submit = 0;
+ process_cmd_rtcreport(p);
+ } else if (!strncmp(p, "trimrtc", 7)) {
+ process_cmd_trimrtc(&tx_message, p);
+ } else if (!strncmp(p, "cyclelogs", 9)) {
+ process_cmd_cyclelogs(&tx_message, p);
+ } else if (!strncmp(p, "dfreq", 5)) {
+ process_cmd_dfreq(&tx_message, p+5);
+ } else if (!strncmp(p, "doffset", 7)) {
+ process_cmd_doffset(&tx_message, p+7);
+ } else if (!strncmp(p, "tracking", 8)) {
+ process_cmd_tracking(p+8);
+ do_normal_submit = 0;
+ } else if (!strncmp(p, "clients", 7)) {
+ process_cmd_clients(p+7);
+ do_normal_submit = 0;
+ } else if (!strncmp(p, "makestep", 8)) {
+ process_cmd_makestep(&tx_message, p+8);
+ } else if (!strncmp(p, "activity", 8)) {
+ process_cmd_activity(p+8);
+ do_normal_submit = 0;
+ } else if (!strncmp(p, "help", 4)) {
+ do_normal_submit = 0;
+ give_help();
+ } else if (!strncmp(p, "quit", 4)) {
+ do_normal_submit = 0;
+ quit = 1;
+ } else if (!strncmp(p, "exit", 4)) {
+ do_normal_submit = 0;
+ quit = 1;
+ } else {
+ fprintf(stderr, "Unrecognized command\n");
+ do_normal_submit = 0;
+ }
+
+ if (do_normal_submit) {
+
+ request_submitted_ok = submit_request(&tx_message, &rx_message, &reply_auth_ok);
+
+ if (request_submitted_ok) {
+ switch(ntohs(rx_message.status)) {
+ case STT_SUCCESS:
+ printf("200 OK");
+ break;
+ case STT_FAILED:
+ printf("500 Failure");
+ break;
+ case STT_UNAUTH:
+ printf("501 Not authorised");
+ break;
+ case STT_INVALID:
+ printf("502 Invalid command");
+ break;
+ case STT_NOSUCHSOURCE:
+ printf("503 No such source");
+ break;
+ case STT_INVALIDTS:
+ printf("504 Duplicate or stale logon detected");
+ break;
+ case STT_NOTENABLED:
+ printf("505 Facility not enabled in daemon");
+ break;
+ case STT_BADSUBNET:
+ printf("507 Bad subnet");
+ break;
+ case STT_ACCESSALLOWED:
+ printf("208 Access allowed");
+ break;
+ case STT_ACCESSDENIED:
+ printf("209 Access denied");
+ break;
+ case STT_NOHOSTACCESS:
+ printf("510 No command access from this host");
+ break;
+ case STT_SOURCEALREADYKNOWN:
+ printf("511 Source already present");
+ break;
+ case STT_TOOMANYSOURCES:
+ printf("512 Too many sources present");
+ break;
+ case STT_NORTC:
+ printf("513 RTC driver not running");
+ break;
+ case STT_BADRTCFILE:
+ printf("514 Can't write RTC parameters");
+ break;
+ }
+
+ if (reply_auth_ok) {
+ printf("\n");
+ } else {
+ printf(" --- Reply not authenticated\n");
+ }
+ }
+ }
+
+ return quit;
+}
+
+/* ================================================== */
+
+static void
+process_args(int argc, char **argv)
+{
+ int total_length, i;
+ char *line;
+
+ total_length = 0;
+ for(i=0; i<argc; i++) {
+ total_length += strlen(argv[i]);
+ }
+
+ line = (char *) malloc((2 + total_length) * sizeof(char));
+ line[0] = 0;
+ for (i=0; i<argc; i++) {
+ strcat(line, argv[i]);
+ }
+ strcat(line, "\n");
+
+ process_line(line);
+
+ free(line);
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+display_gpl(void)
+{
+ printf("chrony version %s, copyright (C) 1997-2002 Richard P. Curnow\n"
+ "chrony comes with ABSOLUTELY NO WARRANTY.\n"
+ "This is free software, and you are welcome to redistribute it\n"
+ "under certain conditions.\n"
+ "See the GNU General Public License version 2 for details.\n\n",
+ PROGRAM_VERSION_STRING);
+}
+
+/* ================================================== */
+
+int
+main(int argc, char **argv)
+{
+ char *line;
+ const char *progname = argv[0];
+ const char *hostname = "localhost";
+ int quit = 0;
+ int port = DEFAULT_CANDM_PORT;
+
+ /* Parse command line options */
+ while (++argv, --argc) {
+ if (!strcmp(*argv, "-h")) {
+ ++argv, --argc;
+ if (*argv) {
+ hostname = *argv;
+ }
+ } else if (!strcmp(*argv, "-p")) {
+ ++argv, --argc;
+ if (*argv) {
+ port = atoi(*argv);
+ }
+ } else if (!strcmp(*argv, "-n")) {
+ no_dns = 1;
+ } else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
+ printf("chronyc (chrony) version %s\n", PROGRAM_VERSION_STRING);
+ exit(0);
+ } else if (!strncmp(*argv, "-", 1)) {
+ fprintf(stderr, "Usage : %s [-h <hostname>] [-p <port-number>] [-n] [command]\n", progname);
+ exit(1);
+ } else {
+ break; /* And process remainder of line as a command */
+ }
+ }
+
+ if (isatty(0) && isatty(1) && isatty(2)) {
+ on_terminal = 1;
+ }
+
+ if (on_terminal && (argc == 0)) {
+ display_gpl();
+ }
+
+ open_io(hostname, port);
+
+ if (argc > 0) {
+ process_args(argc, argv);
+ } else {
+ do {
+ line = read_line();
+ if (line) {
+ quit = process_line(line);
+ }else {
+ /* supply the final '\n' when user exits via ^D */
+ if( on_terminal ) printf("\n");
+ }
+ } while (line && !quit);
+ }
+
+ close_io();
+
+ return 0;
+}
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/clientlog.c,v 1.10 2003/01/20 22:24:20 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module keeps a count of the number of successful accesses by
+ clients, and the times of the last accesses.
+
+ This can be used for status reporting, and (in the case of a
+ server), if it needs to know which clients have made use of its data
+ recently.
+
+ */
+
+#include "sysincl.h"
+#include "clientlog.h"
+#include "conf.h"
+#include "memory.h"
+#include "reports.h"
+#include "util.h"
+
+/* Number of bits of address per layer of the table. This value has
+ been chosen on the basis that a server will predominantly be serving
+ a lot of hosts in a few subnets, rather than a few hosts scattered
+ across many subnets. */
+
+#define NBITS 8
+
+/* Number of entries in each subtable */
+#define TABLE_SIZE (1UL<<NBITS)
+
+typedef struct _Node {
+ unsigned long ip_addr;
+ unsigned long client_hits;
+ unsigned long peer_hits;
+ unsigned long cmd_hits_bad;
+ unsigned long cmd_hits_normal;
+ unsigned long cmd_hits_auth;
+ time_t last_ntp_hit;
+ time_t last_cmd_hit;
+} Node;
+
+typedef struct _Subnet {
+ void *entry[TABLE_SIZE];
+} Subnet;
+
+/* ================================================== */
+
+/* Table for the class A subnet */
+static Subnet top_subnet;
+
+/* Table containing pointers directly to all nodes that have been
+ allocated. */
+static Node **nodes = NULL;
+
+/* Number of nodes actually in the table. */
+static int n_nodes = 0;
+
+/* Number of entries for which the table has been sized. */
+static int max_nodes = 0;
+
+#define NODE_TABLE_INCREMENT 4
+
+/* Flag indicating whether facility is turned on or not */
+static int active = 0;
+
+/* ================================================== */
+
+static void
+clear_subnet(Subnet *subnet)
+{
+ int i;
+
+ for (i=0; i<TABLE_SIZE; i++) {
+ subnet->entry[i] = NULL;
+ }
+}
+
+/* ================================================== */
+
+static void
+clear_node(Node *node)
+{
+ node->client_hits = 0;
+ node->peer_hits = 0;
+ node->cmd_hits_auth = 0;
+ node->cmd_hits_normal = 0;
+ node->cmd_hits_bad = 0;
+ node->last_ntp_hit = (time_t) 0;
+ node->last_cmd_hit = (time_t) 0;
+}
+
+/* ================================================== */
+
+void
+CLG_Initialise(void)
+{
+ clear_subnet(&top_subnet);
+ if (CNF_GetNoClientLog()) {
+ active = 0;
+ } else {
+ active = 1;
+ }
+
+ nodes = NULL;
+ max_nodes = 0;
+ n_nodes = 0;
+
+}
+
+/* ================================================== */
+
+void
+CLG_Finalise(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+static void
+create_subnet(Subnet *parent_subnet, int the_entry)
+{
+ parent_subnet->entry[the_entry] = (void *) MallocNew(Subnet);
+ clear_subnet((Subnet *) parent_subnet->entry[the_entry]);
+}
+
+/* ================================================== */
+
+static void
+create_node(Subnet *parent_subnet, int the_entry)
+{
+ Node *new_node;
+ new_node = MallocNew(Node);
+ parent_subnet->entry[the_entry] = (void *) new_node;
+ clear_node(new_node);
+
+ if (n_nodes == max_nodes) {
+ if (nodes) {
+ max_nodes += NODE_TABLE_INCREMENT;
+ nodes = ReallocArray(Node *, max_nodes, nodes);
+ } else {
+ if (max_nodes != 0) {
+ CROAK("max_nodes should be 0");
+ }
+ max_nodes = NODE_TABLE_INCREMENT;
+ nodes = MallocArray(Node *, max_nodes);
+ }
+ }
+ nodes[n_nodes++] = (Node *) new_node;
+}
+
+/* ================================================== */
+/* Recursively seek out the Node entry for a particular address,
+ expanding subnet tables and node entries as we go if necessary. */
+
+static void *
+find_subnet(Subnet *subnet, CLG_IP_Addr addr, int bits_left)
+{
+ unsigned long this_subnet, new_subnet, mask, shift;
+ unsigned long new_bits_left;
+
+ shift = 32 - NBITS;
+ mask = (1UL<<shift) - 1;
+ this_subnet = addr >> shift;
+ new_subnet = (addr & mask) << NBITS;
+ new_bits_left = bits_left - NBITS;
+
+#if 0
+ fprintf(stderr, "fs addr=%08lx bl=%d ma=%08lx this=%08lx newsn=%08lx nbl=%d\n",
+ addr, bits_left, mask, this_subnet, new_subnet, new_bits_left);
+#endif
+
+ if (new_bits_left > 0) {
+ if (!subnet->entry[this_subnet]) {
+ create_subnet(subnet, this_subnet);
+ }
+ return find_subnet((Subnet *) subnet->entry[this_subnet], new_subnet, new_bits_left);
+ } else {
+ if (!subnet->entry[this_subnet]) {
+ create_node(subnet, this_subnet);
+ }
+ return subnet->entry[this_subnet];
+ }
+}
+
+
+/* ================================================== */
+/* Search for the record for a particular subnet, but return NULL if
+ one of the parents does not exist - never open a node out */
+
+static void *
+find_subnet_dont_open(Subnet *subnet, CLG_IP_Addr addr, int bits_left)
+{
+ unsigned long this_subnet, new_subnet, mask, shift;
+ unsigned long new_bits_left;
+
+ if (bits_left == 0) {
+ return subnet;
+ } else {
+
+ shift = 32 - NBITS;
+ mask = (1UL<<shift) - 1;
+ this_subnet = addr >> shift;
+ new_subnet = (addr & mask) << NBITS;
+ new_bits_left = bits_left - NBITS;
+
+#if 0
+ fprintf(stderr, "fsdo addr=%08lx bl=%d this=%08lx newsn=%08lx nbl=%d\n",
+ addr, bits_left, this_subnet, new_subnet, new_bits_left);
+#endif
+
+ if (!subnet->entry[this_subnet]) {
+ return NULL;
+ } else {
+ return find_subnet_dont_open((Subnet *) subnet->entry[this_subnet], new_subnet, new_bits_left);
+ }
+ }
+}
+
+/* ================================================== */
+
+void
+CLG_LogNTPClientAccess (CLG_IP_Addr client, time_t now)
+{
+ Node *node;
+ if (active) {
+ node = (Node *) find_subnet(&top_subnet, client, 32);
+ node->ip_addr = client;
+ ++node->client_hits;
+ node->last_ntp_hit = now;
+ }
+}
+
+/* ================================================== */
+
+void
+CLG_LogNTPPeerAccess(CLG_IP_Addr client, time_t now)
+{
+ Node *node;
+ if (active) {
+ node = (Node *) find_subnet(&top_subnet, client, 32);
+ node->ip_addr = client;
+ ++node->peer_hits;
+ node->last_ntp_hit = now;
+ }
+}
+
+/* ================================================== */
+
+void
+CLG_LogCommandAccess(CLG_IP_Addr client, CLG_Command_Type type, time_t now)
+{
+ Node *node;
+ if (active) {
+ node = (Node *) find_subnet(&top_subnet, client, 32);
+ node->ip_addr = client;
+ node->last_cmd_hit = now;
+ switch (type) {
+ case CLG_CMD_AUTH:
+ ++node->cmd_hits_auth;
+ break;
+ case CLG_CMD_NORMAL:
+ ++node->cmd_hits_normal;
+ break;
+ case CLG_CMD_BAD_PKT:
+ ++node->cmd_hits_bad;
+ break;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+ }
+}
+
+/* ================================================== */
+
+CLG_Status
+CLG_GetSubnetBitmap(CLG_IP_Addr subnet, int bits, CLG_Bitmap result)
+{
+ Subnet *s;
+ unsigned long i;
+ unsigned long word, bit, mask;
+
+ if ((bits == 0) || (bits == 8) || (bits == 16) || (bits == 24)) {
+ memset (result, 0, TABLE_SIZE/8);
+ if (active) {
+ s = find_subnet_dont_open(&top_subnet, subnet, bits);
+ if (s) {
+ for (i=0; i<256; i++) {
+ if (s->entry[i]) {
+ word = i / 32;
+ bit = i % 32;
+ mask = 1UL << bit;
+ result[word] |= mask;
+ }
+ }
+ return CLG_SUCCESS;
+ } else {
+ return CLG_EMPTYSUBNET;
+ }
+ } else {
+ return CLG_INACTIVE;
+ }
+ } else {
+ return CLG_BADSUBNET;
+ }
+}
+
+/* ================================================== */
+
+CLG_Status
+CLG_GetClientAccessReportByIP(unsigned long ip, RPT_ClientAccess_Report *report, time_t now)
+{
+ Node *node;
+
+ if (!active) {
+ return CLG_INACTIVE;
+ } else {
+ node = (Node *) find_subnet_dont_open(&top_subnet, ip, 32);
+
+ if (!node) {
+ return CLG_EMPTYSUBNET;
+ } else {
+ report->client_hits = node->client_hits;
+ report->peer_hits = node->peer_hits;
+ report->cmd_hits_auth = node->cmd_hits_auth;
+ report->cmd_hits_normal = node->cmd_hits_normal;
+ report->cmd_hits_bad = node->cmd_hits_bad;
+ report->last_ntp_hit_ago = now - node->last_ntp_hit;
+ report->last_cmd_hit_ago = now - node->last_cmd_hit;
+
+ return CLG_SUCCESS;
+ }
+ }
+}
+
+/* ================================================== */
+
+CLG_Status
+CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
+ time_t now, unsigned long *n_indices)
+{
+ Node *node;
+
+ *n_indices = n_nodes;
+
+ if (!active) {
+ return CLG_INACTIVE;
+ } else {
+
+ if ((index < 0) || (index >= n_nodes)) {
+ return CLG_INDEXTOOLARGE;
+ }
+
+ node = nodes[index];
+
+ report->ip_addr = node->ip_addr;
+ report->client_hits = node->client_hits;
+ report->peer_hits = node->peer_hits;
+ report->cmd_hits_auth = node->cmd_hits_auth;
+ report->cmd_hits_normal = node->cmd_hits_normal;
+ report->cmd_hits_bad = node->cmd_hits_bad;
+ report->last_ntp_hit_ago = now - node->last_ntp_hit;
+ report->last_cmd_hit_ago = now - node->last_cmd_hit;
+
+ return CLG_SUCCESS;
+ }
+
+}
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/clientlog.h,v 1.8 2003/04/01 20:54:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module contains facilities for logging access by clients.
+
+ */
+
+#ifndef GOT_CLIENTLOG_H
+#define GOT_CLIENTLOG_H
+
+#include "sysincl.h"
+#include "reports.h"
+
+typedef unsigned long CLG_IP_Addr;
+
+/* Enough to hold flags for 256 hosts in a class C */
+typedef uint32_t CLG_Bitmap[8];
+
+extern void CLG_Initialise(void);
+extern void CLG_Finalise(void);
+extern void CLG_LogNTPClientAccess(CLG_IP_Addr client, time_t now);
+extern void CLG_LogNTPPeerAccess(CLG_IP_Addr client, time_t now);
+
+/* When logging command packets, there are several subtypes */
+
+typedef enum {
+ CLG_CMD_AUTH, /* authenticated */
+ CLG_CMD_NORMAL, /* normal */
+ CLG_CMD_BAD_PKT /* bad version or packet length */
+} CLG_Command_Type;
+
+extern void CLG_LogCommandAccess(CLG_IP_Addr client, CLG_Command_Type type, time_t now);
+
+/* And some reporting functions, for use by chronyc. */
+/* TBD */
+
+typedef enum {
+ CLG_SUCCESS, /* All is well */
+ CLG_EMPTYSUBNET, /* No hosts logged in requested subnet */
+ CLG_BADSUBNET, /* Subnet requested is not 0, 8, 16 or 24 bits */
+ CLG_INACTIVE, /* Facility not active */
+ CLG_INDEXTOOLARGE /* Node index is higher than number of nodes present */
+} CLG_Status;
+
+/* For bits=0, 8, 16, flag which immediate subnets of that subnet are
+ known. For bits=24, flag which hosts in that subnet are known.
+ Other values, return 0 (failed) */
+
+extern CLG_Status CLG_GetSubnetBitmap(CLG_IP_Addr subnet, int bits, CLG_Bitmap result);
+
+extern CLG_Status
+CLG_GetClientAccessReportByIP(unsigned long ip, RPT_ClientAccess_Report *report, time_t now);
+
+CLG_Status
+CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
+ time_t now, unsigned long *n_indices);
+
+/* And an iterating function, to call 'fn' for each client or peer
+ that has accessed us since 'since'. */
+
+extern void CLG_IterateNTPClients
+(void (*fn)(CLG_IP_Addr client, void *arb),
+ void *arb,
+ time_t since);
+
+
+#endif /* GOT_CLIENTLOG_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/cmdmon.c,v 1.54 2003/04/01 20:54:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Command and monitoring module in the main program
+ */
+
+#include "sysincl.h"
+
+#include "cmdmon.h"
+#include "candm.h"
+#include "sched.h"
+#include "util.h"
+#include "logging.h"
+#include "md5.h"
+#include "keys.h"
+#include "ntp_sources.h"
+#include "ntp_core.h"
+#include "sources.h"
+#include "sourcestats.h"
+#include "reference.h"
+#include "manual.h"
+#include "memory.h"
+#include "local.h"
+#include "addrfilt.h"
+#include "conf.h"
+#include "rtc.h"
+#include "pktlength.h"
+#include "clientlog.h"
+
+/* ================================================== */
+
+/* File descriptor for command and monitoring socket */
+static int sock_fd;
+
+/* Flag indicating whether this module has been initialised or not */
+static int initialised = 0;
+
+/* Token which is unique every time the daemon is run */
+static unsigned long utoken;
+
+/* The register of issued tokens */
+static unsigned long issued_tokens;
+
+/* The register of received tokens */
+static unsigned long returned_tokens;
+
+/* The token number corresponding to the base of the registers */
+static unsigned long token_base;
+
+/* The position of the next free token to issue in the issue register */
+static unsigned long issue_pointer;
+
+/* Type and linked list for buffering responses */
+typedef struct _ResponseCell {
+ struct _ResponseCell *next;
+ unsigned long tok; /* The token that the client sent in the message
+ to which this was the reply */
+ unsigned long next_tok; /* The next token issued to the same client.
+ If we receive a request with this token,
+ it implies the reply stored in this cell
+ was successfully received */
+ unsigned long msg_seq; /* Client's sequence number used in request
+ to which this is the response. */
+ unsigned long attempt; /* Attempt number that we saw in the last request
+ with this sequence number (prevents attacker
+ firing the same request at us to make us
+ keep generating the same reply). */
+ struct timeval ts; /* Time we saved the reply - allows purging based
+ on staleness. */
+ CMD_Reply rpy;
+} ResponseCell;
+
+static ResponseCell kept_replies;
+static ResponseCell *free_replies;
+
+/* ================================================== */
+/* Array of permission levels for command types */
+
+static int permissions[] = {
+ PERMIT_OPEN, /* NULL */
+ PERMIT_AUTH, /* ONLINE */
+ PERMIT_AUTH, /* OFFLINE */
+ PERMIT_AUTH, /* BURST */
+ PERMIT_AUTH, /* MODIFY_MINPOLL */
+ PERMIT_AUTH, /* MODIFY_MAXPOLL */
+ PERMIT_AUTH, /* DUMP */
+ PERMIT_AUTH, /* MODIFY_MAXDELAY */
+ PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */
+ PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */
+ PERMIT_OPEN, /* LOGON */
+ PERMIT_AUTH, /* SETTIME */
+ PERMIT_AUTH, /* LOCAL */
+ PERMIT_AUTH, /* MANUAL */
+ PERMIT_OPEN, /* N_SOURCES */
+ PERMIT_OPEN, /* SOURCE_DATA */
+ PERMIT_AUTH, /* REKEY */
+ PERMIT_AUTH, /* ALLOW */
+ PERMIT_AUTH, /* ALLOWALL */
+ PERMIT_AUTH, /* DENY */
+ PERMIT_AUTH, /* DENYALL */
+ PERMIT_AUTH, /* CMDALLOW */
+ PERMIT_AUTH, /* CMDALLOWALL */
+ PERMIT_AUTH, /* CMDDENY */
+ PERMIT_AUTH, /* CMDDENYALL */
+ PERMIT_AUTH, /* ACCHECK */
+ PERMIT_AUTH, /* CMDACCHECK */
+ PERMIT_AUTH, /* ADD_SERVER */
+ PERMIT_AUTH, /* ADD_PEER */
+ PERMIT_AUTH, /* DEL_SOURCE */
+ PERMIT_AUTH, /* WRITERTC */
+ PERMIT_AUTH, /* DFREQ */
+ PERMIT_AUTH, /* DOFFSET */
+ PERMIT_OPEN, /* TRACKING */
+ PERMIT_OPEN, /* SOURCESTATS */
+ PERMIT_OPEN, /* RTCREPORT */
+ PERMIT_AUTH, /* TRIMRTC */
+ PERMIT_AUTH, /* CYCLELOGS */
+ PERMIT_OPEN, /* SUBNETS_ACCESSED */
+ PERMIT_OPEN, /* CLIENT_ACCESSES (by subnet) */
+ PERMIT_OPEN, /* CLIENT_ACCESSES_BY_INDEX */
+ PERMIT_OPEN, /* MANUAL_LIST */
+ PERMIT_AUTH, /* MANUAL_DELETE */
+ PERMIT_AUTH, /* MAKESTEP */
+ PERMIT_OPEN /* ACTIVITY */
+};
+
+/* ================================================== */
+
+/* This authorisation table is used for checking whether particular
+ machines are allowed to make command and monitoring requests. */
+static ADF_AuthTable access_auth_table;
+
+/* ================================================== */
+/* Forward prototypes */
+static void read_from_cmd_socket(void *anything);
+
+/* ================================================== */
+
+void
+CAM_Initialise(void)
+{
+ int port_number;
+ struct sockaddr_in my_addr;
+ unsigned long bind_address;
+ int on_off;
+
+ if (initialised) {
+ CROAK("Shouldn't be initialised");
+ }
+
+ initialised = 1;
+
+ if ((sizeof(permissions)/sizeof(permissions[0])) != N_REQUEST_TYPES) {
+ CROAK("Permissions table size wrong");
+ }
+
+ utoken = (unsigned long) time(NULL);
+
+ issued_tokens = returned_tokens = issue_pointer = 0;
+ token_base = 1; /* zero is the value used when the previous command was
+ unauthenticated */
+
+ free_replies = NULL;
+ kept_replies.next = NULL;
+
+ port_number = CNF_GetCommandPort();
+ if (port_number < 0) {
+ port_number = DEFAULT_CANDM_PORT;
+ }
+
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0) {
+ LOG_FATAL(LOGF_CmdMon, "Could not open socket : %s", strerror(errno));
+ }
+
+ /* Allow reuse of port number */
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
+ LOG(LOGS_ERR, LOGF_CmdMon, "Could not set socket options");
+ /* Don't quit - we might survive anyway */
+ }
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons((unsigned short) port_number);
+
+ CNF_GetBindCommandAddress(&bind_address);
+
+ if (bind_address != 0UL) {
+ my_addr.sin_addr.s_addr = htonl(bind_address);
+ } else {
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+
+ if (bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ LOG_FATAL(LOGF_CmdMon, "Could not bind socket : %s", strerror(errno));
+ }
+
+ /* Register handler for read events on the socket */
+ SCH_AddInputFileHandler(sock_fd, read_from_cmd_socket, NULL);
+
+ access_auth_table = ADF_CreateTable();
+
+}
+
+/* ================================================== */
+
+void
+CAM_Finalise(void)
+{
+ SCH_RemoveInputFileHandler(sock_fd);
+ close(sock_fd);
+ sock_fd = -1;
+
+ ADF_DestroyTable(access_auth_table);
+
+ initialised = 0;
+ return;
+}
+
+/* ================================================== */
+/* This function checks whether the authenticator field of the packet
+ checks correctly against what we would compute locally given the
+ rest of the packet */
+
+static int
+check_rx_packet_auth(CMD_Request *packet)
+{
+
+ char *key;
+ int keylen;
+ int pkt_len;
+ MD5_CTX ctx;
+
+ pkt_len = PKL_CommandLength(packet);
+
+ KEY_CommandKey(&key, &keylen);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *) key, keylen);
+ MD5Update(&ctx, (unsigned char *) packet, offsetof(CMD_Request, auth));
+ if (pkt_len > offsetof(CMD_Request, data)) {
+ MD5Update(&ctx, (unsigned char *) &(packet->data), pkt_len - offsetof(CMD_Request, data));
+ }
+ MD5Final(&ctx);
+
+ if (!memcmp((void *) &ctx.digest, (void *) &(packet->auth), 16)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+generate_tx_packet_auth(CMD_Reply *packet)
+{
+ char *key;
+ int keylen;
+ MD5_CTX ctx;
+ int pkt_len;
+
+ pkt_len = PKL_ReplyLength(packet);
+
+ KEY_CommandKey(&key, &keylen);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *) key, keylen);
+ MD5Update(&ctx, (unsigned char *) packet, offsetof(CMD_Request, auth));
+ if (pkt_len > offsetof(CMD_Reply, data)) {
+ MD5Update(&ctx, (unsigned char *) &(packet->data), pkt_len - offsetof(CMD_Reply, data));
+ }
+ MD5Final(&ctx);
+
+ memcpy(&(packet->auth), &ctx.digest, 16);
+
+}
+
+/* ================================================== */
+
+static void
+shift_tokens(void)
+{
+ do {
+ issued_tokens >>= 1;
+ returned_tokens >>= 1;
+ token_base++;
+ issue_pointer--;
+ } while ((issued_tokens & 1) && (returned_tokens & 1));
+}
+
+/* ================================================== */
+
+static unsigned long
+get_token(void)
+{
+ unsigned long result;
+
+ if (issue_pointer == 32) {
+ /* The lowest number open token has not been returned - bad luck
+ to that command client */
+ shift_tokens();
+ }
+
+ result = token_base + issue_pointer;
+ issued_tokens |= (1UL << issue_pointer);
+ issue_pointer++;
+
+ return result;
+}
+
+/* ================================================== */
+
+static int
+check_token(unsigned long token)
+{
+ int result;
+ unsigned long pos;
+
+ if (token < token_base) {
+ /* Token too old */
+ result = 0;
+ } else {
+ pos = token - token_base;
+ if (pos >= issue_pointer) {
+ /* Token hasn't been issued yet */
+ result = 0;
+ } else {
+ if (returned_tokens & (1UL << pos)) {
+ /* Token has already been returned */
+ result = 0;
+ } else {
+ /* Token is OK */
+ result = 1;
+ returned_tokens |= (1UL << pos);
+ if (pos == 0) {
+ shift_tokens();
+ }
+ }
+ }
+ }
+
+ return result;
+
+}
+
+/* ================================================== */
+
+#define TS_MARGIN 20
+
+/* ================================================== */
+
+typedef struct _TimestampCell {
+ struct _TimestampCell *next;
+ struct timeval ts;
+} TimestampCell;
+
+static struct _TimestampCell seen_ts_list={NULL};
+static struct _TimestampCell *free_ts_list=NULL;
+
+#define EXTEND_QUANTUM 32
+
+/* ================================================== */
+
+static TimestampCell *
+allocate_ts_cell(void)
+{
+ TimestampCell *result;
+ int i;
+ if (free_ts_list == NULL) {
+ free_ts_list = MallocArray(TimestampCell, EXTEND_QUANTUM);
+ for (i=0; i<EXTEND_QUANTUM-1; i++) {
+ free_ts_list[i].next = free_ts_list + i + 1;
+ }
+ free_ts_list[EXTEND_QUANTUM - 1].next = NULL;
+ }
+
+ result = free_ts_list;
+ free_ts_list = free_ts_list->next;
+ return result;
+}
+
+/* ================================================== */
+
+static void
+release_ts_cell(TimestampCell *node)
+{
+ node->next = free_ts_list;
+ free_ts_list = node;
+}
+
+/* ================================================== */
+/* Return 1 if not found, 0 if found (i.e. not unique). Prune out any
+ stale entries. */
+
+static int
+check_unique_ts(struct timeval *ts, struct timeval *now)
+{
+ TimestampCell *last_valid, *cell, *next;
+ int ok;
+
+ ok = 1;
+ last_valid = &(seen_ts_list);
+ cell = last_valid->next;
+
+ while (cell) {
+ next = cell->next;
+ /* Check if stale */
+ if ((now->tv_sec - cell->ts.tv_sec) > TS_MARGIN) {
+ release_ts_cell(cell);
+ last_valid->next = next;
+ } else {
+ /* Timestamp in cell is still within window */
+ last_valid->next = cell;
+ last_valid = cell;
+ if ((cell->ts.tv_sec == ts->tv_sec) && (cell->ts.tv_usec == ts->tv_usec)) {
+ ok = 0;
+ }
+ }
+ cell = next;
+ }
+
+ if (ok) {
+ /* Need to add this timestamp to the list */
+ cell = allocate_ts_cell();
+ last_valid->next = cell;
+ cell->next = NULL;
+ cell->ts = *ts;
+ }
+
+ return ok;
+}
+
+/* ================================================== */
+
+static int
+ts_is_unique_and_not_stale(struct timeval *ts, struct timeval *now)
+{
+ long tv_sec;
+ struct timeval host_order_ts;
+ int within_margin=0;
+ int is_unique=0;
+ long diff;
+
+ host_order_ts.tv_sec = tv_sec = ntohl(ts->tv_sec);
+ host_order_ts.tv_usec = ntohl(ts->tv_usec);
+
+ diff = now->tv_sec - tv_sec;
+ if ((diff < TS_MARGIN) && (diff > -TS_MARGIN)) {
+ within_margin = 1;
+ } else {
+ within_margin = 0;
+ }
+ is_unique = check_unique_ts(&host_order_ts, now);
+
+ return within_margin && is_unique;
+}
+
+/* ================================================== */
+
+#define REPLY_EXTEND_QUANTUM 32
+
+static void
+get_more_replies(void)
+{
+ ResponseCell *new_replies;
+ int i;
+
+ if (!free_replies) {
+ new_replies = MallocArray(ResponseCell, REPLY_EXTEND_QUANTUM);
+ for (i=1; i<REPLY_EXTEND_QUANTUM; i++) {
+ new_replies[i-1].next = new_replies + i;
+ }
+ free_replies = new_replies;
+ }
+}
+
+/* ================================================== */
+
+static ResponseCell *
+get_reply_slot(void)
+{
+ ResponseCell *result;
+ if (!free_replies) {
+ get_more_replies();
+ }
+ result = free_replies;
+ free_replies = result->next;
+ return result;
+}
+
+/* ================================================== */
+
+static void
+free_reply_slot(ResponseCell *cell)
+{
+ cell->next = free_replies;
+ free_replies = cell;
+}
+
+/* ================================================== */
+
+static void
+save_reply(CMD_Reply *msg,
+ unsigned long tok_reply_to,
+ unsigned long new_tok_issued,
+ unsigned long client_msg_seq,
+ unsigned short attempt,
+ struct timeval *now)
+{
+ ResponseCell *cell;
+
+ cell = get_reply_slot();
+
+ cell->ts = *now;
+ memcpy(&cell->rpy, msg, sizeof(CMD_Reply));
+ cell->tok = tok_reply_to;
+ cell->next_tok = new_tok_issued;
+ cell->msg_seq = client_msg_seq;
+ cell->attempt = (unsigned long) attempt;
+
+ cell->next = kept_replies.next;
+ kept_replies.next = cell;
+
+}
+
+/* ================================================== */
+
+static CMD_Reply *
+lookup_reply(unsigned long prev_msg_token, unsigned long client_msg_seq, unsigned short attempt)
+{
+ ResponseCell *ptr;
+
+ ptr = kept_replies.next;
+ while (ptr) {
+ if ((ptr->tok == prev_msg_token) &&
+ (ptr->msg_seq == client_msg_seq) &&
+ ((unsigned long) attempt > ptr->attempt)) {
+
+ /* Set the attempt field to remember the highest number we have
+ had so far */
+ ptr->attempt = (unsigned long) attempt;
+ return &ptr->rpy;
+ }
+ ptr = ptr->next;
+ }
+
+ return NULL;
+}
+
+
+/* ================================================== */
+
+#define REPLY_MAXAGE 300
+
+static void
+token_acknowledged(unsigned long token, struct timeval *now)
+{
+ ResponseCell *last_valid, *cell, *next;
+
+ last_valid = &kept_replies;
+ cell = kept_replies.next;
+
+ while(cell) {
+ next = cell->next;
+
+ /* Discard if it's the one or if the reply is stale */
+ if ((cell->next_tok == token) ||
+ ((now->tv_sec - cell->ts.tv_sec) > REPLY_MAXAGE)) {
+ free_reply_slot(cell);
+ last_valid->next = next;
+ } else {
+ last_valid->next = cell;
+ last_valid = cell;
+ }
+ cell = next;
+ }
+}
+
+/* ================================================== */
+
+#if 0
+/* These two routines are not legal if the program is operating as a daemon, since
+ stderr is no longer open */
+
+static void
+print_command_packet(CMD_Request *pkt, int length)
+{
+ unsigned char *x;
+ int i;
+ x = (unsigned char *) pkt;
+ for (i=0; i<length; i++) {
+ fprintf(stderr, "%02x ", x[i]);
+ if (i%16 == 15) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+/* ================================================== */
+
+static void
+print_reply_packet(CMD_Reply *pkt)
+{
+ unsigned char *x;
+ int i;
+ x = (unsigned char *) pkt;
+ for (i=0; i<sizeof(CMD_Reply); i++) {
+ fprintf(stderr, "%02x ", x[i]);
+ if (i%16 == 15) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+#endif
+
+/* ================================================== */
+
+static void
+transmit_reply(CMD_Reply *msg, struct sockaddr_in *where_to)
+{
+ int status;
+ int tx_message_length;
+ unsigned long remote_ip;
+ unsigned short remote_port;
+
+ tx_message_length = PKL_ReplyLength(msg);
+ status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
+ (struct sockaddr *) where_to, sizeof(struct sockaddr_in));
+
+ if (status < 0) {
+ remote_ip = ntohl(where_to->sin_addr.s_addr);
+ remote_port = ntohs(where_to->sin_port);
+ LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToDottedQuad(remote_ip), remote_port);
+ }
+
+ return;
+}
+
+
+/* ================================================== */
+
+static void
+handle_null(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ tx_message->status = htons(STT_SUCCESS);
+}
+
+/* ================================================== */
+
+static void
+handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_TakeSourcesOnline(ntohl(rx_message->data.online.mask), ntohl(rx_message->data.online.address));
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_TakeSourcesOffline(ntohl(rx_message->data.offline.mask), ntohl(rx_message->data.offline.address));
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_InitiateSampleBurst(ntohl(rx_message->data.burst.n_good_samples),
+ ntohl(rx_message->data.burst.n_total_samples),
+ ntohl(rx_message->data.burst.mask),
+ ntohl(rx_message->data.burst.address));
+
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_modify_minpoll(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_ModifyMinpoll(ntohl(rx_message->data.modify_minpoll.address),
+ ntohl(rx_message->data.modify_minpoll.new_minpoll));
+
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_modify_maxpoll(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_ModifyMaxpoll(ntohl(rx_message->data.modify_minpoll.address),
+ ntohl(rx_message->data.modify_minpoll.new_minpoll));
+
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_modify_maxdelay(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_ModifyMaxdelay(ntohl(rx_message->data.modify_maxdelay.address),
+ WIRE2REAL(rx_message->data.modify_maxdelay.new_max_delay));
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_modify_maxdelayratio(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = NSR_ModifyMaxdelayratio(ntohl(rx_message->data.modify_maxdelayratio.address),
+ WIRE2REAL(rx_message->data.modify_maxdelayratio.new_max_delay_ratio));
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_modify_maxupdateskew(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ REF_ModifyMaxupdateskew(WIRE2REAL(rx_message->data.modify_maxupdateskew.new_max_update_skew));
+ tx_message->status = htons(STT_SUCCESS);
+}
+
+/* ================================================== */
+
+static void
+handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ struct timeval ts;
+ long offset_cs;
+ double dfreq_ppm, new_afreq_ppm;
+ ts.tv_sec = ntohl(rx_message->data.settime.ts.tv_sec);
+ ts.tv_usec = ntohl(rx_message->data.settime.ts.tv_usec);
+ if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_MANUAL_TIMESTAMP);
+ tx_message->data.manual_timestamp.centiseconds = htonl(offset_cs);
+ tx_message->data.manual_timestamp.dfreq_ppm = REAL2WIRE(dfreq_ppm);
+ tx_message->data.manual_timestamp.new_afreq_ppm = REAL2WIRE(new_afreq_ppm);
+ } else {
+ tx_message->status = htons(STT_NOTENABLED);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int on_off, stratum;
+ on_off = ntohl(rx_message->data.local.on_off);
+ if (on_off) {
+ stratum = ntohl(rx_message->data.local.stratum);
+ REF_EnableLocal(stratum);
+ } else {
+ REF_DisableLocal();
+ }
+ tx_message->status = htons(STT_SUCCESS);
+}
+
+/* ================================================== */
+
+static void
+handle_manual(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int option;
+ option = ntohl(rx_message->data.manual.option);
+ switch (option) {
+ case 0:
+ MNL_Disable();
+ break;
+ case 1:
+ MNL_Enable();
+ break;
+ case 2:
+ MNL_Reset();
+ break;
+ }
+ tx_message->status = htons(STT_SUCCESS);
+}
+
+/* ================================================== */
+
+static void
+handle_n_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int n_sources;
+ n_sources = SRC_ReadNumberOfSources();
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_N_SOURCES);
+ tx_message->data.n_sources.n_sources = htonl(n_sources);
+}
+
+/* ================================================== */
+
+static void
+handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ RPT_SourceReport report;
+ struct timeval now_corr;
+ double local_clock_err;
+
+ /* Get data */
+ LCL_ReadCookedTime(&now_corr, &local_clock_err);
+ if (SRC_ReportSource(ntohl(rx_message->data.source_data.index), &report, &now_corr)) {
+ NSR_ReportSource(&report, &now_corr);
+
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_SOURCE_DATA);
+
+ tx_message->data.source_data.ip_addr = htonl(report.ip_addr);
+ tx_message->data.source_data.stratum = htons(report.stratum);
+ tx_message->data.source_data.poll = htons(report.poll);
+ switch (report.state) {
+ case RPT_SYNC:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC);
+ break;
+ case RPT_UNREACH:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH);
+ break;
+ case RPT_FALSETICKER:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER);
+ break;
+ case RPT_JITTERY:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY);
+ break;
+ case RPT_OTHER:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_OTHER);
+ break;
+ }
+ switch (report.mode) {
+ case RPT_NTP_CLIENT:
+ tx_message->data.source_data.mode = htons(RPY_SD_MD_CLIENT);
+ break;
+ case RPT_NTP_PEER:
+ tx_message->data.source_data.mode = htons(RPY_SD_MD_PEER);
+ break;
+ case RPT_LOCAL_REFERENCE:
+ tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
+ break;
+ }
+ tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
+ tx_message->data.source_data.orig_latest_meas = htonl(report.orig_latest_meas);
+ tx_message->data.source_data.latest_meas = htonl(report.latest_meas);
+ tx_message->data.source_data.latest_meas_err = htonl(report.latest_meas_err);
+ tx_message->data.source_data.est_offset = htonl(report.est_offset);
+ tx_message->data.source_data.est_offset_err = htonl(report.est_offset_err);
+ tx_message->data.source_data.resid_freq = htonl(report.resid_freq);
+ tx_message->data.source_data.resid_skew = htonl(report.resid_skew);
+
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ tx_message->status = htons(STT_SUCCESS);
+ KEY_Reload();
+}
+
+/* ================================================== */
+
+static void
+handle_allow(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (NCR_AddAccessRestriction(ip, subnet_bits, 1, 0)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_allowall(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (NCR_AddAccessRestriction(ip, subnet_bits, 1, 1)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_deny(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (NCR_AddAccessRestriction(ip, subnet_bits, 0, 0)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_denyall(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (NCR_AddAccessRestriction(ip, subnet_bits, 0, 1)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_cmdallow(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (CAM_AddAccessRestriction(ip, subnet_bits, 1, 0)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_cmdallowall(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (CAM_AddAccessRestriction(ip, subnet_bits, 1, 1)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_cmddeny(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (CAM_AddAccessRestriction(ip, subnet_bits, 0, 0)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_cmddenyall(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ int subnet_bits;
+ ip = ntohl(rx_message->data.allow_deny.ip);
+ subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
+ if (CAM_AddAccessRestriction(ip, subnet_bits, 0, 1)) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_BADSUBNET);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_accheck(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ ip = ntohl(rx_message->data.ac_check.ip);
+ if (NCR_CheckAccessRestriction(ip)) {
+ tx_message->status = htons(STT_ACCESSALLOWED);
+ } else {
+ tx_message->status = htons(STT_ACCESSDENIED);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ unsigned long ip;
+ ip = ntohl(rx_message->data.ac_check.ip);
+ if (CAM_CheckAccessRestriction(ip)) {
+ tx_message->status = htons(STT_ACCESSALLOWED);
+ } else {
+ tx_message->status = htons(STT_ACCESSDENIED);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_add_server(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ NTP_Remote_Address rem_addr;
+ SourceParameters params;
+ NSR_Status status;
+
+ rem_addr.ip_addr = ntohl(rx_message->data.ntp_source.ip_addr);
+ rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port));
+ params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
+ params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
+ params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
+ params.authkey = ntohl(rx_message->data.ntp_source.authkey);
+ params.online = ntohl(rx_message->data.ntp_source.online);
+ params.auto_offline = ntohl(rx_message->data.ntp_source.auto_offline);
+ params.max_delay = WIRE2REAL(rx_message->data.ntp_source.max_delay);
+ params.max_delay_ratio = WIRE2REAL(rx_message->data.ntp_source.max_delay_ratio);
+ status = NSR_AddServer(&rem_addr, ¶ms);
+ switch (status) {
+ case NSR_Success:
+ tx_message->status = htons(STT_SUCCESS);
+ break;
+ case NSR_AlreadyInUse:
+ tx_message->status = htons(STT_SOURCEALREADYKNOWN);
+ break;
+ case NSR_TooManySources:
+ tx_message->status = htons(STT_TOOMANYSOURCES);
+ break;
+ case NSR_NoSuchSource:
+ CROAK("Impossible");
+ break;
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_add_peer(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ NTP_Remote_Address rem_addr;
+ SourceParameters params;
+ NSR_Status status;
+
+ rem_addr.ip_addr = ntohl(rx_message->data.ntp_source.ip_addr);
+ rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port));
+ params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
+ params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
+ params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
+ params.authkey = ntohl(rx_message->data.ntp_source.authkey);
+ params.online = ntohl(rx_message->data.ntp_source.online);
+ params.max_delay = WIRE2REAL(rx_message->data.ntp_source.max_delay);
+ params.max_delay_ratio = WIRE2REAL(rx_message->data.ntp_source.max_delay_ratio);
+ status = NSR_AddPeer(&rem_addr, ¶ms);
+ switch (status) {
+ case NSR_Success:
+ tx_message->status = htons(STT_SUCCESS);
+ break;
+ case NSR_AlreadyInUse:
+ tx_message->status = htons(STT_SOURCEALREADYKNOWN);
+ break;
+ case NSR_TooManySources:
+ tx_message->status = htons(STT_TOOMANYSOURCES);
+ break;
+ case NSR_NoSuchSource:
+ CROAK("Impossible");
+ break;
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ NTP_Remote_Address rem_addr;
+ NSR_Status status;
+
+ rem_addr.ip_addr = ntohl(rx_message->data.del_source.ip_addr);
+ rem_addr.port = 0;
+
+ status = NSR_RemoveSource(&rem_addr);
+ switch (status) {
+ case NSR_Success:
+ tx_message->status = htons(STT_SUCCESS);
+ break;
+ case NSR_NoSuchSource:
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ break;
+ case NSR_TooManySources:
+ case NSR_AlreadyInUse:
+ CROAK("Impossible");
+ break;
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_writertc(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ switch (RTC_WriteParameters()) {
+ case RTC_ST_OK:
+ tx_message->status = htons(STT_SUCCESS);
+ break;
+ case RTC_ST_NODRV:
+ tx_message->status = htons(STT_NORTC);
+ break;
+ case RTC_ST_BADFILE:
+ tx_message->status = htons(STT_BADRTCFILE);
+ break;
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ double dfreq;
+ dfreq = WIRE2REAL(rx_message->data.dfreq.dfreq);
+ LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6);
+ LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq);
+}
+
+/* ================================================== */
+
+static void
+handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ long sec, usec;
+ double doffset;
+ sec = (long)(ntohl(rx_message->data.doffset.sec));
+ usec = (long)(ntohl(rx_message->data.doffset.usec));
+ doffset = (double) sec + 1.0e-6 * (double) usec;
+ LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
+ LCL_AccumulateOffset(doffset);
+}
+
+/* ================================================== */
+
+static void
+handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ RPT_TrackingReport rpt;
+
+ REF_GetTrackingReport(&rpt);
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_TRACKING);
+ tx_message->data.tracking.ref_id = htonl(rpt.ref_id);
+ tx_message->data.tracking.stratum = htonl(rpt.stratum);
+ tx_message->data.tracking.ref_time_s = htonl(rpt.ref_time.tv_sec);
+ tx_message->data.tracking.ref_time_us = htonl(rpt.ref_time.tv_usec);
+ tx_message->data.tracking.current_correction_s = htonl(rpt.current_correction.tv_sec);
+ tx_message->data.tracking.current_correction_us = htonl(rpt.current_correction.tv_usec);
+ tx_message->data.tracking.freq_ppm = REAL2WIRE(rpt.freq_ppm);
+ tx_message->data.tracking.resid_freq_ppm = REAL2WIRE(rpt.resid_freq_ppm);
+ tx_message->data.tracking.skew_ppm = REAL2WIRE(rpt.skew_ppm);
+ tx_message->data.tracking.root_delay = REAL2WIRE(rpt.root_delay);
+ tx_message->data.tracking.root_dispersion = REAL2WIRE(rpt.root_dispersion);
+}
+
+/* ================================================== */
+
+static void
+handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ RPT_SourcestatsReport report;
+ status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index),
+ &report);
+
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_SOURCESTATS);
+ tx_message->data.sourcestats.ip_addr = htonl(report.ip_addr);
+ tx_message->data.sourcestats.n_samples = htonl(report.n_samples);
+ tx_message->data.sourcestats.n_runs = htonl(report.n_runs);
+ tx_message->data.sourcestats.span_seconds = htonl(report.span_seconds);
+ tx_message->data.sourcestats.resid_freq_ppm = REAL2WIRE(report.resid_freq_ppm);
+ tx_message->data.sourcestats.skew_ppm = REAL2WIRE(report.skew_ppm);
+ tx_message->data.sourcestats.sd_us = htonl((unsigned long) (0.5 + report.sd_us));
+ } else {
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ RPT_RTC_Report report;
+ status = RTC_GetReport(&report);
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_RTC);
+ tx_message->data.rtc.ref_time = htonl(report.ref_time);
+ tx_message->data.rtc.n_samples = htons(report.n_samples);
+ tx_message->data.rtc.n_runs = htons(report.n_runs);
+ tx_message->data.rtc.span_seconds = htonl(report.span_seconds);
+ tx_message->data.rtc.rtc_seconds_fast = REAL2WIRE(report.rtc_seconds_fast);
+ tx_message->data.rtc.rtc_gain_rate_ppm = REAL2WIRE(report.rtc_gain_rate_ppm);
+ } else {
+ tx_message->status = htons(STT_NORTC);
+ }
+ return;
+}
+
+/* ================================================== */
+
+static void
+handle_trimrtc(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = RTC_Trim();
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NORTC);
+ }
+ return;
+}
+
+/* ================================================== */
+
+static void
+handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ NCR_CycleLogFile();
+ SST_CycleLogFile();
+ REF_CycleLogFile();
+ RTC_CycleLogFile();
+
+ tx_message->status = htons(STT_SUCCESS);
+ return;
+}
+
+/* ================================================== */
+
+#define FLIPL(X) ((X) = htonl(X))
+
+static void
+handle_subnets_accessed(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int i, j;
+ unsigned long ns;
+ unsigned long ip, bits_specd;
+ CLG_Status result;
+
+ ns = ntohl(rx_message->data.subnets_accessed.n_subnets);
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_SUBNETS_ACCESSED);
+ tx_message->data.subnets_accessed.n_subnets = htonl(ns);
+
+ for (i=0; i<ns; i++) {
+ ip = ntohl(rx_message->data.subnets_accessed.subnets[i].ip);
+ bits_specd = ntohl(rx_message->data.subnets_accessed.subnets[i].bits_specd);
+
+ tx_message->data.subnets_accessed.subnets[i].ip = htonl(ip);
+ tx_message->data.subnets_accessed.subnets[i].bits_specd = htonl(bits_specd);
+
+ result = CLG_GetSubnetBitmap(ip, bits_specd, tx_message->data.subnets_accessed.subnets[i].bitmap);
+ switch (result) {
+ case CLG_SUCCESS:
+ case CLG_EMPTYSUBNET:
+ /* Flip endianness of each 4 byte word. Don't care if subnet
+ is empty - just return an all-zero bitmap. */
+ for (j=0; j<8; j++) {
+ FLIPL(tx_message->data.subnets_accessed.subnets[i].bitmap[j]);
+ }
+ break;
+ case CLG_BADSUBNET:
+ tx_message->status = htons(STT_BADSUBNET);
+ return;
+ case CLG_INACTIVE:
+ tx_message->status = htons(STT_INACTIVE);
+ return;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+handle_client_accesses(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ CLG_Status result;
+ RPT_ClientAccess_Report report;
+ unsigned long nc;
+ unsigned long ip;
+ int i;
+ struct timeval now;
+ double local_time_error;
+
+ LCL_ReadCookedTime(&now, &local_time_error);
+
+ nc = ntohl(rx_message->data.client_accesses.n_clients);
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_CLIENT_ACCESSES);
+ tx_message->data.client_accesses.n_clients = htonl(nc);
+
+ printf("%d %d\n", (int)sizeof(RPY_ClientAccesses_Client), (int)offsetof(CMD_Reply, data.client_accesses.clients));
+
+ for (i=0; i<nc; i++) {
+ ip = ntohl(rx_message->data.client_accesses.client_ips[i]);
+ tx_message->data.client_accesses.clients[i].ip = htonl(ip);
+
+ result = CLG_GetClientAccessReportByIP(ip, &report, now.tv_sec);
+ switch (result) {
+ case CLG_SUCCESS:
+ tx_message->data.client_accesses.clients[i].client_hits = htonl(report.client_hits);
+ tx_message->data.client_accesses.clients[i].peer_hits = htonl(report.peer_hits);
+ tx_message->data.client_accesses.clients[i].cmd_hits_auth = htonl(report.cmd_hits_auth);
+ tx_message->data.client_accesses.clients[i].cmd_hits_normal = htonl(report.cmd_hits_normal);
+ tx_message->data.client_accesses.clients[i].cmd_hits_bad = htonl(report.cmd_hits_bad);
+ tx_message->data.client_accesses.clients[i].last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
+ tx_message->data.client_accesses.clients[i].last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
+ printf("%08lx %lu %lu %lu %lu %lu %lu %lu\n", ip, report.client_hits, report.peer_hits, report.cmd_hits_auth, report.cmd_hits_normal, report.cmd_hits_bad, report.last_ntp_hit_ago, report.last_cmd_hit_ago);
+ break;
+ case CLG_EMPTYSUBNET:
+ /* Signal back to the client that this single client address
+ was unknown, by specifying the zero ip address, which will
+ always be invalid (hopefully) */
+ tx_message->data.client_accesses.clients[i].ip = htonl(0);
+ break;
+ case CLG_INACTIVE:
+ tx_message->status = htons(STT_INACTIVE);
+ return;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+ }
+
+}
+
+/* ================================================== */
+
+static void
+handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ CLG_Status result;
+ RPT_ClientAccessByIndex_Report report;
+ unsigned long first_index, n_indices, last_index, n_indices_in_table;
+ int i, j;
+ struct timeval now;
+ double local_time_error;
+
+ LCL_ReadCookedTime(&now, &local_time_error);
+
+ first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
+ n_indices = ntohl(rx_message->data.client_accesses_by_index.n_indices);
+ last_index = first_index + n_indices - 1;
+
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX);
+
+ for (i = first_index, j = 0;
+ (i <= last_index) && (j < MAX_CLIENT_ACCESSES);
+ i++) {
+
+ result = CLG_GetClientAccessReportByIndex(i, &report, now.tv_sec, &n_indices_in_table);
+ tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table);
+
+ switch (result) {
+ case CLG_SUCCESS:
+ tx_message->data.client_accesses_by_index.clients[j].ip = htonl(report.ip_addr);
+ tx_message->data.client_accesses_by_index.clients[j].client_hits = htonl(report.client_hits);
+ tx_message->data.client_accesses_by_index.clients[j].peer_hits = htonl(report.peer_hits);
+ tx_message->data.client_accesses_by_index.clients[j].cmd_hits_auth = htonl(report.cmd_hits_auth);
+ tx_message->data.client_accesses_by_index.clients[j].cmd_hits_normal = htonl(report.cmd_hits_normal);
+ tx_message->data.client_accesses_by_index.clients[j].cmd_hits_bad = htonl(report.cmd_hits_bad);
+ tx_message->data.client_accesses_by_index.clients[j].last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
+ tx_message->data.client_accesses_by_index.clients[j].last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
+ j++;
+ break;
+ case CLG_INDEXTOOLARGE:
+ break; /* ignore this index */
+ case CLG_INACTIVE:
+ tx_message->status = htons(STT_INACTIVE);
+ return;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+ }
+
+ tx_message->data.client_accesses_by_index.next_index = htonl(i);
+ tx_message->data.client_accesses_by_index.n_clients = htonl(j);
+}
+
+/* ================================================== */
+
+static void
+handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int n_samples;
+ int i;
+ RPY_ManualListSample *sample;
+ RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
+
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_MANUAL_LIST);
+
+ MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
+ tx_message->data.manual_list.n_samples = htonl(n_samples);
+ for (i=0; i<n_samples; i++) {
+ sample = &tx_message->data.manual_list.samples[i];
+ sample->when = htonl(report[i].when);
+ sample->slewed_offset = REAL2WIRE(report[i].slewed_offset);
+ sample->orig_offset = REAL2WIRE(report[i].orig_offset);
+ sample->residual = REAL2WIRE(report[i].residual);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ int index;
+
+ index = ntohl(rx_message->data.manual_delete.index);
+ status = MNL_DeleteSample(index);
+ if (!status) {
+ tx_message->status = htons(STT_BADSAMPLE);
+ } else {
+ tx_message->status = htons(STT_SUCCESS);
+ }
+}
+
+/* ================================================== */
+
+static void
+handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+ status = LCL_MakeStep();
+ if (status) {
+ tx_message->status = htons(STT_SUCCESS);
+ } else {
+ tx_message->status = htons(STT_NOTENABLED);
+ }
+ return;
+}
+
+/* ================================================== */
+
+static void
+handle_activity(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ RPT_ActivityReport report;
+ NSR_GetActivityReport(&report);
+ tx_message->data.activity.online = htonl(report.online);
+ tx_message->data.activity.offline = htonl(report.offline);
+ tx_message->data.activity.burst_online = htonl(report.burst_online);
+ tx_message->data.activity.burst_offline = htonl(report.burst_offline);
+ tx_message->status = htons(STT_SUCCESS);
+ tx_message->reply = htons(RPY_ACTIVITY);
+}
+
+/* ================================================== */
+
+#if 0
+/* ================================================== */
+
+static void
+handle_(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int status;
+}
+
+
+#endif
+
+/* ================================================== */
+/* Read a packet and process it */
+
+static void
+read_from_cmd_socket(void *anything)
+{
+ int status;
+ int read_length; /* Length of packet read */
+ int expected_length; /* Expected length of packet */
+ unsigned long flags;
+ CMD_Request rx_message;
+ CMD_Reply tx_message, *prev_tx_message;
+ int rx_message_length, tx_message_length;
+ struct sockaddr_in where_from;
+ int from_length;
+ unsigned long remote_ip;
+ unsigned short remote_port;
+ int md5_ok;
+ int utoken_ok, token_ok;
+ int issue_token;
+ int valid_ts;
+ int authenticated;
+ int localhost;
+ unsigned short rx_command;
+ unsigned long rx_message_token;
+ unsigned long tx_message_token;
+ unsigned long rx_message_seq;
+ unsigned long rx_attempt;
+ struct timeval now;
+ struct timeval cooked_now;
+ double local_clock_err;
+
+ flags = 0;
+ rx_message_length = sizeof(rx_message);
+ from_length = sizeof(where_from);
+
+ status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, flags,
+ (struct sockaddr *)&where_from, &from_length);
+
+ if (status < 0) {
+ LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket (IP=%s port=%d)",
+ strerror(errno),
+ UTI_IPToDottedQuad(ntohl(where_from.sin_addr.s_addr)),
+ ntohs(where_from.sin_port));
+ }
+
+ read_length = status;
+ expected_length = PKL_CommandLength(&rx_message);
+
+ LCL_ReadRawTime(&now);
+ LCL_ReadCookedTime(&cooked_now, &local_clock_err);
+
+ tx_message.version = PROTO_VERSION_NUMBER;
+ tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
+ tx_message.res1 = 0;
+ tx_message.res2 = 0;
+ tx_message.command = rx_message.command;
+ tx_message.sequence = rx_message.sequence;
+ tx_message.reply = htons(RPY_NULL);
+ tx_message.number = htons(1);
+ tx_message.total = htons(1);
+ tx_message.utoken = htonl(utoken);
+ /* Set this to a default (invalid) value. This protects against the
+ token field being set to an arbitrary value if we reject the
+ message, e.g. due to the host failing the access check. */
+ tx_message.token = htonl(0xffffffffUL);
+
+ remote_ip = ntohl(where_from.sin_addr.s_addr);
+ remote_port = ntohs(where_from.sin_port);
+
+ localhost = (remote_ip == 0x7f000001UL);
+
+ if ((!ADF_IsAllowed(access_auth_table, remote_ip)) &&
+ (!localhost)) {
+ /* The client is not allowed access, so don't waste any more time
+ on him. Note that localhost is always allowed access
+ regardless of the defined access rules - otherwise, we could
+ shut ourselves out completely! */
+
+ /* We ought to find another way to log this, there is an attack
+ here against the host because an adversary can just keep
+ hitting us with bad packets until our log file(s) fill up. */
+
+ LOG(LOGS_WARN, LOGF_CmdMon, "Command packet received from unauthorised host %s port %d",
+ UTI_IPToDottedQuad(remote_ip),
+ remote_port);
+
+ tx_message.status = htons(STT_NOHOSTACCESS);
+ transmit_reply(&tx_message, &where_from);
+
+ return;
+ }
+
+
+ if (read_length != expected_length) {
+ LOG(LOGS_WARN, LOGF_CmdMon, "Read incorrectly sized packet from %s:%hu", UTI_IPToDottedQuad(remote_ip), remote_port);
+ CLG_LogCommandAccess(remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
+ /* For now, just ignore the packet. We may want to send a reply
+ back eventually */
+ return;
+ }
+
+ if ((rx_message.version != PROTO_VERSION_NUMBER) ||
+ (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST) ||
+ (rx_message.res1 != 0) ||
+ (rx_message.res2 != 0)) {
+
+ /* We don't know how to process anything like this */
+ CLG_LogCommandAccess(remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
+
+ return;
+ }
+
+ rx_command = ntohs(rx_message.command);
+
+ /* OK, we have a valid message. Now dispatch on message type and process it. */
+
+ /* Do authentication stuff and command tokens here. Well-behaved
+ clients will set their utokens to 0 to save us wasting our time
+ if the packet is unauthenticatable. */
+ if (rx_message.utoken != 0) {
+ md5_ok = check_rx_packet_auth(&rx_message);
+ } else {
+ md5_ok = 0;
+ }
+
+ /* All this malarky is to protect the system against various forms
+ of attack.
+
+ Simple packet forgeries are blocked by requiring the packet to
+ authenticate properly with MD5. (The assumption is that the
+ command key is in a read-only keys file read by the daemon, and
+ is known only to administrators.)
+
+ Replay attacks are prevented by 2 fields in the packet. The
+ 'token' field is where the client plays back to us a token that
+ he was issued in an earlier reply. Each time we reply to a
+ suitable packet, we issue a new token. The 'utoken' field is set
+ to a new (hopefully increasing) value each time the daemon is
+ run. This prevents packets from a previous incarnation being
+ played back at us when the same point in the 'token' sequence
+ comes up. (The token mechanism also prevents a non-idempotent
+ command from being executed twice from the same client, if the
+ client fails to receive our reply the first time and tries a
+ resend.)
+
+ The problem is how a client should get its first token. Our
+ token handling only remembers a finite number of issued tokens
+ (actually 32) - if a client replies with a (legitimate) token
+ older than that, it will be treated as though a duplicate token
+ has been supplied. If a simple token-request protocol were used,
+ the whole thing would be vulnerable to a denial of service
+ attack, where an attacker just replays valid token-request
+ packets at us, causing us to keep issuing new tokens,
+ invalidating all the ones we have given out to true clients
+ already.
+
+ To protect against this, the token-request (REQ_LOGON) packet
+ includes a timestamp field. To issue a token, we require that
+ this field is different from any we have processed before. To
+ bound our storage, we require that the timestamp is within a
+ certain period of our current time. For clients running on the
+ same host this will be easily satisfied.
+
+ */
+
+ utoken_ok = (ntohl(rx_message.utoken) == utoken);
+
+ /* Avoid binning a valid user's token if we merely get a forged
+ packet */
+ rx_message_token = ntohl(rx_message.token);
+ rx_message_seq = ntohl(rx_message.sequence);
+ rx_attempt = ntohs(rx_message.attempt);
+
+ if (md5_ok && utoken_ok) {
+ token_ok = check_token(rx_message_token);
+ } else {
+ token_ok = 0;
+ }
+
+ if (md5_ok && utoken_ok && !token_ok) {
+ /* This might be a resent message, due to the client not getting
+ our reply to the first attempt. See if we can find the message. */
+ prev_tx_message = lookup_reply(rx_message_token, rx_message_seq, rx_attempt);
+ if (prev_tx_message) {
+ /* Just send this message again */
+ tx_message_length = PKL_ReplyLength(prev_tx_message);
+ status = sendto(sock_fd, (void *) prev_tx_message, tx_message_length, 0,
+ (struct sockaddr *) &where_from, sizeof(where_from));
+ if (status < 0) {
+ LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToDottedQuad(remote_ip), remote_port);
+ }
+ return;
+ }
+ /* Otherwise, just fall through into normal processing */
+
+ }
+
+ if (md5_ok && utoken_ok && token_ok) {
+ /* See whether we can discard the previous reply from storage */
+ token_acknowledged(rx_message_token, &now);
+ }
+
+ valid_ts = 0;
+
+ if (md5_ok && ((utoken_ok && token_ok) ||
+ ((ntohl(rx_message.utoken) == SPECIAL_UTOKEN) &&
+ (rx_command == REQ_LOGON) &&
+ (valid_ts = ts_is_unique_and_not_stale(&rx_message.data.logon.ts, &now))))) {
+ issue_token = 1;
+ } else {
+ issue_token = 0;
+ }
+
+ authenticated = md5_ok & utoken_ok & token_ok;
+
+ if (authenticated) {
+ CLG_LogCommandAccess(remote_ip, CLG_CMD_AUTH, cooked_now.tv_sec);
+ } else {
+ CLG_LogCommandAccess(remote_ip, CLG_CMD_NORMAL, cooked_now.tv_sec);
+ }
+
+ if (issue_token) {
+ /* Only command clients where the user has apparently 'logged on'
+ get a token to allow them to emit an authenticated command next
+ time */
+ tx_message_token = get_token();
+ } else {
+ tx_message_token = 0xffffffffUL;
+ }
+
+ tx_message.token = htonl(tx_message_token);
+
+
+ /* If command type is invalid, send back reply */
+ if (rx_command >= N_REQUEST_TYPES) {
+ tx_message.status = htons(STT_INVALID);
+ tx_message.reply = htons(RPY_NULL);
+ } else {
+ int allowed = 0;
+
+ /* Check level of authority required to issue the command */
+ switch(permissions[rx_command]) {
+ case PERMIT_AUTH:
+ if (authenticated) {
+ allowed = 1;
+ } else {
+ allowed = 0;
+ }
+ break;
+ case PERMIT_LOCAL:
+ if (authenticated || localhost) {
+ allowed = 1;
+ } else {
+ allowed = 0;
+ }
+ break;
+ case PERMIT_OPEN:
+ allowed = 1;
+ break;
+ default:
+ CROAK("Impossible");
+ }
+
+ if (allowed) {
+ switch(rx_command) {
+ case REQ_NULL:
+ handle_null(&rx_message, &tx_message);
+ break;
+
+ case REQ_ONLINE:
+ handle_online(&rx_message, &tx_message);
+ break;
+
+ case REQ_OFFLINE:
+ handle_offline(&rx_message, &tx_message);
+ break;
+
+ case REQ_BURST:
+ handle_burst(&rx_message, &tx_message);
+ break;
+
+ case REQ_MODIFY_MINPOLL:
+ handle_modify_minpoll(&rx_message, &tx_message);
+ break;
+
+ case REQ_MODIFY_MAXPOLL:
+ handle_modify_maxpoll(&rx_message, &tx_message);
+ break;
+
+ case REQ_DUMP:
+ SRC_DumpSources();
+ tx_message.status = htons(STT_SUCCESS);
+ break;
+
+ case REQ_MODIFY_MAXDELAY:
+ handle_modify_maxdelay(&rx_message, &tx_message);
+ break;
+
+ case REQ_MODIFY_MAXDELAYRATIO:
+ handle_modify_maxdelayratio(&rx_message, &tx_message);
+ break;
+
+ case REQ_MODIFY_MAXUPDATESKEW:
+ handle_modify_maxupdateskew(&rx_message, &tx_message);
+ break;
+
+ case REQ_LOGON:
+ /* If the log-on fails, record the reason why */
+ if (!issue_token) {
+ LOG(LOGS_WARN, LOGF_CmdMon,
+ "Bad command logon from %s port %d (md5_ok=%d valid_ts=%d)\n",
+ UTI_IPToDottedQuad(remote_ip),
+ remote_port,
+ md5_ok, valid_ts);
+ }
+
+ if (issue_token == 1) {
+ tx_message.status = htons(STT_SUCCESS);
+ } else if (!md5_ok) {
+ tx_message.status = htons(STT_UNAUTH);
+ } else if (!valid_ts) {
+ tx_message.status = htons(STT_INVALIDTS);
+ } else {
+ tx_message.status = htons(STT_FAILED);
+ }
+
+ break;
+
+ case REQ_SETTIME:
+ handle_settime(&rx_message, &tx_message);
+ break;
+
+ case REQ_LOCAL:
+ handle_local(&rx_message, &tx_message);
+ break;
+
+ case REQ_MANUAL:
+ handle_manual(&rx_message, &tx_message);
+ break;
+
+ case REQ_N_SOURCES:
+ handle_n_sources(&rx_message, &tx_message);
+ break;
+
+ case REQ_SOURCE_DATA:
+ handle_source_data(&rx_message, &tx_message);
+ break;
+
+ case REQ_REKEY:
+ handle_rekey(&rx_message, &tx_message);
+ break;
+
+ case REQ_ALLOW:
+ handle_allow(&rx_message, &tx_message);
+ break;
+
+ case REQ_ALLOWALL:
+ handle_allowall(&rx_message, &tx_message);
+ break;
+
+ case REQ_DENY:
+ handle_deny(&rx_message, &tx_message);
+ break;
+
+ case REQ_DENYALL:
+ handle_denyall(&rx_message, &tx_message);
+ break;
+
+ case REQ_CMDALLOW:
+ handle_cmdallow(&rx_message, &tx_message);
+ break;
+
+ case REQ_CMDALLOWALL:
+ handle_cmdallowall(&rx_message, &tx_message);
+ break;
+
+ case REQ_CMDDENY:
+ handle_cmddeny(&rx_message, &tx_message);
+ break;
+
+ case REQ_CMDDENYALL:
+ handle_cmddenyall(&rx_message, &tx_message);
+ break;
+
+ case REQ_ACCHECK:
+ handle_accheck(&rx_message, &tx_message);
+ break;
+
+ case REQ_CMDACCHECK:
+ handle_cmdaccheck(&rx_message, &tx_message);
+ break;
+
+ case REQ_ADD_SERVER:
+ handle_add_server(&rx_message, &tx_message);
+ break;
+
+ case REQ_ADD_PEER:
+ handle_add_peer(&rx_message, &tx_message);
+ break;
+
+ case REQ_DEL_SOURCE:
+ handle_del_source(&rx_message, &tx_message);
+ break;
+
+ case REQ_WRITERTC:
+ handle_writertc(&rx_message, &tx_message);
+ break;
+
+ case REQ_DFREQ:
+ handle_dfreq(&rx_message, &tx_message);
+ break;
+
+ case REQ_DOFFSET:
+ handle_doffset(&rx_message, &tx_message);
+ break;
+
+ case REQ_TRACKING:
+ handle_tracking(&rx_message, &tx_message);
+ break;
+
+ case REQ_SOURCESTATS:
+ handle_sourcestats(&rx_message, &tx_message);
+ break;
+
+ case REQ_RTCREPORT:
+ handle_rtcreport(&rx_message, &tx_message);
+ break;
+
+ case REQ_TRIMRTC:
+ handle_trimrtc(&rx_message, &tx_message);
+ break;
+
+ case REQ_CYCLELOGS:
+ handle_cyclelogs(&rx_message, &tx_message);
+ break;
+
+ case REQ_SUBNETS_ACCESSED:
+ handle_subnets_accessed(&rx_message, &tx_message);
+ break;
+
+ case REQ_CLIENT_ACCESSES:
+ handle_client_accesses(&rx_message, &tx_message);
+ break;
+
+ case REQ_CLIENT_ACCESSES_BY_INDEX:
+ handle_client_accesses_by_index(&rx_message, &tx_message);
+ break;
+
+ case REQ_MANUAL_LIST:
+ handle_manual_list(&rx_message, &tx_message);
+ break;
+
+ case REQ_MANUAL_DELETE:
+ handle_manual_delete(&rx_message, &tx_message);
+ break;
+
+ case REQ_MAKESTEP:
+ handle_make_step(&rx_message, &tx_message);
+ break;
+
+ case REQ_ACTIVITY:
+ handle_activity(&rx_message, &tx_message);
+ break;
+
+ default:
+ /* Ignore message */
+ break;
+ }
+ } else {
+ tx_message.status = htons(STT_UNAUTH);
+ }
+ }
+
+ if (md5_ok) {
+ generate_tx_packet_auth(&tx_message);
+ }
+
+ if (token_ok) {
+ save_reply(&tx_message,
+ rx_message_token,
+ tx_message_token,
+ rx_message_seq,
+ rx_attempt,
+ &now);
+ }
+
+ /* Transmit the response */
+ {
+ /* Include a simple way to lose one message in three to test resend */
+
+ static int do_it=1;
+
+ if (do_it) {
+ transmit_reply(&tx_message, &where_from);
+ }
+
+#if 0
+ do_it = ((do_it + 1) % 3);
+#endif
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+
+int
+CAM_AddAccessRestriction(unsigned long ip_addr, int subnet_bits, int allow, int all)
+ {
+ ADF_Status status;
+
+ if (allow) {
+ if (all) {
+ status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits);
+ } else {
+ status = ADF_Allow(access_auth_table, ip_addr, subnet_bits);
+ }
+ } else {
+ if (all) {
+ status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits);
+ } else {
+ status = ADF_Deny(access_auth_table, ip_addr, subnet_bits);
+ }
+ }
+
+ if (status == ADF_BADSUBNET) {
+ return 0;
+ } else if (status == ADF_SUCCESS) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+CAM_CheckAccessRestriction(unsigned long ip_addr)
+{
+ return ADF_IsAllowed(access_auth_table, ip_addr);
+}
+
+
+/* ================================================== */
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/cmdmon.h,v 1.8 2002/02/28 23:27:09 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for the control and monitoring module in the software
+ */
+
+#ifndef GOT_CMDMON_H
+#define GOT_CMDMON_H
+
+extern void CAM_Initialise(void);
+
+extern void CAM_Finalise(void);
+
+extern int CAM_AddAccessRestriction(unsigned long ip_addr, int subnet_bits, int allow, int all);
+extern int CAM_CheckAccessRestriction(unsigned long ip_addr);
+
+#endif /* GOT_CMDMON_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/cmdparse.c,v 1.11 2003/01/20 22:52:07 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module for parsing various forms of directive and command lines that
+ are common to the configuration file and to the command client.
+
+ */
+
+#include "sysincl.h"
+
+#include "cmdparse.h"
+#include "nameserv.h"
+
+#define MAXLEN 2047
+#define SMAXLEN "2047"
+
+/* ================================================== */
+
+CPS_Status
+CPS_ParseNTPSourceAdd(const char *line, CPS_NTP_Source *src)
+{
+ int ok, n, done;
+ char cmd[MAXLEN+1], hostname[MAXLEN+1];
+ CPS_Status result;
+
+ src->port = 123;
+ src->params.minpoll = 6;
+ src->params.maxpoll = 10;
+ src->params.presend_minpoll = 0;
+ src->params.authkey = INACTIVE_AUTHKEY;
+ src->params.max_delay = 16.0;
+ src->params.max_delay_ratio = 16384.0;
+ src->params.online = 1;
+ src->params.auto_offline = 0;
+
+ result = CPS_Success;
+
+ ok = 0;
+ if (sscanf(line, "%" SMAXLEN "s%n", hostname, &n) == 1) {
+ src->ip_addr = DNS_Name2IPAddress(hostname);
+ if (src->ip_addr != DNS_Failed_Address) {
+ ok = 1;
+ }
+ }
+
+ if (!ok) {
+ result = CPS_BadHost;
+ } else {
+
+ line += n;
+
+ /* Parse subfields */
+ ok = 1;
+ done = 0;
+ do {
+ if (sscanf(line, "%" SMAXLEN "s%n", cmd, &n) == 1) {
+
+ line += n;
+
+ if (!strncasecmp(cmd, "port", 4)) {
+ if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
+ result = CPS_BadPort;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "minpoll", 7)) {
+ if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
+ result = CPS_BadMinpoll;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "maxpoll", 7)) {
+ if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
+ result = CPS_BadMaxpoll;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "presend", 7)) {
+ if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
+ result = CPS_BadPresend;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ /* This MUST come before the following one ! */
+ } else if (!strncasecmp(cmd, "maxdelayratio", 13)) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
+ result = CPS_BadMaxdelayratio;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "maxdelay", 8)) {
+ if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
+ result = CPS_BadMaxdelay;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "key", 3)) {
+ if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1) {
+ result = CPS_BadKey;
+ ok = 0;
+ done = 1;
+ } else {
+ line += n;
+ }
+ } else if (!strncasecmp(cmd, "offline", 7)) {
+ src->params.online = 0;
+
+ } else if (!strncasecmp(cmd, "auto_offline", 12)) {
+ src->params.auto_offline = 1;
+
+ } else {
+ result = CPS_BadOption;
+ ok = 0;
+ done = 1;
+ }
+ } else {
+ done = 1;
+ }
+ } while (!done);
+ }
+
+ return result;
+
+}
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/cmdparse.h,v 1.7 2002/02/28 23:27:09 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for the command parser
+ */
+
+#ifndef GOT_CMDPARSE_H
+#define GOT_CMDPARSE_H
+
+#include "srcparams.h"
+
+typedef enum {
+ CPS_Success,
+ CPS_BadOption,
+ CPS_BadHost,
+ CPS_BadPort,
+ CPS_BadMinpoll,
+ CPS_BadMaxpoll,
+ CPS_BadPresend,
+ CPS_BadMaxdelayratio,
+ CPS_BadMaxdelay,
+ CPS_BadKey
+} CPS_Status;
+
+typedef struct {
+ unsigned long ip_addr;
+ unsigned short port;
+ SourceParameters params;
+} CPS_NTP_Source;
+
+/* Parse a command to add an NTP server or peer */
+extern CPS_Status CPS_ParseNTPSourceAdd(const char *line, CPS_NTP_Source *src);
+
+
+
+#endif /* GOT_CMDPARSE_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/conf.c,v 1.42 2003/03/27 23:45:47 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module that reads and processes the configuration file.
+
+1999-12-19 Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
+
+ * conf.c: Added a new configuration setting "acquisitionport" and
+ a function CNF_GetAcquisitionPort to read its value.
+ (acquisition_port): New variable.
+ (parse_port): Delegate most work to new function parse_some_port.
+ (parse_acquisitionport): New function.
+ (commands): Added "acquisitionport".
+ (CNF_GetAcquisitionPort): New function.
+
+ */
+
+#include "sysincl.h"
+
+#include "conf.h"
+#include "ntp_sources.h"
+#include "ntp_core.h"
+#include "cmdmon.h"
+#include "srcparams.h"
+#include "logging.h"
+#include "nameserv.h"
+#include "memory.h"
+#include "acquire.h"
+#include "cmdparse.h"
+#include "broadcast.h"
+
+/* ================================================== */
+
+#define DEFAULT_CONF_FILE "/etc/chrony.conf"
+
+/* ================================================== */
+/* Forward prototypes */
+
+static void parse_commandkey(const char *);
+static void parse_driftfile(const char *);
+static void parse_dumpdir(const char *);
+static void parse_dumponexit(const char *);
+static void parse_keyfile(const char *);
+static void parse_rtcfile(const char *);
+static void parse_log(const char *);
+static void parse_logdir(const char *);
+static void parse_maxupdateskew(const char *);
+static void parse_peer(const char *);
+static void parse_acquisitionport(const char *);
+static void parse_port(const char *);
+static void parse_server(const char *);
+static void parse_local(const char *);
+static void parse_manual(const char *);
+static void parse_initstepslew(const char *);
+static void parse_allow(const char *);
+static void parse_deny(const char *);
+static void parse_cmdallow(const char *);
+static void parse_cmddeny(const char *);
+static void parse_cmdport(const char *);
+static void parse_rtconutc(const char *);
+static void parse_noclientlog(const char *);
+static void parse_logchange(const char *);
+static void parse_mailonchange(const char *);
+static void parse_bindaddress(const char *);
+static void parse_bindcmdaddress(const char *);
+static void parse_rtcdevice(const char *);
+static void parse_pidfile(const char *);
+static void parse_broadcast(const char *);
+static void parse_linux_hz(const char *);
+static void parse_linux_freq_scale(const char *);
+
+/* ================================================== */
+/* Configuration variables */
+
+static char *rtc_device = "/dev/rtc";
+static int acquisition_port = 0; /* 0 means let kernel choose port */
+static int ntp_port = 123;
+static char *keys_file = NULL;
+static char *drift_file = NULL;
+static char *rtc_file = NULL;
+static unsigned long command_key_id;
+static double max_update_skew = DBL_MAX;
+
+static int cmd_port = -1;
+
+static int do_log_measurements = 0;
+static int do_log_statistics = 0;
+static int do_log_tracking = 0;
+static int do_log_rtc = 0;
+static int do_dump_on_exit = 0;
+static char *logdir = ".";
+static char *dumpdir = ".";
+
+static int enable_local=0;
+#define DEFAULT_LOCAL_STRATUM 8
+static int local_stratum;
+
+static int do_init_stepslew = 0;
+static int n_init_srcs;
+
+/* Threshold (in seconds) - if absolute value of initial error is less
+ than this, slew instead of stepping */
+static int init_slew_threshold = -1;
+#define MAX_INIT_SRCS 8
+static unsigned long init_srcs_ip[MAX_INIT_SRCS];
+
+static int enable_manual=0;
+
+/* Flag set if the RTC runs UTC (default is it runs local time
+ incl. daylight saving). */
+static int rtc_on_utc = 0;
+
+/* Flag set if we should log to syslog when a time adjustment
+ exceeding the threshold is initiated */
+static int do_log_change = 0;
+static double log_change_threshold = 0.0;
+
+static char *mail_user_on_change = NULL;
+static double mail_change_threshold = 0.0;
+
+/* Flag indicating that we don't want to log clients, e.g. to save
+ memory */
+static int no_client_log = 0;
+
+/* IP address (host order) for binding the NTP socket to. 0 means INADDR_ANY
+ will be used */
+static unsigned long bind_address = 0UL;
+
+/* IP address (host order) for binding the command socket to. 0 means
+ use the value of bind_address */
+static unsigned long bind_cmd_address = 0UL;
+
+/* Filename to use for storing pid of running chronyd, to prevent multiple
+ * chronyds being started. */
+static char *pidfile = "/var/run/chronyd.pid";
+
+/* Boolean for whether the Linux HZ value has been overridden, and the
+ * new value. */
+static int set_linux_hz = 0;
+static int linux_hz;
+
+/* Boolean for whether the Linux frequency scaling value (i.e. the one that's
+ * approx (1<<SHIFT_HZ)/HZ) has been overridden, and the new value. */
+static int set_linux_freq_scale = 0;
+static double linux_freq_scale;
+
+/* ================================================== */
+
+typedef struct {
+ const char *keyword;
+ int len;
+ void (*handler)(const char *);
+} Command;
+
+static const Command commands[] = {
+ {"server", 6, parse_server},
+ {"peer", 4, parse_peer},
+ {"acquisitionport", 15, parse_acquisitionport},
+ {"port", 4, parse_port},
+ {"driftfile", 9, parse_driftfile},
+ {"keyfile", 7, parse_keyfile},
+ {"rtcfile", 7, parse_rtcfile},
+ {"logdir", 6, parse_logdir},
+ {"log", 3, parse_log},
+ {"dumponexit", 10, parse_dumponexit},
+ {"dumpdir", 7, parse_dumpdir},
+ {"maxupdateskew", 13, parse_maxupdateskew},
+ {"commandkey", 10, parse_commandkey},
+ {"initstepslew", 12, parse_initstepslew},
+ {"local", 5, parse_local},
+ {"manual", 6, parse_manual},
+ {"allow", 5, parse_allow},
+ {"deny", 4, parse_deny},
+ {"cmdallow", 8, parse_cmdallow},
+ {"cmddeny", 7, parse_cmddeny},
+ {"cmdport", 7, parse_cmdport},
+ {"rtconutc", 8, parse_rtconutc},
+ {"noclientlog", 11, parse_noclientlog},
+ {"logchange", 9, parse_logchange},
+ {"mailonchange", 12, parse_mailonchange},
+ {"bindaddress", 11, parse_bindaddress},
+ {"bindcmdaddress", 14, parse_bindcmdaddress},
+ {"rtcdevice", 9, parse_rtcdevice},
+ {"pidfile", 7, parse_pidfile},
+ {"broadcast", 9, parse_broadcast},
+ {"linux_hz", 8, parse_linux_hz},
+ {"linux_freq_scale", 16, parse_linux_freq_scale}
+};
+
+static int n_commands = (sizeof(commands) / sizeof(commands[0]));
+
+/* The line number in the configuration file being processed */
+static int line_number;
+
+/* ================================================== */
+
+typedef enum {
+ SERVER, PEER
+} NTP_Source_Type;
+
+typedef struct {
+ NTP_Source_Type type;
+ unsigned long ip_addr;
+ unsigned short port;
+ SourceParameters params;
+} NTP_Source;
+
+#define MAX_NTP_SOURCES 64
+
+static NTP_Source ntp_sources[MAX_NTP_SOURCES];
+static int n_ntp_sources = 0;
+
+/* ================================================== */
+
+typedef struct _AllowDeny {
+ struct _AllowDeny *next;
+ struct _AllowDeny *prev;
+ unsigned long ip;
+ int subnet_bits;
+ int all; /* 1 to override existing more specific defns */
+ int allow; /* 0 for deny, 1 for allow */
+} AllowDeny;
+
+static AllowDeny ntp_auth_list = {&ntp_auth_list, &ntp_auth_list};
+static AllowDeny cmd_auth_list = {&cmd_auth_list, &cmd_auth_list};
+
+/* ================================================== */
+
+/* Read the configuration file */
+void
+CNF_ReadFile(const char *filename)
+{
+ FILE *in;
+ char line[2048];
+ char *p;
+ int i, ok;
+
+ if (filename == NULL) {
+ filename = DEFAULT_CONF_FILE;
+ }
+
+ in = fopen(filename, "r");
+ if (!in) {
+ LOG(LOGS_ERR, LOGF_Configure, "Could not open configuration file [%s]", filename);
+ } else {
+
+ line_number = 0;
+
+ /* Success */
+ while (fgets(line, sizeof(line), in)) {
+
+ line_number++;
+
+ /* Strip trailing newline */
+ line[strlen(line) - 1] = 0;
+
+ /* Discard comment lines, blank lines etc */
+ p = line;
+ while(*p && (isspace((unsigned char)*p)))
+ p++;
+
+ if (!*p || (strchr("!;#%", *p) != NULL))
+ continue;
+
+ /* We have a real line, now try to match commands */
+ ok = 0;
+ for (i=0; i<n_commands; i++) {
+ if (!strncasecmp(commands[i].keyword, p, commands[i].len)) {
+ (*(commands[i].handler))(p + commands[i].len);
+ ok = 1;
+ }
+ }
+
+ if (!ok) {
+ LOG(LOGS_WARN, LOGF_Configure, "Line %d in configuration file [%s] contains invalid command",
+ line_number, filename);
+ }
+
+ }
+
+ fclose(in);
+ }
+
+}
+
+/* ================================================== */
+
+static void
+parse_source(const char *line, NTP_Source_Type type)
+{
+ int i;
+ NTP_Source s;
+ CPS_Status status;
+ CPS_NTP_Source params;
+
+ s.type = type;
+ status = CPS_ParseNTPSourceAdd(line, ¶ms);
+
+ switch (status) {
+ case CPS_Success:
+ s.port = params.port;
+ s.ip_addr = params.ip_addr;
+ s.params = params.params;
+
+ i = n_ntp_sources++;
+ ntp_sources[i] = s;
+
+ break;
+ case CPS_BadOption:
+ LOG(LOGS_WARN, LOGF_Configure, "Unrecognized subcommand at line %d", line_number);
+ break;
+ case CPS_BadHost:
+ LOG(LOGS_WARN, LOGF_Configure, "Invalid host/IP address at line %d", line_number);
+ break;
+ case CPS_BadPort:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable port number at line %d", line_number);
+ break;
+ case CPS_BadMinpoll:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable minpoll value at line %d", line_number);
+ break;
+ case CPS_BadMaxpoll:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable maxpoll value at line %d", line_number);
+ break;
+ case CPS_BadPresend:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable presend value at line %d", line_number);
+ break;
+ case CPS_BadMaxdelayratio:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable max delay ratio value at line %d", line_number);
+ break;
+ case CPS_BadMaxdelay:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable max delay value at line %d", line_number);
+ break;
+ case CPS_BadKey:
+ LOG(LOGS_WARN, LOGF_Configure, "Unreadable key value at line %d", line_number);
+ break;
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+
+static void
+parse_server(const char *line)
+{
+ parse_source(line, SERVER);
+}
+
+/* ================================================== */
+
+static void
+parse_peer(const char *line)
+{
+ parse_source(line, PEER);
+}
+
+/* ================================================== */
+
+static void
+parse_some_port(const char *line, int *portvar)
+{
+ if (sscanf(line, "%d", portvar) != 1) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read port number at line %d in file", line_number);
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_acquisitionport(const char *line)
+{
+ parse_some_port(line, &acquisition_port);
+}
+
+/* ================================================== */
+
+static void
+parse_port(const char *line)
+{
+ parse_some_port(line, &ntp_port);
+}
+
+/* ================================================== */
+
+static void
+parse_maxupdateskew(const char *line)
+{
+ if (sscanf(line, "%lf", &max_update_skew) != 1) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read max update skew at line %d in file", line_number);
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_driftfile(const char *line)
+{
+ /* This must allocate enough space! */
+ drift_file = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", drift_file);
+}
+
+/* ================================================== */
+
+static void
+strip_trailing_spaces(char *p)
+{
+ char *q;
+ for (q=p; *q; q++)
+ ;
+
+ for (q--; isspace((unsigned char)*q); q--)
+ ;
+
+ *++q = 0;
+}
+
+/* ================================================== */
+
+static void
+parse_keyfile(const char *line)
+{
+ /* This must allocate enough space! */
+ keys_file = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", keys_file);
+ strip_trailing_spaces(keys_file);
+}
+
+/* ================================================== */
+
+static void
+parse_rtcfile(const char *line)
+{
+ rtc_file = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", rtc_file);
+ strip_trailing_spaces(rtc_file);
+}
+
+/* ================================================== */
+
+static void
+parse_rtcdevice(const char *line)
+{
+ rtc_device = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", rtc_device);
+}
+
+/* ================================================== */
+
+static void
+parse_logdir(const char *line)
+{
+ logdir = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", logdir);
+}
+
+/* ================================================== */
+
+static void
+parse_dumpdir(const char *line)
+{
+ dumpdir = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", dumpdir);
+}
+
+/* ================================================== */
+
+static void
+parse_dumponexit(const char *line)
+{
+ do_dump_on_exit = 1;
+}
+
+/* ================================================== */
+
+static void
+parse_log(const char *line)
+{
+ do {
+ while (*line && isspace((unsigned char)*line)) line++;
+ if (*line) {
+ if (!strncmp(line, "measurements", 12)) {
+ do_log_measurements = 1;
+ line += 12;
+ } else if (!strncmp(line, "statistics", 10)) {
+ do_log_statistics = 1;
+ line += 10;
+ } else if (!strncmp(line, "tracking", 8)) {
+ do_log_tracking = 1;
+ line += 8;
+ } else if (!strncmp(line, "rtc", 3)) {
+ do_log_rtc = 1;
+ line += 3;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } while (1);
+}
+
+/* ================================================== */
+
+static void
+parse_commandkey(const char *line)
+{
+ if (sscanf(line, "%lu", &command_key_id) != 1) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read command key ID at line %d", line_number);
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_local(const char *line)
+{
+ int stratum;
+ enable_local = 1;
+ if (sscanf(line, "%*[ \t]stratum%d", &stratum) == 1) {
+ local_stratum = stratum;
+ } else {
+ local_stratum = DEFAULT_LOCAL_STRATUM;
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_cmdport(const char *line)
+{
+ if (sscanf(line, "%d", &cmd_port) != 1) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read command port number at line %d", line_number);
+ }
+}
+
+/* ================================================== */
+
+#define HOSTNAME_LEN 255
+#define SHOSTNAME_LEN "255"
+
+static void
+parse_initstepslew(const char *line)
+{
+ const char *p;
+ char hostname[HOSTNAME_LEN+1];
+ int n;
+ unsigned long ip_addr;
+
+ do_init_stepslew = 1;
+ n_init_srcs = 0;
+ p = line;
+
+ if (sscanf(p, "%d%n", &init_slew_threshold, &n) == 1) {
+ p += n;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not parse initstepslew threshold at line %d", line_number);
+ init_slew_threshold = -1;
+ return;
+ }
+
+ while (*p) {
+ if (sscanf(p, "%" SHOSTNAME_LEN "s%n", hostname, &n) == 1) {
+ ip_addr = DNS_Name2IPAddress(hostname);
+ if (ip_addr != DNS_Failed_Address) {
+ init_srcs_ip[n_init_srcs] = ip_addr;
+ ++n_init_srcs;
+ }
+
+ if (n_init_srcs >= MAX_INIT_SRCS) {
+ return;
+ }
+
+ } else {
+ /* If we get invalid trailing syntax, forget it ... */
+ return;
+ }
+
+ p += n;
+ }
+
+}
+
+/* ================================================== */
+
+static void
+parse_manual(const char *line)
+{
+ enable_manual = 1;
+}
+
+/* ================================================== */
+
+static void
+parse_rtconutc(const char *line)
+{
+ rtc_on_utc = 1;
+}
+
+/* ================================================== */
+
+static void
+parse_noclientlog(const char *line)
+{
+ no_client_log = 1;
+}
+
+/* ================================================== */
+
+static void
+parse_logchange(const char *line)
+{
+ if (sscanf(line, "%lf", &log_change_threshold) == 1) {
+ do_log_change = 1;
+ } else {
+ do_log_change = 0;
+ LOG(LOGS_WARN, LOGF_Configure,
+ "Could not read threshold for logging clock changes at line %d\n",
+ line_number);
+ }
+}
+
+
+/* ================================================== */
+
+#define BUFLEN 127
+#define SBUFLEN "127"
+
+static void
+parse_mailonchange(const char *line)
+{
+ char buffer[BUFLEN+1];
+ if (sscanf(line, "%" SBUFLEN "s%lf", buffer, &mail_change_threshold) == 2) {
+ mail_user_on_change = MallocArray(char, strlen(buffer)+1);
+ strcpy(mail_user_on_change, buffer);
+ } else {
+ mail_user_on_change = NULL;
+ LOG(LOGS_WARN, LOGF_Configure,
+ "Could not read user or threshold for clock change mail notify at line %d\n",
+ line_number);
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_allow_deny(const char *line, AllowDeny *list, int allow)
+{
+ const char *p;
+ unsigned long a, b, c, d, n;
+ int all = 0;
+ AllowDeny *new_node = NULL;
+ unsigned long ip_addr;
+
+ p = line;
+
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ if (!strncmp(p, "all", 3)) {
+ all = 1;
+ p += 3;
+ }
+
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!*p) {
+ /* Empty line applies to all addresses */
+ new_node = MallocNew(AllowDeny);
+ new_node->allow = allow;
+ new_node->all = all;
+ new_node->ip = 0UL;
+ new_node->subnet_bits = 0;
+ } else {
+ char *slashpos;
+ slashpos = strchr(p, '/');
+ if (slashpos) *slashpos = 0;
+
+ n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
+
+ if (n >= 1) {
+ new_node = MallocNew(AllowDeny);
+ new_node->allow = allow;
+ new_node->all = all;
+
+ a &= 0xff;
+ b &= 0xff;
+ c &= 0xff;
+ d &= 0xff;
+
+ switch (n) {
+ case 1:
+ new_node->ip = (a<<24);
+ new_node->subnet_bits = 8;
+ break;
+ case 2:
+ new_node->ip = (a<<24) | (b<<16);
+ new_node->subnet_bits = 16;
+ break;
+ case 3:
+ new_node->ip = (a<<24) | (b<<16) | (c<<8);
+ new_node->subnet_bits = 24;
+ break;
+ case 4:
+ new_node->ip = (a<<24) | (b<<16) | (c<<8) | d;
+ new_node->subnet_bits = 32;
+ break;
+ default:
+ assert(0);
+
+ }
+
+ if (slashpos) {
+ int specified_subnet_bits, n;
+ n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
+ if (n == 1) {
+ new_node->subnet_bits = specified_subnet_bits;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read subnet size at line %d", line_number);
+ }
+ }
+
+ } else {
+ ip_addr = DNS_Name2IPAddress(p);
+ if (ip_addr != DNS_Failed_Address) {
+ new_node = MallocNew(AllowDeny);
+ new_node->allow = allow;
+ new_node->all = all;
+ new_node->ip = ip_addr;
+ new_node->subnet_bits = 32;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read address at line %d", line_number);
+ }
+ }
+ }
+
+ if (new_node) {
+ new_node->prev = list->prev;
+ new_node->next = list;
+ list->prev->next = new_node;
+ list->prev = new_node;
+ }
+
+}
+
+
+/* ================================================== */
+
+static void
+parse_allow(const char *line)
+{
+ parse_allow_deny(line, &ntp_auth_list, 1);
+}
+
+
+/* ================================================== */
+
+static void
+parse_deny(const char *line)
+{
+ parse_allow_deny(line, &ntp_auth_list, 0);
+}
+
+/* ================================================== */
+
+static void
+parse_cmdallow(const char *line)
+{
+ parse_allow_deny(line, &cmd_auth_list, 1);
+}
+
+
+/* ================================================== */
+
+static void
+parse_cmddeny(const char *line)
+{
+ parse_allow_deny(line, &cmd_auth_list, 0);
+}
+
+/* ================================================== */
+
+static unsigned long
+parse_an_address(const char *line, const char *errmsg)
+{
+ unsigned long a, b, c, d;
+ int n;
+ n = sscanf(line, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
+ if (n == 4) {
+ return (((a&0xff)<<24) | ((b&0xff)<<16) |
+ ((c&0xff)<<8) | (d&0xff));
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, errmsg, line_number);
+ return 0UL;
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_bindaddress(const char *line)
+{
+ bind_address = parse_an_address(line, "Could not read bind address at line %d\n");
+}
+
+/* ================================================== */
+
+static void
+parse_bindcmdaddress(const char *line)
+{
+ bind_cmd_address = parse_an_address(line, "Could not read bind command address at line %d\n");
+}
+
+/* ================================================== */
+
+static void
+parse_pidfile(const char *line)
+{
+ pidfile = MallocArray(char, 1 + strlen(line));
+ sscanf(line, "%s", pidfile);
+ strip_trailing_spaces(pidfile);
+}
+
+/* ================================================== */
+
+typedef struct {
+ /* Both in host (not necessarily network) order */
+ unsigned long addr;
+ unsigned short port;
+ int interval;
+} NTP_Broadcast_Destination;
+
+static NTP_Broadcast_Destination *broadcasts = NULL;
+static int max_broadcasts = 0;
+static int n_broadcasts = 0;
+
+/* ================================================== */
+
+static void
+parse_broadcast(const char *line)
+{
+ /* Syntax : broadcast <interval> <broadcast-IP-addr> [<port>] */
+ int port;
+ unsigned int a, b, c, d;
+ int n;
+ int interval;
+ unsigned long addr;
+
+ n = sscanf(line, "%d %u.%u.%u.%u %d", &interval, &a, &b, &c, &d, &port);
+ if (n < 5) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not parse broadcast directive at line %d", line_number);
+ return;
+ } else if (n == 5) {
+ /* default port */
+ port = 123;
+ } else if (n > 6) {
+ LOG(LOGS_WARN, LOGF_Configure, "Too many fields in broadcast directive at line %d", line_number);
+ }
+
+ addr = ((unsigned long) a << 24) |
+ ((unsigned long) b << 16) |
+ ((unsigned long) c << 8) |
+ ((unsigned long) d );
+
+ if (max_broadcasts == n_broadcasts) {
+ /* Expand array */
+ max_broadcasts += 8;
+ if (broadcasts) {
+ broadcasts = ReallocArray(NTP_Broadcast_Destination, max_broadcasts, broadcasts);
+ } else {
+ broadcasts = MallocArray(NTP_Broadcast_Destination, max_broadcasts);
+ }
+ }
+
+ broadcasts[n_broadcasts].addr = addr;
+ broadcasts[n_broadcasts].port = port;
+ broadcasts[n_broadcasts].interval = interval;
+ ++n_broadcasts;
+}
+
+/* ================================================== */
+
+static void
+parse_linux_hz(const char *line)
+{
+ if (1 == sscanf(line, "%d", &linux_hz)) {
+ set_linux_hz = 1;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not parse linux_hz directive at line %d", line_number);
+ }
+}
+
+/* ================================================== */
+
+static void
+parse_linux_freq_scale(const char *line)
+{
+ if (1 == sscanf(line, "%lf", &linux_freq_scale)) {
+ set_linux_freq_scale = 1;
+ } else {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not parse linux_freq_scale directive at line %d", line_number);
+ }
+}
+
+/* ================================================== */
+
+void
+CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything)
+{
+ if (do_init_stepslew) {
+ ACQ_StartAcquisition(n_init_srcs, init_srcs_ip, init_slew_threshold, after_hook, anything);
+ } else {
+ (after_hook)(anything);
+ }
+}
+
+/* ================================================== */
+
+void
+CNF_AddSources(void) {
+ NTP_Remote_Address server;
+ int i;
+
+ for (i=0; i<n_ntp_sources; i++) {
+ server.ip_addr = ntp_sources[i].ip_addr;
+ server.port = ntp_sources[i].port;
+
+ switch (ntp_sources[i].type) {
+ case SERVER:
+ NSR_AddServer(&server, &ntp_sources[i].params);
+ break;
+
+ case PEER:
+ NSR_AddPeer(&server, &ntp_sources[i].params);
+ break;
+ }
+
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+
+void
+CNF_AddBroadcasts(void)
+{
+ int i;
+ for (i=0; i<n_broadcasts; i++) {
+ BRD_AddDestination(broadcasts[i].addr,
+ broadcasts[i].port,
+ broadcasts[i].interval);
+ }
+}
+
+/* ================================================== */
+
+unsigned short
+CNF_GetNTPPort(void)
+{
+ return ntp_port;
+}
+
+/* ================================================== */
+
+unsigned short
+CNF_GetAcquisitionPort(void)
+{
+ return acquisition_port;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetDriftFile(void)
+{
+ return drift_file;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetLogDir(void)
+{
+ return logdir;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetDumpDir(void)
+{
+ return dumpdir;
+}
+
+/* ================================================== */
+
+int
+CNF_GetLogMeasurements(void)
+{
+ return do_log_measurements;
+}
+
+/* ================================================== */
+
+int
+CNF_GetLogStatistics(void)
+{
+ return do_log_statistics;
+}
+
+/* ================================================== */
+
+int
+CNF_GetLogTracking(void)
+{
+ return do_log_tracking;
+}
+
+/* ================================================== */
+
+int
+CNF_GetLogRtc(void)
+{
+ return do_log_rtc;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetKeysFile(void)
+{
+ return keys_file;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetRtcFile(void)
+{
+ return rtc_file;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetRtcDevice(void)
+{
+ return rtc_device;
+}
+
+/* ================================================== */
+
+unsigned long
+CNF_GetCommandKey(void)
+{
+ return command_key_id;
+}
+
+/* ================================================== */
+
+int
+CNF_GetDumpOnExit(void)
+{
+ return do_dump_on_exit;
+}
+
+/* ================================================== */
+
+double
+CNF_GetMaxUpdateSkew(void)
+{
+ return max_update_skew;
+}
+
+/* ================================================== */
+
+int
+CNF_GetManualEnabled(void)
+{
+ return enable_manual;
+}
+
+/* ================================================== */
+
+int
+CNF_GetCommandPort(void) {
+ return cmd_port;
+}
+
+/* ================================================== */
+
+int
+CNF_AllowLocalReference(int *stratum)
+{
+ if (enable_local) {
+ *stratum = local_stratum;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+CNF_GetRTCOnUTC(void)
+{
+ return rtc_on_utc;
+}
+
+/* ================================================== */
+
+void
+CNF_GetLogChange(int *enabled, double *threshold)
+{
+ *enabled = do_log_change;
+ *threshold = log_change_threshold;
+}
+
+/* ================================================== */
+
+void
+CNF_GetMailOnChange(int *enabled, double *threshold, char **user)
+{
+ if (mail_user_on_change) {
+ *enabled = 1;
+ *threshold = mail_change_threshold;
+ *user = mail_user_on_change;
+ } else {
+ *enabled = 0;
+ *threshold = 0.0;
+ *user = NULL;
+ }
+}
+
+/* ================================================== */
+
+void
+CNF_SetupAccessRestrictions(void)
+{
+ AllowDeny *node;
+ int status;
+
+ for (node = ntp_auth_list.next; node != &ntp_auth_list; node = node->next) {
+ status = NCR_AddAccessRestriction(node->ip, node->subnet_bits, node->allow, node->all);
+ if (!status) {
+ LOG(LOGS_WARN, LOGF_Configure, "Bad subnet for %08lx", node->ip);
+ }
+ }
+
+ for (node = cmd_auth_list.next; node != &cmd_auth_list; node = node->next) {
+ status = CAM_AddAccessRestriction(node->ip, node->subnet_bits, node->allow, node->all);
+ if (!status) {
+ LOG(LOGS_WARN, LOGF_Configure, "Bad subnet for %08lx", node->ip);
+ }
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+int
+CNF_GetNoClientLog(void)
+{
+ return no_client_log;
+}
+
+/* ================================================== */
+
+void
+CNF_GetBindAddress(unsigned long *addr)
+{
+ *addr = bind_address;
+}
+
+/* ================================================== */
+
+void
+CNF_GetBindCommandAddress(unsigned long *addr)
+{
+ *addr = bind_cmd_address ? bind_cmd_address : bind_address;
+}
+
+/* ================================================== */
+
+char *
+CNF_GetPidFile(void)
+{
+ return pidfile;
+}
+
+/* ================================================== */
+
+void
+CNF_GetLinuxHz(int *set, int *hz)
+{
+ *set = set_linux_hz;
+ *hz = linux_hz;
+}
+
+/* ================================================== */
+
+void
+CNF_GetLinuxFreqScale(int *set, double *freq_scale)
+{
+ *set = set_linux_freq_scale;
+ *freq_scale = linux_freq_scale ;
+}
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/conf.h,v 1.24 2003/03/27 23:45:47 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for configuration module
+ */
+
+#ifndef GOT_CONF_H
+#define GOT_CONF_H
+
+extern char *CNF_GetRtcDevice(void);
+
+extern void CNF_ReadFile(const char *filename);
+
+extern void CNF_AddSources(void);
+extern void CNF_AddBroadcasts(void);
+
+extern void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything);
+
+extern unsigned short CNF_GetAcquisitionPort(void);
+extern unsigned short CNF_GetNTPPort(void);
+extern char *CNF_GetDriftFile(void);
+extern char *CNF_GetLogDir(void);
+extern char *CNF_GetDumpDir(void);
+extern int CNF_GetLogMeasurements(void);
+extern int CNF_GetLogStatistics(void);
+extern int CNF_GetLogTracking(void);
+extern int CNF_GetLogRtc(void);
+extern char *CNF_GetKeysFile(void);
+extern char *CNF_GetRtcFile(void);
+extern unsigned long CNF_GetCommandKey(void);
+extern int CNF_GetDumpOnExit(void);
+extern int CNF_GetManualEnabled(void);
+extern int CNF_GetCommandPort(void);
+extern int CNF_GetRTCOnUTC(void);
+extern void CNF_GetLogChange(int *enabled, double *threshold);
+extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user);
+extern int CNF_GetNoClientLog(void);
+extern void CNF_GetBindAddress(unsigned long *addr);
+extern void CNF_GetBindCommandAddress(unsigned long *addr);
+extern char *CNF_GetPidFile(void);
+extern void CNF_GetLinuxHz(int *set, int *hz);
+extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale);
+
+/* Value returned in ppm, as read from file */
+extern double CNF_GetMaxUpdateSkew(void);
+extern int CNF_AllowLocalReference(int *stratum);
+
+extern void CNF_SetupAccessRestrictions(void);
+
+#endif /* GOT_CONF_H */
--- /dev/null
+#!/bin/sh
+#
+# $Header: /cvs/src/chrony/configure,v 1.28 2003/07/01 20:53:00 richard Exp $
+#
+# =======================================================================
+#
+# chronyd/chronyc - Programs for keeping computer clocks accurate.
+#
+# #COPYRIGHT#
+#
+# =======================================================================
+
+# This configure script determines the operating system type and version
+
+if [ "x${CC}" = "x" ]; then
+ MYCC="gcc"
+else
+ MYCC="${CC}"
+fi
+
+if [ "x${CFLAGS}" = "x" ]; then
+ MYCFLAGS="-O2 -g"
+else
+ MYCFLAGS="${CFLAGS}"
+fi
+
+# ======================================================================
+# FUNCTIONS
+
+#{{{ test_for_sqrt
+test_for_sqrt () {
+# 0 : doesn't need -lm
+# 1 : needs -lm
+# 2 : doesn't even link with -lm
+
+ cat >docheck.c <<EOF;
+#include <math.h>
+int main(int argc, char **argv) {
+ return (int) sqrt((double)argc);
+}
+EOF
+
+ ${MYCC} ${MYCFLAGS} -c -o docheck.o docheck.c >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ ${MYCC} ${MYCFLAGS} -o docheck docheck.o >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ result=0
+ else
+ ${MYCC} ${MYCFLAGS} -o docheck docheck.o -lm >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ result=1
+ else
+ result=2
+ fi
+ fi
+ else
+ result=2
+ fi
+
+ rm -f docheck.c docheck.o docheck
+ echo $result
+}
+#}}}
+#{{{ test_for_stdint_h
+test_for_stdint_h () {
+ cat >docheck.c <<EOF;
+#include <stdint.h>
+int main(int argc, char **argv) {
+ return 0;
+}
+EOF
+
+ ${MYCC} ${MYCFLAGS} -c -o docheck.o docheck.c >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ result=0
+ else
+ result=1
+ fi
+
+ rm -f docheck.c docheck.o
+ echo $result
+}
+#}}}
+#{{{ test_for_inttypes_h
+test_for_inttypes_h () {
+ cat >docheck.c <<EOF;
+#include <inttypes.h>
+int main(int argc, char **argv) {
+ return 0;
+}
+EOF
+
+ ${MYCC} ${MYCFLAGS} -c -o docheck.o docheck.c >/dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ result=0
+ else
+ result=1
+ fi
+
+ rm -f docheck.c docheck.o
+ echo $result
+}
+#}}}
+
+# ======================================================================
+
+
+
+OPERATINGSYSTEM=`uname -s`
+VERSION=`uname -r`
+MACHINE=`uname -m`
+
+SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
+
+EXTRA_LIBS=""
+EXTRA_CLI_LIBS=""
+EXTRA_OBJECTS=""
+EXTRA_DEFS=""
+INSTALL_PREFIX=/usr/local
+SYSDEFS=""
+
+# Support for readline (on by default)
+feat_readline=1
+readline_lib=""
+readline_inc=""
+ncurses_lib=""
+
+SETINFODIR=""
+SETMANDIR=""
+
+for option
+do
+ case "$option" in
+ --prefix=* | --install_prefix=* )
+ INSTALL_PREFIX=`echo $option | sed -e 's/[^=]*=//;'`
+ ;;
+ --trace )
+ EXTRA_DEFS="-DTRACEON"
+ ;;
+ --disable-readline )
+ feat_readline=0
+ ;;
+ --with-readline-library=* )
+ readline_lib=-L`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ --with-readline-includes=* )
+ readline_inc=-I`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ --with-ncurses-library=* )
+ ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ --infodir=* )
+ SETINFODIR=`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ --mandir=* )
+ SETMANDIR=`echo $option | sed -e 's/^.*=//;'`
+ ;;
+ * )
+ echo "Unrecognized option : " $option
+ esac
+done
+
+case $SYSTEM in
+ SunOS-sun4* )
+ case $VERSION in
+ 4.* )
+ EXTRA_OBJECTS="sys_sunos.o strerror.o"
+ EXTRA_LIBS="-lkvm"
+ SYSDEFS="-DSUNOS"
+ echo "Configuring for SunOS (" $SYSTEM "version" $VERSION ")"
+ ;;
+ 5.* )
+ EXTRA_OBJECTS="sys_solaris.o"
+ EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf"
+ EXTRA_CLI_LIBS="-lsocket -lnsl"
+ SYSDEFS="-DSOLARIS"
+ echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
+ if [ $VERSION = "5.3" ]; then
+ SYSDEFS="$SYSDEFS -DHAS_NO_BZERO"
+ echo "Using memset() instead of bzero()"
+ fi
+ ;;
+ esac
+ ;;
+ Linux* )
+ EXTRA_OBJECTS="sys_linux.o wrap_adjtimex.o rtc_linux.o"
+ SYSDEFS="-DLINUX"
+ echo "Configuring for " $SYSTEM
+ if [ -r /usr/include/linux/spinlock.h ]; then
+ SYSDEFS="$SYSDEFS -DHAS_SPINLOCK_H"
+ echo "The system has <spinlock.h>, using that"
+ else
+ echo "The system does not have <spinlock.h>, using private definition for spinlock_t"
+ fi
+ if [ "${MACHINE}" = "alpha" ]; then
+ echo "Enabling -mieee"
+ # FIXME: Should really test for GCC
+ SYSDEFS="$SYSDEFS -mieee -DALPHA"
+ fi
+ ;;
+
+ BSD/386-i[3456]86 )
+ # Antti Jrvinen <costello@iki.fi> reported that this system can
+ # be supported with the SunOS 4.x driver files.
+ EXTRA_OBJECTS="sys_sunos.o strerror.o"
+ EXTRA_LIBS="-lkvm"
+ SYSDEFS="-DSUNOS"
+ echo "Configuring for BSD/386 (using SunOS driver)"
+ ;;
+ NetBSD-* )
+ EXTRA_OBJECTS="sys_netbsd.o"
+ EXTRA_LIBS="-lkvm"
+ SYSDEFS=""
+ echo "Configuring for $SYSTEM"
+ ;;
+ SunOS-i86pc* )
+ # Doug Woodward <dougw@whistler.com> reported that this configuration
+ # works for Solaris 2.8 / SunOS 5.8 on x86 platforms
+ EXTRA_OBJECTS="sys_solaris.o"
+ EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf"
+ EXTRA_CLI_LIBS="-lsocket -lnsl"
+ SYSDEFS="-DSOLARIS"
+ echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
+ ;;
+ CYGWIN32_NT-i[3456]86 )
+ EXTRA_OBJECTS="sys_winnt.o"
+ EXTRA_LIBS=""
+ SYSDEFS="-DWINNT"
+ echo "Configuring for Windows NT (Cygwin32)"
+ ;;
+ * )
+ echo "Sorry, I don't know how to build this software on your system."
+ exit 1
+ ;;
+esac
+
+printf "Checking if sqrt() needs -lm : "
+case `test_for_sqrt`
+in
+0)
+ printf "No\n"
+ LIBS=""
+;;
+1)
+ printf "Yes\n"
+ LIBS="-lm"
+;;
+*)
+ printf "\nCan't compile/link a program which uses sqrt(), bailing out\n"
+ exit 1
+;;
+esac
+
+printf "Checking for <stdint.h> : "
+if [ `test_for_stdint_h` -eq 0 ]; then
+ printf "Yes\n"
+ SYSDEFS="${SYSDEFS} -DHAS_STDINT_H"
+else
+ printf "No\n"
+fi
+
+printf "Checking for <inttypes.h> : "
+if [ `test_for_inttypes_h` -eq 0 ]; then
+ printf "Yes\n"
+ SYSDEFS="${SYSDEFS} -DHAS_INTTYPES_H"
+else
+ printf "No\n"
+fi
+
+if [ "x${MYCC}" = "xgcc" ]; then
+ CCWARNFLAGS="-Wmissing-prototypes -Wall"
+else
+ CCWARNFLAGS=""
+fi
+
+if [ $feat_readline = "1" ]; then
+ READLINE_COMPILE="-DFEAT_READLINE=1 $readline_inc"
+ READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
+else
+ READLINE_COMPILE=""
+ READLINE_LINK=""
+fi
+
+MANDIR=${INSTALL_PREFIX}/man
+INFODIR=${INSTALL_PREFIX}/info
+if [ "x$SETINFODIR" != "x" ]; then
+ INFODIR=$SETINFODIR
+fi
+if [ "x$SETMANDIR" != "x" ]; then
+ MANDIR=$SETMANDIR
+fi
+
+sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
+ s%@CC@%${MYCC}%;\
+ s%@CFLAGS@%${MYCFLAGS}%;\
+ s%@CCWARNFLAGS@%${CCWARNFLAGS}%;\
+ s%@LIBS@%${LIBS}%;\
+ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
+ s%@SYSDEFS@%${SYSDEFS}%;\
+ s%@EXTRA_DEFS@%${EXTRA_DEFS}%;\
+ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
+ s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
+ s%@READLINE_LINK@%${READLINE_LINK}%;\
+ s%@INSTALL_PREFIX@%${INSTALL_PREFIX}%;\
+ s%@MANDIR@%${MANDIR}%;\
+ s%@INFODIR@%${INFODIR}%;"\
+ < Makefile.in > Makefile
+
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+#!/usr/bin/perl
+# Copyright (C) Paul Elliott 2002
+my($copyrighttext) = <<'EOF';
+# Copyright (C) Paul Elliott 2002
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# SEE COPYING FOR DETAILS
+EOF
+
+#modules we use.
+
+use Socket;
+use Getopt::Std;
+use Net::DNS;
+use Tie::Syslog;
+use File::Temp qw/ :mktemp /;
+use File::Copy;
+
+local($res) = new Net::DNS::Resolver;
+
+#dns lookup of IP address.
+#returns ip or errorstring.
+sub gethostaddr($) #get ip address from host
+{
+ my($host) = shift;
+ $query = $res->search($host);
+ if ($query) {
+ foreach $rr ($query->answer) {
+ next unless $rr->type eq "A";
+ print $rr->address, "\n" if $pedebug;
+ return $rr->address;
+ }
+ } else {
+ print "query failed: ", $res->errorstring, "\n" if $pedebug;
+ return $res->errorstring;
+ }
+
+}
+
+#send messages to syslog
+
+sub Log($$)
+ {
+ if ($log) {
+ my($level) = shift;
+ my($mess) =shift;
+
+ tie *MYLOG, 'Tie::Syslog',$level,$0,'pid','unix';
+ print MYLOG $mess;
+
+ untie *MYLOG;
+ }
+ }
+
+#send message to output or syslog
+#and die.
+
+sub BadDie($)
+{
+ my($myerr) =$!;
+ my($mess)=shift;
+
+ if($log){
+ tie *MYLOG, 'Tie::Syslog','local0.err',$0,'pid','unix';
+ print MYLOG $mess;
+ print MYLOG $myerr;
+
+ untie *MYLOG;
+
+ } else {
+ print "$mess\n$myerr\n";
+ }
+ die $mess;
+}
+
+sub isIpAddr($) #return true if looks like ip address
+{
+ my($ip) = shift;
+ return 1 if ( $ip =~ m/$ipOnlyPAT/ );
+ return 0;
+}
+sub isHostname($) #return true if looks like ip address
+{
+ my($ip) = shift;
+ return 1 if ( $ip =~ m/$hostnameOnlyPAT/ );
+ return 0;
+}
+
+#send commands to chronyc by piping.
+sub chronyc($) #send commands to chronyc
+{
+ my($command) = shift;
+ my($err) = "/var/tmp/chronyc.log";
+ my($chronyP) = "/usr/local/bin/chronyc";
+ open(CHRONY, "| $chronyP 1>$err 2>&1");
+
+ print CHRONY "$passwd$command\n";
+
+ close(CHRONY);
+
+ Log('local0.info',"chronyc command issued=$command");
+ #look at status lines till return bad.
+ open( IN, "<$err");
+ my($status);
+ while (<IN>) {
+ $status = $_;
+
+ unless ( m/\A200 OK/ ) {
+ last;
+ }
+
+ }
+
+ $status ="" if ( $status =~ m/\A200 OK/ );
+ close(IN);
+ unlink $err;
+ Log('local0.info',"chronyc results=$status");
+ return $status;
+
+}
+
+#common patterns
+
+# an ip address patern
+local($ipPAT) = qr/\d{1,3}(?:\.\d{1,3}){3}/;
+# an hostname pattern
+local($hostnamePAT) = qr/\w+(?:\.\w+)*/;
+#line with hostname only
+local($hostnameOnlyPAT) = qr/\A$hostnamePAT\Z/;
+#line with ip address only
+local($ipOnlyPAT) =qr/\A$ipPAT\Z/;
+
+#options hash
+my(%opts);
+
+
+getopts('nuadslPSC', \%opts);
+
+local($log) = ( $opts{'l'} ) ? 1 : 0;
+
+my($offline) = !( $opts{'n'} ) ;
+my($offlineS) = ( $opts{'n'} ) ? " " : " offline" ;
+
+# paul elliotts secret debug var. no one will ever find out about it.
+local($pedebug)=( ($ENV{"PAULELLIOTTDEBUG"}) or ($opts{P}) );
+
+if ($opts{C}) {
+
+ print $copyrighttext;
+ exit 0;
+}
+
+
+print <<"EOF" unless $opts{'S'};
+$0, Copyright (C) 2002 Paul Elliott
+$0 comes with ABSOLUTELY NO WARRANTY; for details
+invoke $0 -C. This is free software, and you are welcome
+to redistribute it under certain conditions; invoke $0 -C
+for details.
+EOF
+
+
+
+local($passwd);
+
+# password to send to chronyc
+my($pl) = $ENV{"CHRONYPASSWORD"};
+
+#password comand to send to chronyc
+if ( $pl ) {
+ $passwd = "password $pl\n";
+} else {
+ $passwd = "";
+}
+print "passwd=$passwd\n" if ($pedebug);
+
+my(%host2ip);
+
+# hash of arrays. host2ip{$host}[0] is ip address for this host
+# host2ip{$host}[1] is rest of paramenters for this host exc offline.
+
+#if debuging do chrony.conf in current directory.
+my($listfile) =( ($pedebug) ? "./chrony.conf" : "/etc/chrony.conf") ;
+
+# This section reads in the old data about
+# hostnames IP addresses and server parameters
+# data is stored as it would be in chrony.conf
+# file i.e.:
+#># HOSTNAME
+#>server IPADDR minpoll 5 maxpoll 10 maxdelay 0.4 offline
+#
+# the parameter offline is omitted if the -n switch is specified.
+# first parameter is the filename of the file usually
+# is /etc/DNSchrony.conf
+# this is where we store the list of DNS hosts.
+# hosts with static IP address shold be kept in chrony.conf
+
+# this is header that marks dnyamic host section
+my($noedithead)=<<'EOF';
+## DNSchrony dynamic dns server section. DO NOT EDIT
+## per entry FORMAT:
+## |--------------------------------------------|
+## |#HOSTNAME |
+## |server IP-ADDRESS extra-params [ offline ] |
+## |--------------------------------------------|
+EOF
+#patern that recognizes above.
+my($noeditheadPAT) =
+qr/\#\#\s+DNSchrony\s+dynamic\s+dns\s+server\s+section\.\s+DO\s+NOT\s+EDIT\s*/;
+
+#end of header marker.
+my($noeditheadend)=<<'EOF';
+## END OF DNSchrony dynamic dns server section.
+EOF
+
+#pattern that matches above.
+my($noeditheadendPAT)=
+qr/\#\#\s+END\s+OF\s+DNSchrony\s+dynamic\s+dns\s+server\s+section.\s*/;
+
+#array to hold non dns portion of chrony.conf
+my(@chronyDconf);
+
+
+my($ip);
+my($rest);
+my($host);
+
+# for each entry in the list of hosts....
+open(READIN, "<$listfile") or BadDie("Can not open $listfile");
+
+# read till dynamic patern read save in @chronyDconf
+
+while ( <READIN> ) {
+
+ my($line) = $_;
+
+ last if ( m/\A$noeditheadPAT\Z/ );
+
+ push(@chronyDconf,$line);
+
+}
+
+while ( <READIN> ) {
+
+ #end loop when end of header encountered
+ last if ( m/\A$noeditheadendPAT/ );
+
+ # parse the line giving ip address, extra pamamters, and host
+ #do host comment line first
+ ($host) = m{
+ \A\#\s*
+ ($hostnamePAT)
+ \s*\z
+ }xio;
+
+ #no match skip this line.
+ next unless ( $host );
+
+ # read next line
+ $_ = <READIN>;
+
+ # parse out ip address extra parameters.
+ ($ip,$rest) =
+ m{
+ \A
+ \s*
+ server #server comand
+ \s+
+ ($ipPAT) #ip address
+ (?ixo: \s )
+ \s*
+ (
+ (?(?!
+ (?iox: offline )? #skip to offline #
+ \s* #or #
+ \Z
+ ).)*
+ )
+ (?ixo:
+ \s*
+ (?ixo: offline )? #consume to #
+ \s*
+ \Z
+ )
+ }xio ;
+
+ #if failure again.
+ next unless ( $ip );
+
+ $rest =~ s/\s*\z//; #remove trail blanks
+ #from parameters
+ # store the data in the list
+ # key is host name value is
+ # array [0] is ip address
+ # [1] is other parameters
+ $host2ip{$host} = [$ip,$rest] ;
+ print "ip=$ip rest=$rest host=$host<\n" if $pedebug;
+
+}
+#read trailing line into @chronyDconf
+while ( <READIN> ) {
+
+ push(@chronyDconf,$_);
+
+}
+
+close(READIN) or BadDie("can not close $listfile");
+
+#if the add command:
+# command can be HOST=IPADDRESS OTHER_PARAMETERS
+# means add the server trust the ip address geven with out a dns lookup
+# good for when dns is down but we know the ip addres
+# or
+# HOST OTHER_PARAMETERS
+#we lookup the ip address with dns.
+
+if ($opts{'a'}) {
+ my($param)= shift;
+
+
+ # parse the param is it hostname
+ if ( ($host,$ip) = $param =~ m/\A($hostnamePAT)=($ipPAT)\Z/ ) {
+ printf "ip=$ip host=$host\n" if ($pedebug);
+ } else {
+
+ $host = $param;
+
+ # get the ip address
+ $ip = gethostaddr($host);
+
+ if ( ! isIpAddr($ip) or ! isHostname($host) ) {
+ print "query failed: ", $ip, "host=$host\n" if $pedebug;
+ exit 1;
+ }
+ }
+ printf "ip=$ip host=$host\n" if ($pedebug);
+
+ # add the server using chronyc
+ my($status) = chronyc("add server $ip $rest");
+ if ($status) { #chronyc error
+ print "chronyc failed, status=$status\n";
+ exit 1;
+ }
+
+ # get rest of arguements
+ $rest = join( ' ', @ARGV);
+ print "rest=$rest\n" if ($pedebug);
+
+ #save node in hash
+ $host2ip{$host} = [$ip,$rest] ;
+ print "ip=$ip rest=$rest host=$host<\n" if $pedebug;
+
+}
+
+#delete command if arguement is ip address
+#just delete it
+#if a hostname look it up
+#then delete it.
+
+if ($opts{'d'}) {
+ $host = shift;
+
+ #get host name is it ap address
+ if ( isIpAddr($host) ) { # if ip address
+ my($hostIT);
+ my($found) =0;
+ foreach $hostIT (keys(%host2ip) ) { #search for match
+ if ( $host2ip{$hostIT}[0] eq $host) {
+ $found=1; #record match
+ }
+ } #end of search
+ if ($found) { #if match found
+ my($status) = chronyc("delete $host"); #chronyc
+ if ($status) { #chronyc error
+ print "chronyc failed, status=$status\n";
+ exit 1;
+ } else { #reiterate
+ foreach $hostIT (keys(%host2ip) ) {
+ if ( $host2ip{$hostIT}[0] eq $host) {
+ delete $host2ip{$hostIT}; #deleting match hosts
+ }
+ }
+
+ }
+
+ }
+ } else { #else not ip address
+ #must be hostname
+ if ( ! $host2ip{$host} ) {
+ print "No such host as $host listed\n";
+ exit 1;
+ }
+ #get ip address
+ $ip=gethostaddr($host);
+ if ( ! isIpAddr($ip) ) { #no ip address
+ print "query failed: ", $ip, "\n" if $pedebug;
+ exit 1;
+ }
+
+ printf "ip=$ip host=$host\n" if ($pedebug);
+
+ my($listed_host_ip) = $host2ip{$host}[0]; # get the ip address saved
+
+ if ( $ip ne $listed_host_ip) {
+ print
+ "Info: listed host ip=>$listed_host_ip".
+ "< is different from DNS ip=>$ip<\n";
+ $ip = $listed_host_ip;
+ }
+
+ # delete the server
+ my($status) = chronyc("delete $listed_host_ip\n");
+
+ if ($status) {
+ print "chronyc failed, status=$status\n";
+ exit 1;
+ }
+ #delete table entry
+ delete$host2ip{$host};
+ }
+
+}
+
+#update for each host who's dns ip address has changed
+#delete the old server and add the new. update the record.
+if ($opts{'u'}) {
+ my($command);
+
+ my(%prospective); # store new IP address we
+ #are thinking of changing.
+
+ Log('local0.info',
+ "Now searching for modified DNS entries.");
+
+ foreach $host (keys(%host2ip)) { #for each listed host
+ my($old_ip) = $host2ip{$host}[0]; #get old ip
+ $rest = $host2ip{$host}[1]; #extra params
+
+ $ip = gethostaddr($host); #get new ip from dns
+ #if error
+ if ( ! isIpAddr($ip) or ! isHostname($host) ) {
+ print "query failed: ", $ip, "host=$host\n";
+
+ Log('local0.err',"query failed: ". $ip . "host=$host");
+
+ exit 1;
+ }
+
+ next if($ip eq $old_ip); #if ip not changed, skip
+
+ Log('local0.info',"Ip address for $host has changed. Old IP address=".
+ "$old_ip, new IP address=$ip");
+ # add command to delete old host, add the new.
+ $command = $command . "delete $old_ip\n" .
+ "add server $ip $rest\n";
+
+ # we are now thinking about changing this host ip
+ $prospective{$host} = [$ip,$rest];
+ }
+ # submit all the accumulated chronyc commands if any.
+ if ($command) {
+ $status = chronyc($command);
+ if ($status) {
+ print "chronyc failed, status=$status\n";
+ Log('local0.err',"query failed: ". $ip . "host=$host");
+ exit 1;
+ }
+ } else { #if no commands exit
+ exit 0; #because no rewrite of file needed
+ }
+
+ #copy prospective modifications back into main table.
+ #we now know that all these mods were done with chronyc
+ foreach $host (keys(%prospective)) {
+ my($ip) = $prospective{$host}[0];
+ $rest = $prospective{$host}[1];
+ $host2ip{$host} = [$ip,$rest];
+ }
+}
+
+#starting for each entry we have read in from the old list
+# add the server in chronyc
+# this option is seldom used.
+
+if ($opts{'s'}) {
+ my($command)="";
+
+ foreach $host (keys(%host2ip)) {
+ $command = $command . "add server $host2ip{$host}[0] ".
+ "$host2ip{$host}[1]\n";
+ }
+ my($status) = chronyc($command);
+ if ($status) {
+ print "chronyc failed, status=$status\n";
+ exit 1;
+ }
+
+}
+# write out the data file in format
+#># HOSTNAME
+#>server IPADDRESS extra parameters [offline]
+# offline is omitted if -n switch is specified.
+
+my(@value);
+my($such);
+{
+ # to start out we write to temporary file.
+ (my($writeout) , my($outname)) = mkstemp( "${listfile}.outXXXXXXX");
+
+ $outname or BadDie("can not open for $listfile");
+
+
+ # save the chrony.conf part!
+ # and write the DYNAMIC header
+ print $writeout @chronyDconf, $noedithead;
+
+
+ # for each entry
+ foreach $host (keys(%host2ip) ){
+
+ #write the record
+
+ # write the comment that indicates the hostname
+ # and the server command.
+ print $writeout
+ "\# $host\nserver $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\n" ;
+
+ print
+ "server $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\# $host\n"
+ if $pedebug;
+
+ }
+
+ #WRITE THE end of dnyamic marker comment
+ print $writeout $noeditheadend;
+
+ # close the output file which was a temporary file.
+ close($writeout) or BadDie("can not close $outname");
+
+ # we now begin a intracate dance to make the the temporary
+ # the main chrony.conf
+ #
+ # if there is a chrony.conf.BAK save it to a temporary.
+ # rename chrony.conf to chrony.conf.BAK
+ # rename the temporary to chrony.conf
+ # if there already was a chrony.conf.BAK, unlink the copy of this.
+
+ my($backname) = "$listfile\.BAK";
+ my($backplain) = ( -f $backname );
+ my($saveback);
+ #if chrony.conf.BAK exists rename to a temporary.
+ if ($backplain ) {
+
+ $saveback = mktemp("${backname}.bakXXXXXXX");
+ move($backname,$saveback) or
+ BadDie "unable to move $backname to $savename";
+
+ }
+
+ # rename old chrony.conf to chrony.conf.BAK
+ move($listfile,$backname) or
+ BadDie "unable to move $listfile to $backname";
+
+ # rename our output to chrony.conf
+ move($outname,$listfile) or
+ BadDie "unable to move $outname to $listfile";
+
+ #if there was a temporary chrony.conf.BAK that we saved to temp
+ #unlink it
+ unlink($saveback) or BadDie "unable to unlink $saveback" if($backplain);
+
+}
--- /dev/null
+#!/usr/bin/bash
+
+# $1 is chrony password.
+# $2 is hostname to add or hostname=ipaddres
+# $3-$9 is rest of extra server parameters
+
+FIRST="$1"
+HOST="$2"
+shift 2
+
+#remaining parameters a the other paramaters to server command
+#excluding "offline"
+ARGS="$*"
+
+#if none use default taken from chrony documentation.
+DEF="minpoll 5 maxpoll 10 maxdelay 0.4"
+
+DARGS=${ARGS:-$DEF}
+
+CHRONYPASSWORD=$FIRST \
+/usr/local/bin/DNSchrony.pl -a "$HOST" "$DARGS"
--- /dev/null
+#!/usr/bin/bash
+
+# $1 is chrony password.
+# $2 host to be deleted if ip nn.n.n.n then no DNS used
+
+CHRONYPASSWORD=$1 \
+/usr/local/bin/DNSchrony.pl -d $2
--- /dev/null
+#!/usr/bin/bash
+
+# $1 is chrony password.
+
+
+CHRONYPASSWORD=$1 \
+/usr/local/bin/DNSchrony.pl -ulS
--- /dev/null
+ Copyright (C) Paul Elliott 2002
+
+
+DNSchrony.pl version -2.0
+
+Problem: If you look at the list of secondary NTP servers:
+http://www.eecis.udel.edu/~mills/ntp/clock2.htm
+
+you will find statements like this:
+
+"Note: IP addresses are subject to change; please use DNS"
+
+These servers represent a problem for chrony. Chrony is a program
+designed to work on hosts with an intermittent connection to the
+internet. Often no DNS is available when chrony starts. As chrony
+is currently designed, chronyd never sees a DNS host name. If a
+user specifies one when using chronyc's "add server" command, the
+DNS lookup is done by chronyc and an IP address is passed to chronyd.
+
+One can imagine I suppose, a redesign to chrony in which chronyd
+keeps track of DNS changes. But this has problems, all the time
+chronyd is fooling around with DNS, it would not be keeping track
+of its prime function, what the clocks and NTP servers are saying.
+This could result in poorer performance. Or perhaps you say that
+chronyd should be multi threaded. One thread to fool with DNS
+and another to keep track of time. But this introduces a great
+deal of complexity, and complexity is the enemy of elegant robust
+code. Besides, Richard probably has better things to do.
+
+I have attempted to address this problem with a humble perl script,
+which I now release under the GPL: DNSchrony.pl
+
+PLEA FOR HELP FROM EXPERIENCED PERL HACKERS.
+
+Please go thru the code and find errors and improvements.
+I am not quite an polished perl hacker. Please fix bugs and
+make improvements. It needs better documentation. Someone
+who knows how, put in some POD.
+
+END OF PLEA
+
+Philosophy of DNSchrony.pl: keep a list of servers that use
+DNS. From time to time, hopefully when DNS is up, go thru
+the list lookup all the hostnames and see if any ip addresses have
+changed. If any have changed, update our list and do chronyc
+"delete" and "add server" commands so that chronyd now talks to
+the right NTP server.
+
+Additional nuance: keep the list in /etc/chrony.conf in the
+form of comments starting with "#" and "server" commands
+legal in a chrony.conf file. Format of a list entry:
+
+# hostname
+server IP-ADDRESS extra server parameters
+
+These entries are delimited by special comments that allow
+DNSchrony.pl to find them and also tell humans not to mess with them.
+
+Example of such a section of a chrony.conf file:
+
+dumpdir /var/log/chrony
+rtcfile /etc/chrony.rtc
+
+## DNSchrony dynamic dns server section. DO NOT EDIT
+## per entry FORMAT:
+## |--------------------------------------------|
+## |#HOSTNAME |
+## |server IP-ADDRESS extra-params [ offline ] |
+## |--------------------------------------------|
+# tock.greyware.com
+server 208.14.208.44 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+# tick.greyware.com
+server 208.14.208.19 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+# ntppub.tamu.edu
+server 128.194.254.9 minpoll 5 maxpoll 10 maxdelay 0.4 offline
+## END OF DNSchrony dynamic dns server section.
+
+This allows the list of dynamic DNS servers to be preserved
+when chronyd is stoped/started.
+
+All servers that do not have ip addresses subject to change
+should be put in the regular part of chrony.conf as described
+in the chrony documentation.
+
+Security philosophy: DNSchrony does no security checking but
+relies on other security factors.
+
+Users without the privilege to modify /etc/chrony.conf and the
+directory /etc will be unable to use DNSchrony to do so, because
+of file protections. DNSchrony passes thru passwords to chronyc.
+Users that do not know the correct chronyc password will be
+unable to get chronyd do do anything. Thus, DNSchrony passes
+the buck to these other security features.
+
+INSTALLATION:
+
+copy the files: DNSchronyADD DNSchronyUPDATE DNSchronyDELETE DNSchrony.pl
+to /usr/local/bin. Backup the file /etc/chrony.conf leave hosts
+with static ip addresses in this file.
+
+DNSchrony uses the following perl modules. See that they are installed.
+Get them from CPAN if needed.
+
+Net::DNS, Tie::Syslog, Getopt::Std, Socket, File.
+
+Cause DNSchronyUPDATE bash script to run from time to time when DNS
+is working. If you have a dialup, one way to do this would be to
+modify your /etc/ppp/ip-up.local file as follows:
+
+cat <<EOF | /usr/local/bin/chronyc
+password mysecret
+online
+EOF
+# update all of the dynamic servers and save the result.
+# do not wait for response
+
+nohup /usr/local/bin/DNSchronyUPDATE mysecret >/dev/null 2>&1 &
+
+Since this file contains the chronyc password you will want to set the
+file permissions so that just everybody will not be able to read
+it. But you already did that when you put in the chronyc command. Any
+other way to make DNSchronyUPDATE run perodicly when DNS is up will
+also work.
+
+To add a server with a varying IP address one could run:
+/usr/local/bin/DNSchronyADD mysecret tock.greyware.com
+
+or if you want to specify different server parameters you
+could say:
+
+/usr/local/bin/DNSchronyADD mysecret tock.greyware.com "minpoll 10 maxpoll 20 maxdelay 0.8"
+
+The DNSchronyADD's default for these parameters is:
+"minpoll 5 maxpoll 10 maxdelay 0.4" values that are often shown
+as examples in the chrony documentation.
+
+If DNS is not running now but you know the IP address, you can say:
+/usr/local/bin/DNSchronyADD mysecret tock.greyware.com=208.14.208.44
+
+Of course, the IP address will be checked next time DNSchronyUPDATE
+runs.
+
+To delete dynamic DNS a server:
+/usr/local/bin/DNSchronyDELETE mysecret tock.greyware.com
+
+To change parameters delete and re-add.
+
+Of course, in all of the above "mysecret" is your chronyc password
+which SHOULD NOT BE "mysecret".
+----------------------------------------------
+DNSchrony.pl is covered by the GPL
+# Copyright (C) Paul Elliott 2002
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# SEE COPYING FOR DETAILS
\ No newline at end of file
--- /dev/null
+#example file /etc/ppp/ip-up.local
+#originally from SuSE distribution
+#modified for chrony
+cat <<EOF | /usr/local/bin/chronyc
+password mysecret
+online
+EOF
+# update all of the dynamic servers and save the result.
+# do not wait for response
+
+nohup /usr/local/bin/DNSchronyUPDATE mysecret >/dev/null 2>&1 &
+#other stuff who knows?
+
+# The following lines added for Linux-HA support # Heartbeat
+DEVFILE=`echo $DEVICE | sed -e 's!^/dev/!!' -e 's!/!.!g'` # Heartbeat
+OUTFILE=/var/run/ppp.d/$DEVFILE # Heartbeat
+( # Heartbeat
+echo "$IPREMOTE" # Heartbeat
+echo "$IFNAME" # Heartbeat
+echo "$PPPD_PID" # Heartbeat
+echo "$IPLOCAL" # Heartbeat
+) > $OUTFILE # Heartbeat
--- /dev/null
+From amb@gedanken.demon.co.uk Tue Aug 17 22:14:00 1999
+Date: Fri, 6 Aug 1999 19:00:24 +0100
+From: Andrew M. Bishop <amb@gedanken.demon.co.uk>
+To: richard@rrbcurnow.freeserve.co.uk
+Subject: Re: Chrony and laptop configuration
+
+Hi,
+
+Attached is the apmd_proxy script from the apmd-3.0beta9 distribution.
+
+The changes that I would make are the following:
+
+Replace the update_clock function (line 122) with
+
+update_clock () {
+
+chronyd -f /etc/chrony.conf
+
+}
+
+Around line 171 (in the suspend actions section) I would kill chronyd.
+
+begin 644 apmd_proxy.gz
+M'XL("+L@JS<``V%P;61?<')O>'D`I5K[;]M&$OY9^BLV2JZQ6TF.`Q2XUDA1
+MQU8<]>('_&A[.!R,-;D2MR&Y+)?TXZ[WO]\W,\N'%-E)[X08CJ2=V7G/-T,_
+M?[9S8_,=GPR?*_S3119?%Z6[?U`3A=_+4F<JMK[059284BU<J?;/CE6L3>9R
+M$!R4VB[5L2X_WNDRKM161!]D/Z:)T9FVZ73I%]$TUUY/E^YV6[W>Q>$'M?O=
+M=]^!^E#?VEB]+=U=;M)4;<4W/Q;.5]-$E[=@-S5QO:V^4S_5N0DD(+I,K%<^
+M(0(?E;:H%-Y'.DU-K&X>5)68GHAJBU3:5G>)R?DK7^G*@(M;*)T_J,+=0:U,
+MYWII,I-7:E'G465!F&AP372^-/%4X5*CS+V.*F5N<<R#EZ[`IBKM<@D.Q)ID
+ML/F267=VC$UA\E@11W?'7Z@[8NWRA5W6I8G)[!7>9X5-C:IL9J:LYR^V2BP)
+M3>J*HJS`@Z],IG2<V=SZJM05?.(35Z>Q*NJ*M0(SJ!1[TK-4FA7RL(&-DN;H
+MC5&%*>'/#&:K"XC'EH%".O>6"42,GBJ=G>\@F_*%B>S"1DJ7RSIKK0*-25Q<
+M0.*RN<A0]`W9U$51799LU#F[KJ?@2Z]*XR&,MS<VM16\Z=3OM2G9K>""P(@1
+M:(;4:ASM37EK(Z.V;JU6.Y`TVH',VQRKF<-9FY.:FG0:DT+P!MA6^J-(J`O0
+M%*4E[<54HO@[T,/E69&:<?";1<R1!=2H9Q5?>_:P.&:D?JM]!?/B3L-ZMRYC
+M-1/$-%D0$BP=1,/OAD'F8A-";<6[X)(A9^ZL3X@LN$W!2V;2^2`X.;50J\Y3
+MIV.*Q;BTMZ:$U8D+$EG?<(B25+FI[ESYD60`1QW1W;^T:=**#(?4%",I[%..
+M0T`\8@HYJW(R=SH:4UQ+T%%4@Z_SC8DI-+M(+`T)B9A@P\\7+,)'4Z(J2"ZS
+M%B0-G:/LW)WNOJ(@2.R2ZM)6[<69),T(/$(&D+T6EM*OKK;'_5B.=(YKJ[K,
+M$1#*E"6817``AXT$6^,7XH/DR&-4%\G^,8P6VP@A!5NBM%1)*`%TGCXC5ZYI
+M^)N)*H[Z3^(J2$0^CN%-R&`78W6C81`E1?;LBG1M'$86O*7DP&?0&W;)$2FU
+MV!4L6\$10A6%-=7%$(IDS;?STXM-HK'Q\7KV3)V<7L[HMY)JBW^YJT*VM`+7
+M(B"41<`?FAL+0TK<^&>!U_GL\NK\1/V\_^%J]KUP5Z_06G*'^-9I\,#>$Z96
+ME.TZBDQ1<:FDURXXB-0]9:D*!LJMXZN+2_5^_^<9ZWMX/O]Y=BXA<WJNWL^/
+MWL_.MUG"]X:XEV:E?GN#DI-'QG,L=`I_OUX/<5]9J977I-][J-SQ&2GR?4)7
+MJ"<(N<'5%8=7C.:X1KU2<'K4X0.ZMB0=/+F^7UTV\T'IK1#,Z3J?$..P0WNB
+M(=GZ>OL34[#IOU"F<)ID4IN%XL!N3'-%;[Y8K<#\LQQZ0JQR"&6LN4(X7/05
+MD9*X*%WVA"`-FW#/9]A\3IJ>EYY@L^ZI55:"9U!:JHIZZB=>*DWA*%I5"J@2
+M3FWD(+!)/<%A_V"GN49H-O+AOO`DG]R0:E23Z6A=Q-2F/PF_P"[2A0[`X1%V
+M`KNF#<'6-U('P'`U`ZD8HS;"&^BNTJZYVZ%.H-.T%8)(OV'26X!=-%>JW1LZ
+MEZ\+$@"5%!`Y?(:?Z5H?$V$NYL=G'^;OYK-#=7!Z\FY^='6^?SD_/5$JX%_`
+M'X``!C0$-OL(D=H:Y(::5>D8JAG+W8ERTTA%D>ZVP-?NCMX#:EL2W?>A4/HP
+M)KFH#L>V(6L!=^5-NI"&<6$J=75YP)"JK%G_!U>CF:8N^DB5K&ED<HK@;>@J
+M)`]Q!A.\<[!':>,8``27$HH-\L)HN=HQ5;0#9XK_=ICY=`B.;Q8Z]::5Y.+J
+MXFQV<GA]>G*]SS+QUT&H%D+I6V?C34T'7$+;X8&!%<ET!!QN&/VZ/.=>24S`
+MG],`ZKRESKW0=5IUL,&S+QCMDO*-&T).T!S5T`]7A'Y#5F1]S@Z.#^;[8%,&
+M9AR0`G[A+;@+H)MCCE3*)=Z#3JS%5,`4:9[H6S0ZYB50'/RHI=^T90Y*D?--
+MF:*!^AJ@#;5%JXN#BSGA*1`"0%5D%<1PAQY&`3V,`N15AN`DM]$^K`UQ*[I0
+MPZ7+B^3!4['"C8'+GD)8PTYC05-<96A$`$C@'NH6"PZT!#E-<)\A'S&-JE1X
+MC!KH-V:_,H!EP,0XB`<=\)&:.1V*B:]G/\T.+LG\P1-=4-&0.GM[=70T/SGB
+M=U<Y\:=)L1FR@+9CB!D<?#R[?']Z"(CBVO^_YOH5FYMZN91PS^`]#)P^Z-)4
+MJ=2A,JD36*8T<DL<BM_2Y$CXE'K:&,`?I8>2IN(RU6@GT0J,_U&B!&[.N7RV
+M`ZP'5W((\*H3"`\NN%1196O5(;,)'F[%1*US-?E51NRN\$H):'6>J`]NZ5OP
+MG5+:=,/AZESL\O1!KJ?YN??%BZ^I&W1L7S=LP<1"K[+.UUC!BCM55NSTY`);
+MRN4H<>KEUVNOE^J''YXB&+UX!2%&?73V*`&WHPVOIV]X.5E[/272O8DV7/#Z
+M40*4336Y9\?L4\5");#4K*@0)ZK$)!2J&<(HK6,I9IZ6$_33#MQ->2*.E:[J
+ML`Y8-^;_\!H.A\SY&L+1\&ZVMM6_AP,IX//#&4_Y$LB^``@%``X#I".\GZ"1
+M\GLB.>215L(RE&#(.=CQM-5*XD)C4)[\KB87:O>O:B<VM_A0#P:@Q/LW;]!_
+MOU4816J@W.&`*N_>\#^->&'0UK!=(V&X[A$I_YQ`KUIY>C<_Y]'K>]4-:LT$
+MCR88<,7KZ6M.4I[(I;(CU6/>*J"O#@4D74L#;B0/R"FP@"4S6NEU"Q4^/1R@
+M4?Y#318;^ZWZYQXW@.&``G"Z\8Q\AT*=4D]]2(U\P&Q'+PX^G![\[?CT<#92
+M;]31\67'<4"MG#L?GU_8X8!^F.X%`8<WH9.OR/#NP_[1F]%H.#!4KGL?3>J1
+M,(`R]TJ,'W107WT5L,G$JQ=T7/WQATKN5C\C7Q"DX`D/U2;3L#_MEDQ<EX1@
+MYDWRD/'".,\ESCM$:F9I&Q?2I[==!(>%I2%S')8QJX&",HVX+B<"F:0K]!%%
+M#ZFTB*`9W\G&=S+,HL=UX.+Y<!B!':R_.T(0#8?DGO7Z$UX-?.*++B[W3P[?
+M_OV1L\SF/8>=BFH,M)G]%REC$(_6=9OB9JYAT?O@Q#,'M$RRYJ%I-B<NH"Y1
+M?LS@I;_]"HB'K>P6S*-*6@^T:S#&.`)QIZ3R*-P]^F,4)!IM#RDO]ION*2C:
+M+-E/:W@Q^"HLMMK9:O`)6OS$_"W4%;[4]!0M<9#/"Z)G7;NE#'MYM+Y1;#8A
+M"\IW>-W&*1<_%"CKJ+GOI]X%4\GRD9ID3N;4Z@.JVWUO)"'";@+9N$RC#LN+
+MM)7UV12434:NPNPF-R=:O7B-=P'.=,D-(I=?Z^A:$-T/7/CR.DW;$P-S#SBS
+M2^X\E\W.%N^@@MQA?FK5DRB(MT$92D4CV2[='V*N$X"YOY*2P%G=WS!5#T5`
+MH;%C7+ITJM"^DI`HG,VKL;JI&3,3>0B.!@?"-K(9N\$(PY@'K?)/AU8`^V&9
+M[:5DTU9^)6<"D`N'@QO#,HTBBR#=H+>YY"4M0>X`S@D+)S+,K:EFR@HECHC;
+M>>.)^0!?06J,B#0>"`7'!TGG&YC?.`LW1V91DX#>+2IJ-Y-N'2H#"NO;0FN6
+MCK:Q8>)@*4H!``CUE9.<"9$KY<%!+`L\,;4`\_/9Q=7Q##<=A/U>,S#T+#7B
+M%2=C9:Y<88V)O*:5=3`W!IF:I^22[]3MR$\&)&,ANR::U.M=1B"]1>..8)AE
+MY])Y--\,=NDE5M>LPDP30EA)$B&^'YE8$/4\?7<A/UAEQ`:G3.!.N?YM6]&Z
+M=.J/.AL"&#-)J8O$1EZ&RM@961++:,5YDG=G>-/7Q8\UM$)7OR)B"3DA>$*A
+ME/K-+N4-C6_6#H8>3#@,?655:YI6<N^H(M?9#7R\.PX/&L2XM:\Y'S`@FONJ
+M.8P[KBBE1,)Q2$.D".^8*M?="J@,+(#K6-%?V]O0R9FQ>.RY:GU6^Y*?HT;)
+M;=7YX+D:K'ZSNZ=\:DRA=NG+8&;'<R.9&K*%_@4`*,]PX%57TVZ&^EV`@D0T
+M^W7_^.P#@.+I!=H&AK.8NR0Y(/AR)^P-&Z,3E:Q"`^.D9<A=6)Z+4:R5A@_4
+MG:T"K5)GF,[YHEM=/M#BHTO,T)A'+(S+:1_`S7?P7(P`>*53>=C,)Q;H?WM[
+M3\*1%I9("C]U;"B=@S6>3J>K.U6**^#JLS%@`CU(D&T_XSV!Q5-""*(J8`&5
+ML;8ET%%IH:&K"1V%#(_OC,`P<D'1/N[^?`/H%B<K>Q[N(DTI;8!A;]]G^=9&
+MV"[L>;%M2ZE?7-D^4\+6*E:ON7,+#QT$+?VQFO1_ER1+T]_C-:D)O7Y)>B17
+M0L1^+E?"L2YEPJ'I>F`_&K3YE\<LX//YY9.GF,U!>*!.B<=/5A>V1-ODQT9^
+MRD?F?6#?+!`9287V2!]E%-8(C&:F'C7XG[`O,4-@*_4%2.R3V;>9K@:K0[N`
+MJ2^WQNG9TX<V6X/"W92$;_@))9TY##F1,3Y8.8JB,@X9<D//_=6HI\<H;"+9
+M(*Z@1-^@Z]Z>>EHC=?!^_^1H1MSAXLNKBT=/TDW23^@N'L*06S;,KB.^G+ZA
+M9+VS]`<VU(5VN'0U`\18Z1B@!,4\0UV(&_>CDE71EP'K#3JV";?N478I^50D
+MI"5+$%`FGU0ZC/P1"#Y?J7@]PB!EH&U"ELIPI4;H"B.5`L^F:DLV'&9!H!EV
+M5&:ZG-+>^=M7?Q%:P-"7%>'RC#;2\D<[%2/8VA.F[-_:/?T)%PLWLA_J]5VW
+>Z]AB9"M2/=L.+(S7D<0S_V\81H;_`M>*^#$A)0``
+`
+end
+
+--
+Andrew.
+----------------------------------------------------------------------
+Andrew M. Bishop amb@gedanken.demon.co.uk
+ http://www.gedanken.demon.co.uk/
+
--- /dev/null
+From amb@gedanken.demon.co.uk Wed Sep 1 22:26:59 1999
+Date: Thu, 19 Aug 1999 17:30:14 +0100
+From: Andrew M. Bishop <amb@gedanken.demon.co.uk>
+To: richard@rrbcurnow.freeserve.co.uk
+Subject: [amb@gedanken.demon.co.uk: Chrony and laptop configuration]
+
+Hi,
+
+What you need to do is replace 10.0.0.0 with the network of the
+freeserve nameservers in the two scripts below.
+
+Other than that you can use it as is.
+
+------- Start of forwarded message -------
+From: "Andrew M. Bishop" <amb@gedanken.demon.co.uk>
+To: richard@rrbcurnow.freeserve.co.uk
+Subject: Chrony and laptop configuration
+Date: Sat, 31 Jul 1999 11:02:04 +0100
+
+Attached are the ip-up and ip-down files that I use for chrony.
+(Actually because of the way that debian works they are separate file
+in the /etc/ppp/ip-up.d directory that are run in a SysV init style).
+
+They rely on the presence of an 'ipparam demon' or 'ipparam freeserve'
+line in the PPP options file.
+
+-------------------- /etc/ppp/ip-up --------------------
+#!/bin/sh -f
+#
+# A script to start chrony
+#
+
+PPP_IPPARAM="$6"
+
+if [ $PPP_IPPARAM = "demon" ]; then
+
+ /usr/local/bin/chronyc << EOF
+password xxxxxxx
+online 255.255.255.0/158.152.1.0
+online 255.255.255.0/194.159.253.0
+EOF
+
+fi
+
+if [ $PPP_IPPARAM = "freeserve" ]; then
+
+ /usr/local/bin/chronyc << EOF
+password xxxxxxx
+online 255.255.255.0/10.0.0.0
+EOF
+
+fi
+-------------------- /etc/ppp/ip-up --------------------
+
+-------------------- /etc/ppp/ip-down --------------------
+#!/bin/sh -f
+#
+# A script to stop chrony
+#
+
+PPP_IPPARAM="$6"
+
+if [ $PPP_IPPARAM = "demon" ]; then
+
+ /usr/local/bin/chronyc << EOF
+password xxxxxxx
+offline 255.255.255.0/158.152.1.0
+offline 255.255.255.0/194.159.253.0
+EOF
+
+fi
+
+if [ $PPP_IPPARAM = "freeserve" ]; then
+
+ /usr/local/bin/chronyc << EOF
+password xxxxxxx
+offline 255.255.255.0/10.0.0.0
+EOF
+
+fi
+-------------------- /etc/ppp/ip-down --------------------
+
+--
+Andrew.
+----------------------------------------------------------------------
+Andrew M. Bishop amb@gedanken.demon.co.uk
+ http://www.gedanken.demon.co.uk/
+------- End of forwarded message -------
+
+--
+Andrew.
+----------------------------------------------------------------------
+Andrew M. Bishop amb@gedanken.demon.co.uk
+ http://www.gedanken.demon.co.uk/
+
--- /dev/null
+#!/bin/sh
+#
+# chrony Start time synchronization. This script
+# starts chronyd.
+#
+# Hacked by: Erik Bryer <ebryer@spots.ab.ca> using inet as a template
+#
+# chkconfig: 2345 02 82
+# description: chronyd helps keep the system time accurate by calculating \
+# and applying correction factors to compensate for the drift \
+# in the clock. chronyd can also correct the hardware clock \
+# (RTC) on some systems.
+# processname: chronyd
+# config: /etc/chrony.conf
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Set path to include chronyd in /usr/local/sbin
+PATH="$PATH:/usr/local/sbin"
+
+[ -f /usr/local/sbin/chronyd ] || exit 0
+
+[ -f /etc/chrony.conf ] || exit 0
+
+RETVAL=0
+
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemons.
+ echo -n "Starting chronyd: "
+ daemon chronyd
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/chrony
+ echo
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down chronyd: "
+# If not dead killproc automatically sleeps for 4.1 seconds then does
+# kill -9. "chrony.txt" prefers a 5 second delay, but this should be ok.
+ killproc chronyd -15
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/chrony
+ echo
+ ;;
+ status)
+ status chronyd
+ exit $?
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "Usage: named {start|stop|status|restart}"
+ exit 1
+esac
+
+exit $RETVAL
+
--- /dev/null
+#!/bin/sh
+#
+# chronyd This shell script takes care of starting and stopping
+# chronyd (NTP daemon).
+#
+# chkconfig: 45 80 20
+# description: chronyd is the NTP daemon.
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+PREDIR="/usr/local"
+CHRONYD=$PREDIR"/sbin/chronyd"
+CHRONYC=$PREDIR"/bin/chronyc"
+
+[ -x $CHRONYD -a -x $CHRONYC -a -f /etc/chrony.conf ] || exit 0
+
+dochrony() {
+ if [ -z "$(pidofproc chronyd)" ]; then
+ echo -e "\n\tchronyd not running\n\n"
+ exit 2
+ fi
+ KEY=`awk '$1 == "commandkey" {print $2; exit}' /etc/chrony.conf`
+ PASSWORD=`awk '$1 == '$KEY' {print $2; exit}' /etc/chrony/keys`
+
+ $CHRONYC <<- EOF
+ password $PASSWORD
+ $@
+ quit
+ EOF
+}
+
+# make the first parameter' lower case
+set - `echo $1 | awk '{print tolower($1)}';shift;echo "$@"`
+
+# Expand any shortcuts.
+case "$1" in
+ on|1)
+ set - "online"
+ ;;
+ off|0)
+ set - "offline"
+esac
+
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemons.
+ echo -n "Starting chronyd: "
+ daemon $CHRONYD
+ if [ $? -eq 0 ]; then
+ echo $(pidofproc chronyd) > /var/run/chronyd.pid
+ touch /var/lock/subsys/chronyd
+ fi
+ echo
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down chronyd: "
+ killproc chronyd
+ echo
+ rm -f /var/lock/subsys/chronyd
+ ;;
+ status)
+ status chronyd
+ ;;
+ restart|reload)
+ $0 stop
+ $0 start
+ ;;
+ condrestart)
+ if [ -f /var/lock/subsys/chronyd ]; then
+ $0 stop
+ $0 start
+ fi
+ ;;
+ "")
+ echo "Usage: chronyd
+{start|stop|restart|reload|condrestart|status|[on|off]line etc}"
+ exit 1
+ ;;
+
+accheck|cmdaccheck|clients|manual|rtcdata|sources|sourcestats|tracking|clients)
+ dochrony "$@"
+ ;;
+ *)
+ echo -n "Chrony $1: "
+ dochrony "$@" > /dev/null
+ [ $? -eq 0 ] && echo_success || echo_failure
+ echo
+esac
+
+exit 0
+
--- /dev/null
+From stephan@nevis1.nevis.columbia.edu Mon Jun 7 20:51:57 1999
+Date: 04 Jun 1999 00:17:25 -0400
+From: Stephan I. Boettcher <stephan@nevis1.nevis.columbia.edu>
+To: richard@rrbcurnow.freeserve.co.uk
+Subject: chrony 1.1 sysV startup script for notebooks
+
+
+Dear Richard,
+
+I installed chrony on my notebook, running RedHat 5.1 Linux.
+It looks like it works. No problems.
+
+Thank you!
+
+I like to donate my sysV startup script, appended below.
+
+Special feature: the `online' command scans the config file to
+selectively turn some servers online, depending on the pcmcia SCHEME.
+
+booting: /etc/rc.d/init.d/chrony start
+/etc/ppp/ip-up: /etc/rc.d/init.d/chrony online
+/etc/ppp/ip-down: /etc/rc.d/init.d/chrony offline
+logrotate cron: /etc/rc.d/init.d/chrony cyclelogs
+a user: /etc/rc.d/init.d/chrony status
+a sysadmin: /etc/rc.d/init.d/chrony restart
+shutdown: /etc/rc.d/init.d/chrony stop
+
+Best regards
+Stephan
+
+--
+
+------------------------------------------------------------------------
+Stephan Boettcher FAX: +1-914-591-4540
+Columbia University, Nevis Labs Tel: +1-914-591-2863
+P.O. Box 137, 136 South Broadway mailto:stephan@nevis1.columbia.edu
+Irvington, NY 10533, USA http://www.nevis.columbia.edu/~stephan
+------------------------------------------------------------------------
+
+########################### cut here ###################################
+#! /bin/bash
+#
+# /etc/rc.d/init.d/chrony
+#
+# SYS V startup script for
+# chrony ntp daemon
+# on Linux 2.0.3x notebooks with pcmcia scheme support
+# $Id: stephan_boettcher_1,v 1.1 2000/04/24 21:36:04 richard Exp $
+#
+# 1999-06-02 SiB <stephan@nevis1.columbia.edu>
+#
+# For PCMCIA users:
+# In /etc/chrony.conf, precede the server commands for each SCHEME
+# with a comment line that contains the word SCHEME and the name of
+# the scheme(s) that should use the servers, up to the next line that
+# contains the word SCHEME. The servers must be `offline' and
+# specified by their IP address. The hostname will not do.
+#
+# Like:
+#
+# # SCHEME nevisppp nevislan
+# # stephanpc.nevis.columbia.edu
+# server 192.12.82.222 offline
+#
+# # SCHEME desyppp desylan
+#
+# # dsygw2.desy.de
+# server 131.169.30.15 offline
+# # dscomsa.desy.de
+# server 131.169.197.35 offline
+
+CONF=/etc/chrony.conf
+CHRONYD=/usr/local/sbin/chronyd
+CHRONYC=/usr/local/bin/chronyc
+KEYS=/etc/chrony.keys
+
+# See if we got all we need:
+
+[ -f $CHRONYD -a -f $CHRONYC -a -r $CONF ] || exit
+
+
+[ -r $KEYS ] \
+&& CMDKEY=`awk '/^commandkey/{print $2}' $CONF` \
+&& PASSWORD=`awk -v KEY=$CMDKEY '$1==KEY{print $2}' $KEYS`
+
+
+case "$1" in
+
+ start)
+ echo -n "Starting chronyd "
+ $CHRONYD -r -s -f $CONF
+ echo
+ ;;
+
+ stop)
+ echo -n "Shutting down chronyd "
+ /usr/bin/killall chronyd
+ echo
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ on*)
+
+ [ -f /var/run/pcmcia-scheme ] && SCHEME=`cat /var/run/pcmcia-scheme`
+
+ awk -v SCHEME=${SCHEME:-default} -v PASSWORD=$PASSWORD \
+ '
+ BEGIN {
+ SEL=1;
+ print "password", PASSWORD;
+ }
+ /SCHEME/ {
+ SEL=match($0, SCHEME);
+ }
+ SEL && /^server[ \t]*[0-9.]+[ \t].*offline/ {
+ print "online 255.255.255.255/" $2;
+ }
+ ' \
+ $CONF \
+ | $CHRONYC
+
+ ;;
+
+ off*)
+ cat <<-EOF | $CHRONYC
+ password $PASSWORD
+ offline
+ trimrtc
+ dump
+ EOF
+ ;;
+
+ *log*)
+ cat <<-EOF | $CHRONYC
+ password $PASSWORD
+ cyclelogs
+ EOF
+ ;;
+
+ stat*)
+ cat <<-EOF | $CHRONYC
+ sources
+ sourcestats
+ tracking
+ rtcdata
+ EOF
+ ;;
+
+ *)
+ echo "Usage: chronyd {start|stop|restart|status|online|offline|cyclelogs}"
+ exit 1
+ ;;
+
+esac
+
+exit 0
+
+
--- /dev/null
+
+> Is it possible to limit chronyc to only those commands that
+> are readonly plus those necessary to bring a dialup connection up
+> and down? That is: online offline dump writertc and password.
+
+This is trivial on the same host and workable for non-local
+hosts: use a wrapper program or script. An *untested*
+sample follows. To use it, best create a special user (say
+chronyc) and a special group (say chronyg). Make the script
+chronyc:chronyg, and 4750 (suid, rwxr-x---). Add all users
+who may run the script to the group chronyg.
+
+Make a chrony password file e.g.
+/usr/local/etc/chrony_password. It should be owned by chronyc
+and readable only for the owner, containing only the chrony
+password (and maybe a newline) in the first line.
+
+In this way only the script (call it run_chrony, for example)
+can read the password. It will allow only those commands you
+explicitely allow. You can add a password check -- especially
+if you add an internet port so you can access it over the
+internet this is advisable. You really want to add logging
+to this untested script as well.
+
+
+BTW, if you use some sort of PPP, you probably can use
+/etc/ppp/ip-up and /etc/ppp/ip-down to transparently set chrony
+on- and offline as the ip connection goes up and comes down.
+This is _far_ more user friendly, IMHO, and a DOS by switching
+chrony offline all the time is avoided as well.
+
+
+#! /usr/bin/perl -T
+use v5.6.1;
+use warnings;
+use strict;
+
+sub laundered_command();
+sub order_chrony($$);
+sub read_password();
+sub usage($);
+
+our $CHRONY = "/usr/local/bin/chronyc";
+
+# NOTE: select the file system protection wisely for the
+# PASSWORDFILE!
+our $PASSWORDFILE = "/usr/local/etc/chrony_password";
+
+our @ALLOWED_COMMANDS = (
+ 'online', # switch online mode on
+ 'offline', # switch online mode off
+ 'dump', # save measurements to file
+ 'writerc', # save RTC accumulated data
+
+ 'clients', # which clients are served by us?
+ 'rtcdata', # Quality of RTC measurements
+ 'sources(?: -v)?', # Show our sources (verbose)
+ 'sourcestats(?: -v)?', # How good are our sources (verbose)?
+ 'tracking', # whom do we adjust to?
+
+ # 'burst \d+/\d+', # allow them to send bursts?
+);
+
+usage("No command given.") unless $ARGV[0];
+
+%ENV = (); # nuke all environment variables. Rather
+ # drastic, but better safe than sorry!
+ # Add whatever you really need to get it
+ # working (again).
+$ENV{'PATH'} = '/usr/local/bin:/bin:/usr/bin';
+
+order_chrony(laundered_command(), read_password());
+
+exit 0; # command succeeded
+
+############################################################
+
+sub usage($) {
+ print STDERR "Error: ", shift, "\n";
+
+ # OK, this eats the -v...
+ print STDERR "Legal commands are:\n\t", join "\n",
+ map { $_ =~ m:(\w+):; $1 } @ALLOWED_COMMANDS;
+ exit 1; # error
+}
+
+############################################################
+
+sub laundered_command() {
+ my $regexp = "^(" . join ( "|", @ALLOWED_COMMANDS ) . ")\$";
+ my $parameters = join " ", @ARGV;
+ $parameters =~ m:$regexp: or usage("Command $parameters not allowed.");
+
+ return $1; # this value, then, is untainted.
+};
+
+############################################################
+
+sub read_password() {
+ open PASS, $PASSWORDFILE
+ or die "Could not read protected password file: $!";
+ my $password = <PASS>;
+ chomp $password;
+ return $password;
+};
+
+############################################################
+
+sub order_chrony($$) {
+ my ($clean_command, $password) = @_;
+ open CHRONY, "| $CHRONY &> /dev/null" or die "could not run $CHRONY: $!\n";
+ print CHRONY "password $password\n";
+ print CHRONY "$clean_command\n";
+ close CHRONY
+ or die "Error running command $clean_command\n", "\ton $CHRONY: $!\n";
+}
+
+############################################################
--- /dev/null
+#######################################################################
+# $Header: /cvs/src/chrony/examples/chrony.conf.example,v 1.2 2002/02/03 21:46:29 richard Exp $
+#
+# This is an example chrony configuration file. You should copy it to
+# /etc/chrony.conf after uncommenting and editing the options that you
+# want to enable. I have not included the more obscure options. Refer
+# to the documentation for these.
+#
+# Copyright 2002 Richard P. Curnow
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+#
+#
+#######################################################################
+### COMMENTS
+# Any of the following lines are comments (you have a choice of
+# comment start character):
+# a comment
+% a comment
+! a comment
+; a comment
+#
+# Below, the '!' form is used for lines that you might want to
+# uncomment and edit to make your own chrony.conf file.
+#
+#######################################################################
+#######################################################################
+### SPECIFY YOUR NTP SERVERS
+# Most computers using chrony will send measurement requests to one or
+# more 'NTP servers'. You will probably find that your Internet Service
+# Provider or company have one or more NTP servers that you can specify.
+# Failing that, there are a lot of public NTP servers. There is a list
+# you can access at
+# http://www.eecis.udel.edu/~mills/ntp/servers.htm.
+
+! server ntp0.your-isp.com
+! server ntp1.your-isp.com
+! server ntp.public-server.org
+
+# However, for dial-up use you probably want these instead. The word
+# 'offline' means that the server is not visible at boot time. Use
+# chronyc's 'online' command to tell chronyd that these servers have
+# become visible after you go on-line.
+
+! server ntp0.your-isp.com offline
+! server ntp1.your-isp.com offline
+! server ntp.public-server.org offline
+
+# You may want to specify NTP 'peers' instead. If you run a network
+# with a lot of computers and want several computers running chrony to
+# have the 'front-line' interface to the public NTP servers, you can
+# 'peer' these machines together to increase robustness.
+
+! peer ntp0.my-company.com
+
+# There are other options to the 'server' and 'peer' directives that you
+# might want to use. For example, you can ignore measurements whose
+# round-trip-time is too large (indicating that the measurement is
+# probably useless, because you don't know which way the measurement
+# message got held up.) Consult the full documentation for details.
+
+#######################################################################
+### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
+#
+# To avoid changes being made to your computer's gain/loss compensation
+# when the measurement history is too erratic, you might want to enable
+# one of the following lines. The first seems good for dial-up (or
+# other high-latency connections like slow leased lines), the second
+# seems OK for a LAN environment.
+
+! maxupdateskew 100
+! maxupdateskew 5
+
+#######################################################################
+### FILENAMES ETC
+# Chrony likes to keep information about your computer's clock in files.
+# The 'driftfile' stores the computer's clock gain/loss rate in parts
+# per million. When chronyd starts, the system clock can be tuned
+# immediately so that it doesn't gain or lose any more time. You
+# generally want this, so it is uncommented.
+
+driftfile /etc/chrony.drift
+
+# If you want to use the program called chronyc to configure aspects of
+# chronyd's operation once it is running (e.g. tell it the Internet link
+# has gone up or down), you need a password. This is stored in the
+# following keys file. (You also need keys to support authenticated NTP
+# exchanges between cooperating machines.) Again, this option is
+# assumed by default.
+
+keyfile /etc/chrony.keys
+
+# Tell chronyd which numbered key in the file is used as the password
+# for chronyc. (You can pick any integer up to 2**32-1. '1' is just a
+# default. Using another value will _NOT_ increase security.)
+
+commandkey 1
+
+# chronyd can save the measurement history for the servers to files when
+# it it exits. This is useful in 2 situations:
+#
+# 1. On Linux, if you stop chronyd and restart it with '-r' (e.g. after
+# an upgrade), the old measurements will still be relevant when chronyd
+# is restarted. This will reduce the time needed to get accurate
+# gain/loss measurements, especially with a dial-up link.
+#
+# 2. Again on Linux, if you use the RTC support and start chronyd with
+# '-r -s' on bootup, measurements from the last boot will still be
+# useful (the real time clock is used to 'flywheel' chronyd between
+# boots).
+#
+# Enable these two options to use this.
+
+! dumponexit
+! dumpdir /var/log/chrony
+
+# chronyd writes its process ID to a file. If you try to start a second
+# copy of chronyd, it will detect that the process named in the file is
+# still running and bail out. If you want to change the path to the PID
+# file, uncomment this line and edit it. The default path is shown.
+
+! pidfile /var/run/chronyd.pid
+
+#######################################################################
+### INITIAL CLOCK CORRECTION
+# This option is only useful if your NTP servers are visible at boot
+# time. This probably means you are on a LAN. If so, the following
+# option will choose the best-looking of the servers and correct the
+# system time to that. The value '10' means that if the error is less
+# than 10 seconds, it will be gradually removed by speeding up or
+# slowing down your computer's clock until it is correct. If the error
+# is above 10 seconds, an immediate time jump will be applied to correct
+# it. Some software can get upset if the system clock jumps (especially
+# backwards), so be careful!
+
+! initstepslew 10 ntp0.your-company.com ntp1.your-company.com ntp2.your-company.com
+
+#######################################################################
+### LOGGING
+# If you want to log information about the time measurements chronyd has
+# gathered, you might want to enable the following lines. You probably
+# only need this if you really enjoy looking at the logs, you want to
+# produce some graphs of your system's timekeeping performance, or you
+# need help in debugging a problem.
+
+! logdir /var/log/chrony
+! log measurements statistics tracking
+
+If you have real time clock support enabled (see below), you might want
+this line instead:
+
+! log measurements statistics tracking rtc
+
+#######################################################################
+### ACTING AS AN NTP SERVER
+# You might want the computer to be an NTP server for other computers.
+# e.g. you might be running chronyd on a dial-up machine that has a LAN
+# sitting behind it with several 'satellite' computers on it.
+#
+# By default, chronyd does not allow any clients to access it. You need
+# to explicitly enable access using 'allow' and 'deny' directives.
+#
+# e.g. to enable client access from the 192.168.*.* class B subnet,
+
+! allow 192.168/16
+
+# .. but disallow the 192.168.100.* subnet of that,
+
+! deny 192.168.100/24
+
+# You can have as many allow and deny directives as you need. The order
+# is unimportant.
+
+# If you want chronyd to act as an NTP broadcast server, enable and edit
+# (and maybe copy) the following line. This means that a broadcast
+# packet is sent to the address 192.168.1.255 every 60 seconds. The
+# address MUST correspond to the broadcast address of one of the network
+# interfaces on your machine. If you have multiple network interfaces,
+# add a broadcast line for each.
+
+! broadcast 60 192.168.1.255
+
+# If you want to present your computer's time for others to synchronise
+# with, even if you don't seem to be synchronised to any NTP servers
+# yourself, enable the following line. The value 10 may be varied
+# between 1 and 15. You should avoid small values because you will look
+# like a real NTP server. The value 10 means that you appear to be 10
+# NTP 'hops' away from an authoritative source (atomic clock, GPS
+# receiver, radio clock etc).
+
+! local stratum 10
+
+# Normally, chronyd will keep track of how many times each client
+# machine accesses it. The information can be accessed by the 'clients'
+# command of chronyc. You can disable this facility by uncommenting the
+# following line. This will save a bit of memory if you have many
+# clients.
+
+! noclientlog
+
+#######################################################################
+### REPORTING BIG CLOCK CHANGES
+# Perhaps you want to know if chronyd suddenly detects any large error
+# in your computer's clock. This might indicate a fault or a problem
+# with the server(s) you are using, for example.
+#
+# The next option causes a message to be written to syslog when chronyd
+# has to correct an error above 0.5 seconds (you can use any amount you
+# like).
+
+! logchange 0.5
+
+# The next option will send email to the named person when chronyd has
+# to correct an error above 0.5 seconds. (If you need to send mail to
+# several people, you need to set up a mailing list or sendmail alias
+# for them and use the address of that.)
+
+! mailonchange wibble@foobar.org 0.5
+
+#######################################################################
+### COMMAND ACCESS
+# The program chronyc is used to show the current operation of chronyd
+# and to change parts of its configuration whilst it is running.
+
+# Normally, chronyd will only allow connections from chronyc on the same
+# machine as itself. This is for security. If you have a subnet
+# 192.168.*.* and you want to be able to use chronyc from any machine on
+# it, you could uncomment the following line. (Edit this to your own
+# situation.)
+
+! cmdallow 192.168/16
+
+# You can add as many 'cmdallow' and 'cmddeny' lines as you like. The
+# syntax and meaning is the same as for 'allow' and 'deny', except that
+# 'cmdallow' and 'cmddeny' control access to the chronyd's command port.
+
+# NOTE, even if the host where you run chronyc is granted access, you
+# still need a command key set up and you have to know the password to
+# put into chronyc to allow you to modify chronyd's parameters. By
+# default all you can do is view information about chronyd's operation.
+
+# Some people have reported that the need the following line to allow
+# chronyc to work even on the same machine. This should not be
+# necessary, and the problem is being investigated. You can leave this
+# line enabled, as it's benign otherwise.
+
+cmdallow 127.0.0.1
+
+#######################################################################
+### REAL TIME CLOCK
+# chronyd can characterise the system's real-time clock. This is the
+# clock that keeps running when the power is turned off, so that the
+# machine knows the approximate time when it boots again. The error at
+# a particular epoch and gain/loss rate can be written to a file and
+# used later by chronyd when it is started with the '-s' option.
+#
+# You need to have 'enhanced RTC support' compiled into your Linux
+# kernel. (Note, these options apply only to Linux.)
+
+! rtcfile /etc/chrony.rtc
+
+# Your RTC can be set to keep Universal Coordinated Time (UTC) or local
+# time. (Local time means UTC +/- the effect of your timezone.) If you
+# use UTC, chronyd will function correctly even if the computer is off
+# at the epoch when you enter or leave summer time (aka daylight saving
+# time). However, if you dual boot your system with Microsoft Windows,
+# that will work better if your RTC maintains local time. You take your
+# pick!
+
+! rtconutc
+
+# By default chronyd assumes that the enhanced RTC device is accessed as
+# /dev/rtc. If it's accessed somewhere else on your system (e.g. you're
+# using devfs), uncomment and edit the following line.
+
+! rtcdevice /dev/misc/rtc
+
+#######################################################################
--- /dev/null
+#######################################################################
+# $Header: /cvs/src/chrony/examples/chrony.keys.example,v 1.1 2002/01/31 00:00:08 richard Exp $
+#
+# This is an example chrony keys file. You should copy it to /etc/chrony.keys
+# after editing it to set up the key(s) you want to use. In most situations,
+# you will require a single key (the 'commandkey') so that you can supply a
+# password to chronyc to enable you to modify chronyd's operation whilst it is
+# running.
+#
+# Copyright 2002 Richard P. Curnow
+#
+#######################################################################
+# A valid key line looks like this
+
+1 a_key
+
+# It must consist of an integer, followed by whitespace, followed by a block of
+# text with no spaces in it. (You cannot put a space in a key). If you wanted
+# to use the above line as your commandkey (i.e. chronyc password), you would
+# put the following line into chrony.conf (remove the # from the start):
+
+# commandkey 1
+
+# You might want to define more keys if you use the MD5 authentication facility
+# in the network time protocol to authenticate request/response packets between
+# trusted clients and servers.
+
--- /dev/null
+@@PROLOGUE
+<html>
+<head>
+<title>Frequently asked questions</title>
+<meta name="description" content="Chrony FAQ (frequently asked questions)">
+<meta name="keywords" content="chrony,network time protocol,NTP,RFC 1305,dial-up connection,real time clock,RTC,Linux,FAQ,frequently asked questns">
+<?php
+ $root = ".";
+ include "$root/styles.php";
+?>
+</head>
+
+<body>
+<?php
+ include 'main_banner.php';
+ include 'header.php';
+?>
+<?php pretty_h1("Introduction") ?>
+<p>
+This is a set of questions and answers to common problems and issues.
+<p>
+As I receive more emails about the software, I will add new questions
+to this page.
+
+<hr>
+<p>
+The author can be reached by email
+<a href="mailto:rc@rc0.org.uk">
+</a>
+<p>
+<b>PLEASE</b>
+include the word "chrony" in your subject line if possible (so that my
+mail reader can keep my mail sorted by topic)!
+<hr>
+
+<br clear=all>
+@@ENDPROLOGUE
+S: Administrative issues
+
+Q: Where can I get chrony source code?
+Via the home page, see below.
+
+Q: Are there any packaged versions of chrony?
+I am aware of packages for Debian, Mandrake and Redhat. I am not personally
+involved with how these are built or distributed.
+
+Q: Where is the home page?
+It is currently at <a href="http://chrony.sunsite.dk/">http://chrony.sunsite.dk/</a>.
+
+Q: Is there a mailing list?
+Yes, it's currently at chrony-users@sunsite.dk. There is a low-volume
+list called chrony-announce which is just for announcements of new releases or
+similar matters of high importance. You can join the lists by sending a
+message to <a href="mailto:chrony-users-subscribe@sunsite.dk">chrony-users-subscribe@sunsite.dk</a> or
+<a href="mailto:chrony-announce-subscribe@sunsite.dk">chrony-announce-subscribe@sunsite.dk</a> respectively.
+
+For those who want to contribute to the development of chrony, there is a
+developers' mailing list. You can subscribe by sending mail to
+<a href="mailto:chrony-dev-subscribe@sunsite.dk">chrony-dev-subscribe@sunsite.dk</a>.
+
+Q: What licence is applied to chrony?
+Starting from version 1.15, chrony is licensed under the GNU General Public
+License. Versions prior to 1.15 were licensed under a custom BSD-like
+license.
+
+If you want to use parts of chrony in non-free software, you will need to use
+older versions of the source code. Alternatively, contact me - I may be
+prepared to licence parts of the source code to suit your purposes. I am quite
+sympathetic to projects licensed under other free/open-source (but non-GPL)
+licences, as well as to commercial projects which are of a single-customer
+"turnkey" nature (as opposed to mass-market "shrink-wrap" or "floating-licence"
+products).
+
+S: Chrony compared to other programs
+Q: How does chrony compare to xntpd?
+If your computer is permenently connected, or connected for long periods (that
+is, for the several hours it takes xntpd to settle down), or you need to
+support hardware reference clocks to your computer, then xntpd will work fine.
+Apart from not supporting hardware clocks, chrony will work fine too.
+
+If your computer connects to the 'net for 5 minutes once a day (or something
+like that), or you turn your (Linux v2.0) computer off when you're not using
+it, or you want to use NTP on an isolated network with no hardware clocks in
+sight, chrony will work much better for you.
+
+The reason I wrote chrony was that I could not get xntpd to do
+anything sensible on my PC at home, which is connected to the 'net for
+about 5 minutes once or twice a day, mainly to upload/download email
+and news. Nowadays it is also turned off for 22-23 hours a day, when
+not in use. I wanted a program which would :
+
+- slew the time to correct it when I go online and NTP servers become visible
+
+- determine the rate at which the computer gains or loses time and use this
+ information to keep it reasonably correct between connects to the 'net. This
+ has to be done using a method that does not care about the intermittent
+ availability of the references or the fact the computer is turned off between
+ groups of measurements..
+
+- maintain the time across reboots, by working out the error and drift rate of
+ the computer's real-time clock and using this information to set the system
+ clock correctly at boot up. (In the last few months, it became impossible for
+ me to leave my computer powered permanently.)
+
+Also, when working with isolated networks with no true time references
+at all, I found xntpd gave me no help with managing the local clock's
+gain/loss rate on the NTP master node (which I set from my watch). I
+added some automated support in chrony to deal with this.
+
+S: Compilation issues
+Q:How do I apply source patches?
+Sometimes I release source patches rather than a full version when I need to
+provide a fix for small problems. Supposing you have chrony-1.X.tar.gz and a
+source patch chrony-1.X-1.X.1.gz. The steps required are:
+
+ tar xzvf ../chrony-1.X.tar.gz
+ cd chrony-1.X
+ gunzip < ../../chrony-1.X-1.X.1.gz | patch -p1
+ ./configure
+ make
+ make install
+
+Q:Can I compile chrony with an ANSI-C compiler that is not GCC v2.x?
+I have had reports that chrony can be compiled with GCC v1.42, by using the
+following trick when running make
+
+ make CC='gcc -D__FUNCTION__=\"function_not_available\"'
+
+(this gets around the lack of a __FUNCTION__ macro in GCC v1.)
+
+The same trick may be enough to allow other compilers to be used.
+
+Q: I get errors like 'client.c:44: readline/readline.h: file not found'
+Read the section about 'readline' in the INSTALL file or in chrony.txt. You
+may need to disable readline support (e.g. if you haven't got readline
+installed at all, or just don't want it), or specify the location of the
+readline files (e.g. if you've installed them in a non-standard place).
+
+Q: I have RedHat 7.3 and can't compile rtc_linux.c (error in spinlock.h)
+The following solution has been found for this. Enter the following 3 commands
+(as root):
+
+ cd /usr/include/
+ mv linux linux.rh
+ ln -s /usr/src/linux/include/linux ./linux
+
+The problem seems to be that RedHat provide their own kernel header files in
+/usr/include/linux. Besides differing from those used by your current kernel,
+if you compiled it yourself, they also seem to have been changed in a way that
+causes a problem compiling chrony. Chrony compiles fine with standard kernel
+header files.
+
+There have also been reports that just replacing the file
+/usr/src/linux/spinlock.h by the equivalent file from a vanilla kernel source
+tree is sufficient to fix the problem.
+
+S: Selection of NTP servers
+Q: I have several computers on a LAN. Should I make one the master, or make them all clients of an external server?
+I think the best configuration is to make one computer the master, with the
+others as clients of it. Add a 'local' directive to the master's chrony.conf
+file. This configuration will be better because
+
+* the load on the external connection is less
+* the load on the external NTP server(s) is less
+* if your external connection goes down, the computers on the LAN will maintain
+ a common time with each other.
+
+S: Addressing issues
+Q: I get the following error message : "Could not get IP adress for localhost"
+Add a line like the following to your /etc/hosts file
+ 127.0.0.1 localhost
+
+Q: I have problems if I put the names of my NTP servers in the chrony.conf file.
+If you have no connection to the Internet at boot time, chrony won't be able to
+turn the names into IP addresses when it starts. There seem to be 2 solutions:
+
+1. Put the numeric IP addresses in the chrony.conf file
+or
+2. Put the server->IP address mappings in your /etc/hosts file and ensure that
+ /etc/host.conf reads 'order hosts,bind'.
+
+The problem is that chronyd (currently) isn't designed in a way that allows
+hostname->IP address lookups during normal operation. I hope to work on this
+problem very soon.
+
+S: My computer is not synchronising.
+This is the most common problem. There are a number of reasons, see the
+following questions.
+
+Q: Behind a firewall?
+If there is a firewall between you and the NTP server you're trying to use,
+the packets may be blocked. Try using a tool like etherfind or tcpdump to see
+if you're getting responses from the server. If you have an external modem,
+see if the receive light blinks straight after the transmit light (when the
+link is quiet apart from the NTP traffic.) Try adding 'log measurements' to
+the chrony.conf file and look in the measurements.log file after chrony has
+been running for a short period. See if any measurements appear.
+
+Most people run chronyd on the firewall itself, to avoid all issues of UDP
+packet forwarding and/or masquerading.
+
+Q: Do you have a non-permanant (i.e. intermittent) Internet connection?
+Check that you're using chronyc's 'online' and 'offline' commands
+appropriately. Again, check in measurements.log to see if you're getting any
+data back from the server.
+
+Q: In measurements.log, do the '7' and '8' flag columns always show zero?
+Do you have a 'local stratum X' directive in the chrony.conf file? If X is
+lower than the stratum of the server you're trying to use, this situation will
+arise. You should always make X quite high (e.g. 10) in this directive.
+
+S: Issues with chronyd
+
+Q: chronyd crashes after a syslog message "adjtimex failed for set frequency"
+The usual cause is that the kernel is running with a different value of 'HZ'
+(the timer interrupt rate) than the value that was found in the kernel header
+files when chrony was compiled. The chrony.conf file can include options to
+modify the HZ value (see the discussion of linux_hz and linux_freq_scale in the
+documentation), however the problem is to find the value of HZ being used.
+
+At the end of the chrony v1.18 section of the <a href="./download.php">download page</a>
+you'll find instructions on how to do this.
+
+This will be fixed in version 1.19, by getting chronyd to auto-detect the
+kernel's value rather than relying on the compiled-in default.
+
+S: Issues with chronyc
+
+Q: I keep getting the error '510 No command access from this host --- Reply not authenticated'.
+Make sure that the chrony.conf file (on the computer where chronyd is running)
+has a 'cmdallow' entry for the computer you are running chronyc on. This
+shouldn't be necessary for localhost, but some people still seem to need an
+entry like 'cmdallow 127.0.0.1'. (It would be good to understand why problem
+only affects some people).
+
+Q: I cannot log in from chronyc to carry out privileged tasks.
+This is the second most common problem.
+
+Perhaps your /etc/chrony.keys file is badly formatted. Make sure that the
+final line has a line feed at the end, otherwise the key on that line will work
+as though the last character is missing. (Note, this bug was fixed in version
+1.16.)
+
+Q: When I enter a command and hit <Return>, chronyc hangs
+This probably means that chronyc cannot communicate with chronyd.
+
+Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, 'ps
+-auxw') to see if it's running. Or try 'netstat -a' and see if the ports
+123/udp and 323/udp are listening. If chronyd is not running, you may have a
+problem with the way you are trying to start it (e.g. at boot time).
+
+Perhaps you have a firewall set up in a way that blocks packets on port
+323/udp. You need to amend the firewall configuration in this case.
+
+Q: Is the chronyc<->chronyd protocol documented anywhere?
+Only by the source code :-) See cmdmon.c (chronyd side) and client.c (chronyc
+side).
+
+S: Real-time clock issues.
+Q: What is the real-time clock (RTC)?
+This is the clock which keeps the time even when your computer is turned off.
+It works with 1 second resolution. chronyd can monitor the rate at which the
+real-time clock gains or loses time, and compensate for it when you set the
+system time from it at the next reboot. See the documentation for details.
+
+Q: I want to use chronyd's real-time clock support. Must I disable hwclock?
+The hwclock program is often set-up by default in the boot and shutdown scripts
+with many Linux installations. If you want to use chronyd's real-time clock
+support, the important thing is to disable hwclock in the <b>shutdown</b>
+procedure. If you don't, it will over-write the RTC with a new value, unknown
+to chronyd. At the next reboot, chronyd will compensate this (wrong) time with
+its estimate of how far the RTC has drifted whilst the power was off, giving a
+meaningless initial system time.
+
+There is no need to remove hwclock from the boot process, as long as chronyd is
+started after it has run.
+
+Q: I just keep getting the '513 RTC driver not running' message
+For the real time clock support to work, you need the following three things:
+
+* a kernel that is supported (e.g. 2.2 onwards)
+* enhanced RTC support compiled into the kernel
+* an 'rtcfile' directive in your chrony.conf file.
+
+S: Problems with isolated networks.
+
+Q: When I use the 'settime' command, chronyd crashes.
+If you enter times that are too far away from the real time, chronyd will
+think the system clock runs fast or slow by an excessive amount. The required
+compensation factor will be outside the bounds for the adjtimex() system call.
+chronyd will crash when it tries to apply such an excessive adjustment.
+
+S: Microsoft Windows
+
+Q: Does chrony support Windows?
+No. The chronyc program (the command-line client used for configuring
+chronyd while it is running) has been successfully built and run under Cygwin
+in the past. chronyd is not portable, because part of it is very
+system-dependent. It needs adapting to work with Windows' equivalent of the
+adjtimex() call, and it needs to be made to work as an NT service.
+
+Q: Are there any plans to support Windows?
+I have no personal plans to do this. I have neither the time nor the
+Windows programming expertise. Some time ago I did start work on a port which
+I was developing under Cygwin. Anyone is welcome to pick this work up and
+contribute it back to the project.
+
+Q: What alternative NTP clients are there for Windows?
+Some of the names I've seen mentioned are
+ - Automachron
+ - NetTime (nettime.sourceforge.net)
+
+S: NTP-specific issues
+Q: Can chrony be driven from broadcast NTP servers?
+No. I remember looking at how they worked when I was first writing chrony.
+Since the 'target market' then was dial-up systems, broadcast packets were not
+relevant so I didn't bother working out how to deal with the complexities of
+doing the delay estimation.
+
+I no longer have root access to a LAN environment to develop and test broadcast
+server support. Neither have I the time to work on this. I would be very
+happy to accept a patch from anyone who can develop, test and debug the
+necessary changes!
+
+Q: Can chronyd transmit broadcast NTP packets (e.g. to synchronise other computers on a private LAN)?
+Yes. Starting from version 1.17, chrony has this capability.
+
+Q: Can chrony keep the system clock a fixed offset away from real time?
+I have not experimented much, but I don't believe this would be possible as
+the program currently stands.
+
+Q: What happens if the network connection is dropped without using chronyc's 'offline' command first?
+In this case chronyd will keep trying to access the server(s) that it thinks
+are online. Eventually it will decide that they are unreachable and no longer
+consider itself synchronised to them. If you have other computers on your LAN
+accessing the computer that is affected this way, they too will become
+'unsynchronised', unless you have the 'local' directive set up on the master
+computer.
+
+The 'auto_offline' option to the 'server' entry in the chrony.conf file may be
+useful to avoid this situation.
+
+S: Development
+
+Q: Can I get the source via CVS from anywhere?
+Yes. See <a href="http://chrony.sunsite.dk/cvs.php">http://chrony.sunsite.dk/cvs.php</a> for information. Currently there is
+only anonymous read-only access. I keep the master copy on my own PC, which is
+more convenient for me because I don't have to connect to the Internet to do
+CVS operations on the files. So for now, there is no read-write access for
+other developers. Please email me your patches + documentation instead.
+
+S: Linux-specific issues
+
+Q: Why does the source code include kernel header files?
+The program needs to see the definitions of structures used to interact with
+the real time clock (via /dev/rtc) and with the adjtimex() system call. Sadly
+this has led to a number of compilation problems with newer kernels which have
+been increasingly hard to fix in a way that makes the code compilable on all
+Linux kernel versions (from 2.0 up anyway, I doubt 1.x still works.) Hopefully
+the situation will not deteriorate further with future kernel versions.
+
+Q: I get "Could not open /dev/rtc, Device or resource busy" in my syslog file.
+Check that you haven't accidentally got two copies of chronyd running (perhaps
+defined in different start-up scripts.)
+
+S: Solaris-specific issues
+Q: On Solaris 2.8, I get an error message about not being able to open kvm to change dosynctodr.
+(The dosynctodr variable controls whether Solaris couples the equivalent of its
+BIOS clock into its system clock at regular intervals). The Solaris port of
+chrony was developed in the Solaris 2.5 era. Some aspect of the Solaris kernel
+has changed which prevents the same technique working. I no longer have root
+access to any Solaris machines to work on this, and am reliant on somebody
+developing the patch and testing it. A good starting point would be to see if
+xntpd has been modified to work for Solaris 2.8.
+
+@@EPILOGUE
+<hr>
+
+Back to
+<a href="mailto:rc@rc0.org.uk?subject=chrony">the author</a>'s
+<a href="http://www.rc0.org.uk/">main page</a>
+</body>
+</html>
+@@ENDEPILOGUE
--- /dev/null
+#!/usr/bin/env perl
+
+# $Header
+
+# Copyright 2001 Richard P. Curnow
+# LICENCE
+
+# A script to generate an HTML FAQ page from a text input file. The input is assumed to consist of the following:
+# Lines starting with 'S:'. These introduce sections.
+# Lines starting with 'Q:'. These are the topics of questions.
+# Body text (either as an introduction to the sections, or as answers to the questions.
+# The body text is set as pre-formatted.
+
+$| = 1;
+
+@prologue = ();
+@epilogue = ();
+
+@sections=(); # section titles
+@sect_text=(); # introductory text in sections
+
+@questions=(); # questions in sections
+@answers=(); # answers to questions
+
+$sn = -1;
+$had_q = 0;
+
+#{{{ Parse input
+while (<>) {
+ if (m/\@\@PROLOG/o) {
+ while (<>) {
+ last if (m/^\@\@ENDPROLOG/);
+ push (@prologue, $_);
+ }
+ } elsif (m/\@\@EPILOG/o) {
+ while (<>) {
+ last if (m/^\@\@ENDEPILOG/);
+ push (@epilogue, $_);
+ }
+ } elsif (m/^[sS]:[ \t]*(.*)$/) {
+ chomp;
+ $qn = -1;
+ ++$sn;
+ $sections[$sn] = &guard($1);
+ $sect_text[$sn] = "";
+ $questions[$sn] = [ ];
+ $answers[$sn] = [ ];
+ $had_q = 0;
+ } elsif (/^[qQ]:[ \t]*(.*)$/) {
+ chomp;
+ die unless ($sn >= 0);
+ ++$qn;
+ $questions[$sn]->[$qn] = &guard($1);
+ $had_q = 1;
+ } else {
+ if ($had_q) {
+ if ($qn >= 0) {
+ $answers[$sn]->[$qn] .= $_;
+ }
+ } else {
+ if ($sect_text[$sn] ne "" || $_ !~ /^\s*$/) {
+ $sect_text[$sn] .= $_;
+ }
+ }
+ }
+}
+#}}}
+
+# Emit file header
+if ($#prologue >= 0) {
+ print @prologue;
+} else {
+print <<EOF;
+<html>
+<head>
+<title>
+Chrony Frequently Asked Questions
+</title>
+</head>
+<body>
+<font face=\"arial,helvetica\" size=+4><b>Table of contents</b></font>
+EOF
+}
+
+# Emit table of contents
+print "<ul>\n";
+for $sn (0 .. $#sections) {
+ print "<b><li> <a href=\"#section_".($sn+1)."\">".($sn+1).".</a> ".$sections[$sn]."</b>\n";
+ print " <ul>\n";
+ for $qn (0 .. $#{$questions[$sn]}) {
+ $sq = ($sn+1).".".($qn+1);
+ print " <li> <a href=\"#question_".$sq."\">".$sq.".</a> ".$questions[$sn]->[$qn]."\n";
+ #print " <li> ".$sq.". ".$questions[$sn]->[$qn]."\n";
+ }
+ print " </ul>\n";
+}
+print "</ul>\n";
+
+# Emit main sections
+for $sn (0 .. $#sections) {
+ print "<hr>\n";
+ print "<a name=section_".($sn+1).">\n";
+ #print "<b><font size=+2 face=\"arial,helvetica\">".($sn+1).". ".$sections[$sn]."</font></b>\n";
+ print "<?php pretty_h2(\"".($sn+1).". ".$sections[$sn]."\"); ?>\n";
+ if ($sect_text[$sn] ne "") {
+ print "<pre>\n";
+ print $sect_text[$sn];
+ print "</pre>\n";
+ }
+ for $qn (0 .. $#{$questions[$sn]}) {
+ $sq = ($sn+1).".".($qn+1);
+ print "<p>\n";
+ print "<a name=question_".$sq.">\n";
+ print "<font size=+1 face=\"arial,helvetica\">".$sq.". ".$questions[$sn]->[$qn]."</font>\n";
+ print "<pre>\n";
+ print $answers[$sn]->[$qn];
+ print "</pre>\n";
+ }
+}
+
+# Print footer
+if ($#epilogue >= 0) {
+ print @epilogue;
+} else {
+print <<EOF;
+</body>
+</html>
+EOF
+}
+
+
+#{{{ sub guard {
+sub guard {
+# Hide wierd tags etc
+ my ($x) = @_;
+ return $x;
+}
+#}}}
+
+
--- /dev/null
+
+/* A Bison parser, made from getdate.y
+ by GNU Bison version 1.25
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define tAGO 258
+#define tDAY 259
+#define tDAY_UNIT 260
+#define tDAYZONE 261
+#define tDST 262
+#define tHOUR_UNIT 263
+#define tID 264
+#define tMERIDIAN 265
+#define tMINUTE_UNIT 266
+#define tMONTH 267
+#define tMONTH_UNIT 268
+#define tSEC_UNIT 269
+#define tSNUMBER 270
+#define tUNUMBER 271
+#define tYEAR_UNIT 272
+#define tZONE 273
+
+#line 1 "getdate.y"
+
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+** This grammar has 13 shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+# ifdef FORCE_ALLOCA_H
+# include <alloca.h>
+# endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+# undef static
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii(c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
+ only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
+ it's important to use the locale's definition of `digit' even when the
+ host does not conform to Posix. */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#include "getdate.h"
+
+#if defined (STDC_HEADERS) || defined (USG)
+# include <string.h>
+#endif
+
+/* Some old versions of bison generate parsers that use bcopy.
+ That loses on systems that don't provide the function, so we have
+ to redefine it here. */
+#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
+# define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+
+extern struct tm *gmtime ();
+extern struct tm *localtime ();
+extern time_t mktime ();
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in the same program. Note that these are only
+ the variables produced by yacc. If other parser generators (bison,
+ byacc, etc) produce additional global names that conflict at link time,
+ then those parser generators need to be fixed instead of adding those
+ names to this list. */
+
+#define yymaxdepth gd_maxdepth
+#define yyparse gd_parse
+#define yylex gd_lex
+#define yyerror gd_error
+#define yylval gd_lval
+#define yychar gd_char
+#define yydebug gd_debug
+#define yypact gd_pact
+#define yyr1 gd_r1
+#define yyr2 gd_r2
+#define yydef gd_def
+#define yychk gd_chk
+#define yypgo gd_pgo
+#define yyact gd_act
+#define yyexca gd_exca
+#define yyerrflag gd_errflag
+#define yynerrs gd_nerrs
+#define yyps gd_ps
+#define yypv gd_pv
+#define yys gd_s
+#define yy_yys gd_yys
+#define yystate gd_state
+#define yytmp gd_tmp
+#define yyv gd_v
+#define yy_yyv gd_yyv
+#define yyval gd_val
+#define yylloc gd_lloc
+#define yyreds gd_reds /* With YYDEBUG defined */
+#define yytoks gd_toks /* With YYDEBUG defined */
+#define yylhs gd_yylhs
+#define yylen gd_yylen
+#define yydefred gd_yydefred
+#define yydgoto gd_yydgoto
+#define yysindex gd_yysindex
+#define yyrindex gd_yyrindex
+#define yygindex gd_yygindex
+#define yytable gd_yytable
+#define yycheck gd_yycheck
+
+static int yylex ();
+static int yyerror ();
+
+#define EPOCH 1970
+#define HOUR(x) ((x) * 60)
+
+#define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ const char *name;
+ int type;
+ int value;
+} TABLE;
+
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static const char *yyInput;
+static int yyDayOrdinal;
+static int yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static int yyTimezone;
+static int yyDay;
+static int yyHour;
+static int yyMinutes;
+static int yyMonth;
+static int yySeconds;
+static int yyYear;
+static MERIDIAN yyMeridian;
+static int yyRelDay;
+static int yyRelHour;
+static int yyRelMinutes;
+static int yyRelMonth;
+static int yyRelSeconds;
+static int yyRelYear;
+
+
+#line 175 "getdate.y"
+typedef union {
+ int Number;
+ enum _MERIDIAN Meridian;
+} YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 61
+#define YYFLAG -32768
+#define YYNTBASE 22
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 273 ? yytranslate[x] : 32)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 20, 2, 2, 21, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 19, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 1, 4, 6, 8, 10, 12, 14, 16, 19,
+ 24, 29, 36, 43, 45, 47, 50, 52, 55, 58,
+ 62, 68, 72, 76, 79, 84, 87, 91, 94, 96,
+ 99, 102, 104, 107, 110, 112, 115, 118, 120, 123,
+ 126, 128, 131, 134, 136, 139, 142, 144, 146, 147
+};
+
+static const short yyrhs[] = { -1,
+ 22, 23, 0, 24, 0, 25, 0, 27, 0, 26,
+ 0, 28, 0, 30, 0, 16, 10, 0, 16, 19,
+ 16, 31, 0, 16, 19, 16, 15, 0, 16, 19,
+ 16, 19, 16, 31, 0, 16, 19, 16, 19, 16,
+ 15, 0, 18, 0, 6, 0, 18, 7, 0, 4,
+ 0, 4, 20, 0, 16, 4, 0, 16, 21, 16,
+ 0, 16, 21, 16, 21, 16, 0, 16, 15, 15,
+ 0, 16, 12, 15, 0, 12, 16, 0, 12, 16,
+ 20, 16, 0, 16, 12, 0, 16, 12, 16, 0,
+ 29, 3, 0, 29, 0, 16, 17, 0, 15, 17,
+ 0, 17, 0, 16, 13, 0, 15, 13, 0, 13,
+ 0, 16, 5, 0, 15, 5, 0, 5, 0, 16,
+ 8, 0, 15, 8, 0, 8, 0, 16, 11, 0,
+ 15, 11, 0, 11, 0, 16, 14, 0, 15, 14,
+ 0, 14, 0, 16, 0, 0, 10, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 191, 192, 195, 198, 201, 204, 207, 210, 213, 219,
+ 225, 234, 240, 252, 255, 258, 264, 268, 272, 278,
+ 282, 300, 306, 312, 316, 321, 325, 332, 340, 343,
+ 346, 349, 352, 355, 358, 361, 364, 367, 370, 373,
+ 376, 379, 382, 385, 388, 391, 394, 399, 432, 436
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = { "$","error","$undefined.","tAGO","tDAY",
+"tDAY_UNIT","tDAYZONE","tDST","tHOUR_UNIT","tID","tMERIDIAN","tMINUTE_UNIT",
+"tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tYEAR_UNIT","tZONE",
+"':'","','","'/'","spec","item","time","zone","day","date","rel","relunit","number",
+"o_merid", NULL
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 22, 22, 23, 23, 23, 23, 23, 23, 24, 24,
+ 24, 24, 24, 25, 25, 25, 26, 26, 26, 27,
+ 27, 27, 27, 27, 27, 27, 27, 28, 28, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 30, 31, 31
+};
+
+static const short yyr2[] = { 0,
+ 0, 2, 1, 1, 1, 1, 1, 1, 2, 4,
+ 4, 6, 6, 1, 1, 2, 1, 2, 2, 3,
+ 5, 3, 3, 2, 4, 2, 3, 2, 1, 2,
+ 2, 1, 2, 2, 1, 2, 2, 1, 2, 2,
+ 1, 2, 2, 1, 2, 2, 1, 1, 0, 1
+};
+
+static const short yydefact[] = { 1,
+ 0, 17, 38, 15, 41, 44, 0, 35, 47, 0,
+ 48, 32, 14, 2, 3, 4, 6, 5, 7, 29,
+ 8, 18, 24, 37, 40, 43, 34, 46, 31, 19,
+ 36, 39, 9, 42, 26, 33, 45, 0, 30, 0,
+ 0, 16, 28, 0, 23, 27, 22, 49, 20, 25,
+ 50, 11, 0, 10, 0, 49, 21, 13, 12, 0,
+ 0
+};
+
+static const short yydefgoto[] = { 1,
+ 14, 15, 16, 17, 18, 19, 20, 21, 54
+};
+
+static const short yypact[] = {-32768,
+ 0, -19,-32768,-32768,-32768,-32768, -13,-32768,-32768, 30,
+ 15,-32768, 14,-32768,-32768,-32768,-32768,-32768,-32768, 19,
+-32768,-32768, 4,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768, -6,-32768,-32768, 16,-32768, 17,
+ 23,-32768,-32768, 24,-32768,-32768,-32768, 27, 28,-32768,
+-32768,-32768, 29,-32768, 32, -8,-32768,-32768,-32768, 50,
+-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, -5
+};
+
+
+#define YYLAST 51
+
+
+static const short yytable[] = { 60,
+ 22, 51, 23, 2, 3, 4, 58, 5, 45, 46,
+ 6, 7, 8, 9, 10, 11, 12, 13, 30, 31,
+ 42, 43, 32, 44, 33, 34, 35, 36, 37, 38,
+ 47, 39, 48, 40, 24, 41, 51, 25, 49, 50,
+ 26, 52, 27, 28, 56, 53, 29, 57, 55, 61,
+ 59
+};
+
+static const short yycheck[] = { 0,
+ 20, 10, 16, 4, 5, 6, 15, 8, 15, 16,
+ 11, 12, 13, 14, 15, 16, 17, 18, 4, 5,
+ 7, 3, 8, 20, 10, 11, 12, 13, 14, 15,
+ 15, 17, 16, 19, 5, 21, 10, 8, 16, 16,
+ 11, 15, 13, 14, 16, 19, 17, 16, 21, 0,
+ 56
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/share/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#include <sys/types.h>
+#ifdef _ULONG_T /* defined in <sys/types.h> on HP-UX 10 */
+#include <alloca.h>
+#else /* earlier HP-UX versions have alloca as a library function */
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+#define alloca __builtin_alloca
+#endif /* not __cplusplus */
+#endif /* HP-UX <= 9 */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+\f
+/* Define __yy_memcpy. Note that the size argument
+ should be passed with type unsigned int, because that is what the non-GCC
+ definitions require. With GCC, __builtin_memcpy takes an arg
+ of type size_t, but it can handle unsigned int. */
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (to, from, count)
+ char *to;
+ char *from;
+ unsigned int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+\f
+#line 196 "/usr/local/share/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss, (char *)yyss1,
+ (unsigned int) size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+ (unsigned int) size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls, (char *)yyls1,
+ (unsigned int) size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 3:
+#line 195 "getdate.y"
+{
+ yyHaveTime++;
+ ;
+ break;}
+case 4:
+#line 198 "getdate.y"
+{
+ yyHaveZone++;
+ ;
+ break;}
+case 5:
+#line 201 "getdate.y"
+{
+ yyHaveDate++;
+ ;
+ break;}
+case 6:
+#line 204 "getdate.y"
+{
+ yyHaveDay++;
+ ;
+ break;}
+case 7:
+#line 207 "getdate.y"
+{
+ yyHaveRel++;
+ ;
+ break;}
+case 9:
+#line 213 "getdate.y"
+{
+ yyHour = yyvsp[-1].Number;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = yyvsp[0].Meridian;
+ ;
+ break;}
+case 10:
+#line 219 "getdate.y"
+{
+ yyHour = yyvsp[-3].Number;
+ yyMinutes = yyvsp[-1].Number;
+ yySeconds = 0;
+ yyMeridian = yyvsp[0].Meridian;
+ ;
+ break;}
+case 11:
+#line 225 "getdate.y"
+{
+ yyHour = yyvsp[-3].Number;
+ yyMinutes = yyvsp[-1].Number;
+ yyMeridian = MER24;
+ yyHaveZone++;
+ yyTimezone = (yyvsp[0].Number < 0
+ ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60
+ : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60));
+ ;
+ break;}
+case 12:
+#line 234 "getdate.y"
+{
+ yyHour = yyvsp[-5].Number;
+ yyMinutes = yyvsp[-3].Number;
+ yySeconds = yyvsp[-1].Number;
+ yyMeridian = yyvsp[0].Meridian;
+ ;
+ break;}
+case 13:
+#line 240 "getdate.y"
+{
+ yyHour = yyvsp[-5].Number;
+ yyMinutes = yyvsp[-3].Number;
+ yySeconds = yyvsp[-1].Number;
+ yyMeridian = MER24;
+ yyHaveZone++;
+ yyTimezone = (yyvsp[0].Number < 0
+ ? -yyvsp[0].Number % 100 + (-yyvsp[0].Number / 100) * 60
+ : - (yyvsp[0].Number % 100 + (yyvsp[0].Number / 100) * 60));
+ ;
+ break;}
+case 14:
+#line 252 "getdate.y"
+{
+ yyTimezone = yyvsp[0].Number;
+ ;
+ break;}
+case 15:
+#line 255 "getdate.y"
+{
+ yyTimezone = yyvsp[0].Number - 60;
+ ;
+ break;}
+case 16:
+#line 259 "getdate.y"
+{
+ yyTimezone = yyvsp[-1].Number - 60;
+ ;
+ break;}
+case 17:
+#line 264 "getdate.y"
+{
+ yyDayOrdinal = 1;
+ yyDayNumber = yyvsp[0].Number;
+ ;
+ break;}
+case 18:
+#line 268 "getdate.y"
+{
+ yyDayOrdinal = 1;
+ yyDayNumber = yyvsp[-1].Number;
+ ;
+ break;}
+case 19:
+#line 272 "getdate.y"
+{
+ yyDayOrdinal = yyvsp[-1].Number;
+ yyDayNumber = yyvsp[0].Number;
+ ;
+ break;}
+case 20:
+#line 278 "getdate.y"
+{
+ yyMonth = yyvsp[-2].Number;
+ yyDay = yyvsp[0].Number;
+ ;
+ break;}
+case 21:
+#line 282 "getdate.y"
+{
+ /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
+ The goal in recognizing YYYY/MM/DD is solely to support legacy
+ machine-generated dates like those in an RCS log listing. If
+ you want portability, use the ISO 8601 format. */
+ if (yyvsp[-4].Number >= 1000)
+ {
+ yyYear = yyvsp[-4].Number;
+ yyMonth = yyvsp[-2].Number;
+ yyDay = yyvsp[0].Number;
+ }
+ else
+ {
+ yyMonth = yyvsp[-4].Number;
+ yyDay = yyvsp[-2].Number;
+ yyYear = yyvsp[0].Number;
+ }
+ ;
+ break;}
+case 22:
+#line 300 "getdate.y"
+{
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = yyvsp[-2].Number;
+ yyMonth = -yyvsp[-1].Number;
+ yyDay = -yyvsp[0].Number;
+ ;
+ break;}
+case 23:
+#line 306 "getdate.y"
+{
+ /* e.g. 17-JUN-1992. */
+ yyDay = yyvsp[-2].Number;
+ yyMonth = yyvsp[-1].Number;
+ yyYear = -yyvsp[0].Number;
+ ;
+ break;}
+case 24:
+#line 312 "getdate.y"
+{
+ yyMonth = yyvsp[-1].Number;
+ yyDay = yyvsp[0].Number;
+ ;
+ break;}
+case 25:
+#line 316 "getdate.y"
+{
+ yyMonth = yyvsp[-3].Number;
+ yyDay = yyvsp[-2].Number;
+ yyYear = yyvsp[0].Number;
+ ;
+ break;}
+case 26:
+#line 321 "getdate.y"
+{
+ yyMonth = yyvsp[0].Number;
+ yyDay = yyvsp[-1].Number;
+ ;
+ break;}
+case 27:
+#line 325 "getdate.y"
+{
+ yyMonth = yyvsp[-1].Number;
+ yyDay = yyvsp[-2].Number;
+ yyYear = yyvsp[0].Number;
+ ;
+ break;}
+case 28:
+#line 332 "getdate.y"
+{
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMinutes = -yyRelMinutes;
+ yyRelHour = -yyRelHour;
+ yyRelDay = -yyRelDay;
+ yyRelMonth = -yyRelMonth;
+ yyRelYear = -yyRelYear;
+ ;
+ break;}
+case 30:
+#line 343 "getdate.y"
+{
+ yyRelYear += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 31:
+#line 346 "getdate.y"
+{
+ yyRelYear += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 32:
+#line 349 "getdate.y"
+{
+ yyRelYear += yyvsp[0].Number;
+ ;
+ break;}
+case 33:
+#line 352 "getdate.y"
+{
+ yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 34:
+#line 355 "getdate.y"
+{
+ yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 35:
+#line 358 "getdate.y"
+{
+ yyRelMonth += yyvsp[0].Number;
+ ;
+ break;}
+case 36:
+#line 361 "getdate.y"
+{
+ yyRelDay += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 37:
+#line 364 "getdate.y"
+{
+ yyRelDay += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 38:
+#line 367 "getdate.y"
+{
+ yyRelDay += yyvsp[0].Number;
+ ;
+ break;}
+case 39:
+#line 370 "getdate.y"
+{
+ yyRelHour += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 40:
+#line 373 "getdate.y"
+{
+ yyRelHour += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 41:
+#line 376 "getdate.y"
+{
+ yyRelHour += yyvsp[0].Number;
+ ;
+ break;}
+case 42:
+#line 379 "getdate.y"
+{
+ yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 43:
+#line 382 "getdate.y"
+{
+ yyRelMinutes += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 44:
+#line 385 "getdate.y"
+{
+ yyRelMinutes += yyvsp[0].Number;
+ ;
+ break;}
+case 45:
+#line 388 "getdate.y"
+{
+ yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 46:
+#line 391 "getdate.y"
+{
+ yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+ ;
+ break;}
+case 47:
+#line 394 "getdate.y"
+{
+ yyRelSeconds += yyvsp[0].Number;
+ ;
+ break;}
+case 48:
+#line 400 "getdate.y"
+{
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = yyvsp[0].Number;
+ else
+ {
+ if (yyvsp[0].Number>10000)
+ {
+ yyHaveDate++;
+ yyDay= (yyvsp[0].Number)%100;
+ yyMonth= (yyvsp[0].Number/100)%100;
+ yyYear = yyvsp[0].Number/10000;
+ }
+ else
+ {
+ yyHaveTime++;
+ if (yyvsp[0].Number < 100)
+ {
+ yyHour = yyvsp[0].Number;
+ yyMinutes = 0;
+ }
+ else
+ {
+ yyHour = yyvsp[0].Number / 100;
+ yyMinutes = yyvsp[0].Number % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ ;
+ break;}
+case 49:
+#line 433 "getdate.y"
+{
+ yyval.Meridian = MER24;
+ ;
+ break;}
+case 50:
+#line 437 "getdate.y"
+{
+ yyval.Meridian = yyvsp[0].Meridian;
+ ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 498 "/usr/local/share/bison.simple"
+\f
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 442 "getdate.y"
+
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tYEAR_UNIT, 1 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tDAY_UNIT, 14 },
+ { "week", tDAY_UNIT, 7 },
+ { "day", tDAY_UNIT, 1 },
+ { "hour", tHOUR_UNIT, 1 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR ( 0) },
+ { "wet", tZONE, HOUR ( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR ( 1) }, /* West Africa */
+ { "at", tZONE, HOUR ( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR (10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR (11) }, /* Nome */
+ { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR (1) }, /* Central European */
+ { "met", tZONE, -HOUR (1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
+ { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR (1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
+ { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR (3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Standard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR ( 1) },
+ { "b", tZONE, HOUR ( 2) },
+ { "c", tZONE, HOUR ( 3) },
+ { "d", tZONE, HOUR ( 4) },
+ { "e", tZONE, HOUR ( 5) },
+ { "f", tZONE, HOUR ( 6) },
+ { "g", tZONE, HOUR ( 7) },
+ { "h", tZONE, HOUR ( 8) },
+ { "i", tZONE, HOUR ( 9) },
+ { "k", tZONE, HOUR ( 10) },
+ { "l", tZONE, HOUR ( 11) },
+ { "m", tZONE, HOUR ( 12) },
+ { "n", tZONE, HOUR (- 1) },
+ { "o", tZONE, HOUR (- 2) },
+ { "p", tZONE, HOUR (- 3) },
+ { "q", tZONE, HOUR (- 4) },
+ { "r", tZONE, HOUR (- 5) },
+ { "s", tZONE, HOUR (- 6) },
+ { "t", tZONE, HOUR (- 7) },
+ { "u", tZONE, HOUR (- 8) },
+ { "v", tZONE, HOUR (- 9) },
+ { "w", tZONE, HOUR (-10) },
+ { "x", tZONE, HOUR (-11) },
+ { "y", tZONE, HOUR (-12) },
+ { "z", tZONE, HOUR ( 0) },
+ { NULL }
+};
+
+\f
+
+
+/* ARGSUSED */
+static int
+yyerror (s)
+ char *s;
+{
+ return 0;
+}
+
+static int
+ToHour (Hours, Meridian)
+ int Hours;
+ MERIDIAN Meridian;
+{
+ switch (Meridian)
+ {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return Hours;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return Hours;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return Hours + 12;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+}
+
+static int
+ToYear (Year)
+ int Year;
+{
+ if (Year < 0)
+ Year = -Year;
+
+ /* XPG4 suggests that years 00-68 map to 2000-2068, and
+ years 69-99 map to 1969-1999. */
+ if (Year < 69)
+ Year += 2000;
+ else if (Year < 100)
+ Year += 1900;
+
+ return Year;
+}
+
+static int
+LookupWord (buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (ISUPPER (*p))
+ *p = tolower (*p);
+
+ if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
+ {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
+ {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen (buff) == 3)
+ abbrev = 1;
+ else if (strlen (buff) == 4 && buff[3] == '.')
+ {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++)
+ {
+ if (abbrev)
+ {
+ if (strncmp (buff, tp->name, 3) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp (buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen (buff) - 1;
+ if (buff[i] == 's')
+ {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && ISALPHA (*buff))
+ {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp (buff, tp->name) == 0)
+ {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+static int
+yylex ()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for (;;)
+ {
+ while (ISSPACE (*yyInput))
+ yyInput++;
+
+ if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
+ {
+ if (c == '-' || c == '+')
+ {
+ sign = c == '-' ? -1 : 1;
+ if (!ISDIGIT (*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (ISALPHA (c))
+ {
+ for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord (buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do
+ {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ }
+ while (Count > 0);
+ }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static long
+difftm (a, b)
+ struct tm *a, *b;
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ long days = (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay / 100 - by / 100)
+ + ((ay / 100 >> 2) - (by / 100 >> 2))
+ /* + difference in years * 365 */
+ + (long) (ay - by) * 365
+ );
+ return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+
+time_t
+get_date (p, now)
+ const char *p;
+ const time_t *now;
+{
+ struct tm tm, tm0, *tmp;
+ time_t Start;
+
+ yyInput = p;
+ Start = now ? *now : time ((time_t *) NULL);
+ tmp = localtime (&Start);
+ yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
+ yyMonth = tmp->tm_mon + 1;
+ yyDay = tmp->tm_mday;
+ yyHour = tmp->tm_hour;
+ yyMinutes = tmp->tm_min;
+ yySeconds = tmp->tm_sec;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMinutes = 0;
+ yyRelHour = 0;
+ yyRelDay = 0;
+ yyRelMonth = 0;
+ yyRelYear = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse ()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
+ tm.tm_mon = yyMonth - 1 + yyRelMonth;
+ tm.tm_mday = yyDay + yyRelDay;
+ if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
+ {
+ tm.tm_hour = ToHour (yyHour, yyMeridian);
+ if (tm.tm_hour < 0)
+ return -1;
+ tm.tm_min = yyMinutes;
+ tm.tm_sec = yySeconds;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ }
+ tm.tm_hour += yyRelHour;
+ tm.tm_min += yyRelMinutes;
+ tm.tm_sec += yyRelSeconds;
+ tm.tm_isdst = -1;
+ tm0 = tm;
+
+ Start = mktime (&tm);
+
+ if (Start == (time_t) -1)
+ {
+
+ /* Guard against falsely reporting errors near the time_t boundaries
+ when parsing times in other time zones. For example, if the min
+ time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+ of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+ we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+ we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+ zone by 24 hours to compensate. This algorithm assumes that
+ there is no DST transition within a day of the time_t boundaries. */
+ if (yyHaveZone)
+ {
+ tm = tm0;
+ if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
+ {
+ tm.tm_mday++;
+ yyTimezone -= 24 * 60;
+ }
+ else
+ {
+ tm.tm_mday--;
+ yyTimezone += 24 * 60;
+ }
+ Start = mktime (&tm);
+ }
+
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (yyHaveDay && !yyHaveDate)
+ {
+ tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
+ + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (yyHaveZone)
+ {
+ long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
+ if ((Start + delta < Start) != (delta < 0))
+ return -1; /* time_t overflow */
+ Start += delta;
+ }
+
+ return Start;
+}
+
+#if defined (TEST)
+
+/* ARGSUSED */
+int
+main (ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[MAX_BUFF_LEN + 1];
+ time_t d;
+
+ (void) printf ("Enter date, or blank line to exit.\n\t> ");
+ (void) fflush (stdout);
+
+ buff[MAX_BUFF_LEN] = 0;
+ while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
+ {
+ d = get_date (buff, (time_t *) NULL);
+ if (d == -1)
+ (void) printf ("Bad format - couldn't convert.\n");
+ else
+ (void) printf ("%s", ctime (&d));
+ (void) printf ("\t> ");
+ (void) fflush (stdout);
+ }
+ exit (0);
+ /* NOTREACHED */
+}
+#endif /* defined (TEST) */
--- /dev/null
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Modified from the original to add stdlib.h and string.h */
+
+#ifndef GOT_GETDATE_H
+#define GOT_GETDATE_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+time_t get_date (const char *p, const time_t *now);
+
+#endif /* GOT_GETDATE_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/keys.c,v 1.11 2003/01/20 22:52:07 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module for managing keys used for authenticating NTP packets and commands
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "keys.h"
+#include "conf.h"
+#include "memory.h"
+
+typedef struct {
+ unsigned long id;
+ char *val;
+ int len;
+} Key;
+
+#define MAX_KEYS 256
+
+static int n_keys;
+static Key keys[MAX_KEYS];
+
+static int command_key_valid;
+static int command_key_pos;
+static int cache_valid;
+static unsigned long cache_key_id;
+static int cache_key_pos;
+
+/* ================================================== */
+
+void
+KEY_Initialise(void)
+{
+ n_keys = 0;
+ command_key_valid = 0;
+ cache_valid = 0;
+ KEY_Reload();
+ return;
+}
+
+/* ================================================== */
+
+void
+KEY_Finalise(void)
+{
+ /* Nothing to do */
+ return;
+}
+
+/* ================================================== */
+
+/* Compare two keys */
+
+static int
+compare_keys_by_id(const void *a, const void *b)
+{
+ const Key *c = (const Key *) a;
+ const Key *d = (const Key *) b;
+
+ if (c->id < d->id) {
+ return -1;
+ } else if (c->id > d->id) {
+ return +1;
+ } else {
+ return 0;
+ }
+
+}
+
+/* ================================================== */
+
+
+#define KEYLEN 2047
+#define SKEYLEN "2047"
+
+void
+KEY_Reload(void)
+{
+ int i, len1;
+ char *key_file;
+ FILE *in;
+ unsigned long key_id;
+ char line[KEYLEN+1], keyval[KEYLEN+1];
+
+ for (i=0; i<n_keys; i++) {
+ Free(keys[i].val);
+ }
+
+ n_keys = 0;
+
+ key_file = CNF_GetKeysFile();
+
+ if (key_file) {
+ in = fopen(key_file, "r");
+ if (in) {
+ while (fgets(line, sizeof(line), in)) {
+ len1 = strlen(line) - 1;
+
+ /* Guard against removing last character of the line
+ * if the last line of the file is missing an end-of-line */
+ if (line[len1] == '\n') {
+ line[len1] = '\0';
+ }
+
+ if (sscanf(line, "%lu%" SKEYLEN "s", &key_id, keyval) == 2) {
+ keys[n_keys].id = key_id;
+ keys[n_keys].len = strlen(keyval);
+ keys[n_keys].val = MallocArray(char, 1 + keys[n_keys].len);
+ strcpy(keys[n_keys].val, keyval);
+ n_keys++;
+ }
+ }
+ fclose(in);
+
+ /* Sort keys into order. Note, if there's a duplicate, it is
+ arbitrary which one we use later - the user should have been
+ more careful! */
+ qsort((void *) keys, n_keys, sizeof(Key), compare_keys_by_id);
+
+ }
+ }
+
+ command_key_valid = 0;
+ cache_valid = 0;
+
+ return;
+}
+
+/* ================================================== */
+
+static int
+lookup_key(unsigned long id)
+{
+ Key specimen, *where;
+ int pos;
+
+ specimen.id = id;
+ where = (Key *) bsearch((void *)&specimen, (void *)keys, n_keys, sizeof(Key), compare_keys_by_id);
+ if (!where) {
+ return -1;
+ } else {
+ pos = where - keys;
+ return pos;
+ }
+}
+
+/* ================================================== */
+
+void
+KEY_CommandKey(char **key, int *len)
+{
+ unsigned long command_key_id;
+
+ if (!command_key_valid) {
+ command_key_id = CNF_GetCommandKey();
+ command_key_pos = lookup_key(command_key_id);
+ command_key_valid = 1;
+ }
+
+ if (command_key_pos >= 0) {
+ *key = keys[command_key_pos].val;
+ *len = keys[command_key_pos].len;
+ } else {
+ *key = "";
+ *len = 0;
+ }
+}
+
+/* ================================================== */
+
+int
+KEY_GetKey(unsigned long key_id, char **key, int *len)
+{
+ if (!cache_valid || key_id != cache_key_id) {
+ cache_valid = 1;
+ cache_key_pos = lookup_key(key_id);
+ cache_key_id = key_id;
+ }
+
+ if (cache_key_pos >= 0) {
+ *key = keys[cache_key_pos].val;
+ *len = keys[cache_key_pos].len;
+ return 1;
+ } else {
+ *key = "";
+ *len = 0;
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+KEY_KeyKnown(unsigned long key_id)
+{
+ int position;
+
+ if (cache_valid && (key_id == cache_key_id)) {
+ return 1;
+ } else {
+
+ position = lookup_key(key_id);
+
+ if (position >= 0) {
+ /* Store key in cache, we will probably be using it in a
+ minute... */
+ cache_valid = 1;
+ cache_key_pos = position;
+ cache_key_id = key_id;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/keys.h,v 1.8 2002/02/28 23:27:10 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header for key management module
+ */
+
+#ifndef GOT_KEYS_H
+#define GOT_KEYS_H
+
+extern void KEY_Initialise(void);
+extern void KEY_Finalise(void);
+
+extern void KEY_Reload(void);
+
+extern void KEY_CommandKey(char **key, int *len);
+
+extern int KEY_GetKey(unsigned long key_id, char **key, int *len);
+extern int KEY_KeyKnown(unsigned long key_id);
+
+#endif /* GOT_KEYS_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/local.c,v 1.20 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ The routines in this file present a common local (system) clock
+ interface to the rest of the software.
+
+ They interface with the system specific driver files in sys_*.c
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "local.h"
+#include "localp.h"
+#include "memory.h"
+#include "util.h"
+#include "logging.h"
+
+/* ================================================== */
+
+/* Variable to store the current frequency, in ppm */
+static double current_freq_ppm;
+
+/* ================================================== */
+/* Store the system dependent drivers */
+
+static lcl_ReadFrequencyDriver drv_read_freq;
+static lcl_SetFrequencyDriver drv_set_freq;
+static lcl_AccrueOffsetDriver drv_accrue_offset;
+static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
+static lcl_OffsetCorrectionDriver drv_offset_convert;
+static lcl_ImmediateStepDriver drv_immediate_step;
+
+/* ================================================== */
+
+/* Types and variables associated with handling the parameter change
+ list */
+
+typedef struct _ChangeListEntry {
+ struct _ChangeListEntry *next;
+ struct _ChangeListEntry *prev;
+ LCL_ParameterChangeHandler handler;
+ void *anything;
+} ChangeListEntry;
+
+static ChangeListEntry change_list;
+
+/* ================================================== */
+
+/* Types and variables associated with handling the parameter change
+ list */
+
+typedef struct _DispersionNotifyListEntry {
+ struct _DispersionNotifyListEntry *next;
+ struct _DispersionNotifyListEntry *prev;
+ LCL_DispersionNotifyHandler handler;
+ void *anything;
+} DispersionNotifyListEntry;
+
+static DispersionNotifyListEntry dispersion_notify_list;
+
+/* ================================================== */
+
+static int precision_log;
+static double precision_quantum;
+
+/* ================================================== */
+
+/* Define the number of increments of the system clock that we want
+ to see to be fairly sure that we've got something approaching
+ the minimum increment. Even on a crummy implementation that can't
+ interpolate between 10ms ticks, we should get this done in
+ under 1s of busy waiting. */
+#define NITERS 100
+
+static void
+calculate_sys_precision(void)
+{
+ struct timeval tv, old_tv, first_tv;
+ struct timezone tz;
+ int dusec, best_dusec;
+ int iters;
+
+ gettimeofday(&old_tv, &tz);
+ first_tv = old_tv;
+ best_dusec = 1000000; /* Assume we must be better than a second */
+ iters = 0;
+ do {
+ gettimeofday(&tv, &tz);
+ dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
+ old_tv = tv;
+ if (dusec > 0) {
+ if (dusec < best_dusec) {
+ best_dusec = dusec;
+ }
+ iters++;
+ }
+ } while (iters < NITERS);
+ if (!(best_dusec > 0)) {
+ CROAK("best_dusec should be positive");
+ }
+ precision_log = 0;
+ while (best_dusec < 500000) {
+ precision_log--;
+ best_dusec *= 2;
+ }
+
+ precision_quantum = 1.0 / (double)(1<<(-precision_log));
+
+ return;
+}
+
+/* ================================================== */
+
+void
+LCL_Initialise(void)
+{
+ change_list.next = change_list.prev = &change_list;
+
+ dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
+
+ /* Null out the system drivers, so that we die
+ if they never get defined before use */
+
+ drv_read_freq = NULL;
+ drv_set_freq = NULL;
+ drv_accrue_offset = NULL;
+ drv_offset_convert = NULL;
+
+ /* This ought to be set from the system driver layer */
+ current_freq_ppm = 0.0;
+
+ calculate_sys_precision();
+}
+
+/* ================================================== */
+
+void
+LCL_Finalise(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+/* Routine to read the system precision as a log to base 2 value. */
+int
+LCL_GetSysPrecisionAsLog(void)
+{
+ return precision_log;
+}
+
+/* ================================================== */
+/* Routine to read the system precision in terms of the actual time step */
+
+double
+LCL_GetSysPrecisionAsQuantum(void)
+{
+ return precision_quantum;
+}
+
+/* ================================================== */
+
+void
+LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
+{
+ ChangeListEntry *ptr, *new_entry;
+
+ /* Check that the handler is not already registered */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ if (!(ptr->handler != handler || ptr->anything != anything)) {
+ CROAK("a handler is already registered");
+ }
+ }
+
+ new_entry = MallocNew(ChangeListEntry);
+
+ new_entry->handler = handler;
+ new_entry->anything = anything;
+
+ /* Chain it into the list */
+ new_entry->next = &change_list;
+ new_entry->prev = change_list.prev;
+ change_list.prev->next = new_entry;
+ change_list.prev = new_entry;
+
+ return;
+}
+
+/* ================================================== */
+
+/* Remove a handler */
+extern
+void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
+{
+
+ ChangeListEntry *ptr;
+ int ok;
+
+ ptr = NULL;
+ ok = 0;
+
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ if (ptr->handler == handler && ptr->anything == anything) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (!ok) {
+ CROAK("did not find a matching handler");
+ }
+
+ /* Unlink entry from the list */
+ ptr->next->prev = ptr->prev;
+ ptr->prev->next = ptr->next;
+
+ free(ptr);
+
+ return;
+}
+
+/* ================================================== */
+
+void
+LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
+{
+ DispersionNotifyListEntry *ptr, *new_entry;
+
+ /* Check that the handler is not already registered */
+ for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
+ if (!(ptr->handler != handler || ptr->anything != anything)) {
+ CROAK("a handler is already registered");
+ }
+ }
+
+ new_entry = MallocNew(DispersionNotifyListEntry);
+
+ new_entry->handler = handler;
+ new_entry->anything = anything;
+
+ /* Chain it into the list */
+ new_entry->next = &dispersion_notify_list;
+ new_entry->prev = dispersion_notify_list.prev;
+ dispersion_notify_list.prev->next = new_entry;
+ dispersion_notify_list.prev = new_entry;
+
+ return;
+}
+
+/* ================================================== */
+
+/* Remove a handler */
+extern
+void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
+{
+
+ DispersionNotifyListEntry *ptr;
+ int ok;
+
+ ptr = NULL;
+ ok = 0;
+
+ for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
+ if (ptr->handler == handler && ptr->anything == anything) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (!ok) {
+ CROAK("no matching handler found");
+ }
+
+ /* Unlink entry from the list */
+ ptr->next->prev = ptr->prev;
+ ptr->prev->next = ptr->next;
+
+ free(ptr);
+
+ return;
+}
+
+/* ================================================== */
+/* At the moment, this is just gettimeofday(), because
+ I can't think of a Unix system where it would not be */
+
+void
+LCL_ReadRawTime(struct timeval *result)
+{
+ struct timezone tz;
+
+ if (!(gettimeofday(result, &tz) >= 0)) {
+ CROAK("Could not get time of day");
+ }
+ return;
+
+}
+
+/* ================================================== */
+
+void
+LCL_ReadCookedTime(struct timeval *result, double *err)
+{
+ struct timeval raw;
+ double correction;
+
+ LCL_ReadRawTime(&raw);
+
+ /* For now, cheat and set the error to zero in all cases.
+ */
+
+ *err = 0.0;
+
+ /* Call system specific driver to get correction */
+ (*drv_offset_convert)(&raw, &correction);
+ UTI_AddDoubleToTimeval(&raw, correction, result);
+
+ return;
+}
+
+/* ================================================== */
+
+double
+LCL_GetOffsetCorrection(struct timeval *raw)
+{
+ double correction;
+ (*drv_offset_convert)(raw, &correction);
+ return correction;
+}
+
+/* ================================================== */
+/* This is just a simple passthrough of the system specific routine */
+
+double
+LCL_ReadAbsoluteFrequency(void)
+{
+ return (*drv_read_freq)();
+}
+
+/* ================================================== */
+/* This involves both setting the absolute frequency with the
+ system-specific driver, as well as calling all notify handlers */
+
+void
+LCL_SetAbsoluteFrequency(double afreq_ppm)
+{
+ ChangeListEntry *ptr;
+ struct timeval raw, cooked;
+ double correction;
+ double dfreq;
+
+ /* Call the system-specific driver for setting the frequency */
+
+ (*drv_set_freq)(afreq_ppm);
+
+ dfreq = 1.0e-6 * (afreq_ppm - current_freq_ppm) / (1.0 - 1.0e-6 * current_freq_ppm);
+
+ LCL_ReadRawTime(&raw);
+ (drv_offset_convert)(&raw, &correction);
+ UTI_AddDoubleToTimeval(&raw, correction, &cooked);
+
+ /* Dispatch to all handlers */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ (ptr->handler)(&raw, &cooked, dfreq, afreq_ppm, 0.0, 0, ptr->anything);
+ }
+
+ current_freq_ppm = afreq_ppm;
+
+}
+
+/* ================================================== */
+
+void
+LCL_AccumulateDeltaFrequency(double dfreq)
+{
+ ChangeListEntry *ptr;
+ struct timeval raw, cooked;
+ double correction;
+
+ /* Work out new absolute frequency. Note that absolute frequencies
+ are handled in units of ppm, whereas the 'dfreq' argument is in
+ terms of the gradient of the (offset) v (local time) function. */
+
+ current_freq_ppm = (1.0 - dfreq) * current_freq_ppm +
+ (1.0e6 * dfreq);
+
+ /* Call the system-specific driver for setting the frequency */
+ (*drv_set_freq)(current_freq_ppm);
+
+ LCL_ReadRawTime(&raw);
+ (drv_offset_convert)(&raw, &correction);
+ UTI_AddDoubleToTimeval(&raw, correction, &cooked);
+
+ /* Dispatch to all handlers */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, 0.0, 0, ptr->anything);
+ }
+
+}
+
+/* ================================================== */
+
+void
+LCL_AccumulateOffset(double offset)
+{
+ ChangeListEntry *ptr;
+ struct timeval raw, cooked;
+ double correction;
+
+ /* In this case, the cooked time to be passed to the notify clients
+ has to be the cooked time BEFORE the change was made */
+
+ LCL_ReadRawTime(&raw);
+ (drv_offset_convert)(&raw, &correction);
+ UTI_AddDoubleToTimeval(&raw, correction, &cooked);
+
+ (*drv_accrue_offset)(offset);
+
+ /* Dispatch to all handlers */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 0, ptr->anything);
+ }
+
+}
+
+/* ================================================== */
+
+void
+LCL_ApplyStepOffset(double offset)
+{
+ ChangeListEntry *ptr;
+ struct timeval raw, cooked;
+ double correction;
+
+ /* In this case, the cooked time to be passed to the notify clients
+ has to be the cooked time BEFORE the change was made */
+
+ LCL_ReadRawTime(&raw);
+ (drv_offset_convert)(&raw, &correction);
+ UTI_AddDoubleToTimeval(&raw, correction, &cooked);
+
+ (*drv_apply_step_offset)(offset);
+
+ /* Dispatch to all handlers */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 1, ptr->anything);
+ }
+
+}
+
+/* ================================================== */
+
+void
+LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
+{
+ ChangeListEntry *ptr;
+ struct timeval raw, cooked;
+ double correction;
+ double old_freq_ppm;
+
+ LCL_ReadRawTime(&raw);
+ (drv_offset_convert)(&raw, &correction);
+ /* Due to modifying the offset, this has to be the cooked time prior
+ to the change we are about to make */
+ UTI_AddDoubleToTimeval(&raw, correction, &cooked);
+
+ old_freq_ppm = current_freq_ppm;
+
+ /* Work out new absolute frequency. Note that absolute frequencies
+ are handled in units of ppm, whereas the 'dfreq' argument is in
+ terms of the gradient of the (offset) v (local time) function. */
+ current_freq_ppm = (1.0 - dfreq) * old_freq_ppm +
+ (1.0e6 * dfreq);
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
+ old_freq_ppm, current_freq_ppm, doffset);
+#endif
+
+ /* Call the system-specific driver for setting the frequency */
+ (*drv_set_freq)(current_freq_ppm);
+ (*drv_accrue_offset)(doffset);
+
+ /* Dispatch to all handlers */
+ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
+ (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, doffset, 0, ptr->anything);
+ }
+
+
+}
+
+/* ================================================== */
+
+void
+lcl_InvokeDispersionNotifyHandlers(double dispersion)
+{
+ DispersionNotifyListEntry *ptr;
+
+ for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
+ (ptr->handler)(dispersion, ptr->anything);
+ }
+
+}
+
+/* ================================================== */
+
+void
+lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
+ lcl_SetFrequencyDriver set_freq,
+ lcl_AccrueOffsetDriver accrue_offset,
+ lcl_ApplyStepOffsetDriver apply_step_offset,
+ lcl_OffsetCorrectionDriver offset_convert,
+ lcl_ImmediateStepDriver immediate_step)
+{
+ drv_read_freq = read_freq;
+ drv_set_freq = set_freq;
+ drv_accrue_offset = accrue_offset;
+ drv_apply_step_offset = apply_step_offset;
+ drv_offset_convert = offset_convert;
+ drv_immediate_step = immediate_step;
+
+ current_freq_ppm = (*drv_read_freq)();
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
+#endif
+
+ return;
+}
+
+/* ================================================== */
+/* Look at the current difference between the system time and the NTP
+ time, and make a step to cancel it. */
+
+int
+LCL_MakeStep(void)
+{
+ if (drv_immediate_step) {
+ (drv_immediate_step)();
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Local, "Made step to system time to apply remaining slew");
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/local.h,v 1.16 2002/02/28 23:27:10 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module provides an interface to the system time, and
+ insulates the rest of the program from the different way
+ that interface has to be done on various operating systems.
+ */
+
+#ifndef GOT_LOCAL_H
+#define GOT_LOCAL_H
+
+#include "sysincl.h"
+
+/* Read the system clock. This is analogous to gettimeofday(),
+ but with the timezone information ignored */
+extern void LCL_ReadRawTime(struct timeval *);
+
+/* Read the system clock, corrected according to all accumulated
+ drifts and uncompensated offsets.
+
+ In a kernel implementation with vernier frequency control (like
+ Linux), and if we were to apply offsets by stepping the clock, this
+ would be identical to raw time. In any other case (use of
+ adjtime()-like interface to correct offsets, and to adjust the
+ frequency), we must correct the raw time to get this value */
+
+extern void LCL_ReadCookedTime(struct timeval *t, double *err);
+
+/* Read the current offset between the system clock and true time
+ (i.e. 'cooked' - 'raw') (in seconds). Only intended for use in
+ status reporting, really. */
+
+extern double LCL_GetOffsetCorrection(struct timeval *raw);
+
+/* Type of routines that may be invoked as callbacks when there is a
+ change to the frequency or offset.
+
+ raw : raw local clock time at which change occurred
+
+ cooked : cooked local time at which change occurred
+
+ dfreq : delta frequency relative to previous value (in terms of
+ seconds gained by system clock per unit system clock time)
+
+ afreq : absolute frequency relative to uncompensated system (in
+ terms of ppm seconds gained by system clock per unit of the
+ uncalibrated system clock)
+
+ doffset : delta offset applied (positive => make local system fast
+ by that amount, negative => make it slow by that amount)
+
+ is_step_change : true if change is being applied as a jump (using
+ settimeofday rather than adjtime)
+
+ anything : Passthrough argument from call to registration routine */
+
+
+typedef void (*LCL_ParameterChangeHandler)
+ (struct timeval *raw, struct timeval *cooked,
+ double dfreq, double afreq_ppm,
+ double doffset, int is_step_change,
+ void *anything
+ );
+
+/* Add a handler. Then handler MUST NOT deregister itself!!! */
+extern void LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything);
+
+/* Remove a handler */
+extern void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler, void *anything);
+
+/* Function type for handlers to be called back when an indeterminate
+ offset is introduced into the local time. This situation occurs
+ when the frequency must be adjusted to effect a clock slew and
+ there is doubt about one of the endpoints of the interval over
+ which the frequency change was applied.It is expected that such
+ handlers will add extra dispersion to any existing samples stored
+ in their registers.
+
+ dispersion : The bound on how much error has been introduced in the
+ local clock, in seconds.
+
+ anything : passthrough from the registration routine
+
+ */
+
+typedef void (*LCL_DispersionNotifyHandler)(double dispersion, void *anything);
+
+/* Register a handler for being notified of dispersion being added to
+ the local clock. The handler MUST NOT unregister itself!!! */
+
+extern void LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything);
+
+/* Delete a handler */
+
+extern void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything);
+
+
+/* Read the absolute system frequency, relative to the uncompensated
+ system. Returned in units of parts per million. Thus the result of
+ this is how many seconds fast the uncompensated system would be after
+ its own time has reached 1 million seconds from the start of the
+ measurement. */
+extern double LCL_ReadAbsoluteFrequency(void);
+
+/* Routine to set the absolute frequency. Only expected to be used
+ when either (i) reading the drift from a file at the start of a
+ run, or (ii) responsing to a user parameter 'poke'. This is
+ defined in ppm, as for the absolute frequency reading routine. */
+
+extern void LCL_SetAbsoluteFrequency(double afreq);
+
+/* Routine to apply a change of frequency to the local clock. The
+ argument is the estimated gain (positive) or loss (negative) of the
+ local clock relative to true time, per unit time of the PREVIOUS
+ frequency setting of the local clock. This is assumed to be based
+ on a regression of y=offset v x=cooked local time. */
+
+extern void LCL_AccumulateDeltaFrequency(double dfreq);
+
+/* Routine to apply an offset (in seconds) to the local clock. The
+ argument should be positive to move the clock backwards (i.e. the
+ local clock is currently fast of true time), or negative to move it
+ forwards (i.e. it is currently slow of true time). */
+
+extern void LCL_AccumulateOffset(double offset);
+
+/* Routine to apply an immediate offset by doing a sudden step if
+ possible. (Intended for use after an initial estimate of offset has
+ been obtained, so that we don't end up using adjtime to achieve a
+ slew of an hour or something like that). A positive argument means
+ the system clock is fast on true time, i.e. it needs to be stepped
+ backwards. (Same convention as for AccumulateOffset routine). */
+
+extern void LCL_ApplyStepOffset(double offset);
+
+/* Perform the combination of modifying the frequency and applying
+ a slew, in one easy step */
+extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset);
+
+/* Routine to read the system precision as a log to base 2 value. */
+extern int LCL_GetSysPrecisionAsLog(void);
+
+/* Routine to read the system precision in terms of the actual time step */
+extern double LCL_GetSysPrecisionAsQuantum(void);
+
+/* Routine to initialise the module (to be called once at program
+ start-up) */
+
+extern void LCL_Initialise(void);
+
+/* Routine to finalise the module (to be called once at end of
+ run). */
+extern void LCL_Finalise(void);
+
+/* Routine to convert the outstanding system clock error to a step and
+ apply it, e.g. if the system clock has ended up an hour wrong due
+ to a timezone problem. */
+extern int LCL_MakeStep(void);
+
+#endif /* GOT_LOCAL_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/localp.h,v 1.9 2002/02/28 23:27:10 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Private include file for local.c and all system dependent
+ driver modules.
+ */
+
+
+#ifndef GOT_LOCALP_H
+#define GOT_LOCALP_H
+
+/* System driver to read the current local frequency, in ppm relative
+ to nominal. A positive value indicates that the local clock runs
+ fast when uncompensated. */
+typedef double (*lcl_ReadFrequencyDriver)(void);
+
+/* System driver to set the current local frequency, in ppm relative
+ to nominal. A positive value indicates that the local clock runs
+ fast when uncompensated. */
+typedef void (*lcl_SetFrequencyDriver)(double freq_ppm);
+
+/* System driver to accrue an offset. A positive argument means slew
+ the clock forwards. */
+typedef void (*lcl_AccrueOffsetDriver)(double offset);
+
+/* System driver to apply a step offset. A positive argument means step
+ the clock forwards. */
+typedef void (*lcl_ApplyStepOffsetDriver)(double offset);
+
+/* System driver to convert a raw time to an adjusted (cooked) time.
+ The number of seconds returned in 'corr' have to be added to the
+ raw time to get the corrected time */
+typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr);
+
+/* System driver to stop slewing the current offset and to apply is
+ as an immediate step instead */
+typedef void (*lcl_ImmediateStepDriver)(void);
+
+extern void lcl_InvokeDispersionNotifyHandlers(double dispersion);
+
+extern void
+lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
+ lcl_SetFrequencyDriver set_freq,
+ lcl_AccrueOffsetDriver accrue_offset,
+ lcl_ApplyStepOffsetDriver apply_step_offset,
+ lcl_OffsetCorrectionDriver offset_convert,
+ lcl_ImmediateStepDriver immediate_step_driver);
+
+#endif /* GOT_LOCALP_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/logging.c,v 1.13 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module to handle logging of diagnostic information
+ */
+
+#include "sysincl.h"
+
+#include "main.h"
+#include "logging.h"
+#include "version.h"
+
+/* ================================================== */
+/* Flag indicating we have initialised */
+static int initialised = 0;
+
+static int is_detached = 0;
+
+#ifdef WINNT
+static FILE *logfile;
+#endif
+
+/* ================================================== */
+/* Init function */
+
+void
+LOG_Initialise(void)
+{
+ initialised = 1;
+
+#ifdef WINNT
+ logfile = fopen("./chronyd.err", "a");
+#endif
+
+ return;
+}
+
+/* ================================================== */
+/* Fini function */
+
+void
+LOG_Finalise(void)
+{
+#ifdef WINNT
+ if (logfile) {
+ fclose(logfile);
+ }
+#else
+ if (is_detached) {
+ closelog();
+ }
+#endif
+
+ initialised = 0;
+ return;
+}
+
+/* ================================================== */
+
+void
+LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...)
+{
+ char buf[2048];
+ va_list other_args;
+ va_start(other_args, format);
+ vsprintf(buf, format, other_args);
+ va_end(other_args);
+#ifdef WINNT
+ if (logfile) {
+ fprintf(logfile, "%s\n", buf);
+ }
+#else
+ if (is_detached) {
+ switch (severity) {
+ case LOGS_INFO:
+ syslog(LOG_INFO, "%s", buf);
+ break;
+ case LOGS_WARN:
+ syslog(LOG_WARNING, "%s", buf);
+ break;
+ case LOGS_ERR:
+ default:
+ syslog(LOG_ERR, "%s", buf);
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s\n", buf);
+ }
+#endif
+ return;
+}
+
+/* ================================================== */
+
+volatile void
+LOG_Fatal_Function(LOG_Facility facility, const char *format, ...)
+{
+ char buf[2048];
+ va_list other_args;
+ va_start(other_args, format);
+ vsprintf(buf, format, other_args);
+ va_end(other_args);
+
+#ifdef WINNT
+ if (logfile) {
+ fprintf(logfile, "Fatal error : %s\n", buf);
+ }
+#else
+ if (is_detached) {
+ syslog(LOG_CRIT, "Fatal error : %s", buf);
+ } else {
+ fprintf(stderr, "Fatal error : %s\n", buf);
+ }
+#endif
+
+ MAI_CleanupAndExit();
+
+ return;
+}
+
+/* ================================================== */
+
+void
+LOG_Position(const char *filename, int line_number, const char *function_name)
+{
+#ifdef WINNT
+#else
+ time_t t;
+ struct tm stm;
+ char buf[64];
+ if (!is_detached) {
+ /* Don't clutter up syslog with internal debugging info */
+ time(&t);
+ stm = *gmtime(&t);
+ strftime(buf, sizeof(buf), "%d-%H:%M:%S", &stm);
+ fprintf(stderr, "%s:%d:(%s)[%s] ", filename, line_number, function_name, buf);
+ }
+#endif
+ return;
+}
+
+/* ================================================== */
+
+void
+LOG_GoDaemon(void)
+{
+#ifdef WINNT
+
+
+#else
+
+ int pid, fd;
+
+ /* Does this preserve existing signal handlers? */
+ pid = fork();
+
+ if (pid < 0) {
+ LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
+ } else if (pid > 0) {
+ exit(0); /* In the 'grandparent' */
+ } else {
+
+ setsid();
+
+ /* Do 2nd fork, as-per recommended practice for launching daemons. */
+ pid = fork();
+
+ if (pid < 0) {
+ LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
+ } else if (pid > 0) {
+ exit(0); /* In the 'parent' */
+ } else {
+ /* In the child we want to leave running as the daemon */
+
+ /* Don't keep stdin/out/err from before. */
+ for (fd=0; fd<1024; fd++) {
+ close(fd);
+ }
+
+ is_detached = 1;
+
+ openlog("chronyd", LOG_PID, LOG_DAEMON);
+
+ LOG(LOGS_INFO, LOGF_Logging, "chronyd version %s starting", PROGRAM_VERSION_STRING);
+
+ }
+ }
+
+#endif
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/logging.h,v 1.15 2002/02/28 23:27:10 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for diagnostic logging module
+
+ */
+
+#ifndef GOT_LOGGING_H
+#define GOT_LOGGING_H
+
+/* Definition of severity */
+typedef enum {
+ LOGS_INFO,
+ LOGS_WARN,
+ LOGS_ERR
+} LOG_Severity;
+
+/* Definition of facility. Each message is tagged with who generated
+ it, so that the user can customise what level of reporting he gets
+ for each area of the software */
+typedef enum {
+ LOGF_Reference,
+ LOGF_NtpIO,
+ LOGF_NtpCore,
+ LOGF_NtpSources,
+ LOGF_Scheduler,
+ LOGF_SourceStats,
+ LOGF_Sources,
+ LOGF_Local,
+ LOGF_Util,
+ LOGF_Main,
+ LOGF_Configure,
+ LOGF_CmdMon,
+ LOGF_Acquire,
+ LOGF_Manual,
+ LOGF_Logging,
+ LOGF_Rtc,
+ LOGF_Regress,
+ LOGF_SysLinux,
+ LOGF_SysSolaris,
+ LOGF_SysSunOS,
+ LOGF_SysWinnt,
+ LOGF_RtcLinux
+} LOG_Facility;
+
+/* Init function */
+extern void LOG_Initialise(void);
+
+/* Fini function */
+extern void LOG_Finalise(void);
+
+/* Line logging function */
+extern void LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...);
+
+/* Logging function for fatal errors */
+extern volatile void LOG_Fatal_Function(LOG_Facility facility, const char *format, ...);
+
+/* Position in code reporting function */
+extern void LOG_Position(const char *filename, int line_number, const char *function_name);
+
+extern void LOG_GoDaemon(void);
+
+/* Line logging macro. If the compiler is GNU C, we take advantage of
+ being able to get the function name also. */
+#if defined(__GNUC__)
+#define LOG LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Line_Function
+#define LOG_FATAL LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Fatal_Function
+#else
+#define LOG LOG_Position(__FILE__, __LINE__, ""); LOG_Line_Function
+#define LOG_FATAL LOG_Position(__FILE__, __LINE__, ""); LOG_Fatal_Function
+#endif /* defined (__GNUC__) */
+
+#endif /* GOT_LOGGING_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/main.c,v 1.30 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ The main program
+ */
+
+#include "sysincl.h"
+
+#include "main.h"
+#include "sched.h"
+#include "local.h"
+#include "sys.h"
+#include "ntp_io.h"
+#include "ntp_sources.h"
+#include "ntp_core.h"
+#include "sources.h"
+#include "sourcestats.h"
+#include "reference.h"
+#include "logging.h"
+#include "conf.h"
+#include "cmdmon.h"
+#include "keys.h"
+#include "acquire.h"
+#include "manual.h"
+#include "version.h"
+#include "rtc.h"
+#include "clientlog.h"
+#include "broadcast.h"
+
+/* ================================================== */
+
+/* Set when the initialisation chain has been completed. Prevents finalisation
+ * chain being run if a fatal error happened early. */
+
+static int initialised = 0;
+
+/* ================================================== */
+
+static int reload = 0;
+
+/* ================================================== */
+
+static void
+delete_pidfile(void)
+{
+ const char *pidfile = CNF_GetPidFile();
+ /* Don't care if this fails, there's not a lot we can do */
+ unlink(pidfile);
+}
+
+/* ================================================== */
+
+volatile void
+MAI_CleanupAndExit(void)
+{
+ if (!initialised) exit(0);
+
+ if (CNF_GetDumpOnExit()) {
+ SRC_DumpSources();
+ }
+
+ RTC_Finalise();
+ MNL_Finalise();
+ ACQ_Finalise();
+ CAM_Finalise();
+ KEY_Finalise();
+ CLG_Finalise();
+ NIO_Finalise();
+ NSR_Finalise();
+ NCR_Finalise();
+ BRD_Finalise();
+ SRC_Finalise();
+ SST_Finalise();
+ REF_Finalise();
+ SYS_Finalise();
+ SCH_Finalise();
+ LCL_Finalise();
+
+ delete_pidfile();
+
+ LOG_Finalise();
+
+ exit(0);
+}
+
+/* ================================================== */
+
+static void
+signal_cleanup(int x)
+{
+ LOG(LOGS_WARN, LOGF_Main, "chronyd exiting on signal");
+ MAI_CleanupAndExit();
+}
+
+/* ================================================== */
+
+static void
+post_acquire_hook(void *anything)
+{
+
+ CNF_AddSources();
+ CNF_AddBroadcasts();
+ if (reload) {
+ /* Note, we want reload to come well after the initialisation from
+ the real time clock - this gives us a fighting chance that the
+ system-clock scale for the reloaded samples still has a
+ semblence of validity about it. */
+ SRC_ReloadSources();
+ }
+ CNF_SetupAccessRestrictions();
+
+ RTC_StartMeasurements();
+}
+
+/* ================================================== */
+
+static void
+post_init_rtc_hook(void *anything)
+{
+ CNF_ProcessInitStepSlew(post_acquire_hook, NULL);
+}
+
+/* ================================================== */
+/* Return 1 if the process exists on the system. */
+
+static int
+does_process_exist(int pid)
+{
+ int status;
+ status = getsid(pid);
+ if (status >= 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static int
+maybe_another_chronyd_running(int *other_pid)
+{
+ const char *pidfile = CNF_GetPidFile();
+ FILE *in;
+ int pid, count;
+
+ *other_pid = 0;
+
+ in = fopen(pidfile, "r");
+ if (!in) return 0;
+
+ count = fscanf(in, "%d", &pid);
+ fclose(in);
+
+ if (count != 1) return 0;
+
+ *other_pid = pid;
+ return does_process_exist(pid);
+
+}
+
+/* ================================================== */
+
+static void
+write_lockfile(void)
+{
+ const char *pidfile = CNF_GetPidFile();
+ FILE *out;
+
+ out = fopen(pidfile, "w");
+ if (!out) {
+ LOG(LOGS_ERR, LOGF_Main, "could not open lockfile %s for writing", pidfile);
+ } else {
+ fprintf(out, "%d\n", getpid());
+ fclose(out);
+ }
+}
+
+/* ================================================== */
+
+int main
+(int argc, char **argv)
+{
+ char *conf_file = NULL;
+ int debug = 0;
+ int do_init_rtc = 0;
+ int other_pid;
+
+ LOG_Initialise();
+
+ /* Parse command line options */
+ while (++argv, (--argc)>0) {
+
+ if (!strcmp("-f", *argv)) {
+ ++argv, --argc;
+ conf_file = *argv;
+ } else if (!strcmp("-r", *argv)) {
+ reload = 1;
+ } else if (!strcmp("-s", *argv)) {
+ do_init_rtc = 1;
+ } else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
+ /* This write to the terminal is OK, it comes before we turn into a daemon */
+ printf("chronyd (chrony) version %s\n", PROGRAM_VERSION_STRING);
+ exit(0);
+ } else if (!strcmp("-d", *argv)) {
+ debug = 1;
+ } else {
+ LOG(LOGS_WARN, LOGF_Main, "Unrecognized command line option [%s]", *argv);
+ }
+ }
+
+#ifndef SYS_WINNT
+ if (getuid() != 0) {
+ /* This write to the terminal is OK, it comes before we turn into a daemon */
+ fprintf(stderr,"Not superuser\n");
+ exit(1);
+ }
+
+
+ /* Turn into a daemon */
+ if (!debug) {
+ LOG_GoDaemon();
+ }
+
+ /* Check whether another chronyd may already be running. Do this after
+ * forking, so that message logging goes to the right place (i.e. syslog), in
+ * case this chronyd is being run from a boot script. */
+ if (maybe_another_chronyd_running(&other_pid)) {
+ LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
+ other_pid, CNF_GetPidFile());
+ exit(1);
+ }
+
+ /* Write our lockfile to prevent other chronyds running. This has *GOT* to
+ * be done *AFTER* the daemon-creation fork() */
+ write_lockfile();
+#endif
+
+ CNF_ReadFile(conf_file);
+
+ if (do_init_rtc) {
+ RTC_TimePreInit();
+ }
+
+ LCL_Initialise();
+ SCH_Initialise();
+ SYS_Initialise();
+ REF_Initialise();
+ SST_Initialise();
+ SRC_Initialise();
+ BRD_Initialise();
+ NCR_Initialise();
+ NSR_Initialise();
+ NIO_Initialise();
+ CLG_Initialise();
+ KEY_Initialise();
+ CAM_Initialise();
+ ACQ_Initialise();
+ MNL_Initialise();
+ RTC_Initialise();
+
+ /* From now on, it is safe to do finalisation on exit */
+ initialised = 1;
+
+ if (do_init_rtc) {
+ RTC_TimeInit(post_init_rtc_hook, NULL);
+ } else {
+ post_init_rtc_hook(NULL);
+ }
+
+ signal(SIGINT, signal_cleanup);
+ signal(SIGTERM, signal_cleanup);
+#if !defined(WINNT)
+ signal(SIGQUIT, signal_cleanup);
+ signal(SIGHUP, signal_cleanup);
+#endif /* WINNT */
+
+ /* The program normally runs under control of the main loop in
+ the scheduler. */
+ SCH_MainLoop();
+
+ MAI_CleanupAndExit();
+
+ return 0;
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/main.h,v 1.8 2002/02/28 23:27:10 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for main routine
+ */
+
+#ifndef GOT_MAIN_H
+#define GOT_MAIN_H
+
+/* Function to clean up at end of run */
+extern volatile void MAI_CleanupAndExit(void);
+
+#endif /* GOT_MAIN_H */
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/manual.c,v 1.20 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines for implementing manual input of real time.
+
+ The daemon accepts manual time input over the control connection,
+ and adjusts the system time to match. Besides this, though, it can
+ determine the average rate of time loss or gain of the local system
+ and adjust the frequency accordingly.
+
+ */
+
+#include <stddef.h>
+
+#include "manual.h"
+#include "logging.h"
+#include "local.h"
+#include "conf.h"
+#include "util.h"
+#include "ntp.h"
+#include "reference.h"
+#include "regress.h"
+
+static int enabled = 0;
+
+/* More recent samples at highest indices */
+typedef struct {
+ struct timeval when; /* This is our 'cooked' time */
+ double orig_offset; /*+ Not modified by slew samples */
+ double offset; /*+ if we are fast of the supplied reference */
+ double residual; /*+ regression residual (sign convention given by
+ (measured-predicted)) */
+} Sample;
+
+#define MAX_SAMPLES 16
+
+static Sample samples[16];
+static int n_samples;
+
+static int replace_margin;
+static int error;
+
+/* Eventually these constants need to be user-defined in conf file */
+#define REPLACE_MARGIN 300
+#define ERROR_MARGIN 0.2
+
+/* ================================================== */
+
+static void
+slew_samples(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *not_used);
+
+/* ================================================== */
+
+void
+MNL_Initialise(void)
+{
+ if (CNF_GetManualEnabled()) {
+ enabled = 1;
+ } else {
+ enabled = 0;
+ }
+
+ n_samples = 0;
+
+ replace_margin = REPLACE_MARGIN;
+ error = ERROR_MARGIN;
+
+ LCL_AddParameterChangeHandler(slew_samples, NULL);
+
+ return;
+}
+
+/* ================================================== */
+
+void
+MNL_Finalise(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+static void
+estimate_and_set_system(struct timeval *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
+{
+ double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
+ double b0, b1;
+ int n_runs, best_start; /* Unused results from regression analyser */
+ int i;
+ double freq = 0.0;
+ double skew = 0.099999999; /* All 9's when printed to log file */
+ int found_freq;
+ double slew_by;
+
+ if (n_samples > 1) {
+ for (i=0; i<n_samples; i++) {
+ UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when);
+ offsets[i] = samples[i].offset;
+ }
+
+ RGR_FindBestRobustRegression(agos, offsets, n_samples,
+ 1.0e-8, /* 0.01ppm easily good enough for this! */
+ &b0, &b1, &n_runs, &best_start);
+
+
+ /* Ignore b0 from regression; treat offset as being the most
+ recently entered value. (If the administrator knows he's put
+ an outlier in, he will rerun the settime operation.) However,
+ the frequency estimate comes from the regression. */
+
+ freq = -b1;
+ found_freq = 1;
+ } else {
+ if (offset_provided) {
+ b0 = offset;
+ } else {
+ b0 = 0.0;
+ }
+ b1 = freq = 0.0;
+ found_freq = 0;
+ }
+
+ if (offset_provided) {
+ slew_by = offset;
+ } else {
+ slew_by = b0;
+ }
+
+ if (found_freq) {
+ LOG(LOGS_INFO, LOGF_Manual,
+ "Making a frequency change of %.3fppm and a slew of %.6f\n",
+ 1.0e6 * freq, slew_by);
+
+ REF_SetManualReference(now,
+ slew_by,
+ freq, skew);
+ } else {
+ LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
+ REF_SetManualReference(now,
+ slew_by,
+ 0.0, skew);
+ }
+
+ if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
+ if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
+ if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
+
+ /* Calculate residuals to store them */
+ for (i=0; i<n_samples; i++) {
+ samples[i].residual = offsets[i] - (b0 + agos[i] * b1);
+ }
+
+}
+
+/* ================================================== */
+
+int
+MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
+{
+ struct timeval now;
+ double local_clock_err;
+ double offset;
+ int i;
+
+ if (enabled) {
+
+ /* Check whether timestamp is within margin of old one */
+ LCL_ReadCookedTime(&now, &local_clock_err);
+
+ UTI_DiffTimevalsToDouble(&offset, &now, ts);
+
+ /* Check if buffer full up */
+ if (n_samples == MAX_SAMPLES) {
+ /* Shift samples down */
+ for (i=1; i<n_samples; i++) {
+ samples[i-1] = samples[i];
+ }
+ --n_samples;
+ }
+
+ samples[n_samples].when = now;
+ samples[n_samples].offset = offset;
+ samples[n_samples].orig_offset = offset;
+ ++n_samples;
+
+ estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
+
+ return 1;
+
+ } else {
+
+ return 0;
+
+ }
+}
+
+/* ================================================== */
+
+static void
+slew_samples(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *not_used)
+{
+ double elapsed, delta_time;
+ int i;
+ for (i=0; i<n_samples; i++) {
+ UTI_DiffTimevalsToDouble(&elapsed, cooked, &samples[i].when);
+ delta_time = elapsed * dfreq - doffset;
+ UTI_AddDoubleToTimeval(&samples[i].when, delta_time, &samples[i].when);
+ samples[i].offset += delta_time;
+ }
+ return;
+}
+
+/* ================================================== */
+
+void
+MNL_Enable(void)
+{
+ enabled = 1;
+}
+
+
+/* ================================================== */
+
+void
+MNL_Disable(void)
+{
+ enabled = 0;
+}
+
+/* ================================================== */
+
+void
+MNL_Reset(void)
+{
+ n_samples = 0;
+}
+
+/* ================================================== */
+/* Generate report data for the REQ_MANUAL_LIST command/monitoring
+ protocol */
+
+void
+MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n)
+{
+ int i;
+
+ if (n_samples > max) {
+ *n = max;
+ } else {
+ *n = n_samples;
+ }
+
+ for (i=0; i<n_samples && i<max; i++) {
+ report[i].when = samples[i].when.tv_sec;
+ report[i].slewed_offset = samples[i].offset;
+ report[i].orig_offset = samples[i].orig_offset;
+ report[i].residual = samples[i].residual;
+ }
+}
+
+/* ================================================== */
+/* Delete a sample if it's within range, re-estimate the error and
+ drift and apply it to the system clock. */
+
+int
+MNL_DeleteSample(int index)
+{
+ int i;
+ struct timeval now;
+ double local_clock_err;
+
+ if ((index < 0) || (index >= n_samples)) {
+ return 0;
+ }
+
+ /* Crunch the samples down onto the one being deleted */
+
+ for (i=index; i<(n_samples-1); i++) {
+ samples[i] = samples[i+1];
+ }
+
+ n_samples -= 1;
+
+ /* Now re-estimate. NULLs because we don't want the parameters back
+ in this case. */
+ LCL_ReadCookedTime(&now, &local_clock_err);
+ estimate_and_set_system(&now, 0, 0.0, NULL, NULL, NULL);
+
+ return 1;
+
+}
+
+/* ================================================== */
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/manual.h,v 1.12 2002/02/28 23:27:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for manual time input module.
+
+ */
+
+#ifndef GOT_MANUAL_H
+#define GOT_MANUAL_H
+
+#include "sysincl.h"
+#include "reports.h"
+
+extern void MNL_Initialise(void);
+extern void MNL_Finalise(void);
+extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
+
+extern void MNL_Enable(void);
+extern void MNL_Disable(void);
+extern void MNL_Reset(void);
+
+extern void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n);
+extern int MNL_DeleteSample(int index);
+
+#endif /* GOT_MANUAL_H */
--- /dev/null
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform (UINT4 *, UINT4 *);
+
+#ifdef __STDC__
+static const
+#else
+static
+#endif
+unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#if defined(FAST_MD5) && defined(__GNUC__) && defined(mc68000)
+/*
+ * If we're on a 68000 based CPU and using a GNU C compiler with
+ * inline assembly code, we can speed this up a bit.
+ */
+inline UINT4 ROTATE_LEFT(UINT4 x, int n)
+{
+ asm("roll %2,%0" : "=d" (x) : "0" (x), "Ir" (n));
+ return x;
+}
+#else
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+#endif
+
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned const char *inBuf;
+unsigned int inLen;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+
+void MD5Final (mdContext)
+MD5_CTX *mdContext;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+
+ FF ( a, b, c, d, in[ 0], S11, 0xd76aa478); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, 0xe8c7b756); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, 0x242070db); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, 0xc1bdceee); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, 0xf57c0faf); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, 0x4787c62a); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, 0xa8304613); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, 0xfd469501); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, 0x698098d8); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, 0x8b44f7af); /* 10 */
+ FF ( c, d, a, b, in[10], S13, 0xffff5bb1); /* 11 */
+ FF ( b, c, d, a, in[11], S14, 0x895cd7be); /* 12 */
+ FF ( a, b, c, d, in[12], S11, 0x6b901122); /* 13 */
+ FF ( d, a, b, c, in[13], S12, 0xfd987193); /* 14 */
+ FF ( c, d, a, b, in[14], S13, 0xa679438e); /* 15 */
+ FF ( b, c, d, a, in[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, 0xf61e2562); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, 0xc040b340); /* 18 */
+ GG ( c, d, a, b, in[11], S23, 0x265e5a51); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, 0xd62f105d); /* 21 */
+ GG ( d, a, b, c, in[10], S22, 0x2441453); /* 22 */
+ GG ( c, d, a, b, in[15], S23, 0xd8a1e681); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, 0x21e1cde6); /* 25 */
+ GG ( d, a, b, c, in[14], S22, 0xc33707d6); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, 0xf4d50d87); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, 0x455a14ed); /* 28 */
+ GG ( a, b, c, d, in[13], S21, 0xa9e3e905); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, 0x676f02d9); /* 31 */
+ GG ( b, c, d, a, in[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, 0xfffa3942); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, 0x8771f681); /* 34 */
+ HH ( c, d, a, b, in[11], S33, 0x6d9d6122); /* 35 */
+ HH ( b, c, d, a, in[14], S34, 0xfde5380c); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, 0xa4beea44); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH ( b, c, d, a, in[10], S34, 0xbebfbc70); /* 40 */
+ HH ( a, b, c, d, in[13], S31, 0x289b7ec6); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, 0xeaa127fa); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, 0xd4ef3085); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, 0x4881d05); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, 0xd9d4d039); /* 45 */
+ HH ( d, a, b, c, in[12], S32, 0xe6db99e5); /* 46 */
+ HH ( c, d, a, b, in[15], S33, 0x1fa27cf8); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, 0xf4292244); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, 0x432aff97); /* 50 */
+ II ( c, d, a, b, in[14], S43, 0xab9423a7); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, 0xfc93a039); /* 52 */
+ II ( a, b, c, d, in[12], S41, 0x655b59c3); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, 0x8f0ccc92); /* 54 */
+ II ( c, d, a, b, in[10], S43, 0xffeff47d); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, 0x85845dd1); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, 0x6fa87e4f); /* 57 */
+ II ( d, a, b, c, in[15], S42, 0xfe2ce6e0); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, 0xa3014314); /* 59 */
+ II ( b, c, d, a, in[13], S44, 0x4e0811a1); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, 0xf7537e82); /* 61 */
+ II ( d, a, b, c, in[11], S42, 0xbd3af235); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, 0xeb86d391); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c **
+ ******************************** (cut) ********************************
+ */
--- /dev/null
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifdef HAS_STDINT_H
+#include <stdint.h>
+#elif defined(HAS_INTTYPES_H)
+#include <inttypes.h>
+#endif
+
+/* typedef a 32-bit type */
+typedef uint32_t UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init (MD5_CTX *mdContext);
+void MD5Update (MD5_CTX *, unsigned const char *, unsigned int);
+void MD5Final (MD5_CTX *);
+
+/*
+ ***********************************************************************
+ ** End of md5.h **
+ ******************************** (cut) ********************************
+ */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/memory.h,v 1.7 2002/02/28 23:27:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for memory functions
+ */
+
+#ifndef GOT_MEMORY_H
+#define GOT_MEMORY_H
+
+#include <stdlib.h>
+
+#define Malloc(x) malloc(x)
+#define MallocNew(T) ((T *) malloc(sizeof(T)))
+#define MallocArray(T, n) ((T *) malloc((n) * sizeof(T)))
+#define Realloc(x,y) realloc(x,y)
+#define ReallocArray(T,n,x) ((T *) realloc((void *)(x), (n)*sizeof(T)))
+#define Free(x) free(x)
+
+#endif /* GOT_MEMORY_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/mkdirpp.c,v 1.10 2002/11/03 22:49:17 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ A function for creating a directory and any parent directories that
+ don't exist.
+
+ */
+
+#include "sysincl.h"
+
+#include "mkdirpp.h"
+
+static int
+do_dir(char *p)
+{
+ int status;
+ struct stat buf;
+
+#if defined(TEST)
+ fprintf(stderr, "do_dir(%s)\n", p);
+#endif
+
+ /* See if directory exists */
+ status = stat(p, &buf);
+
+ if (status < 0) {
+ if (errno == ENOENT) {
+ /* Try to create directory */
+ status = mkdir(p, 0755);
+ return status;
+ } else {
+ return status;
+ }
+ }
+
+ if (!S_ISDIR(buf.st_mode)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ================================================== */
+/* Return 0 if the directory couldn't be created, 1 if it could (or
+ already existed) */
+
+int
+mkdir_and_parents(const char *path)
+{
+ char *p;
+ int len;
+ int i, j, k, last;
+ len = strlen(path);
+
+ p = (char *) malloc(1 + len);
+
+ i = k = 0;
+ while (1) {
+ p[i++] = path[k++];
+
+ if (path[k] == '/' || !path[k]) {
+ p[i] = 0;
+
+ if (do_dir(p) < 0) {
+ return 0;
+ }
+
+ if (!path[k]) {
+ /* End of the string */
+ break;
+ }
+
+ /* check whether its a trailing / or group of / */
+ last = 1;
+ j = k+1;
+ while (path[j]) {
+ if (path[j] != '/') {
+ k = j - 1; /* Pick up a / into p[] thru the assignment at the top of the loop */
+ last = 0;
+ break;
+ }
+ j++;
+ }
+
+ if (last) {
+ break;
+ }
+ }
+
+ if (!path[k]) break;
+
+ }
+
+ free(p);
+ return 1;
+
+}
+
+/* ================================================== */
+
+#if defined(TEST)
+int main(int argc, char **argv) {
+ if (argc > 1) {
+ /* Invert sense of result */
+ return mkdir_and_parents(argv[1]) ? 0 : 1;
+ } else {
+ return 1;
+ }
+}
+#endif
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/mkdirpp.h,v 1.6 2002/02/28 23:27:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ */
+
+#ifndef GOT_MKDIRPP_H
+#define GOT_MKDIRPP_H
+
+extern int mkdir_and_parents(const char *path);
+
+#endif
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/nameserv.c,v 1.13 2002/02/28 23:27:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Functions to do name to IP address conversion
+
+ */
+
+#include "sysincl.h"
+
+#include "nameserv.h"
+
+/* ================================================== */
+
+unsigned long
+DNS_Name2IPAddress(const char *name)
+{
+ struct hostent *host;
+ unsigned char *address0;
+ unsigned long result;
+
+ host = gethostbyname(name);
+ if (host == NULL) {
+ result = DNS_Failed_Address;
+ } else {
+ address0 = host->h_addr_list[0];
+ result = ((((unsigned long)address0[0])<<24) |
+ (((unsigned long)address0[1])<<16) |
+ (((unsigned long)address0[2])<<8) |
+ (((unsigned long)address0[3])));
+ }
+
+ return result;
+
+}
+
+/* ================================================== */
+
+const char *
+DNS_IPAddress2Name(unsigned long ip_addr)
+{
+ struct hostent *host;
+ static char buffer[16];
+ unsigned int a, b, c, d;
+ unsigned long addr;
+
+ addr = htonl(ip_addr);
+ if (addr == 0UL) {
+ /* Catch this as a special case that will never resolve to
+ anything */
+ strcpy(buffer, "0.0.0.0");
+ return buffer;
+ } else {
+ host = gethostbyaddr((const char *) &addr, sizeof(ip_addr), AF_INET);
+ if (!host) {
+ a = (ip_addr >> 24) & 0xff;
+ b = (ip_addr >> 16) & 0xff;
+ c = (ip_addr >> 8) & 0xff;
+ d = (ip_addr) & 0xff;
+ sprintf(buffer, "%u.%u.%u.%u", a, b, c, d);
+ return buffer;
+ } else {
+ return host->h_name;
+ }
+ }
+}
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/nameserv.h,v 1.8 2002/02/28 23:27:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Module header for nameserver functions
+ */
+
+
+#ifndef GOT_NAMESERV_H
+#define GOT_NAMESERV_H
+
+static const unsigned long DNS_Failed_Address = 0x0UL;
+
+extern unsigned long DNS_Name2IPAddress(const char *name);
+
+const char *DNS_IPAddress2Name(unsigned long ip_addr);
+
+#endif /* GOT_NAMESERV_H */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp.h,v 1.11 2003/04/10 21:28:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file containing common NTP bits and pieces
+ */
+
+#ifndef GOT_NTP_H
+#define GOT_NTP_H
+
+#ifdef HAS_STDINT_H
+#include <stdint.h>
+#elif defined(HAS_INTTYPES_H)
+#include <inttypes.h>
+#endif
+
+typedef struct {
+ uint32_t hi;
+ uint32_t lo;
+} NTP_int64;
+
+typedef uint32_t NTP_int32;
+
+#define AUTH_DATA_LEN 16
+
+/* Type definition for leap bits */
+typedef enum {
+ LEAP_Normal = 0,
+ LEAP_InsertSecond = 1,
+ LEAP_DeleteSecond = 2,
+ LEAP_Unsynchronised = 3
+} NTP_Leap;
+
+typedef enum {
+ MODE_UNDEFINED = 0,
+ MODE_ACTIVE = 1,
+ MODE_PASSIVE = 2,
+ MODE_CLIENT = 3,
+ MODE_SERVER = 4,
+ MODE_BROADCAST = 5
+} NTP_Mode;
+
+typedef struct {
+ uint8_t lvm;
+ uint8_t stratum;
+ int8_t poll;
+ int8_t precision;
+ NTP_int32 root_delay;
+ NTP_int32 root_dispersion;
+ NTP_int32 reference_id;
+ NTP_int64 reference_ts;
+ NTP_int64 originate_ts;
+ NTP_int64 receive_ts;
+ NTP_int64 transmit_ts;
+ NTP_int32 auth_keyid;
+ uint8_t auth_data[AUTH_DATA_LEN];
+} NTP_Packet;
+
+/* We have to declare a buffer type to hold a datagram read from the
+ network. Even though we won't be using them (yet?!), this must be
+ large enough to hold NTP control messages. */
+
+/* Define the maximum number of bytes that can be read in a single
+ message. (This is cribbed from ntp.h in the xntpd source code). */
+
+#define MAX_NTP_MESSAGE_SIZE (468+12+16+4)
+
+typedef union {
+ NTP_Packet ntp_pkt;
+ uint8_t arbitrary[MAX_NTP_MESSAGE_SIZE];
+} ReceiveBuffer;
+
+#define NTP_NORMAL_PACKET_SIZE (sizeof(NTP_Packet) - (sizeof(NTP_int32) + AUTH_DATA_LEN))
+
+/* ================================================== */
+
+inline static double
+int32_to_double(NTP_int32 x)
+{
+ return (double) ntohl(x) / 65536.0;
+}
+
+/* ================================================== */
+
+inline static NTP_int32
+double_to_int32(double x)
+{
+ return htonl((NTP_int32)(0.5 + 65536.0 * x));
+}
+
+/* ================================================== */
+
+#endif /* GOT_NTP_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_core.c,v 1.49 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Core NTP protocol engine
+ */
+
+#include "sysincl.h"
+
+#include "ntp_core.h"
+#include "ntp_io.h"
+#include "memory.h"
+#include "sched.h"
+#include "reference.h"
+#include "local.h"
+#include "sources.h"
+#include "util.h"
+#include "conf.h"
+#include "logging.h"
+#include "keys.h"
+#include "md5.h"
+#include "addrfilt.h"
+#include "mkdirpp.h"
+#include "clientlog.h"
+
+/* ================================================== */
+/* File handle for file to which we write the measurement log */
+static FILE *logfile = NULL;
+
+static char *logfilename = NULL;
+static unsigned long logwrites=0;
+
+#define MEASUREMENTS_LOG "measurements.log"
+
+/* ================================================== */
+
+/* Day number of 1 Jan 1970 */
+#define MJD_1970 40587
+
+/* ================================================== */
+
+#define ZONE_WIDTH 4
+
+/* ================================================== */
+/* Enumeration used for remembering the operating mode of one of the
+ sources */
+
+typedef enum {
+ MD_OFFLINE, /* No sampling at all */
+ MD_ONLINE, /* Normal sampling based on sampling interval */
+ MD_BURST_WAS_OFFLINE, /* Burst sampling, return to offline afterwards */
+ MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
+} OperatingMode;
+
+/* ================================================== */
+/* Structure used for holding a single peer/server's
+ protocol machine */
+
+struct NCR_Instance_Record {
+ NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */
+ NTP_Mode mode; /* The source's NTP mode
+ (client/server or symmetric active peer) */
+ OperatingMode opmode; /* Whether we are sampling this source
+ or not and in what way */
+ int timer_running; /* Boolean indicating whether we have a timeout
+ pending to transmit to the source */
+ SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are
+ running on a timer. */
+
+ int auto_offline; /* If 1, automatically go offline if server/peer
+ isn't responding */
+
+ int local_poll; /* Log2 of polling interval at our end */
+ int remote_poll; /* Log2 of server/peer's polling interval (recovered
+ from received packets) */
+
+ int presend_minpoll; /* If the current polling interval is
+ at least this, an echo datagram
+ will be send some time before every
+ transmit. This ensures that both
+ us and the server/peer have an ARP
+ entry for each other ready, which
+ means our measurement is not
+ botched by an ARP round-trip on one
+ side or the other. */
+
+ int presend_done; /* The presend packet has been sent */
+
+ int minpoll; /* Log2 of minimum defined polling interval */
+ int maxpoll; /* Log2 of maximum defined polling interval */
+
+ double max_delay; /* Maximum round-trip delay to the
+ peer that we can tolerate and still
+ use the sample for generating
+ statistics from */
+
+ double max_delay_ratio; /* Largest ratio of delta /
+ min_delay_in_register that we can
+ tolerate. */
+
+ int do_auth; /* Flag indicating whether we
+ authenticate packets we send to
+ this machine (if it's serving us or
+ the association is symmetric). Note
+ : we don't authenticate if we can't
+ find the key in our database. */
+ unsigned long auth_key_id; /* The ID of the authentication key to
+ use. */
+
+ /* Count of how many packets we have transmitted since last successful
+ receive from this peer */
+ int tx_count;
+
+ /* Timestamp in tx field of last received packet. We have to
+ reproduce this exactly as the orig field or our outgoing
+ packet. */
+ NTP_int64 remote_orig;
+
+ /* Local timestamp when the last packet was received from the
+ source. We have to be prepared to tinker with this if the local
+ clock has its frequency adjusted before we repond. The value we
+ store here is what our own local time was when the same arrived.
+ Before replying, we have to correct this to fit with the
+ parameters for the current reference. (It must be stored
+ relative to local time to permit frequency and offset adjustments
+ to be made when we trim the local clock). */
+ struct timeval local_rx;
+
+ /* Local timestamp when we last transmitted a packet to the source.
+ We store two versions. The first is in NTP format, and is used
+ to validate the next received packet from the source.
+ Additionally, this is corrected to bring it into line with the
+ current reference. The second is in timeval format, and is kept
+ relative to the local clock. We modify this in accordance with
+ local clock frequency/offset changes, and use this for computing
+ statistics about the source when a return packet arrives. */
+ NTP_int64 local_ntp_tx;
+ struct timeval local_tx;
+
+ /* The instance record in the main source management module. This
+ performs the statistical analysis on the samples we generate */
+
+ SRC_Instance source;
+
+ int score;
+
+ int burst_good_samples_to_go;
+ int burst_total_samples_to_go;
+
+};
+
+/* ================================================== */
+/* Initial delay period before first packet is transmitted (in seconds) */
+#define INITIAL_DELAY 2.0
+
+/* Spacing required between samples for any two servers/peers (to
+ minimise risk of network collisions) (in seconds) */
+#define SAMPLING_SEPARATION 2.0
+
+/* Time to wait before retransmitting in burst mode, if we did not get
+ a reply to the previous probe */
+#define BURST_TIMEOUT 8.0
+
+/* The NTP protocol version that we support */
+#define NTP_VERSION 3
+
+/* Maximum allowed dispersion - as defined in RFC1305 (16 seconds) */
+#define NTP_MAX_DISPERSION 16.0
+
+/* Maximum allowed age of a reference to be supplied to a client (1 day) */
+#define NTP_MAXAGE 86400
+
+/* Maximum allowed stratum */
+#define NTP_MAX_STRATUM 15
+
+/* ================================================== */
+
+static ADF_AuthTable access_auth_table;
+
+static int md5_offset_usecs;
+
+/* ================================================== */
+/* Forward prototypes */
+
+static void transmit_timeout(void *arg);
+static void determine_md5_delay(void);
+
+/* ================================================== */
+
+void
+NCR_Initialise(void)
+{
+ char *direc;
+
+ if (CNF_GetLogMeasurements()) {
+ direc = CNF_GetLogDir();
+ if (!mkdir_and_parents(direc)) {
+ LOG(LOGS_ERR, LOGF_NtpCore, "Could not create directory %s", direc);
+ logfile = NULL;
+ } else {
+ logfilename = MallocArray(char, 2 + strlen(direc) + strlen(MEASUREMENTS_LOG));
+ strcpy(logfilename, direc);
+ strcat(logfilename, "/");
+ strcat(logfilename, MEASUREMENTS_LOG);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_NtpCore, "Couldn't open logfile %s for update", logfilename);
+ }
+ }
+ }
+
+ access_auth_table = ADF_CreateTable();
+
+ determine_md5_delay();
+
+}
+
+/* ================================================== */
+
+void
+NCR_Finalise(void)
+{
+ if (logfile) {
+ fclose(logfile);
+ }
+
+ ADF_DestroyTable(access_auth_table);
+
+}
+
+/* ================================================== */
+
+static void
+start_initial_timeout(NCR_Instance inst)
+{
+
+ /* Start timer for first transmission */
+ inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *)inst);
+ inst->timer_running = 1;
+
+ return;
+}
+
+/* ================================================== */
+
+static NCR_Instance
+create_instance(NTP_Remote_Address *remote_addr, NTP_Mode mode, SourceParameters *params)
+{
+ NCR_Instance result;
+
+ result = MallocNew(struct NCR_Instance_Record);
+
+ result->remote_addr = *remote_addr;
+ result->mode = mode;
+
+ result->minpoll = params->minpoll;
+ result->maxpoll = params->maxpoll;
+
+ result->presend_minpoll = params->presend_minpoll;
+ result->presend_done = 0;
+
+ if (params->authkey == INACTIVE_AUTHKEY) {
+ result->do_auth = 0;
+ result->auth_key_id = 0UL;
+ } else {
+ result->do_auth = 1;
+ result->auth_key_id = params->authkey;
+ }
+
+ result->max_delay = params->max_delay;
+ result->max_delay_ratio = params->max_delay_ratio;
+
+ result->tx_count = 0;
+
+ result->score = 0;
+
+ if (params->online) {
+ start_initial_timeout(result);
+ result->opmode = MD_ONLINE;
+ } else {
+ result->timer_running = 0;
+ result->timeout_id = 0;
+ result->opmode = MD_OFFLINE;
+ }
+
+ result->auto_offline = params->auto_offline;
+
+ result->local_poll = params->minpoll;
+
+ /* Create a source instance for this NTP source */
+ result->source = SRC_CreateNewInstance(remote_addr->ip_addr); /* Will need extra params eventually */
+
+ result->local_rx.tv_sec = 0;
+ result->local_rx.tv_usec = 0;
+
+ return result;
+
+}
+
+/* ================================================== */
+
+/* Get a new instance for a server */
+NCR_Instance
+NCR_GetServerInstance(NTP_Remote_Address *remote_addr, SourceParameters *params)
+{
+ return create_instance(remote_addr, MODE_CLIENT, params);
+}
+
+/* ================================================== */
+
+/* Get a new instance for a peer */
+NCR_Instance
+NCR_GetPeerInstance(NTP_Remote_Address *remote_addr, SourceParameters *params)
+{
+ return create_instance(remote_addr, MODE_ACTIVE, params);
+}
+
+/* ================================================== */
+
+/* Destroy an instance */
+void
+NCR_DestroyInstance(NCR_Instance instance)
+{
+ /* This will destroy the source instance inside the
+ structure, which will cause reselection if this was the
+ synchronising source etc. */
+ SRC_DestroyInstance(instance->source);
+
+ /* Cancel any pending timeouts */
+ if (instance->timer_running) {
+ SCH_RemoveTimeout(instance->timeout_id);
+ instance->timer_running = 0;
+ }
+
+ /* Free the data structure */
+ Free(instance);
+ return;
+}
+
+/* ================================================== */
+
+/* ================================================== */
+
+static int
+generate_packet_auth(NTP_Packet *pkt, unsigned long keyid)
+{
+ int keylen;
+ char *keytext;
+ int keyok;
+ MD5_CTX ctx;
+
+ keyok = KEY_GetKey(keyid, &keytext, &keylen);
+ if (keyok) {
+ pkt->auth_keyid = htonl(keyid);
+ MD5Init(&ctx);
+ MD5Update(&ctx, keytext, keylen);
+ MD5Update(&ctx, (unsigned char *) pkt, offsetof(NTP_Packet, auth_keyid));
+ MD5Final(&ctx);
+ memcpy(&(pkt->auth_data), &ctx.digest, 16);
+ return 1;
+ } else {
+ pkt->auth_keyid = htonl(0);
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+determine_md5_delay(void)
+{
+ NTP_Packet pkt;
+ struct timeval before, after;
+ unsigned long usecs, min_usecs=0;
+ MD5_CTX ctx;
+ static const char *example_key = "#a0,243asd=-b ds";
+ int slen;
+ int i;
+
+ slen = strlen(example_key);
+
+ for (i=0; i<10; i++) {
+ LCL_ReadRawTime(&before);
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned const char *) example_key, slen);
+ MD5Update(&ctx, (unsigned const char *) &pkt, offsetof(NTP_Packet, auth_keyid));
+ MD5Final(&ctx);
+ LCL_ReadRawTime(&after);
+
+ usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
+
+ if (i == 0) {
+ min_usecs = usecs;
+ } else {
+ if (usecs < min_usecs) {
+ min_usecs = usecs;
+ }
+ }
+
+ }
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_NtpCore, "MD5 took %d useconds", min_usecs);
+#endif
+
+ /* Add on a bit extra to allow for copying, conversions etc */
+ md5_offset_usecs = min_usecs + (min_usecs >> 4);
+
+}
+
+/* ================================================== */
+
+static int
+check_packet_auth(NTP_Packet *pkt, unsigned long keyid)
+{
+ int keylen;
+ char *keytext;
+ int keyok;
+ MD5_CTX ctx;
+
+ keyok = KEY_GetKey(keyid, &keytext, &keylen);
+ if (keyok) {
+ pkt->auth_keyid = htonl(keyid);
+ MD5Init(&ctx);
+ MD5Update(&ctx, keytext, keylen);
+ MD5Update(&ctx, (unsigned char *) pkt, offsetof(NTP_Packet, auth_keyid));
+ MD5Final(&ctx);
+ if (!memcmp((void *) &ctx.digest, (void *) &(pkt->auth_data), 16)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+normalise_score(NCR_Instance inst)
+{
+
+ while (inst->score >= ZONE_WIDTH) {
+ ++inst->local_poll;
+ inst->score -= ZONE_WIDTH;
+ }
+ while (inst->score < 0) {
+ if (inst->local_poll > 0) {
+ --inst->local_poll;
+ }
+ inst->score += ZONE_WIDTH;
+ }
+
+ /* Clamp polling interval to defined range */
+ if (inst->local_poll < inst->minpoll) {
+ inst->local_poll = inst->minpoll;
+ inst->score = 0;
+ } else if (inst->local_poll > inst->maxpoll) {
+ inst->local_poll = inst->maxpoll;
+ inst->score = ZONE_WIDTH - 1;
+ }
+
+}
+
+/* ================================================== */
+
+static void
+transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
+ int my_poll, /* The log2 of the local poll interval */
+ int do_auth, /* Boolean indicating whether to authenticate the packet or not */
+ unsigned long key_id, /* The authentication key ID */
+ NTP_int64 *orig_ts, /* Originate timestamp (from received packet) */
+ struct timeval *local_rx, /* Local time request packet was received */
+ struct timeval *local_tx, /* RESULT : Time this reply
+ is sent as local time, or
+ NULL if don't want to
+ know */
+ NTP_int64 *local_ntp_tx, /* RESULT : Time reply sent
+ as NTP timestamp
+ (including adjustment to
+ reference), ignored if
+ NULL */
+ NTP_Remote_Address *where_to /* Where to address the reponse to */
+ )
+{
+ NTP_Packet message;
+ int version;
+ int leap;
+ double local_time_err;
+ struct timeval local_transmit;
+
+ /* Parameters read from reference module */
+ int are_we_synchronised, our_stratum;
+ NTP_Leap leap_status;
+ unsigned long our_ref_id;
+ struct timeval our_ref_time;
+ double our_root_delay, our_root_dispersion;
+
+ version = 3;
+
+ LCL_ReadCookedTime(&local_transmit, &local_time_err);
+ REF_GetReferenceParams(&local_transmit,
+ &are_we_synchronised, &leap_status,
+ &our_stratum,
+ &our_ref_id, &our_ref_time,
+ &our_root_delay, &our_root_dispersion);
+
+ if (are_we_synchronised) {
+ leap = (int) leap_status;
+ } else {
+ leap = 3;
+ }
+
+ /* Generate transmit packet */
+ message.lvm = ((leap << 6) &0xc0) | ((version << 3) & 0x38) | (my_mode & 0x07);
+ message.stratum = our_stratum;
+ message.poll = my_poll;
+ message.precision = LCL_GetSysPrecisionAsLog();
+
+ /* If we're sending a client mode packet and we aren't synchronized yet,
+ we might have to set up artificial values for some of these parameters */
+ message.root_delay = double_to_int32(our_root_delay);
+ message.root_dispersion = double_to_int32(our_root_dispersion);
+
+ message.reference_id = htonl((NTP_int32) our_ref_id);
+
+ /* Now fill in timestamps */
+ UTI_TimevalToInt64(&our_ref_time, &message.reference_ts);
+
+ /* Originate - this comes from the last packet the source sent us */
+ message.originate_ts = *orig_ts;
+
+ /* Receive - this is when we received the last packet from the source.
+ This timestamp will have been adjusted so that it will now look to
+ the source like we have been running on our latest estimate of
+ frequency all along */
+ UTI_TimevalToInt64(local_rx, &message.receive_ts);
+
+ /* Transmit - this our local time right now! Also, we might need to
+ store this for our own use later, next time we receive a message
+ from the source we're sending to now. */
+ LCL_ReadCookedTime(&local_transmit, &local_time_err);
+
+ /* Authenticate */
+ if (do_auth) {
+ /* Pre-compensate the transmit time by approx. how long it will
+ take to generate the MD5 authentication bytes. */
+ local_transmit.tv_usec += md5_offset_usecs;
+ UTI_NormaliseTimeval(&local_transmit);
+ UTI_TimevalToInt64(&local_transmit, &message.transmit_ts);
+ generate_packet_auth(&message, key_id);
+ NIO_SendAuthenticatedPacket(&message, where_to);
+ } else {
+ UTI_TimevalToInt64(&local_transmit, &message.transmit_ts);
+ NIO_SendNormalPacket(&message, where_to);
+ }
+
+ if (local_tx) {
+ *local_tx = local_transmit;
+ }
+
+ if (local_ntp_tx) {
+ *local_ntp_tx = message.transmit_ts;
+ }
+
+}
+
+
+/* ================================================== */
+
+/* ================================================== */
+
+#define WARM_UP_DELAY 4.0
+
+/* ================================================== */
+/* Timeout handler for transmitting to a source. */
+
+static void
+transmit_timeout(void *arg)
+{
+ NCR_Instance inst = (NCR_Instance) arg;
+ NTP_Mode my_mode;
+ double timeout_delay=0.0;
+ int do_timer = 0;
+ int do_auth;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_NtpCore, "Transmit timeout for [%s:%d]",
+ UTI_IPToDottedQuad(inst->remote_addr.ip_addr), inst->remote_addr.port);
+#endif
+
+ /* Check whether we need to 'warm up' the link to the other end by
+ sending an echo exchange to ensure both ends' ARP caches are
+ primed. On loaded systems this might also help ensure that bits
+ of the program are paged in properly before we start. */
+
+ if ((inst->presend_minpoll > 0) &&
+ (inst->presend_minpoll <= inst->local_poll) &&
+ !inst->presend_done) {
+
+ /* Send */
+ NIO_SendEcho(&inst->remote_addr);
+
+ inst->presend_done = 1;
+
+ /* Requeue timeout */
+ inst->timer_running = 1;
+ inst->timeout_id = SCH_AddTimeoutInClass(WARM_UP_DELAY, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *)inst);
+
+ return;
+ }
+
+ inst->presend_done = 0; /* Reset for next time */
+
+ ++inst->tx_count;
+ if (inst->tx_count >= 9) {
+ /* Mark source unreachable */
+ SRC_UnsetReachable(inst->source);
+ } else if (inst->tx_count >= 3) {
+ if (inst->auto_offline) {
+ NCR_TakeSourceOffline(inst);
+ }
+ /* Do reselection */
+ SRC_SelectSource(0);
+ } else {
+ /* Nothing */
+ }
+
+ /* If the source to which we are currently locked starts to lose
+ connectivity, increase the sampling rate to try and bring it
+ back. If any other source loses connectivity, back off the
+ sampling rate to reduce wasted sampling. */
+
+ if (SRC_IsSyncPeer(inst->source)) {
+ if (inst->tx_count >= 2) {
+ /* Implies we have missed at least one transmission */
+ inst->score -= 3;
+ normalise_score(inst);
+ }
+ } else {
+ if (inst->tx_count >= 2) {
+ inst->score += 1;
+ normalise_score(inst);
+ }
+ }
+
+ my_mode = inst->mode;
+
+ if (inst->do_auth && KEY_KeyKnown(inst->auth_key_id)) {
+ do_auth = 1;
+ } else {
+ do_auth = 0;
+ }
+
+ transmit_packet(my_mode, inst->local_poll,
+ do_auth, inst->auth_key_id,
+ &inst->remote_orig,
+ &inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
+ &inst->remote_addr);
+
+
+ switch (inst->opmode) {
+ case MD_BURST_WAS_OFFLINE:
+ --inst->burst_total_samples_to_go;
+ if (inst->burst_total_samples_to_go <= 0) {
+ inst->opmode = MD_OFFLINE;
+ }
+ break;
+ case MD_BURST_WAS_ONLINE:
+ --inst->burst_total_samples_to_go;
+ if (inst->burst_total_samples_to_go <= 0) {
+ inst->opmode = MD_ONLINE;
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ /* Restart timer for this message */
+ switch (inst->opmode) {
+ case MD_ONLINE:
+ timeout_delay = (double)(1 << inst->local_poll);
+ do_timer = 1;
+ break;
+ case MD_OFFLINE:
+ do_timer = 0;
+ break;
+ case MD_BURST_WAS_ONLINE:
+ case MD_BURST_WAS_OFFLINE:
+ timeout_delay = BURST_TIMEOUT;
+ do_timer = 1;
+ break;
+ }
+
+ if (do_timer) {
+ inst->timer_running = 1;
+ inst->timeout_id = SCH_AddTimeoutInClass(timeout_delay, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *)inst);
+ } else {
+ inst->timer_running = 0;
+ }
+
+ /* And we're done */
+ return;
+}
+
+
+/* ================================================== */
+
+static void
+receive_packet(NTP_Packet *message, struct timeval *now, NCR_Instance inst, int do_auth)
+{
+ int pkt_leap;
+ int source_is_synchronized;
+ double pkt_root_delay;
+ double pkt_root_dispersion;
+
+ unsigned long auth_key_id;
+
+ /* The local time to which the (offset, delay, dispersion) triple will
+ be taken to relate. For client/server operation this is practically
+ the same as either the transmit or receive time. The difference comes
+ in symmetric active mode, when the receive may come minutes after the transmit, and this time
+ will be midway between the two */
+ struct timeval sample_time;
+
+ /* The estimated offset (nomenclature from RFC1305 section 3.4.4).
+ In seconds, a positive value indicates that the local clock is
+ SLOW of the remote source and a negative value indicates that the local
+ clock is FAST of the remote source. */
+ double theta;
+
+ /* The estimated round delay in seconds */
+ double delta;
+
+ /* The estimated peer dispersion in seconds */
+ double epsilon;
+
+ /* The estimated peer distance in seconds */
+ double peer_distance;
+
+ /* The total root delay */
+ double root_delay;
+
+ /* The total root dispersion */
+ double root_dispersion;
+
+ /* The skew relative to the remote source */
+ double skew;
+
+ /* The estimated skew relative to the remote source. */
+ double source_freq_lo, source_freq_hi;
+
+ /* These are the timeval equivalents of the remote epochs */
+ struct timeval remote_receive_tv, remote_transmit_tv;
+ struct timeval remote_reference_tv;
+ struct timeval local_average, remote_average;
+ double local_interval, remote_interval;
+
+ int test1, test2, test3, test4, test5, test6, test7, test8;
+
+ int test4a, test4b;
+
+ /* In the words of section 3.4.4 of RFC1305, valid_data means
+ that the NTP protocol association with the peer/server is
+ properly synchronised. valid_header means that the measurement
+ obtained from the packet is suitable for use in synchronising
+ our local clock. Wierd choice of terminology. */
+
+ int valid_data;
+ int valid_header;
+
+ /* Variables used for doing logging */
+ static char sync_stats[4] = {'N', '-', '+', '?'};
+
+ /* The estimated offset predicted from previous samples. The
+ convention here is that positive means local clock FAST of
+ reference, i.e. backwards to the way that 'theta' is defined. */
+ double estimated_offset;
+
+ /* The absolute difference between the offset estimate and
+ measurement in seconds */
+ double error_in_estimate;
+ int poll_to_use;
+ double delay_time = 0;
+ int requeue_transmit = 0;
+ int delta_score;
+
+ /* ==================== */
+
+ /* Save local receive timestamp */
+ inst->local_rx = *now;
+
+ pkt_leap = (message->lvm >> 6) & 0x3;
+ if (pkt_leap == 0x3) {
+ source_is_synchronized = 0;
+ } else {
+ source_is_synchronized = 1;
+ }
+
+ pkt_root_delay = int32_to_double(message->root_delay);
+ pkt_root_dispersion = int32_to_double(message->root_dispersion);
+
+ /* Perform packet validity tests */
+
+ /* Test 1 requires that pkt.xmt != peer.org. This protects
+ against receiving a duplicate packet */
+
+ if ((message->transmit_ts.hi == inst->remote_orig.hi) &&
+ (message->transmit_ts.lo == inst->remote_orig.lo)) {
+ test1 = 0; /* Failed */
+ } else {
+ test1 = 1; /* Success */
+ }
+
+ /* Test 2 requires pkt.org == peer.xmt. This ensures the source
+ is responding to the latest packet we sent to it. */
+ if ((message->originate_ts.hi != inst->local_ntp_tx.hi) ||
+ (message->originate_ts.lo != inst->local_ntp_tx.lo)) {
+ test2 = 0; /* Failed */
+ } else {
+ test2 = 1; /* Success */
+ }
+
+ /* Regardless of any validity checks we apply, we are required to
+ save these two fields from the packet into the ntp source
+ instance record. See RFC1305 section 3.4.4, peer.org <- pkt.xmt
+ & peer.peerpoll <- pkt.poll. Note we can't do this assignment
+ before test1 has been carried out!! */
+
+ inst->remote_orig = message->transmit_ts;
+ inst->remote_poll = message->poll;
+
+ /* Test 3 requires that pkt.org != 0 and pkt.rec != 0. If
+ either of these are true it means the association is not properly
+ 'up'. */
+
+ if (((message->originate_ts.hi == 0) && (message->originate_ts.lo == 0)) ||
+ ((message->receive_ts.hi == 0) && (message->receive_ts.lo == 0))) {
+ test3 = 0; /* Failed */
+ } else {
+ test3 = 1; /* Success */
+ }
+
+ SRC_GetFrequencyRange(inst->source, &source_freq_lo, &source_freq_hi);
+
+ UTI_Int64ToTimeval(&message->receive_ts, &remote_receive_tv);
+ UTI_Int64ToTimeval(&message->transmit_ts, &remote_transmit_tv);
+
+ if (test3) {
+
+ UTI_AverageDiffTimevals(&remote_receive_tv, &remote_transmit_tv,
+ &remote_average, &remote_interval);
+
+ UTI_AverageDiffTimevals(&inst->local_tx, now,
+ &local_average, &local_interval);
+
+ /* In our case, we work out 'delta' as the worst case delay,
+ assuming worst case frequency error between us and the other
+ source */
+
+ delta = local_interval - remote_interval / (1.0 - source_freq_lo);
+
+ /* Calculate theta. Following the NTP definition, this is negative
+ if we are fast of the remote source. */
+
+ theta = (double) (remote_average.tv_sec - local_average.tv_sec) +
+ (double) (remote_average.tv_usec - local_average.tv_usec) * 1.0e-6;
+
+ /* We treat the time of the sample as being midway through the local
+ measurement period. An analysis assuming constant relative
+ frequency and zero network delay shows this is the only possible
+ choice to estimate the frequency difference correctly for every
+ sample pair. */
+ sample_time = local_average;
+
+ /* Calculate skew */
+ skew = source_freq_hi - source_freq_lo;
+
+ /* and then calculate peer dispersion */
+ epsilon = LCL_GetSysPrecisionAsQuantum() + skew * local_interval;
+
+ } else {
+ /* If test3 failed, we probably can't calculate these quantities
+ properly (e.g. for the first sample received in a peering
+ connection). */
+ theta = delta = epsilon = 0.0;
+
+ }
+
+ peer_distance = epsilon + 0.5 * fabs(delta);
+
+ /* Test 4 requires that the round trip delay to the source and the
+ source (RFC1305 'peer') dispersion are less than a cutoff value */
+
+ if ((fabs(delta) >= NTP_MAX_DISPERSION) ||
+ (epsilon >= NTP_MAX_DISPERSION)) {
+ test4 = 0; /* Failed */
+ } else {
+ test4 = 1; /* Success */
+ }
+
+ /* Test 4a (additional to RFC1305) requires that the round trip
+ delay is less than an administrator-defined value */
+
+ if (fabs(delta) > inst->max_delay) {
+ test4a = 0; /* Failed */
+ } else {
+ test4a = 1; /* Success */
+ }
+
+ /* Test 4b (additional to RFC1305) requires that the ratio of the
+ round trip delay to the minimum one currently in the stats data
+ register is less than an administrator-defined value */
+
+ if (fabs(delta/SRC_MinRoundTripDelay(inst->source)) > inst->max_delay_ratio) {
+ test4b = 0; /* Failed */
+ } else {
+ test4b = 1; /* Success */
+ }
+
+ /* Test 5 relates to authentication. */
+ if (inst->do_auth) {
+ if (do_auth) {
+ auth_key_id = ntohl(message->auth_keyid);
+ if (!KEY_KeyKnown(auth_key_id)) {
+ test5 = 0;
+ } else {
+ test5 = check_packet_auth(message, auth_key_id);
+ }
+ } else {
+ /* If we expect authenticated info from this peer/server and the packet
+ doesn't have it, it's got to fail */
+ test5 = 0;
+ }
+ } else {
+ /* If the peer or server sends us an authenticated frame, but
+ we're not bothered about whether he authenticates or not, just
+ ignore the test. */
+ test5 = 1;
+ }
+
+ /* Test 6 checks that (i) the remote clock is synchronised (ii) the
+ transmit timestamp is not before the time it was synchronized (clearly
+ bogus if it is), and (iii) that it was not synchronised too long ago
+ */
+ UTI_Int64ToTimeval(&message->reference_ts, &remote_reference_tv);
+ if ((!source_is_synchronized) ||
+ (UTI_CompareTimevals(&remote_reference_tv, &remote_transmit_tv) == 1) ||
+ ((remote_reference_tv.tv_sec + NTP_MAXAGE - remote_transmit_tv.tv_sec) < 0)) {
+ test6 = 0; /* Failed */
+ } else {
+ test6 = 1; /* Succeeded */
+ }
+
+ /* Test 7 checks that the stratum in the packet is appropriate */
+ if ((message->stratum > REF_GetOurStratum()) ||
+ (message->stratum > NTP_MAX_STRATUM)) {
+ test7 = 0; /* Failed */
+ } else {
+ test7 = 1;
+ }
+
+ /* Test 8 checks that the root delay and dispersion quoted in
+ the packet are appropriate */
+ if ((fabs(pkt_root_delay) >= NTP_MAX_DISPERSION) ||
+ (pkt_root_dispersion >= NTP_MAX_DISPERSION)) {
+ test8 = 0; /* Failed */
+ } else {
+ test8 = 1;
+ }
+
+ valid_data = test1 && test2 && test3 && test4 && test4a && test4b;
+ valid_header = test5 && test6 && test7 && test8;
+
+ root_delay = pkt_root_delay + delta;
+ root_dispersion = pkt_root_dispersion + epsilon;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_NtpCore, "lvm=%o stratum=%d poll=%d prec=%d",
+ message->lvm, message->stratum, message->poll, message->precision);
+ LOG(LOGS_INFO, LOGF_NtpCore, "Root delay=%08lx (%f), dispersion=%08lx (%f)",
+ message->root_delay, pkt_root_delay, message->root_dispersion, pkt_root_dispersion);
+ LOG(LOGS_INFO, LOGF_NtpCore, "Ref id=[%s], ref_time=%08lx.%08lx [%s]",
+ UTI_IPToDottedQuad(ntohl(message->reference_id)),
+ message->reference_ts.hi, message->reference_ts.lo,
+ UTI_TimestampToString(&message->reference_ts));
+ LOG(LOGS_INFO, LOGF_NtpCore, "Originate=%08lx.%08lx [%s]",
+ message->originate_ts.hi, message->originate_ts.lo,
+ UTI_TimestampToString(&message->originate_ts));
+ LOG(LOGS_INFO, LOGF_NtpCore, "Message receive=%08lx.%08lx [%s]",
+ message->receive_ts.hi, message->receive_ts.lo,
+ UTI_TimestampToString(&message->receive_ts));
+
+ LOG(LOGS_INFO, LOGF_NtpCore, "Transmit=%08lx.%08lx [%s]",
+ message->transmit_ts.hi, message->transmit_ts.lo,
+ UTI_TimestampToString(&message->transmit_ts));
+
+ LOG(LOGS_INFO, LOGF_NtpCore, "theta=%f delta=%f epsilon=%f root_delay=%f root_dispersion=%f",
+ theta, delta, epsilon, root_delay, root_dispersion);
+
+ LOG(LOGS_INFO, LOGF_NtpCore, "test1=%d test2=%d test3=%d test4=%d valid_data=%d",
+ test1, test2, test3, test4, valid_data);
+
+ LOG(LOGS_INFO, LOGF_NtpCore, "test5=%d test6=%d test7=%d test8=%d valid_header=%d",
+ test5, test6, test7, test8, valid_header);
+#endif
+
+ if (valid_header) {
+ inst->tx_count = 0;
+ SRC_SetReachable(inst->source);
+ }
+
+ /* Do this before we accumulate a new sample into the stats registers, obviously */
+ estimated_offset = SRC_PredictOffset(inst->source, &sample_time);
+
+ if (valid_data) {
+ SRC_AccumulateSample(inst->source,
+ &sample_time,
+ theta, delta, epsilon,
+ root_delay, root_dispersion,
+ message->stratum, (NTP_Leap) pkt_leap);
+ }
+
+
+ /* Only do performance monitoring if we got valid data! */
+
+ if (valid_data) {
+
+ /* Now examine the registers. First though, if the prediction is
+ not even within +/- the peer distance of the peer, we are clearly
+ not tracking the peer at all well, so we back off the sampling
+ rate depending on just how bad the situation is. */
+ error_in_estimate = fabs(-theta - estimated_offset);
+ /* Now update the polling interval */
+
+ if (error_in_estimate > peer_distance) {
+ int shift = 0;
+ unsigned long temp = (int)(error_in_estimate / peer_distance);
+ do {
+ shift++;
+ temp>>=1;
+ } while (temp);
+
+ inst->local_poll -= shift;
+ inst->score = 0;
+
+ } else {
+
+ switch (SRC_LastSkewChange(inst->source)) {
+ case SRC_Skew_Decrease:
+ delta_score = 1;
+ break;
+ case SRC_Skew_Nochange:
+ delta_score = 0;
+ break;
+ case SRC_Skew_Increase:
+ delta_score = -2;
+ break;
+ default: /* Should not happen! */
+ delta_score = 0;
+ break;
+ }
+
+ inst->score += delta_score;
+ }
+
+ normalise_score(inst);
+
+ }
+
+ /* If we're in burst mode, check whether the burst is completed and
+ revert to the previous mode */
+
+ switch (inst->opmode) {
+ case MD_BURST_WAS_ONLINE:
+ if (valid_data) {
+ --inst->burst_good_samples_to_go;
+ }
+
+ if (inst->burst_good_samples_to_go <= 0) {
+ inst->opmode = MD_ONLINE;
+ }
+ break;
+
+ case MD_BURST_WAS_OFFLINE:
+ if (valid_data) {
+ --inst->burst_good_samples_to_go;
+ }
+
+ if (inst->burst_good_samples_to_go <= 0) {
+ inst->opmode = MD_OFFLINE;
+ if (inst->timer_running) {
+ SCH_RemoveTimeout(inst->timeout_id);
+ }
+ inst->timer_running = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* And now, requeue the timer.
+
+ If we're in burst mode, queue for immediate dispatch.
+
+ If we're operating in client/server mode, queue the timeout for
+ the poll interval hence. The fact that a timeout has been queued
+ in the transmit handler is immaterial - that is only done so that
+ we at least send something, if no reply is heard.
+
+ If we're in symmetric mode, we have to take account of the peer's
+ wishes, otherwise his sampling regime will fall to pieces. If
+ we're in client/server mode, we don't care what poll interval the
+ server responded with last time. */
+
+ switch (inst->opmode) {
+ case MD_OFFLINE:
+ requeue_transmit = 0;
+ break; /* Even if we've received something, we don't want to
+ transmit back. This might be a symmetric active peer
+ that is trying to talk to us. */
+
+ case MD_ONLINE:
+ /* Normal processing, depending on whether we're in
+ client/server or symmetric mode */
+
+ requeue_transmit = 1;
+
+ switch(inst->mode) {
+ case MODE_CLIENT:
+ /* Client/server association - aim at some randomised time
+ approx the poll interval away */
+ poll_to_use = inst->local_poll;
+
+ delay_time = (double) (1UL<<poll_to_use);
+
+ break;
+
+ case MODE_ACTIVE:
+ /* Symmetric active association - aim at some randomised time approx
+ half the poll interval away, to interleave 50-50 with the peer */
+
+ poll_to_use = (inst->local_poll < inst->remote_poll) ? inst->local_poll : inst->remote_poll;
+
+ /* Limit by min and max poll */
+ if (poll_to_use < inst->minpoll) poll_to_use = inst->minpoll;
+ if (poll_to_use > inst->maxpoll) poll_to_use = inst->maxpoll;
+
+ delay_time = (double) (1UL<<(poll_to_use - 1));
+
+ break;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+
+ break;
+
+ case MD_BURST_WAS_ONLINE:
+ case MD_BURST_WAS_OFFLINE:
+
+ requeue_transmit = 1;
+ delay_time = 0.0;
+ break;
+
+ default:
+ CROAK("Impossible");
+ break;
+
+ }
+
+
+ if (requeue_transmit) {
+ /* Get rid of old timeout and start a new one */
+ SCH_RemoveTimeout(inst->timeout_id);
+ inst->timeout_id = SCH_AddTimeoutInClass(delay_time, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *)inst);
+ }
+
+ /* Do measurement logging */
+ if (logfile) {
+ if (((logwrites++) % 32) == 0) {
+ fprintf(logfile,
+ "=====================================================================================================================\n"
+ " Date (UTC) Time IP Address L St 1234 ab 5678 LP RP SC Offset Peer del. Peer disp. Root del. Root disp.\n"
+ "=====================================================================================================================\n");
+ }
+
+ fprintf(logfile, "%s %-15s %1c %2d %1d%1d%1d%1d %1d%1d %1d%1d%1d%1d %2d %2d %2d %10.3e %10.3e %10.3e %10.3e %10.3e\n",
+ UTI_TimeToLogForm(sample_time.tv_sec),
+ UTI_IPToDottedQuad(inst->remote_addr.ip_addr),
+ sync_stats[pkt_leap],
+ message->stratum,
+ test1, test2, test3, test4,
+ test4a, test4b,
+ test5, test6, test7, test8,
+ inst->local_poll, inst->remote_poll,
+ (inst->score),
+ theta, delta, epsilon,
+ pkt_root_delay, pkt_root_dispersion);
+ fflush(logfile);
+ }
+
+
+ /* At this point we will have to do something about trimming the
+ poll interval for the source and requeueing the polling timeout.
+
+ Left until the source statistics management has been written */
+
+ return;
+}
+
+/* ================================================== */
+/* From RFC1305, the standard handling of receive packets, depending
+ on the mode of the packet and of the source, is :
+
+ Source mode>>>
+ Packet
+ mode active passive client server bcast
+ vvv
+
+ active recv pkt recv xmit xmit
+ passive recv error recv error error
+ client xmit xmit error xmit xmit
+ server recv error recv error error
+ bcast recv error recv error error
+
+ We ignore broadcasts in this implementation - they create too many
+ problems.
+
+ */
+
+/* ================================================== */
+
+static void
+process_known
+(NTP_Packet *message, /* the received message */
+ struct timeval *now, /* timestamp at time of receipt */
+ NCR_Instance inst, /* the instance record for this peer/server */
+ int do_auth /* whether the received packet allegedly contains
+ authentication info*/
+ )
+{
+ int pkt_mode;
+ int version;
+ int valid_auth, valid_key;
+ int authenticate_reply;
+ unsigned long auth_key_id;
+ unsigned long reply_auth_key_id;
+
+ /* Check version */
+ version = (message->lvm >> 3) & 0x7;
+ if (version != NTP_VERSION) {
+ /* Ignore packet, but might want to log it */
+ return;
+ }
+
+ /* Perform tests mentioned in RFC1305 to validate packet contents */
+ pkt_mode = (message->lvm >> 0) & 0x7;
+
+ /* Now, depending on the mode we decide what to do */
+ switch (pkt_mode) {
+ case MODE_CLIENT:
+ /* If message is client mode, we just respond with a server mode
+ packet, regardless of what we think the remote machine is
+ supposed to be. However, even though this is a configured
+ peer or server, we still implement access restrictions on
+ client mode operation.
+
+ This is an extension to RFC1305, as we don't bother to check
+ whether we are a client of the remote machine.
+
+ This copes with the case for an isolated network where one
+ machine is set by eye and is used as the master, with the
+ other machines pointed at it. If the master goes down, we
+ want to be able to reset its time at startup by relying on
+ one of the secondaries to flywheel it. The behaviour coded here
+ is required in the secondaries to make this possible. */
+
+ if (ADF_IsAllowed(access_auth_table, inst->remote_addr.ip_addr)) {
+
+ CLG_LogNTPClientAccess(inst->remote_addr.ip_addr, (time_t) now->tv_sec);
+
+ if (do_auth) {
+ auth_key_id = ntohl(message->auth_keyid);
+ valid_key = KEY_KeyKnown(auth_key_id);
+ if (valid_key) {
+ valid_auth = check_packet_auth(message, auth_key_id);
+ } else {
+ valid_auth = 0;
+ }
+
+ if (valid_key && valid_auth) {
+ authenticate_reply = 1;
+ reply_auth_key_id = auth_key_id;
+ } else {
+ authenticate_reply = 0;
+ reply_auth_key_id = 0UL;
+ }
+ } else {
+ authenticate_reply = 0;
+ reply_auth_key_id = 0UL;
+ }
+
+ transmit_packet(MODE_SERVER, inst->local_poll,
+ authenticate_reply, reply_auth_key_id,
+ &message->transmit_ts,
+ now,
+ &inst->local_tx,
+ &inst->local_ntp_tx,
+ &inst->remote_addr);
+
+ } else {
+ LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
+ UTI_IPToDottedQuad(inst->remote_addr.ip_addr),
+ inst->remote_addr.port);
+ }
+
+ break;
+
+ case MODE_ACTIVE:
+
+ switch(inst->mode) {
+ case MODE_ACTIVE:
+ /* Ordinary symmetric peering */
+ CLG_LogNTPPeerAccess(inst->remote_addr.ip_addr, (time_t) now->tv_sec);
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_PASSIVE:
+ /* In this software this case should not arise, we don't
+ support unconfigured peers */
+ break;
+ case MODE_CLIENT:
+ /* This is where we have the remote configured as a server and he has
+ us configured as a peer - fair enough. */
+ CLG_LogNTPPeerAccess(inst->remote_addr.ip_addr, (time_t) now->tv_sec);
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_SERVER:
+ /* Nonsense - we can't have a preconfigured server */
+ break;
+ case MODE_BROADCAST:
+ /* We don't handle broadcasts */
+ break;
+ default:
+ /* Obviously ignore */
+ break;
+ }
+
+ break;
+
+ case MODE_SERVER:
+
+ switch(inst->mode) {
+ case MODE_ACTIVE:
+ /* Slightly bizarre combination, but we can still process it */
+ CLG_LogNTPPeerAccess(inst->remote_addr.ip_addr, (time_t) now->tv_sec);
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_PASSIVE:
+ /* We have no passive peers in this software */
+ break;
+ case MODE_CLIENT:
+ /* Standard case where he's a server and we're the client */
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_SERVER:
+ /* RFC1305 error condition. */
+ break;
+ case MODE_BROADCAST:
+ /* RFC1305 error condition */
+ break;
+ default:
+ /* Obviously ignore */
+ break;
+ }
+ break;
+
+ case MODE_PASSIVE:
+
+ switch(inst->mode) {
+ case MODE_ACTIVE:
+ /* This would arise if we have the remote configured as a peer and
+ he does not have us configured */
+ CLG_LogNTPPeerAccess(inst->remote_addr.ip_addr, (time_t) now->tv_sec);
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_PASSIVE:
+ /* Error condition in RFC1305. Also, we can't have any
+ non-transient PASSIVE sources in this version, we only
+ allow configured peers! */
+ break;
+ case MODE_CLIENT:
+ /* This is a wierd combination - how could it arise? */
+ receive_packet(message, now, inst, do_auth);
+ break;
+ case MODE_SERVER:
+ /* Error condition in RFC1305 */
+ break;
+ case MODE_BROADCAST:
+ /* Error condition in RFC1305 */
+ break;
+ default:
+ /* Obviously ignore */
+ break;
+ }
+ break;
+
+ case MODE_BROADCAST:
+ /* Just ignore these, but might want to log them */
+ break;
+
+ default:
+ /* Obviously ignore */
+ break;
+
+ }
+
+
+
+}
+
+/* ================================================== */
+/* This routine is called when a new packet arrives off the network,
+ and it relates to a source we have an ongoing protocol exchange with */
+
+void
+NCR_ProcessNoauthKnown(NTP_Packet *message, struct timeval *now, NCR_Instance inst)
+{
+
+ process_known(message, now, inst, 0);
+
+}
+
+/* ================================================== */
+/* This routine is called when a new packet arrives off the network,
+ and we do not recognize its source */
+
+void
+NCR_ProcessNoauthUnknown(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr)
+{
+
+ NTP_Mode his_mode;
+ NTP_Mode my_mode;
+ int my_poll;
+
+ if (ADF_IsAllowed(access_auth_table, remote_addr->ip_addr)) {
+
+ his_mode = message->lvm & 0x07;
+
+ if (his_mode == MODE_CLIENT) {
+ /* We are server */
+ my_mode = MODE_SERVER;
+ CLG_LogNTPClientAccess(remote_addr->ip_addr, (time_t) now->tv_sec);
+
+ } else if (his_mode == MODE_ACTIVE) {
+ /* We are symmetric passive, even though we don't ever lock to him */
+ my_mode = MODE_PASSIVE;
+ CLG_LogNTPPeerAccess(remote_addr->ip_addr, (time_t) now->tv_sec);
+
+ } else {
+ my_mode = MODE_UNDEFINED;
+ }
+
+ /* If we can't determine a sensible mode to reply with, it means
+ he has supplied a wierd mode in his request, so ignore it. */
+
+ if (my_mode != MODE_UNDEFINED) {
+
+ my_poll = message->poll; /* What should this be set to? Does the client actually care? */
+
+ transmit_packet(my_mode, my_poll,
+ 0, 0UL,
+ &message->transmit_ts, /* Originate (for us) is the transmit time for the client */
+ now, /* Time we received the packet */
+ NULL, /* Don't care when we send reply, we aren't maintaining state about this client */
+ NULL, /* Ditto */
+ remote_addr);
+
+ }
+ } else {
+ LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
+ UTI_IPToDottedQuad(remote_addr->ip_addr),
+ remote_addr->port);
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+/* This routine is called when a new authenticated packet arrives off
+ the network, and it relates to a source we have an ongoing protocol
+ exchange with */
+
+void
+NCR_ProcessAuthKnown(NTP_Packet *message, struct timeval *now, NCR_Instance data)
+{
+ process_known(message, now, data, 1);
+
+}
+
+/* ================================================== */
+/* This routine is called when a new authenticated packet arrives off
+ the network, and we do not recognize its source */
+
+void
+NCR_ProcessAuthUnknown(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr)
+{
+
+ NTP_Mode his_mode;
+ NTP_Mode my_mode;
+ int my_poll;
+ int valid_key, valid_auth;
+ unsigned long key_id;
+
+ if (ADF_IsAllowed(access_auth_table, remote_addr->ip_addr)) {
+
+ his_mode = message->lvm & 0x07;
+
+ if (his_mode == MODE_CLIENT) {
+ /* We are server */
+ my_mode = MODE_SERVER;
+ CLG_LogNTPClientAccess(remote_addr->ip_addr, (time_t) now->tv_sec);
+
+ } else if (his_mode == MODE_ACTIVE) {
+ /* We are symmetric passive, even though we don't ever lock to him */
+ my_mode = MODE_PASSIVE;
+ CLG_LogNTPPeerAccess(remote_addr->ip_addr, (time_t) now->tv_sec);
+
+ } else {
+ my_mode = MODE_UNDEFINED;
+ }
+
+ /* If we can't determine a sensible mode to reply with, it means
+ he has supplied a wierd mode in his request, so ignore it. */
+
+ if (my_mode != MODE_UNDEFINED) {
+
+ /* Only reply if we know the key and the packet authenticates
+ properly. */
+ key_id = ntohl(message->auth_keyid);
+ valid_key = KEY_KeyKnown(key_id);
+
+ if (valid_key) {
+ valid_auth = check_packet_auth(message, key_id);
+ } else {
+ valid_auth = 0;
+ }
+
+ if (valid_key && valid_auth) {
+ my_poll = message->poll; /* What should this be set to? Does the client actually care? */
+
+ transmit_packet(my_mode, my_poll,
+ 1, key_id,
+ &message->transmit_ts, /* Originate (for us) is the transmit time for the client */
+ now, /* Time we received the packet */
+ NULL, /* Don't care when we send reply, we aren't maintaining state about this client */
+ NULL, /* Ditto */
+ remote_addr);
+ }
+ }
+ }
+ return;
+
+
+}
+
+/* ================================================== */
+
+void
+NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset)
+{
+ struct timeval prev;
+ prev = inst->local_rx;
+ UTI_AdjustTimeval(&inst->local_rx, when, &inst->local_rx, dfreq, doffset);
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_NtpCore, "rx prev=[%s] new=[%s]",
+ UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_rx));
+#endif
+ prev = inst->local_tx;
+ UTI_AdjustTimeval(&inst->local_tx, when, &inst->local_tx, dfreq, doffset);
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_NtpCore, "tx prev=[%s] new=[%s]",
+ UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_tx));
+#endif
+}
+
+/* ================================================== */
+
+void
+NCR_TakeSourceOnline(NCR_Instance inst)
+{
+ switch (inst->opmode) {
+ case MD_ONLINE:
+ /* Nothing to do */
+ break;
+ case MD_OFFLINE:
+ if (!inst->timer_running) {
+ /* We are not already actively polling it */
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToDottedQuad(inst->remote_addr.ip_addr));
+ inst->local_poll = inst->minpoll;
+ inst->score = (ZONE_WIDTH >> 1);
+ inst->opmode = MD_ONLINE;
+ start_initial_timeout(inst);
+ }
+ break;
+ case MD_BURST_WAS_ONLINE:
+ /* Will revert */
+ break;
+ case MD_BURST_WAS_OFFLINE:
+ inst->opmode = MD_BURST_WAS_ONLINE;
+ break;
+ }
+}
+
+/* ================================================== */
+
+void
+NCR_TakeSourceOffline(NCR_Instance inst)
+{
+ switch (inst->opmode) {
+ case MD_ONLINE:
+ if (inst->timer_running) {
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToDottedQuad(inst->remote_addr.ip_addr));
+ SCH_RemoveTimeout(inst->timeout_id);
+ inst->timer_running = 0;
+ inst->opmode = MD_OFFLINE;
+ }
+ break;
+ case MD_OFFLINE:
+ break;
+ case MD_BURST_WAS_ONLINE:
+ inst->opmode = MD_BURST_WAS_OFFLINE;
+ break;
+ case MD_BURST_WAS_OFFLINE:
+ break;
+ }
+
+}
+
+/* ================================================== */
+
+void
+NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
+{
+ inst->minpoll = new_minpoll;
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToDottedQuad(inst->remote_addr.ip_addr), new_minpoll);
+}
+
+/* ================================================== */
+
+void
+NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
+{
+ inst->maxpoll = new_maxpoll;
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToDottedQuad(inst->remote_addr.ip_addr), new_maxpoll);
+}
+
+/* ================================================== */
+
+void
+NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
+{
+ inst->max_delay = new_max_delay;
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f",
+ UTI_IPToDottedQuad(inst->remote_addr.ip_addr), new_max_delay);
+}
+
+/* ================================================== */
+
+void
+NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
+{
+ inst->max_delay_ratio = new_max_delay_ratio;
+ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay ratio %f",
+ UTI_IPToDottedQuad(inst->remote_addr.ip_addr), new_max_delay_ratio);
+}
+
+/* ================================================== */
+
+void
+NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples)
+{
+
+ if (inst->mode == MODE_CLIENT) {
+
+ /* We want to prevent burst mode being used on symmetric active
+ associations - it will play havoc with the peer's sampling
+ strategy. (This obviously relies on us having the peer
+ configured that way if he has us configured symmetric active -
+ but there's not much else we can do.) */
+
+ switch (inst->opmode) {
+ case MD_BURST_WAS_OFFLINE:
+ case MD_BURST_WAS_ONLINE:
+ /* If already burst sampling, don't start again */
+ break;
+
+ case MD_ONLINE:
+ inst->opmode = MD_BURST_WAS_ONLINE;
+ inst->burst_good_samples_to_go = n_good_samples;
+ inst->burst_total_samples_to_go = n_total_samples;
+ if (inst->timer_running) {
+ SCH_RemoveTimeout(inst->timeout_id);
+ }
+ inst->timer_running = 1;
+ inst->timeout_id = SCH_AddTimeoutInClass(0.0, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *) inst);
+ break;
+
+ case MD_OFFLINE:
+ inst->opmode = MD_BURST_WAS_OFFLINE;
+ inst->burst_good_samples_to_go = n_good_samples;
+ inst->burst_total_samples_to_go = n_total_samples;
+ if (inst->timer_running) {
+ SCH_RemoveTimeout(inst->timeout_id);
+ }
+ inst->timer_running = 1;
+ inst->timeout_id = SCH_AddTimeoutInClass(0.0, SAMPLING_SEPARATION,
+ SCH_NtpSamplingClass,
+ transmit_timeout, (void *) inst);
+ break;
+
+
+ default:
+ CROAK("Impossible");
+ break;
+ }
+ }
+
+}
+
+/* ================================================== */
+
+void
+NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now)
+{
+ report->poll = inst->local_poll;
+
+ switch (inst->mode) {
+ case MODE_CLIENT:
+ report->mode = RPT_NTP_CLIENT;
+ break;
+ case MODE_ACTIVE:
+ report->mode = RPT_NTP_PEER;
+ break;
+ default:
+ CROAK("Impossible");
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+int
+NCR_AddAccessRestriction(unsigned long ip_addr, int subnet_bits, int allow, int all)
+ {
+ ADF_Status status;
+
+ if (allow) {
+ if (all) {
+ status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits);
+ } else {
+ status = ADF_Allow(access_auth_table, ip_addr, subnet_bits);
+ }
+ } else {
+ if (all) {
+ status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits);
+ } else {
+ status = ADF_Deny(access_auth_table, ip_addr, subnet_bits);
+ }
+ }
+
+ if (status == ADF_BADSUBNET) {
+ return 0;
+ } else if (status == ADF_SUCCESS) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+NCR_CheckAccessRestriction(unsigned long ip_addr)
+{
+ return ADF_IsAllowed(access_auth_table, ip_addr);
+}
+
+/* ================================================== */
+
+void
+NCR_CycleLogFile(void)
+{
+ if (logfile && logfilename) {
+ fclose(logfile);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_NtpCore, "Could not reopen logfile %s", logfilename);
+ }
+ logwrites = 0;
+ }
+}
+
+/* ================================================== */
+
+void
+NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *offline,
+ int *burst_online, int *burst_offline)
+{
+ switch (inst->opmode) {
+ case MD_BURST_WAS_OFFLINE:
+ ++*burst_offline;
+ break;
+ case MD_BURST_WAS_ONLINE:
+ ++*burst_online;
+ break;
+ case MD_ONLINE:
+ ++*online;
+ break;
+ case MD_OFFLINE:
+ ++*offline;
+ break;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_core.h,v 1.16 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for the main NTP protocol engine
+ */
+
+#ifndef GOT_NTP_CORE_H
+#define GOT_NTP_CORE_H
+
+#include "sysincl.h"
+
+#include "addressing.h"
+#include "srcparams.h"
+#include "ntp.h"
+#include "reports.h"
+
+/* This is a private data type used for storing the instance record for
+ each source that we are chiming with */
+typedef struct NCR_Instance_Record *NCR_Instance;
+
+/* Init and fini functions */
+extern void NCR_Initialise(void);
+extern void NCR_Finalise(void);
+
+/* Get a new instance for a server */
+extern NCR_Instance NCR_GetServerInstance(NTP_Remote_Address *remote_addr, SourceParameters *params);
+
+/* Get a new instance for a peer */
+extern NCR_Instance NCR_GetPeerInstance(NTP_Remote_Address *remote_addr, SourceParameters *params);
+
+/* Destroy an instance */
+extern void NCR_DestroyInstance(NCR_Instance instance);
+
+/* This routine is called when a new packet arrives off the network,
+ and it relates to a source we have an ongoing protocol exchange with */
+extern void NCR_ProcessNoauthKnown(NTP_Packet *message, struct timeval *now, NCR_Instance data);
+
+/* This routine is called when a new packet arrives off the network,
+ and we do not recognize its source */
+extern void NCR_ProcessNoauthUnknown(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr);
+
+/* This routine is called when a new authenticated packet arrives off
+ the network, and it relates to a source we have an ongoing protocol
+ exchange with */
+extern void NCR_ProcessAuthKnown(NTP_Packet *message, struct timeval *now, NCR_Instance data);
+
+/* This routine is called when a new authenticated packet arrives off
+ the network, and we do not recognize its source */
+extern void NCR_ProcessAuthUnknown(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr);
+
+/* Slew receive and transmit times in instance records */
+extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
+
+/* Take a particular source online (i.e. start sampling it) */
+extern void NCR_TakeSourceOnline(NCR_Instance inst);
+
+/* Take a particular source offline (i.e. stop sampling it, without
+ marking it unreachable in the source selection stuff) */
+extern void NCR_TakeSourceOffline(NCR_Instance inst);
+
+extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);
+
+extern void NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll);
+
+extern void NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay);
+
+extern void NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio);
+
+extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
+
+extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now);
+
+extern int NCR_AddAccessRestriction(unsigned long ip_addr, int subnet_bits, int allow, int all);
+extern int NCR_CheckAccessRestriction(unsigned long ip_addr);
+
+extern void NCR_CycleLogFile(void);
+
+extern void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *offline,
+ int *burst_online, int *burst_offline);
+
+#endif /* GOT_NTP_CORE_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_io.c,v 1.23 2003/04/01 20:54:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This file deals with the IO aspects of reading and writing NTP packets
+ */
+
+#include "sysincl.h"
+
+#include "ntp_io.h"
+#include "ntp_core.h"
+#include "ntp_sources.h"
+#include "sched.h"
+#include "local.h"
+#include "logging.h"
+#include "conf.h"
+#include "util.h"
+
+#include <fcntl.h>
+
+/* The file descriptor for the socket */
+static int sock_fd;
+
+/* Flag indicating that we have been initialised */
+static int initialised=0;
+
+/* ================================================== */
+
+/* Forward prototypes */
+static void read_from_socket(void *anything);
+
+/* ================================================== */
+
+static void
+do_size_checks(void)
+{
+ /* Assertions to check the sizes of certain data types
+ and the positions of certain record fields */
+
+ /* Check that certain invariants are true */
+ assert(sizeof(NTP_int32) == 4);
+ assert(sizeof(NTP_int64) == 8);
+
+ /* Check offsets of all fields in the NTP packet format */
+ assert(offsetof(NTP_Packet, lvm) == 0);
+ assert(offsetof(NTP_Packet, stratum) == 1);
+ assert(offsetof(NTP_Packet, poll) == 2);
+ assert(offsetof(NTP_Packet, precision) == 3);
+ assert(offsetof(NTP_Packet, root_delay) == 4);
+ assert(offsetof(NTP_Packet, root_dispersion) == 8);
+ assert(offsetof(NTP_Packet, reference_id) == 12);
+ assert(offsetof(NTP_Packet, reference_ts) == 16);
+ assert(offsetof(NTP_Packet, originate_ts) == 24);
+ assert(offsetof(NTP_Packet, receive_ts) == 32);
+ assert(offsetof(NTP_Packet, transmit_ts) == 40);
+
+}
+
+/* ================================================== */
+
+void
+NIO_Initialise(void)
+{
+ struct sockaddr_in my_addr;
+ unsigned short port_number;
+ unsigned long bind_address;
+ int on_off = 1;
+
+ assert(!initialised);
+ initialised = 1;
+
+ do_size_checks();
+
+ port_number = CNF_GetNTPPort();
+
+ /* Open Internet domain UDP socket for NTP message transmissions */
+
+#if 0
+ sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+#endif
+ if (sock_fd < 0) {
+ LOG_FATAL(LOGF_NtpIO, "Could not open socket : %s", strerror(errno));
+ }
+
+ /* Make the socket capable of re-using an old address */
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
+ /* Don't quit - we might survive anyway */
+ }
+
+ /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
+ if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
+ /* Don't quit - we might survive anyway */
+ }
+
+ /* Bind the port */
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port_number);
+
+ CNF_GetBindAddress(&bind_address);
+
+ if (bind_address != 0UL) {
+ my_addr.sin_addr.s_addr = htonl(bind_address);
+ } else {
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_NtpIO, "Initialising, socket fd=%d", sock_fd);
+#endif
+
+ if (bind(sock_fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ LOG_FATAL(LOGF_NtpIO, "Could not bind socket : %s", strerror(errno));
+ }
+
+ /* Register handler for read events on the socket */
+ SCH_AddInputFileHandler(sock_fd, read_from_socket, NULL);
+
+#if 0
+ if (fcntl(sock_fd, F_SETFL, O_NONBLOCK | O_NDELAY) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIO, "Could not make socket non-blocking");
+ }
+
+ if (ioctl(sock_fd, I_SETSIG, S_INPUT) < 0) {
+ LOG(LOGS_ERR, LOGF_NtpIO, "Could not enable signal");
+ }
+#endif
+
+ return;
+}
+
+/* ================================================== */
+
+void
+NIO_Finalise(void)
+{
+ if (sock_fd >= 0) {
+ SCH_RemoveInputFileHandler(sock_fd);
+ close(sock_fd);
+ }
+ sock_fd = -1;
+ initialised = 0;
+ return;
+}
+
+/* ================================================== */
+
+
+/* ================================================== */
+
+static void
+read_from_socket(void *anything)
+{
+ /* This should only be called when there is something
+ to read, otherwise it will block. */
+
+ int status;
+ ReceiveBuffer message;
+ int message_length;
+ struct sockaddr_in where_from;
+ int from_length;
+ unsigned int flags = 0;
+ struct timeval now;
+ NTP_Remote_Address remote_addr;
+ double local_clock_err;
+
+ assert(initialised);
+
+ from_length = sizeof(where_from);
+ message_length = sizeof(message);
+
+ LCL_ReadCookedTime(&now, &local_clock_err);
+ status = recvfrom(sock_fd, (char *)&message, message_length, flags,
+ (struct sockaddr *)&where_from, &from_length);
+
+ /* Don't bother checking if read failed or why if it did. More
+ likely than not, it will be connection refused, resulting from a
+ previous sendto() directing a datagram at a port that is not
+ listening (which appears to generate an ICMP response, and on
+ some architectures e.g. Linux this is translated into an error
+ reponse on a subsequent recvfrom). */
+
+ if (status > 0) {
+ remote_addr.ip_addr = ntohl(where_from.sin_addr.s_addr);
+ remote_addr.port = ntohs(where_from.sin_port);
+
+ if (status == NTP_NORMAL_PACKET_SIZE) {
+
+ NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, &remote_addr);
+
+ } else if (status == sizeof(NTP_Packet)) {
+
+ NSR_ProcessAuthenticatedReceive((NTP_Packet *) &message.ntp_pkt, &now, &remote_addr);
+
+ } else {
+
+ /* Just ignore the packet if it's not of a recognized length */
+
+ }
+ }
+
+ return;
+}
+
+/* ================================================== */
+/* Send an unauthenticated packet to a given address */
+
+void
+NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
+{
+ struct sockaddr_in remote;
+
+ assert(initialised);
+
+ remote.sin_family = AF_INET;
+ remote.sin_port = htons(remote_addr->port);
+ remote.sin_addr.s_addr = htonl(remote_addr->ip_addr);
+
+ if (sendto(sock_fd, (void *) packet, NTP_NORMAL_PACKET_SIZE, 0,
+ (struct sockaddr *) &remote, sizeof(remote)) < 0) {
+ LOG(LOGS_WARN, LOGF_NtpIO, "Could not send to :%s%d : %s",
+ UTI_IPToDottedQuad(remote_addr->ip_addr), remote_addr->port, strerror(errno));
+ }
+
+ return;
+}
+
+/* ================================================== */
+/* Send an authenticated packet to a given address */
+
+void
+NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
+{
+ struct sockaddr_in remote;
+
+ assert(initialised);
+
+ remote.sin_family = AF_INET;
+ remote.sin_port = htons(remote_addr->port);
+ remote.sin_addr.s_addr = htonl(remote_addr->ip_addr);
+
+ if (sendto(sock_fd, (void *) packet, sizeof(NTP_Packet), 0,
+ (struct sockaddr *) &remote, sizeof(remote)) < 0) {
+ LOG(LOGS_WARN, LOGF_NtpIO, "Could not send to :%s%d : %s",
+ UTI_IPToDottedQuad(remote_addr->ip_addr), remote_addr->port, strerror(errno));
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+/* We ought to use getservbyname, but I can't really see this changing */
+#define ECHO_PORT 7
+
+void
+NIO_SendEcho(NTP_Remote_Address *remote_addr)
+{
+ unsigned long magic_message = 0xbe7ab1e7UL;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(ECHO_PORT);
+ addr.sin_addr.s_addr = htonl(remote_addr->ip_addr);
+
+ /* Just ignore error status on send - this is not a big deal anyway */
+ sendto(sock_fd, (void *) &magic_message, sizeof(unsigned long), 0,
+ (struct sockaddr *) &addr, sizeof(addr));
+
+
+}
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_io.h,v 1.9 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the header file for the NTP socket I/O bits.
+
+ */
+
+#ifndef GOT_NTP_IO_H
+#define GOT_NTP_IO_H
+
+#include "ntp.h"
+#include "addressing.h"
+
+/* Function to initialise the module. */
+extern void NIO_Initialise(void);
+
+/* Function to finalise the module */
+extern void NIO_Finalise(void);
+
+/* Function to transmit a packet */
+extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
+
+/* Function to transmit an authenticated packet */
+extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
+
+/* Function to send a datagram to a remote machine's UDP echo port. */
+extern void NIO_SendEcho(NTP_Remote_Address *remote_addr);
+
+#endif /* GOT_NTP_IO_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_sources.c,v 1.17 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Functions which manage the pool of NTP sources that we are currently
+ a client of or peering with.
+
+ */
+
+#include "sysincl.h"
+
+#include "ntp_sources.h"
+#include "ntp_core.h"
+#include "util.h"
+#include "logging.h"
+#include "local.h"
+
+/* ================================================== */
+
+/* Record type private to this file, used to store information about
+ particular sources */
+typedef struct {
+ NTP_Remote_Address remote_addr; /* The address of this source */
+ int in_use; /* Whether this slot in the table is in use */
+ NCR_Instance data; /* Data for the protocol engine for this source */
+} SourceRecord;
+
+#define N_RECORDS 256
+
+/* Fixed size table, because we use a hard coded hash algorithm. It
+ is rather unlikely we would have anything approaching this number
+ of sources. */
+static SourceRecord records[N_RECORDS];
+
+static int n_sources;
+
+/* The largest number of sources we want to have stored in the hash table */
+#define MAX_SOURCES 64
+
+/* ================================================== */
+/* Forward prototypes */
+static void
+slew_sources(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *anything);
+
+/* ================================================== */
+
+/* Flag indicating whether module is initialised */
+static int initialised = 0;
+
+/* ================================================== */
+
+void
+NSR_Initialise(void)
+{
+ int i;
+ for (i=0; i<N_RECORDS; i++) {
+ records[i].in_use = 0;
+ }
+ n_sources = 0;
+ initialised = 1;
+
+ LCL_AddParameterChangeHandler(slew_sources, NULL);
+
+ return;
+}
+
+/* ================================================== */
+
+void
+NSR_Finalise(void)
+{
+ initialised = 0;
+ return; /* Nothing to do yet */
+}
+
+/* ================================================== */
+/* Return slot number and whether the IP address was matched or not.
+ found = 0 => Neither IP nor port matched, empty slot returned
+ found = 1 => Only IP matched, port doesn't match
+ found = 2 => Both IP and port matched.
+
+ It is assumed that there can only ever be one record for a
+ particular IP address. (If a different port comes up, it probably
+ means someone is running ntpdate -d or something). Thus, if we
+ match the IP address we stop the search regardless of whether the
+ port number matches.
+
+ */
+
+static void
+find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
+{
+ unsigned long hash;
+ unsigned long ip = remote_addr->ip_addr;
+ unsigned short port = remote_addr->port;
+
+ assert(N_RECORDS == 256);
+
+ /* Compute hash value just by xor'ing the 4 bytes of the address together */
+ hash = ip ^ (ip >> 16);
+ hash = (hash ^ (hash >> 8)) & 0xff;
+
+ while ((records[hash].in_use) &&
+ (records[hash].remote_addr.ip_addr != ip)) {
+ hash++;
+ if (hash == 256) hash = 0;
+ }
+
+ if (records[hash].in_use) {
+ if (records[hash].remote_addr.port == port) {
+ *found = 2;
+ } else {
+ *found = 1;
+ }
+ *slot = hash;
+ } else {
+ *found = 0;
+ *slot = hash;
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+/* Procedure to add a new server source (to which this machine will be
+ a client) */
+NSR_Status
+NSR_AddServer(NTP_Remote_Address *remote_addr, SourceParameters *params)
+{
+ int slot, found;
+
+ assert(initialised);
+
+#if 0
+ LOG(LOGS_INFO, LOGF_NtpSources, "IP=%08lx port=%d", (unsigned long)remote_addr->ip_addr, remote_addr->port);
+#endif
+
+ /* Find empty bin & check that we don't have the address already */
+ find_slot(remote_addr, &slot, &found);
+ if (found) {
+ return NSR_AlreadyInUse;
+ } else {
+ if (n_sources == MAX_SOURCES) {
+ return NSR_TooManySources;
+ } else {
+ n_sources++;
+ records[slot].remote_addr = *remote_addr;
+ records[slot].in_use = 1;
+ records[slot].data = NCR_GetServerInstance(remote_addr, params); /* Will need params passing through */
+ return NSR_Success;
+ }
+ }
+}
+
+/* ================================================== */
+
+/* Procedure to add a new peer. */
+NSR_Status
+NSR_AddPeer(NTP_Remote_Address *remote_addr, SourceParameters *params)
+{
+ int slot, found;
+
+ assert(initialised);
+
+#if 0
+ LOG(LOGS_INFO, LOGF_NtpSources, "IP=%08lx port=%d", (unsigned long) remote_addr->ip_addr, remote_addr->port);
+#endif
+
+ /* Find empty bin & check that we don't have the address already */
+ find_slot(remote_addr, &slot, &found);
+ if (found) {
+ return NSR_AlreadyInUse;
+ } else {
+ if (n_sources == MAX_SOURCES) {
+ return NSR_TooManySources;
+ } else {
+ n_sources++;
+ records[slot].remote_addr = *remote_addr;
+ records[slot].in_use = 1;
+ records[slot].data = NCR_GetPeerInstance(remote_addr, params); /* Will need params passing through */
+ return NSR_Success;
+ }
+ }
+}
+
+/* ================================================== */
+
+/* Procedure to remove a source. We don't bother whether the port
+ address is matched - we're only interested in removing a record for
+ the right IP address. Thus the caller can specify the port number
+ as zero if it wishes. */
+NSR_Status
+NSR_RemoveSource(NTP_Remote_Address *remote_addr)
+{
+ int slot, found;
+
+ assert(initialised);
+
+ find_slot(remote_addr, &slot, &found);
+ if (!found) {
+ return NSR_NoSuchSource;
+ } else {
+ n_sources--;
+ records[slot].in_use = 0;
+ NCR_DestroyInstance(records[slot].data);
+ return NSR_Success;
+ }
+}
+
+/* ================================================== */
+
+/* This routine is called by ntp_io when a new packet arrives off the network.*/
+void
+NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr)
+{
+ int slot, found;
+
+ assert(initialised);
+
+#if 0
+ LOG(LOGS_INFO, LOGF_NtpSources, "from (%s,%d) at %s",
+ UTI_IPToDottedQuad(remote_addr->ip_addr),
+ remote_addr->port, UTI_TimevalToString(now));
+#endif
+
+ find_slot(remote_addr, &slot, &found);
+ if (found == 2) { /* Must match IP address AND port number */
+ NCR_ProcessNoauthKnown(message, now, records[slot].data);
+ } else {
+ NCR_ProcessNoauthUnknown(message, now, remote_addr);
+ }
+}
+
+/* ================================================== */
+
+/* This routine is called by ntp_io when a new packet with an authentication tail arrives off the network */
+void
+NSR_ProcessAuthenticatedReceive(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr)
+{
+ int slot, found;
+
+ assert(initialised);
+
+ find_slot(remote_addr, &slot, &found);
+ if (found == 2) {
+ NCR_ProcessAuthKnown(message, now, records[slot].data);
+ } else {
+ NCR_ProcessAuthUnknown(message, now, remote_addr);
+ }
+}
+
+/* ================================================== */
+
+static void
+slew_sources(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *anything)
+{
+ int i;
+
+ for (i=0; i<N_RECORDS; i++) {
+ if (records[i].in_use) {
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "IP=%s dfreq=%f doff=%f",
+ UTI_IPToDottedQuad(records[i].remote_addr.ip_addr), dfreq, doffset);
+#endif
+
+ NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
+ }
+ }
+
+}
+
+/* ================================================== */
+
+int
+NSR_TakeSourcesOnline(unsigned long mask, unsigned long address)
+{
+ int i;
+ int any;
+ unsigned long ip;
+
+ any = 0;
+ for (i=0; i<N_RECORDS; i++) {
+ if (records[i].in_use) {
+ ip = records[i].remote_addr.ip_addr;
+ if ((ip & mask) == address) {
+ any = 1;
+ NCR_TakeSourceOnline(records[i].data);
+ }
+ }
+ }
+
+ return any;
+}
+
+/* ================================================== */
+
+int
+NSR_TakeSourcesOffline(unsigned long mask, unsigned long address)
+{
+ int i;
+ int any;
+ unsigned long ip;
+
+ any = 0;
+ for (i=0; i<N_RECORDS; i++) {
+ if (records[i].in_use) {
+ ip = records[i].remote_addr.ip_addr;
+ if ((ip & mask) == address) {
+ any = 1;
+ NCR_TakeSourceOffline(records[i].data);
+ }
+ }
+ }
+
+ return any;
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMinpoll(unsigned long address, int new_minpoll)
+{
+ int slot, found;
+ NTP_Remote_Address addr;
+ addr.ip_addr = address;
+ addr.port = 0;
+
+ find_slot(&addr, &slot, &found);
+ if (found == 0) {
+ return 0;
+ } else {
+ NCR_ModifyMinpoll(records[slot].data, new_minpoll);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxpoll(unsigned long address, int new_maxpoll)
+{
+ int slot, found;
+ NTP_Remote_Address addr;
+ addr.ip_addr = address;
+ addr.port = 0;
+
+ find_slot(&addr, &slot, &found);
+ if (found == 0) {
+ return 0;
+ } else {
+ NCR_ModifyMaxpoll(records[slot].data, new_maxpoll);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxdelay(unsigned long address, double new_max_delay)
+{
+ int slot, found;
+ NTP_Remote_Address addr;
+ addr.ip_addr = address;
+ addr.port = 0;
+
+ find_slot(&addr, &slot, &found);
+ if (found == 0) {
+ return 0;
+ } else {
+ NCR_ModifyMaxdelay(records[slot].data, new_max_delay);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+int
+NSR_ModifyMaxdelayratio(unsigned long address, double new_max_delay_ratio)
+{
+ int slot, found;
+ NTP_Remote_Address addr;
+ addr.ip_addr = address;
+ addr.port = 0;
+
+ find_slot(&addr, &slot, &found);
+ if (found == 0) {
+ return 0;
+ } else {
+ NCR_ModifyMaxdelayratio(records[slot].data, new_max_delay_ratio);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+int
+NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
+ unsigned long mask, unsigned long address)
+{
+ int i;
+ int any;
+ unsigned long ip;
+
+ any = 0;
+ for (i=0; i<N_RECORDS; i++) {
+ if (records[i].in_use) {
+ ip = records[i].remote_addr.ip_addr;
+ if ((ip & mask) == address) {
+ any = 1;
+ NCR_InitiateSampleBurst(records[i].data, n_good_samples, n_total_samples);
+ }
+ }
+ }
+
+ return any;
+
+}
+
+/* ================================================== */
+/* The ip address is assumed to be completed on input, that is how we
+ identify the source record. */
+
+void
+NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
+{
+ NTP_Remote_Address rem_addr;
+ int slot, found;
+
+ rem_addr.ip_addr = report->ip_addr;
+ rem_addr.port = 0;
+ find_slot(&rem_addr, &slot, &found);
+ if (found) {
+ NCR_ReportSource(records[slot].data, report, now);
+ } else {
+ report->poll = 0;
+ report->latest_meas_ago = 0;
+ }
+}
+
+/* ================================================== */
+
+void
+NSR_GetActivityReport(RPT_ActivityReport *report)
+{
+ int i;
+
+ report->online = 0;
+ report->offline = 0;
+ report->burst_online = 0;
+ report->burst_offline = 0;
+
+ for (i=0; i<N_RECORDS; i++) {
+ if (records[i].in_use) {
+ NCR_IncrementActivityCounters(records[i].data, &report->online, &report->offline,
+ &report->burst_online, &report->burst_offline);
+ }
+ }
+ return;
+}
+
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/ntp_sources.h,v 1.12 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header for the part of the software that deals with the set of
+ current NTP servers and peers, which can resolve an IP address into
+ a source record for further processing.
+
+ */
+
+#ifndef GOT_NTP_SOURCES_H
+#define GOT_NTP_SOURCES_H
+
+#include "ntp.h"
+#include "addressing.h"
+#include "srcparams.h"
+#include "ntp_core.h"
+#include "reports.h"
+
+/* Status values returned by operations that indirectly result from user
+ input. */
+typedef enum {
+ NSR_Success, /* Operation successful */
+ NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */
+ NSR_AlreadyInUse, /* AddServer, AddPeer - attempt to add a source that is already known */
+ NSR_TooManySources /* AddServer, AddPeer - too many sources already present */
+} NSR_Status;
+
+/* Procedure to add a new server source (to which this machine will be
+ a client) */
+extern NSR_Status NSR_AddServer(NTP_Remote_Address *remote_addr, SourceParameters *params);
+
+/* Procedure to add a new peer source. We will use symmetric active
+ mode packets when communicating with this source */
+extern NSR_Status NSR_AddPeer(NTP_Remote_Address *remote_addr, SourceParameters *params);
+
+/* Procedure to remove a source */
+extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
+
+/* This routine is called by ntp_io when a new packet arrives off the network */
+extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr);
+
+/* This routine is called by ntp_io when a new packet with an authentication tail arrives off the network */
+extern void NSR_ProcessAuthenticatedReceive(NTP_Packet *message, struct timeval *now, NTP_Remote_Address *remote_addr);
+
+/* Initialisation function */
+extern void NSR_Initialise(void);
+
+/* Finalisation function */
+extern void NSR_Finalise(void);
+
+/* This routine is used to indicate that sources whose IP addresses
+ match a particular subnet should be set online again. Returns a
+ flag indicating whether any hosts matched the address */
+extern int NSR_TakeSourcesOnline(unsigned long mask, unsigned long address);
+
+/* This routine is used to indicate that sources whose IP addresses
+ match a particular subnet should be set offline. Returns a flag
+ indicating whether any hosts matched the address */
+extern int NSR_TakeSourcesOffline(unsigned long mask, unsigned long address);
+
+extern int NSR_ModifyMinpoll(unsigned long address, int new_minpoll);
+
+extern int NSR_ModifyMaxpoll(unsigned long address, int new_maxpoll);
+
+extern int NSR_ModifyMaxdelay(unsigned long address, double new_max_delay);
+
+extern int NSR_ModifyMaxdelayratio(unsigned long address, double new_max_delay_ratio);
+
+extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, unsigned long mask, unsigned long address);
+
+extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now);
+
+extern void NSR_GetActivityReport(RPT_ActivityReport *report);
+
+#endif /* GOT_NTP_SOURCES_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/pktlength.c,v 1.12 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines to compute the expected length of a command or reply packet.
+ These operate on the RAW NETWORK packets, from the point of view of
+ integer endianness within the structures.
+
+ */
+#include "sysincl.h"
+
+#include "util.h"
+#include "pktlength.h"
+
+/* ================================================== */
+
+int
+PKL_CommandLength(CMD_Request *r)
+{
+ int type;
+ type = ntohs(r->command);
+ if (type < 0 || type >= N_REQUEST_TYPES) {
+ return 0;
+ } else {
+ switch (type) {
+
+ case REQ_NULL:
+ return offsetof(CMD_Request, data);
+ case REQ_ONLINE:
+ return offsetof(CMD_Request, data.online.EOR);
+ case REQ_OFFLINE:
+ return offsetof(CMD_Request, data.offline.EOR);
+ case REQ_BURST:
+ return offsetof(CMD_Request, data.burst.EOR);
+ case REQ_MODIFY_MINPOLL:
+ return offsetof(CMD_Request, data.modify_minpoll.EOR);
+ case REQ_MODIFY_MAXPOLL:
+ return offsetof(CMD_Request, data.modify_maxpoll.EOR);
+ case REQ_DUMP:
+ return offsetof(CMD_Request, data.dump.EOR);
+ case REQ_MODIFY_MAXDELAY:
+ return offsetof(CMD_Request, data.modify_maxdelay.EOR);
+ case REQ_MODIFY_MAXDELAYRATIO:
+ return offsetof(CMD_Request, data.modify_maxdelayratio.EOR);
+ case REQ_MODIFY_MAXUPDATESKEW:
+ return offsetof(CMD_Request, data.modify_maxupdateskew.EOR);
+ case REQ_LOGON :
+ return offsetof(CMD_Request, data.logon.EOR);
+ case REQ_SETTIME :
+ return offsetof(CMD_Request, data.settime.EOR);
+ case REQ_LOCAL :
+ return offsetof(CMD_Request, data.local.EOR);
+ case REQ_MANUAL :
+ return offsetof(CMD_Request, data.manual.EOR);
+ case REQ_N_SOURCES :
+ return offsetof(CMD_Request, data.n_sources.EOR);
+ case REQ_SOURCE_DATA :
+ return offsetof(CMD_Request, data.source_data.EOR);
+ case REQ_REKEY :
+ return offsetof(CMD_Request, data.rekey.EOR);
+ case REQ_ALLOW :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_ALLOWALL :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_DENY :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_DENYALL :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_CMDALLOW :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_CMDALLOWALL :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_CMDDENY :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_CMDDENYALL :
+ return offsetof(CMD_Request, data.allow_deny.EOR);
+ case REQ_ACCHECK :
+ return offsetof(CMD_Request, data.ac_check.EOR);
+ case REQ_CMDACCHECK :
+ return offsetof(CMD_Request, data.ac_check.EOR);
+ case REQ_ADD_SERVER :
+ return offsetof(CMD_Request, data.ntp_source.EOR);
+ case REQ_ADD_PEER :
+ return offsetof(CMD_Request, data.ntp_source.EOR);
+ case REQ_DEL_SOURCE :
+ return offsetof(CMD_Request, data.del_source.EOR);
+ case REQ_WRITERTC :
+ return offsetof(CMD_Request, data.writertc.EOR);
+ case REQ_DFREQ :
+ return offsetof(CMD_Request, data.dfreq.EOR);
+ case REQ_DOFFSET :
+ return offsetof(CMD_Request, data.doffset.EOR);
+ case REQ_TRACKING :
+ return offsetof(CMD_Request, data.tracking.EOR);
+ case REQ_SOURCESTATS :
+ return offsetof(CMD_Request, data.sourcestats.EOR);
+ case REQ_RTCREPORT :
+ return offsetof(CMD_Request, data.rtcreport.EOR);
+ case REQ_TRIMRTC :
+ return offsetof(CMD_Request, data.trimrtc.EOR);
+ case REQ_CYCLELOGS :
+ return offsetof(CMD_Request, data.cyclelogs.EOR);
+ case REQ_SUBNETS_ACCESSED :
+ {
+ unsigned long ns;
+ ns = ntohl(r->data.subnets_accessed.n_subnets);
+ return (offsetof(CMD_Request, data.subnets_accessed.subnets) +
+ ns * sizeof(REQ_SubnetsAccessed_Subnet));
+ }
+ case REQ_CLIENT_ACCESSES:
+ {
+ unsigned long nc;
+ nc = ntohl(r->data.client_accesses.n_clients);
+ return (offsetof(CMD_Request, data.client_accesses.client_ips) +
+ nc * sizeof(unsigned long));
+ }
+ case REQ_CLIENT_ACCESSES_BY_INDEX:
+ return offsetof(CMD_Request, data.client_accesses_by_index.EOR);
+ case REQ_MANUAL_LIST:
+ return offsetof(CMD_Request, data.manual_list.EOR);
+ case REQ_MANUAL_DELETE:
+ return offsetof(CMD_Request, data.manual_delete.EOR);
+ case REQ_MAKESTEP:
+ return offsetof(CMD_Request, data.make_step.EOR);
+ case REQ_ACTIVITY:
+ return offsetof(CMD_Request, data.activity.EOR);
+ default:
+ /* If we fall through the switch, it most likely means we've forgotten to implement a new case */
+ assert(0);
+ }
+ }
+
+ /* Catch-all case */
+ return 0;
+
+}
+
+
+/* ================================================== */
+
+int
+PKL_ReplyLength(CMD_Reply *r)
+{
+ int type;
+ type = ntohs(r->reply);
+ /* Note that reply type codes start from 1, not 0 */
+ if (type < 1 || type >= N_REPLY_TYPES) {
+ return 0;
+ } else {
+ switch (type) {
+ case RPY_NULL:
+ return offsetof(CMD_Reply, data.null.EOR);
+ case RPY_N_SOURCES:
+ return offsetof(CMD_Reply, data.n_sources.EOR);
+ case RPY_SOURCE_DATA:
+ return offsetof(CMD_Reply, data.source_data.EOR);
+ case RPY_MANUAL_TIMESTAMP:
+ return offsetof(CMD_Reply, data.manual_timestamp.EOR);
+ case RPY_TRACKING:
+ return offsetof(CMD_Reply, data.tracking.EOR);
+ case RPY_SOURCESTATS:
+ return offsetof(CMD_Reply, data.sourcestats.EOR);
+ case RPY_RTC:
+ return offsetof(CMD_Reply, data.rtc.EOR);
+ case RPY_SUBNETS_ACCESSED :
+ {
+ unsigned long ns = ntohl(r->data.subnets_accessed.n_subnets);
+ if (r->status == htons(STT_SUCCESS)) {
+ return (offsetof(CMD_Reply, data.subnets_accessed.subnets) +
+ ns * sizeof(RPY_SubnetsAccessed_Subnet));
+ } else {
+ return offsetof(CMD_Reply, data);
+ }
+ }
+ case RPY_CLIENT_ACCESSES:
+ {
+ unsigned long nc = ntohl(r->data.client_accesses.n_clients);
+ if (r->status == htons(STT_SUCCESS)) {
+ return (offsetof(CMD_Reply, data.client_accesses.clients) +
+ nc * sizeof(RPY_ClientAccesses_Client));
+ } else {
+ return offsetof(CMD_Reply, data);
+ }
+ }
+ case RPY_CLIENT_ACCESSES_BY_INDEX:
+ {
+ unsigned long nc = ntohl(r->data.client_accesses_by_index.n_clients);
+ if (r->status == htons(STT_SUCCESS)) {
+ return (offsetof(CMD_Reply, data.client_accesses_by_index.clients) +
+ nc * sizeof(RPY_ClientAccesses_Client));
+ } else {
+ return offsetof(CMD_Reply, data);
+ }
+ }
+ case RPY_MANUAL_LIST:
+ {
+ unsigned long ns = ntohl(r->data.manual_list.n_samples);
+ if (r->status == htons(STT_SUCCESS)) {
+ return (offsetof(CMD_Reply, data.manual_list.samples) +
+ ns * sizeof(RPY_ManualListSample));
+ } else {
+ return offsetof(CMD_Reply, data);
+ }
+ }
+ case RPY_ACTIVITY:
+ return offsetof(CMD_Reply, data.activity.EOR);
+
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/pktlength.h,v 1.4 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header for pktlength.c, routines for working out the expected length
+ of a network command/reply packet.
+
+ */
+
+#ifndef GOT_PKTLENGTH_H
+#define GOT_PKTLENGTH_H
+
+#include "candm.h"
+
+extern int PKL_CommandLength(CMD_Request *r);
+
+extern int PKL_ReplyLength(CMD_Reply *r);
+
+#endif /* GOT_PKTLENGTH_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/reference.c,v 1.40 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module keeps track of the source which we are claiming to be
+ our reference, for the purposes of generating outgoing NTP packets */
+
+#include "sysincl.h"
+
+#include "memory.h"
+#include "reference.h"
+#include "util.h"
+#include "conf.h"
+#include "logging.h"
+#include "local.h"
+#include "mkdirpp.h"
+
+/* ================================================== */
+
+static int are_we_synchronised;
+static int enable_local_stratum;
+static int local_stratum;
+static NTP_Leap our_leap_status;
+static int our_stratum;
+static unsigned long our_ref_id;
+struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
+static double our_offset;
+static double our_skew;
+static double our_residual_freq;
+static double our_root_delay;
+static double our_root_dispersion;
+
+static double max_update_skew;
+
+/* Flag indicating that we are initialised */
+static int initialised = 0;
+
+/* Flag and threshold for logging clock changes to syslog */
+static int do_log_change;
+static double log_change_threshold;
+
+/* Flag, threshold and user for sending mail notification on large clock changes */
+static int do_mail_change;
+static double mail_change_threshold;
+static char *mail_change_user;
+
+/* Filename of the drift file. */
+static char *drift_file=NULL;
+
+static void update_drift_file(double, double);
+
+#define MAIL_PROGRAM "/usr/lib/sendmail"
+
+/* ================================================== */
+/* File to which statistics are logged, NULL if none */
+static FILE *logfile = NULL;
+static char *logfilename = NULL;
+static unsigned long logwrites = 0;
+
+#define TRACKING_LOG "tracking.log"
+
+/* ================================================== */
+
+/* Day number of 1 Jan 1970 */
+#define MJD_1970 40587
+
+/* Reference ID supplied when we are locally referenced */
+#define LOCAL_REFERENCE_ID 0x7f7f0101UL
+
+/* ================================================== */
+
+void
+REF_Initialise(void)
+{
+ char *direc;
+ FILE *in;
+ char line[1024];
+ double file_freq_ppm, file_skew_ppm;
+ double our_frequency_ppm;
+
+ are_we_synchronised = 0;
+ our_leap_status = LEAP_Normal;
+ initialised = 1;
+ our_root_dispersion = 1.0;
+ our_root_delay = 1.0;
+ our_frequency_ppm = 0.0;
+ our_skew = 1.0; /* i.e. rather bad */
+ our_residual_freq = 0.0;
+
+ /* Now see if we can get the drift file opened */
+ drift_file = CNF_GetDriftFile();
+ if (drift_file) {
+ in = fopen(drift_file, "r");
+ if (in) {
+ if (fgets(line, sizeof(line), in)) {
+ if (sscanf(line, "%lf%lf", &file_freq_ppm, &file_skew_ppm) == 2) {
+ /* We have read valid data */
+ our_frequency_ppm = file_freq_ppm;
+ our_skew = 1.0e-6 * file_skew_ppm;
+ } else {
+ LOG(LOGS_WARN, LOGF_Reference, "Could not parse valid frequency and skew from driftfile %s",
+ drift_file);
+ }
+ } else {
+ LOG(LOGS_WARN, LOGF_Reference, "Could not read valid frequency and skew from driftfile %s",
+ drift_file);
+ }
+ fclose(in);
+ } else {
+ LOG(LOGS_WARN, LOGF_Reference, "Could not open driftfile %s for reading",
+ drift_file);
+ }
+
+ update_drift_file(our_frequency_ppm,our_skew);
+ }
+
+ LCL_SetAbsoluteFrequency(our_frequency_ppm);
+
+ if (CNF_GetLogTracking()) {
+ direc = CNF_GetLogDir();
+ if (!mkdir_and_parents(direc)) {
+ LOG(LOGS_ERR, LOGF_Reference, "Could not create directory %s", direc);
+ logfile = NULL;
+ } else {
+ logfilename = MallocArray(char, 2 + strlen(direc) + strlen(TRACKING_LOG));
+ strcpy(logfilename, direc);
+ strcat(logfilename, "/");
+ strcat(logfilename, TRACKING_LOG);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_Reference, "Couldn't open logfile %s for update", logfilename);
+ }
+ }
+ }
+
+ max_update_skew = fabs(CNF_GetMaxUpdateSkew()) * 1.0e-6;
+
+ enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
+
+ CNF_GetLogChange(&do_log_change, &log_change_threshold);
+ CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
+
+ /* And just to prevent anything wierd ... */
+ if (do_log_change) {
+ log_change_threshold = fabs(log_change_threshold);
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+void
+REF_Finalise(void)
+{
+ if (logfile) {
+ fclose(logfile);
+ }
+
+ initialised = 0;
+ return;
+}
+
+/* ================================================== */
+
+static double
+Sqr(double x)
+{
+ return x*x;
+}
+
+/* ================================================== */
+#if 0
+static double
+Cube(double x)
+{
+ return x*x*x;
+}
+#endif
+
+/* ================================================== */
+/* Update the drift coefficients to the file. */
+
+static void
+update_drift_file(double freq_ppm, double skew)
+{
+ struct stat buf;
+ char *temp_drift_file;
+ FILE *out;
+
+ /* Create a temporary file with a '.tmp' extension. */
+
+ temp_drift_file = (char*) Malloc(strlen(drift_file)+8);
+
+ if(!temp_drift_file) {
+ return;
+ }
+
+ strcpy(temp_drift_file,drift_file);
+ strcat(temp_drift_file,".tmp");
+
+ out = fopen(temp_drift_file, "w");
+ if (!out) {
+ Free(temp_drift_file);
+ LOG(LOGS_WARN, LOGF_Reference, "Could not open temporary driftfile %s.tmp for writing",
+ drift_file);
+ return;
+ }
+
+ /* Write the frequency and skew parameters in ppm */
+ fprintf(out, "%20.4f %20.4f\n", freq_ppm, 1.0e6 * skew);
+
+ fclose(out);
+
+ /* Clone the file attributes from the existing file if there is one. */
+
+ if (!stat(drift_file,&buf)) {
+ chown(temp_drift_file,buf.st_uid,buf.st_gid);
+ chmod(temp_drift_file,buf.st_mode&0777);
+ }
+
+ /* Rename the temporary file to the correct location (see rename(2) for details). */
+
+ if (rename(temp_drift_file,drift_file)) {
+ unlink(temp_drift_file);
+ Free(temp_drift_file);
+ LOG(LOGS_WARN, LOGF_Reference, "Could not replace old driftfile %s with new one %s.tmp (%d)",
+ drift_file,drift_file);
+ return;
+ }
+
+ Free(temp_drift_file);
+}
+
+/* ================================================== */
+
+#define BUFLEN 255
+#define S_MAX_USER_LEN "128"
+
+static void
+maybe_log_offset(double offset)
+{
+ double abs_offset;
+ FILE *p;
+ char buffer[BUFLEN], host[BUFLEN];
+ time_t now;
+ struct tm stm;
+
+ abs_offset = fabs(offset);
+
+ if (do_log_change &&
+ (abs_offset > log_change_threshold)) {
+ LOG(LOGS_WARN, LOGF_Reference,
+ "System clock wrong by %.6f seconds, adjustment started",
+ -offset);
+ }
+
+ if (do_mail_change &&
+ (abs_offset > mail_change_threshold)) {
+ sprintf(buffer, "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
+ p = popen(buffer, "w");
+ if (p) {
+ if (gethostname(host, sizeof(host)) < 0) {
+ strcpy(host, "<UNKNOWN>");
+ }
+ fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
+ fputs("\n", p);
+ now = time(NULL);
+ stm = *localtime(&now);
+ strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", &stm);
+ fputs(buffer, p);
+ /* If offset < 0 the local clock is slow, so we are applying a
+ positive change to it to bring it into line, hence the
+ negation of 'offset' in the next statement (and earlier) */
+ fprintf(p,
+ "\n\nchronyd started to apply an adjustment of %.3f seconds to it,\n"
+ " which exceeded the reporting threshold of %.3f seconds\n\n",
+ -offset, mail_change_threshold);
+ pclose(p);
+ } else {
+ LOG(LOGS_ERR, LOGF_Reference,
+ "Could not send mail notification to user %s\n",
+ mail_change_user);
+ }
+ }
+
+}
+
+/* ================================================== */
+
+
+void
+REF_SetReference(int stratum,
+ NTP_Leap leap,
+ unsigned long ref_id,
+ struct timeval *ref_time,
+ double offset,
+ double frequency,
+ double skew,
+ double root_delay,
+ double root_dispersion
+ )
+{
+ double previous_skew, new_skew;
+ double previous_freq, new_freq;
+ double old_weight, new_weight, sum_weight;
+ double delta_freq1, delta_freq2;
+ double skew1, skew2;
+ double our_frequency;
+
+ double abs_freq_ppm;
+
+ assert(initialised);
+
+ /* If we get a serious rounding error in the source stats regression
+ processing, there is a remote chance that the skew argument is a
+ 'not a number'. If such a quantity gets propagated into the
+ machine's kernel clock variables, nasty things will happen ..
+
+ To guard against this we need to check whether the skew argument
+ is a reasonable real number. I don't think isnan, isinf etc are
+ platform independent, so the following algorithm is used. */
+
+ {
+ double t;
+ t = (skew + skew) / skew; /* Skew shouldn't be zero either */
+ if ((t < 1.9) || (t > 2.1)) {
+ LOG(LOGS_WARN, LOGF_Reference, "Bogus skew value encountered");
+ return;
+ }
+ }
+
+
+ are_we_synchronised = 1;
+ our_stratum = stratum + 1;
+ our_leap_status = leap;
+ our_ref_id = ref_id;
+ our_ref_time = *ref_time;
+ our_offset = offset;
+ our_root_delay = root_delay;
+ our_root_dispersion = root_dispersion;
+
+ /* Eliminate updates that are based on totally unreliable frequency
+ information */
+
+ if (fabs(skew) < max_update_skew) {
+
+ previous_skew = our_skew;
+ new_skew = skew;
+
+ previous_freq = 0.0; /* We assume that the local clock is running
+ according to our previously determined
+ value; note that this is a delta frequency
+ --- absolute frequencies are only known in
+ the local module. */
+ new_freq = frequency;
+
+ /* Set new frequency based on weighted average of old and new skew. */
+
+ old_weight = 1.0 / Sqr(previous_skew);
+ new_weight = 3.0 / Sqr(new_skew);
+
+ sum_weight = old_weight + new_weight;
+
+ our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
+
+ delta_freq1 = previous_freq - our_frequency;
+ delta_freq2 = new_freq - our_frequency;
+
+ skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
+ skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
+ our_skew = skew1 + skew2;
+
+ our_residual_freq = new_freq - our_frequency;
+
+ maybe_log_offset(our_offset);
+ LCL_AccumulateFrequencyAndOffset(our_frequency, our_offset);
+
+ } else {
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Reference, "Skew %f too large to track, offset=%f", skew, our_offset);
+#endif
+ maybe_log_offset(our_offset);
+ LCL_AccumulateOffset(our_offset);
+
+ our_residual_freq = frequency;
+ }
+
+ abs_freq_ppm = LCL_ReadAbsoluteFrequency();
+
+ if (logfile) {
+
+ if (((logwrites++) % 32) == 0) {
+ fprintf(logfile,
+ "=======================================================================\n"
+ " Date (UTC) Time IP Address St Freq ppm Skew ppm Offset\n"
+ "=======================================================================\n");
+ }
+
+ fprintf(logfile, "%s %-15s %2d %10.3f %10.3f %10.3e\n",
+ UTI_TimeToLogForm(ref_time->tv_sec),
+ UTI_IPToDottedQuad(our_ref_id),
+ our_stratum,
+ abs_freq_ppm,
+ 1.0e6*our_skew,
+ our_offset);
+
+ fflush(logfile);
+ }
+
+ if (drift_file) {
+ update_drift_file(abs_freq_ppm, our_skew);
+ }
+
+ /* And now set the freq and offset to zero */
+ our_frequency = 0.0;
+ our_offset = 0.0;
+
+ return;
+}
+
+/* ================================================== */
+
+void
+REF_SetManualReference
+(
+ struct timeval *ref_time,
+ double offset,
+ double frequency,
+ double skew
+)
+{
+ int millisecond;
+ double abs_freq_ppm;
+
+ /* We are not synchronised to an external source, as such. This is
+ only supposed to be used with the local source option, really
+ ... */
+ are_we_synchronised = 0;
+
+ our_skew = skew;
+ our_residual_freq = 0.0;
+
+ maybe_log_offset(offset);
+ LCL_AccumulateFrequencyAndOffset(frequency, offset);
+
+ abs_freq_ppm = LCL_ReadAbsoluteFrequency();
+
+ if (logfile) {
+ millisecond = ref_time->tv_usec / 1000;
+
+ fprintf(logfile, "%5s %-15s %2d %10.3f %10.3f %10.3e\n",
+ UTI_TimeToLogForm(ref_time->tv_sec),
+ "127.127.1.1",
+ our_stratum,
+ abs_freq_ppm,
+ 1.0e6*our_skew,
+ our_offset);
+
+ fflush(logfile);
+ }
+
+ if (drift_file) {
+ update_drift_file(abs_freq_ppm, our_skew);
+ }
+}
+
+/* ================================================== */
+
+void
+REF_SetUnsynchronised(void)
+{
+ /* Variables required for logging to statistics log */
+ int millisecond;
+ struct timeval now;
+ double local_clock_err;
+
+ assert(initialised);
+
+ if (logfile) {
+ LCL_ReadCookedTime(&now, &local_clock_err);
+
+ millisecond = now.tv_usec / 1000;
+
+ fprintf(logfile, "%s %-15s 0 %10.3f %10.3f %10.3e\n",
+ UTI_TimeToLogForm(now.tv_sec),
+ "0.0.0.0",
+ LCL_ReadAbsoluteFrequency(),
+ 1.0e6*our_skew,
+ 0.0);
+
+ fflush(logfile);
+ }
+
+ are_we_synchronised = 0;
+}
+
+/* ================================================== */
+
+void
+REF_GetReferenceParams
+(
+ struct timeval *local_time,
+ int *is_synchronised,
+ NTP_Leap *leap_status,
+ int *stratum,
+ unsigned long *ref_id,
+ struct timeval *ref_time,
+ double *root_delay,
+ double *root_dispersion
+)
+{
+ double elapsed;
+ double extra_dispersion;
+
+ assert(initialised);
+
+ if (are_we_synchronised) {
+
+ *is_synchronised = 1;
+
+ *stratum = our_stratum;
+
+ UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
+ extra_dispersion = (our_skew + fabs(our_residual_freq)) * elapsed;
+
+ *leap_status = our_leap_status;
+ *ref_id = our_ref_id;
+ *ref_time = our_ref_time;
+ *root_delay = our_root_delay;
+ *root_dispersion = our_root_dispersion + extra_dispersion;
+
+ } else if (enable_local_stratum) {
+
+ *is_synchronised = 1;
+
+ *stratum = local_stratum;
+ *ref_id = LOCAL_REFERENCE_ID;
+ /* Make the reference time be now less a second - this will
+ scarcely affect the client, but will ensure that the transmit
+ timestamp cannot come before this (which would cause test 6 to
+ fail in the client's read routine) if the local system clock's
+ read routine is broken in any way. */
+ *ref_time = *local_time;
+ --ref_time->tv_sec;
+
+ /* Not much else we can do for leap second bits - maybe need to
+ have a way for the administrator to feed leap bits in */
+ *leap_status = LEAP_Normal;
+
+ *root_delay = 0.0;
+ *root_dispersion = LCL_GetSysPrecisionAsQuantum();
+
+ } else {
+
+ *is_synchronised = 0;
+
+ *leap_status = LEAP_Unsynchronised;
+ *stratum = 0;
+ *ref_id = 0UL;
+ ref_time->tv_sec = ref_time->tv_usec = 0;
+ /* These values seem to be standard for a client, and
+ any peer or client of ours will ignore them anyway because
+ we don't claim to be synchronised */
+ *root_dispersion = 1.0;
+ *root_delay = 1.0;
+
+ }
+}
+
+/* ================================================== */
+
+int
+REF_GetOurStratum(void)
+{
+ if (are_we_synchronised) {
+ return our_stratum;
+ } else if (enable_local_stratum) {
+ return local_stratum;
+ } else {
+ return 16;
+ }
+}
+
+/* ================================================== */
+
+void
+REF_ModifyMaxupdateskew(double new_max_update_skew)
+{
+ max_update_skew = new_max_update_skew * 1.0e-6;
+#if 0
+ LOG(LOGS_INFO, LOGF_Reference, "New max update skew = %.3fppm", new_max_update_skew);
+#endif
+}
+
+/* ================================================== */
+
+void
+REF_EnableLocal(int stratum)
+{
+ enable_local_stratum = 1;
+ local_stratum = stratum;
+}
+
+/* ================================================== */
+
+void
+REF_DisableLocal(void)
+{
+ enable_local_stratum = 0;
+}
+
+/* ================================================== */
+
+void
+REF_GetTrackingReport(RPT_TrackingReport *rep)
+{
+ double elapsed;
+ double extra_dispersion;
+ struct timeval now_raw, now_cooked;
+ double correction;
+
+ LCL_ReadRawTime(&now_raw);
+ correction = LCL_GetOffsetCorrection(&now_raw);
+ UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked);
+
+ if (are_we_synchronised) {
+
+ UTI_DiffTimevalsToDouble(&elapsed, &now_cooked, &our_ref_time);
+ extra_dispersion = (our_skew + fabs(our_residual_freq)) * elapsed;
+
+ rep->ref_id = our_ref_id;
+ rep->stratum = our_stratum;
+ rep->ref_time = our_ref_time;
+ UTI_DoubleToTimeval(correction, &rep->current_correction);
+ rep->freq_ppm = LCL_ReadAbsoluteFrequency();
+ rep->resid_freq_ppm = 1.0e6 * our_residual_freq;
+ rep->skew_ppm = 1.0e6 * our_skew;
+ rep->root_delay = our_root_delay;
+ rep->root_dispersion = our_root_dispersion + extra_dispersion;
+
+ } else if (enable_local_stratum) {
+
+ rep->ref_id = LOCAL_REFERENCE_ID;
+ rep->stratum = local_stratum;
+ rep->ref_time = now_cooked;
+ UTI_DoubleToTimeval(correction, &rep->current_correction);
+ rep->freq_ppm = LCL_ReadAbsoluteFrequency();
+ rep->resid_freq_ppm = 0.0;
+ rep->skew_ppm = 0.0;
+ rep->root_delay = 0.0;
+ rep->root_dispersion = LCL_GetSysPrecisionAsQuantum();
+
+ } else {
+
+ rep->ref_id = 0UL;
+ rep->stratum = 0;
+ rep->ref_time.tv_sec = 0;
+ rep->ref_time.tv_usec = 0;
+ UTI_DoubleToTimeval(correction, &rep->current_correction);
+ rep->freq_ppm = LCL_ReadAbsoluteFrequency();
+ rep->resid_freq_ppm = 0.0;
+ rep->skew_ppm = 0.0;
+ rep->root_delay = 0.0;
+ rep->root_dispersion = 0.0;
+ }
+
+}
+
+/* ================================================== */
+
+void
+REF_CycleLogFile(void)
+{
+ if (logfile && logfilename) {
+ fclose(logfile);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_Reference, "Could not reopen logfile %s", logfilename);
+ }
+ logwrites = 0;
+ }
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/reference.h,v 1.13 2002/02/28 23:27:12 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the header file for the module that keeps track of the current
+ reference.
+
+ */
+
+#ifndef GOT_REFERENCE_H
+#define GOT_REFERENCE_H
+
+#include "sysincl.h"
+
+#include "ntp.h"
+#include "reports.h"
+
+/* Init function */
+extern void REF_Initialise(void);
+
+/* Fini function */
+extern void REF_Finalise(void);
+
+/* Function which takes a local cooked time and returns the estimated
+ time of the reference. It also returns the other parameters
+ required for forming the outgoing NTP packet.
+
+ local_time is the cooked local time returned by the LCL module
+
+ is_synchronised indicates whether we are synchronised to anything
+ at the moment.
+
+ leap indicates the current leap status
+
+ stratum is the stratum of this machine, when considered to be sync'd to the
+ reference
+
+ ref_id is the reference_id of the source
+
+ ref_time is the time at which the we last set the reference source up
+
+ root_delay is the root delay of the sample we are using
+
+ root_dispersion is the root dispersion of the sample we are using, with all the
+ skew etc added on.
+
+ */
+
+extern void REF_GetReferenceParams
+(
+ struct timeval *local_time,
+ int *is_synchronised,
+ NTP_Leap *leap,
+ int *stratum,
+ unsigned long *ref_id,
+ struct timeval *ref_time,
+ double *root_delay,
+ double *root_dispersion
+);
+
+/* Function called by the clock selection process to register a new
+ reference source and its parameters
+
+ stratum is the stratum of the reference
+
+ leap is the leap status read from the source
+
+ ref_id is the reference id of the reference
+
+ ref_time is the time at which the parameters are assumed to be
+ correct, in terms of local time
+
+ frequency is the amount of local clock gain relative to the
+ reference per unit time interval of the local clock
+
+ skew is the maximum estimated frequency error (so we are within
+ [frequency+-skew])
+
+ root_delay is the root delay of the sample we are using
+
+ root_dispersion is the root dispersion of the sample we are using
+
+ */
+
+extern void REF_SetReference
+(
+ int stratum,
+ NTP_Leap leap,
+ unsigned long ref_id,
+ struct timeval *ref_time,
+ double offset,
+ double frequency,
+ double skew,
+ double root_delay,
+ double root_dispersion
+);
+
+extern void REF_SetManualReference
+(
+ struct timeval *ref_time,
+ double offset,
+ double frequency,
+ double skew
+);
+
+/* Mark the local clock as unsynchronised */
+extern void
+REF_SetUnsynchronised(void);
+
+/* Return the current stratum of this host or zero if the host is not
+ synchronised */
+extern int REF_GetOurStratum(void);
+
+/* Modify the setting for the maximum skew we are prepared to allow updates on (in ppm). */
+extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
+
+extern void REF_EnableLocal(int stratum);
+extern void REF_DisableLocal(void);
+
+extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
+
+extern void REF_CycleLogFile(void);
+
+#endif /* GOT_REFERENCE_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/regress.c,v 1.31 2003/01/20 22:24:20 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Regression algorithms.
+
+ */
+
+#include <assert.h>
+#include <math.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "regress.h"
+#include "logging.h"
+#include "util.h"
+
+#define MAX_POINTS 128
+
+void
+RGR_WeightedRegression
+(double *x, /* independent variable */
+ double *y, /* measured data */
+ double *w, /* weightings (large => data
+ less reliable) */
+
+ int n, /* number of data points */
+
+ /* And now the results */
+
+ double *b0, /* estimated y axis intercept */
+ double *b1, /* estimated slope */
+ double *s2, /* estimated variance of data points */
+
+ double *sb0, /* estimated standard deviation of
+ intercept */
+ double *sb1 /* estimated standard deviation of
+ slope */
+
+ /* Could add correlation stuff later if required */
+)
+{
+ double P, Q, U, V, W;
+ double diff;
+ double u, ui, aa;
+ int i;
+
+ if (n<3) {
+ CROAK("Insufficient points");
+ }
+
+ W = U = 0;
+ for (i=0; i<n; i++) {
+ U += x[i] / w[i];
+ W += 1.0 / w[i];
+ }
+
+ u = U / W;
+
+ /* Calculate statistics from data */
+ P = Q = V = 0.0;
+ for (i=0; i<n; i++) {
+ ui = x[i] - u;
+ P += y[i] / w[i];
+ Q += y[i] * ui / w[i];
+ V += ui * ui / w[i];
+ }
+
+ *b1 = Q / V;
+ *b0 = (P / W) - (*b1) * u;
+
+ *s2 = 0.0;
+ for (i=0; i<n; i++) {
+ diff = y[i] - *b0 - *b1*x[i];
+ *s2 += diff*diff / w[i];
+ }
+
+ *s2 /= (double)(n-2);
+
+ *sb1 = sqrt(*s2 / V);
+ aa = u * (*sb1);
+ *sb0 = sqrt(*s2 / W + aa * aa);
+
+ *s2 *= (n / W); /* Giving weighted average of variances */
+
+ return;
+}
+
+/* ================================================== */
+/* Get the coefficient to multiply the standard deviation by, to get a
+ particular size of confidence interval (assuming a t-distribution) */
+
+double
+RGR_GetTCoef(int dof)
+{
+ /* Assuming now the 99.95% quantile */
+ static double coefs[] =
+ { 636.6, 31.6, 12.92, 8.61, 6.869,
+ 5.959, 5.408, 5.041, 4.781, 4.587,
+ 4.437, 4.318, 4.221, 4.140, 4.073,
+ 4.015, 3.965, 3.922, 3.883, 3.850,
+ 3.819, 3.792, 3.768, 3.745, 3.725,
+ 3.707, 3.690, 3.674, 3.659, 3.646,
+ 3.633, 3.622, 3.611, 3.601, 3.591,
+ 3.582, 3.574, 3.566, 3.558, 3.551};
+
+ if (dof <= 40) {
+ return coefs[dof-1];
+ } else {
+ return 3.5; /* Until I can be bothered to do something better */
+ }
+}
+
+/* ================================================== */
+/* Structure used for holding results of each regression */
+
+typedef struct {
+ double variance;
+ double slope_sd;
+ double slope;
+ double offset;
+ double offset_sd;
+ double K2; /* Variance / slope_var */
+ int n; /* Number of points */
+ int dof; /* Number of degrees of freedom */
+} RegressionResult;
+
+/* ================================================== */
+/* Critical value for number of runs of residuals with same sign. 10%
+ critical region for now */
+
+static int critical_runs10[] =
+{ 0, 0, 0, 0, 0, 0, 0, 0, 3, 3,
+ 4, 4, 4, 4, 5, 5, 6, 6, 7, 7,
+ 7, 8, 8, 9, 9, 10, 10, 10, 11, 11,
+ 12, 12, 13, 13, 14, 14, 14, 15, 15, 16,
+ 16, 17, 17, 18, 18, 18, 19, 19, 20, 20,
+
+ /* Note that 66 onwards are bogus - I haven't worked out the
+ critical values */
+ 21, 21, 22, 22, 23, 23, 23, 24, 24, 25,
+ 25, 26, 26, 27, 27, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28
+};
+
+/* ================================================== */
+
+static int
+n_runs_from_residuals(double *resid, int n)
+{
+ int nruns;
+ int i;
+
+ nruns = 1;
+ for (i=1; i<n; i++) {
+ if (((resid[i-1] < 0.0) && (resid[i] < 0.0)) ||
+ ((resid[i-1] > 0.0) && (resid[i] > 0.0))) {
+ /* Nothing to do */
+ } else {
+ nruns++;
+ }
+ }
+
+ return nruns;
+}
+
+/* ================================================== */
+/* Return a boolean indicating whether we had enough points for
+ regression */
+
+#define RESID_SIZE 1024
+#define MIN_SAMPLES_FOR_REGRESS 3
+
+int
+RGR_FindBestRegression
+(double *x, /* independent variable */
+ double *y, /* measured data */
+ double *w, /* weightings (large => data
+ less reliable) */
+
+ int n, /* number of data points */
+
+ /* And now the results */
+
+ double *b0, /* estimated y axis intercept */
+ double *b1, /* estimated slope */
+ double *s2, /* estimated variance of data points */
+
+ double *sb0, /* estimated standard deviation of
+ intercept */
+ double *sb1, /* estimated standard deviation of
+ slope */
+
+ int *new_start, /* the new starting index to make the
+ residuals pass the two tests */
+
+ int *n_runs, /* number of runs amongst the residuals */
+
+ int *dof /* degrees of freedom in statistics (needed
+ to get confidence intervals later) */
+
+)
+{
+ double P, Q, U, V, W; /* total */
+ double resid[RESID_SIZE];
+ double ss;
+ double a, b, u, ui, aa;
+
+ int start, nruns, npoints, npoints_left;
+ int i;
+
+ if (n < MIN_SAMPLES_FOR_REGRESS) {
+ return 0;
+ }
+
+ start = 0;
+ do {
+
+ W = U = 0;
+ for (i=start; i<n; i++) {
+ U += x[i] / w[i];
+ W += 1.0 / w[i];
+ }
+
+ u = U / W;
+
+ P = Q = V = 0.0;
+ for (i=start; i<n; i++) {
+ ui = x[i] - u;
+ P += y[i] / w[i];
+ Q += y[i] * ui / w[i];
+ V += ui * ui / w[i];
+ }
+
+ npoints = n - start;
+ b = Q / V;
+ a = (P / W) - (b * u);
+
+ for (i=start; i<n; i++) {
+ resid[i] = y[i] - a - b*x[i];
+ }
+
+ /* Count number of runs */
+ nruns = n_runs_from_residuals(resid + start, npoints);
+
+ npoints_left = n - start - 1;
+
+ if ((nruns > critical_runs10[npoints]) || (npoints_left < MIN_SAMPLES_FOR_REGRESS)) {
+ break;
+ } else {
+ /* Try dropping one sample at a time until the runs test passes. */
+ ++start;
+ }
+
+ } while (1);
+
+ /* Work out statistics from full dataset */
+ *b1 = b;
+ *b0 = a;
+
+ ss = 0.0;
+ for (i=start; i<n; i++) {
+ ss += resid[i]*resid[i] / w[i];
+ }
+
+ npoints = n - start;
+ ss /= (double)(npoints - 2);
+ *sb1 = sqrt(ss / V);
+ aa = u * (*sb1);
+ *sb0 = sqrt((ss / W) + (aa * aa));
+ *s2 = ss * (double) npoints / W;
+
+ *new_start = start;
+ *dof = npoints - 2;
+ *n_runs = nruns;
+
+ return 1;
+
+}
+
+/* ================================================== */
+
+#define EXCH(a,b) temp=(a); (a)=(b); (b)=temp
+
+/* ================================================== */
+/* Find the index'th biggest element in the array x of n elements.
+ flags is an array where a 1 indicates that the corresponding entry
+ in x is known to be sorted into its correct position and a 0
+ indicates that the corresponding entry is not sorted. However, if
+ flags[m] = flags[n] = 1 with m<n, then x[m] must be <= x[n] and for
+ all i with m<i<n, x[m] <= x[i] <= x[n]. In practice, this means
+ flags[] has to be the result of a previous call to this routine
+ with the same array x, and is used to remember which parts of the
+ x[] array we have already sorted.
+
+ The approach used is a cut-down quicksort, where we only bother to
+ keep sorting the partition that contains the index we are after.
+ The approach comes from Numerical Recipes in C (ISBN
+ 0-521-43108-5). */
+
+static double
+find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
+{
+ int u, v, l, r;
+ double temp;
+ double piv;
+ int pivind;
+
+ if (index < 0) {
+ CROAK("Negative index");
+ }
+
+ /* If this bit of the array is already sorted, simple! */
+ if (flags[index]) {
+ return x[index];
+ }
+
+ /* Find subrange to look at */
+ u = v = index;
+ while (u > 0 && !flags[u]) u--;
+ if (flags[u]) u++;
+
+ while (v < (n-1) && !flags[v]) v++;
+ if (flags[v]) v--;
+
+ do {
+ if (v - u < 2) {
+ if (x[v] < x[u]) {
+ EXCH(x[v], x[u]);
+ }
+ flags[v] = flags[u] = 1;
+ return x[index];
+ } else {
+ pivind = (u + v) >> 1;
+ EXCH(x[u], x[pivind]);
+ piv = x[u]; /* New value */
+ l = u + 1;
+ r = v;
+ do {
+ while (x[l] < piv) l++;
+ while (x[r] > piv) r--;
+ if (r <= l) break;
+ EXCH(x[l], x[r]);
+ l++;
+ r--;
+ } while (1);
+ EXCH(x[u], x[r]);
+ flags[r] = 1; /* Pivot now in correct place */
+ if (index == r) {
+ return x[r];
+ } else if (index < r) {
+ v = r - 1;
+ } else if (index > r) {
+ u = l;
+ } else {
+ CROAK("Impossible");
+ }
+ }
+ } while (1);
+}
+
+/* ================================================== */
+
+#if 0
+/* Not used, but this is how it can be done */
+static double
+find_ordered_entry(double *x, int n, int index)
+{
+ int flags[MAX_POINTS];
+
+ bzero(flags, n * sizeof(int));
+ return find_ordered_entry_with_flags(x, n, index, flags);
+}
+#endif
+
+/* ================================================== */
+/* Find the median entry of an array x[] with n elements. */
+
+static double
+find_median(double *x, int n)
+{
+ int k;
+ int flags[MAX_POINTS];
+
+ memset(flags, 0, n*sizeof(int));
+ k = n>>1;
+ if (n&1) {
+ return find_ordered_entry_with_flags(x, n, k, flags);
+ } else {
+ return 0.5 * (find_ordered_entry_with_flags(x, n, k, flags) +
+ find_ordered_entry_with_flags(x, n, k-1, flags));
+ }
+}
+
+/* ================================================== */
+/* This function evaluates the equation
+
+ \sum_{i=0}^{n-1} x_i sign(y_i - a - b x_i)
+
+ and chooses the value of a that minimises the absolute value of the
+ result. (See pp703-704 of Numerical Recipes in C). */
+
+static void
+eval_robust_residual
+(double *x, /* The independent points */
+ double *y, /* The dependent points */
+ int n, /* Number of points */
+ double b, /* Slope */
+ double *aa, /* Intercept giving smallest absolute
+ value for the above equation */
+ double *rr /* Corresponding value of equation */
+)
+{
+ int i;
+ double a, res, del;
+ double d[MAX_POINTS];
+
+ for (i=0; i<n; i++) {
+ d[i] = y[i] - b * x[i];
+ }
+
+ a = find_median(d, n);
+
+ res = 0.0;
+ for (i=0; i<n; i++) {
+ del = y[i] - a - b * x[i];
+ if (del > 0.0) {
+ res += x[i];
+ } else if (del < 0.0) {
+ res -= x[i];
+ }
+ }
+
+ *aa = a;
+ *rr = res;
+}
+
+/* ================================================== */
+/* This routine performs a 'robust' regression, i.e. one which has low
+ susceptibility to outliers amongst the data. If one thinks of a
+ normal (least squares) linear regression in 2D being analogous to
+ the arithmetic mean in 1D, this algorithm in 2D is roughly
+ analogous to the median in 1D. This algorithm seems to work quite
+ well until the number of outliers is approximately half the number
+ of data points.
+
+ The return value is a status indicating whether there were enough
+ data points to run the routine or not. */
+
+int
+RGR_FindBestRobustRegression
+(double *x, /* The independent axis points */
+ double *y, /* The dependent axis points (which
+ may contain outliers). */
+ int n, /* The number of points */
+ double tol, /* The tolerance required in
+ determining the value of b1 */
+ double *b0, /* The estimated Y-axis intercept */
+ double *b1, /* The estimated slope */
+ int *n_runs, /* The number of runs of residuals */
+ int *best_start /* The best starting index */
+)
+{
+ int i;
+ int start;
+ int n_points;
+ double a, b;
+ double P, U, V, W, X;
+ double resid, resids[MAX_POINTS];
+ double blo, bhi, bmid, rlo, rhi, rmid;
+ double s2, sb, incr;
+ double mx, dx, my, dy;
+ int nruns = 0;
+
+ if (n < 2) {
+ return 0;
+ } else if (n == 2) {
+ /* Just a straight line fit (we need this for the manual mode) */
+ *b1 = (y[1] - y[0]) / (x[1] - x[0]);
+ *b0 = y[0] - (*b1) * x[0];
+ *n_runs = 0;
+ *best_start = 0;
+ return 1;
+ }
+
+ /* else at least 3 points, apply normal algorithm */
+
+ start = 0;
+
+ /* Loop to strip oldest points that cause the regression residuals
+ to fail the number of runs test */
+ do {
+
+ n_points = n - start;
+
+ /* Use standard least squares regression to get starting estimate */
+
+ P = U = 0.0;
+ for (i=start; i<n; i++) {
+ P += y[i];
+ U += x[i];
+ }
+
+ W = (double) n_points;
+
+ my = P/W;
+ mx = U/W;
+
+ X = V = 0.0;
+ for (i=start; i<n; i++) {
+ dy = y[i] - my;
+ dx = x[i] - mx;
+ X += dy * dx;
+ V += dx * dx;
+ }
+
+ b = X / V;
+ a = my - b*mx;
+
+
+#if 0
+ printf("my=%20.12f mx=%20.12f a=%20.12f b=%20.12f\n", my, mx, a, b);
+#endif
+
+ s2 = 0.0;
+ for (i=start; i<n; i++) {
+ resid = y[i] - a - b * x[i];
+ s2 += resid * resid;
+ }
+
+ /* Need to expand range of b to get a root in the interval.
+ Estimate standard deviation of b and expand range about b based
+ on that. */
+ sb = sqrt(s2 * W/V);
+ if (sb > 0.0) {
+ incr = 3.0 * sb;
+ } else {
+ incr = 3.0 * tol;
+ }
+
+ blo = b;
+ bhi = b;
+
+ do {
+ blo -= incr;
+ bhi += incr;
+
+ /* We don't want 'a' yet */
+ eval_robust_residual(x + start, y + start, n_points, blo, &a, &rlo);
+ eval_robust_residual(x + start, y + start, n_points, bhi, &a, &rhi);
+
+ } while (rlo * rhi > 0.0); /* fn vals have same sign, i.e. root not
+ in interval. */
+
+ /* OK, so the root for b lies in (blo, bhi). Start bisecting */
+ do {
+ bmid = 0.5 * (blo + bhi);
+ eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid);
+ if (rmid == 0.0) {
+ break;
+ } else if (rmid * rlo > 0.0) {
+ blo = bmid;
+ rlo = rmid;
+ } else if (rmid * rhi > 0.0) {
+ bhi = bmid;
+ rhi = rmid;
+ } else {
+ CROAK("Impossible");
+ }
+ } while ((bhi - blo) > tol);
+
+ *b0 = a;
+ *b1 = bmid;
+
+ /* Number of runs test, but not if we're already down to the
+ minimum number of points */
+ if (n_points == MIN_SAMPLES_FOR_REGRESS) {
+ break;
+ }
+
+ for (i=start; i<n; i++) {
+ resids[i] = y[i] - a - bmid * x[i];
+ }
+
+ nruns = n_runs_from_residuals(resids + start, n_points);
+
+ if (nruns > critical_runs10[n_points]) {
+ break;
+ } else {
+ start++;
+ }
+
+ } while (1);
+
+ *n_runs = nruns;
+ *best_start = start;
+
+ return 1;
+
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/regress.h,v 1.13 2002/02/28 23:27:13 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for regression routine(s)
+
+ */
+
+#ifndef GOT_REGRESS_H
+#define GOT_REGRESS_H
+
+extern void
+RGR_WeightedRegression
+(double *x, /* independent variable */
+ double *y, /* measured data */
+ double *w, /* weightings (large => data
+ less reliable) */
+
+ int n, /* number of data points */
+
+ /* And now the results */
+
+ double *b0, /* estimated y axis intercept */
+ double *b1, /* estimated slope */
+ double *s2, /* estimated variance (weighted) of
+ data points */
+
+ double *sb0, /* estimated standard deviation of
+ intercept */
+ double *sb1 /* estimated standard deviation of
+ slope */
+
+ /* Could add correlation stuff later if required */
+);
+
+/* Return the weighting to apply to the standard deviation to get a
+ given size of confidence interval assuming a T distribution */
+
+extern double RGR_GetTCoef(int dof);
+
+/* Return a status indicating whether there were enough points to
+ carry out the regression */
+
+extern int
+RGR_FindBestRegression
+(double *x, /* independent variable */
+ double *y, /* measured data */
+ double *w, /* weightings (large => data
+ less reliable) */
+
+ int n, /* number of data points */
+
+ /* And now the results */
+
+ double *b0, /* estimated y axis intercept */
+ double *b1, /* estimated slope */
+ double *s2, /* estimated variance of data points */
+
+ double *sb0, /* estimated standard deviation of
+ intercept */
+ double *sb1, /* estimated standard deviation of
+ slope */
+
+ int *new_start, /* the new starting index to make the
+ residuals pass the two tests */
+
+ int *n_runs, /* number of runs amongst the residuals */
+
+ int *dof /* degrees of freedom in statistics (needed
+ to get confidence intervals later) */
+
+);
+
+int
+RGR_FindBestRobustRegression
+(double *x,
+ double *y,
+ int n,
+ double tol,
+ double *b0,
+ double *b1,
+ int *n_runs,
+ int *best_start);
+
+#endif /* GOT_REGRESS_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/reports.h,v 1.17 2002/02/28 23:27:13 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Data structure definitions within the daemon for various reports that
+ can be generated */
+
+#ifndef GOT_REPORTS_H
+#define GOT_REPORTS_H
+
+#include "sysincl.h"
+
+#define REPORT_INVALID_OFFSET 0x80000000
+
+typedef struct {
+ unsigned long ip_addr;
+ int stratum;
+ int poll;
+ enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode;
+ enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_OTHER} state;
+
+ unsigned long latest_meas_ago; /* seconds */
+ long orig_latest_meas; /* microseconds (us) */
+ long latest_meas; /* us */
+ unsigned long latest_meas_err; /* us */
+ long est_offset; /* us */
+ unsigned long est_offset_err; /* us */
+ long resid_freq; /* ppm * 1000 */
+ unsigned long resid_skew; /* ppm * 1000 */
+} RPT_SourceReport ;
+
+typedef struct {
+ unsigned long ref_id;
+ unsigned long stratum;
+ struct timeval ref_time;
+ struct timeval current_correction;
+ double freq_ppm;
+ double resid_freq_ppm;
+ double skew_ppm;
+ double root_delay;
+ double root_dispersion;
+} RPT_TrackingReport;
+
+typedef struct {
+ unsigned long ip_addr;
+ unsigned long n_samples;
+ unsigned long n_runs;
+ unsigned long span_seconds;
+ double resid_freq_ppm;
+ double skew_ppm;
+ double sd_us;
+} RPT_SourcestatsReport;
+
+typedef struct {
+ unsigned long ref_time;
+ unsigned short n_samples;
+ unsigned short n_runs;
+ unsigned long span_seconds;
+ double rtc_seconds_fast;
+ double rtc_gain_rate_ppm;
+} RPT_RTC_Report;
+
+typedef struct {
+ unsigned long client_hits;
+ unsigned long peer_hits;
+ unsigned long cmd_hits_auth;
+ unsigned long cmd_hits_normal;
+ unsigned long cmd_hits_bad;
+ unsigned long last_ntp_hit_ago;
+ unsigned long last_cmd_hit_ago;
+} RPT_ClientAccess_Report;
+
+typedef struct {
+ unsigned long ip_addr;
+ unsigned long client_hits;
+ unsigned long peer_hits;
+ unsigned long cmd_hits_auth;
+ unsigned long cmd_hits_normal;
+ unsigned long cmd_hits_bad;
+ unsigned long last_ntp_hit_ago;
+ unsigned long last_cmd_hit_ago;
+} RPT_ClientAccessByIndex_Report;
+
+typedef struct {
+ time_t when;
+ double slewed_offset;
+ double orig_offset;
+ double residual;
+} RPT_ManualSamplesReport;
+
+typedef struct {
+ int online;
+ int offline;
+ int burst_online;
+ int burst_offline;
+} RPT_ActivityReport;
+
+#endif /* GOT_REPORTS_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/rtc.c,v 1.13 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ */
+
+#include "sysincl.h"
+
+#include "rtc.h"
+#include "logging.h"
+#include "conf.h"
+
+#if defined LINUX
+#include "rtc_linux.h"
+#endif /* defined LINUX */
+
+/* ================================================== */
+
+static int driver_initialised = 0;
+
+static struct {
+ int (*init)(void);
+ void (*fini)(void);
+ void (*time_pre_init)(void);
+ void (*time_init)(void (*after_hook)(void*), void *anything);
+ void (*start_measurements)(void);
+ int (*write_parameters)(void);
+ int (*get_report)(RPT_RTC_Report *report);
+ int (*trim)(void);
+ void (*cycle_logfile)(void);
+} driver =
+{
+#if defined LINUX
+ RTC_Linux_Initialise,
+ RTC_Linux_Finalise,
+ RTC_Linux_TimePreInit,
+ RTC_Linux_TimeInit,
+ RTC_Linux_StartMeasurements,
+ RTC_Linux_WriteParameters,
+ RTC_Linux_GetReport,
+ RTC_Linux_Trim,
+ RTC_Linux_CycleLogFile
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+#endif
+};
+
+/* ================================================== */
+
+void
+RTC_Initialise(void)
+{
+ char *file_name;
+ int ok;
+
+ /* This is how we tell whether the user wants to load the RTC
+ driver, if he is on a machine where it is an option. */
+ file_name = CNF_GetRtcFile();
+
+ if (file_name) {
+ if (driver.init) {
+ if ((driver.init)()) {
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+ } else {
+ ok = 0;
+ }
+
+ if (ok) {
+ driver_initialised = 1;
+ } else {
+ driver_initialised = 0;
+ LOG(LOGS_ERR, LOGF_Rtc, "Real time clock not supported on this operating system");
+ }
+
+ } else {
+ driver_initialised = 0;
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+
+void
+RTC_Finalise(void)
+{
+ if (driver.fini) {
+ (driver.fini)();
+ }
+}
+
+/* ================================================== */
+/* Start the processing to get a single measurement from the real time
+ clock, and use it to trim the system time, based on knowing the
+ drift rate of the RTC and the error the last time we set it. The
+ TimePreInit routine has already run, so we can be sure that the
+ trim required is not *too* large.
+
+ We are called with a hook to a function to be called after the
+ initialisation is complete. We also call this if we cannot do the
+ initialisation. */
+
+void
+RTC_TimeInit(void (*after_hook)(void *), void *anything)
+{
+ if (driver_initialised) {
+ (driver.time_init)(after_hook, anything);
+ } else {
+ LOG(LOGS_ERR, LOGF_Rtc, "Can't initialise from real time clock, driver not loaded");
+ (after_hook)(anything);
+ }
+}
+
+/* ================================================== */
+/* Do an initial read of the RTC and set the system time to it. This
+ is analogous to what /sbin/clock -s -u would do on Linux. */
+
+void
+RTC_TimePreInit(void)
+{
+ if (driver.time_pre_init) {
+ (driver.time_pre_init)();
+ }
+}
+
+/* ================================================== */
+/* Start the RTC measurement process */
+
+void
+RTC_StartMeasurements(void)
+{
+ if (driver_initialised) {
+ (driver.start_measurements)();
+ }
+ /* Benign if driver not present */
+}
+
+/* ================================================== */
+/* Write RTC information out to RTC file. Return 0 for success, 1 if
+ RTC driver not running, or 2 if the file cannot be written. */
+
+int
+RTC_WriteParameters(void)
+{
+ if (driver_initialised) {
+ return (driver.write_parameters)();
+ } else {
+ return RTC_ST_NODRV;
+ }
+}
+
+/* ================================================== */
+
+int
+RTC_GetReport(RPT_RTC_Report *report)
+{
+ if (driver_initialised) {
+ return (driver.get_report)(report);
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+RTC_Trim(void)
+{
+ if (driver_initialised) {
+ return (driver.trim)();
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+void
+RTC_CycleLogFile(void)
+{
+ if (driver_initialised) {
+ (driver.cycle_logfile)();
+ }
+}
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/rtc.h,v 1.9 2002/02/28 23:27:13 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ */
+
+#ifndef GOT_RTC_H
+#define GOT_RTC_H
+
+#include "reports.h"
+
+extern void RTC_Initialise(void);
+extern void RTC_Finalise(void);
+extern void RTC_TimePreInit(void);
+extern void RTC_TimeInit(void (*after_hook)(void *), void *anything);
+extern void RTC_StartMeasurements(void);
+extern int RTC_GetReport(RPT_RTC_Report *report);
+
+#define RTC_ST_OK 0
+#define RTC_ST_NODRV 1
+#define RTC_ST_BADFILE 2
+
+extern int RTC_WriteParameters(void);
+
+extern int RTC_Trim(void);
+
+extern void RTC_CycleLogFile(void);
+
+#endif /* GOT_RTC_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/rtc_linux.c,v 1.30 2003/04/01 20:07:20 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Real-time clock driver for linux. This interfaces the program with
+ the clock that keeps time when the machine is turned off.
+
+ */
+
+#if defined LINUX
+
+#ifdef sparc
+#define __KERNEL__
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#ifdef HAS_SPINLOCK_H
+#include <linux/spinlock.h>
+#else
+/* Include dummy definition of spinlock_t to cope with earlier kernels. */
+typedef int spinlock_t;
+#endif
+
+/* This is a complete hack since the alpha sys/io.h needs these types
+ * but does not arrange them to be defined. This is almost certainly
+ * not how one should do these things. -- broonie
+ */
+#include <linux/types.h>
+#ifdef __alpha__
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+#endif
+
+#if defined(__i386__) /* || defined(__sparc__) */
+#include <linux/mc146818rtc.h>
+#else
+#include <linux/rtc.h>
+#define RTC_UIE 0x10 /* update-finished interrupt enable */
+#endif
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+
+#include "logging.h"
+#include "sched.h"
+#include "local.h"
+#include "util.h"
+#include "sys_linux.h"
+#include "regress.h"
+#include "rtc.h"
+#include "rtc_linux.h"
+#include "conf.h"
+#include "memory.h"
+#include "mkdirpp.h"
+
+/* ================================================== */
+/* Forward prototypes */
+
+static void measurement_timeout(void *any);
+
+static void read_from_device(void *any);
+
+/* ================================================== */
+
+typedef enum {
+ OM_NORMAL,
+ OM_INITIAL,
+ OM_AFTERTRIM
+} OperatingMode;
+
+static OperatingMode operating_mode = OM_NORMAL;
+
+/* ================================================== */
+
+static int fd = -1;
+
+#define LOWEST_MEASUREMENT_PERIOD 15
+#define HIGHEST_MEASUREMENT_PERIOD 480
+
+/* Try to avoid doing regression after _every_ sample we accumulate */
+#define N_SAMPLES_PER_REGRESSION 4
+
+static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
+
+static int timeout_running = 0;
+static SCH_TimeoutID timeout_id;
+
+/* ================================================== */
+
+/* Maximum number of samples held */
+#define MAX_SAMPLES 64
+
+/* Real time clock samples. We store the seconds count as originally
+ measured, together with a 'trim' that compensates these values for
+ any steps made to the RTC to bring it back into line
+ occasionally. The trim is in seconds. */
+static time_t rtc_sec[MAX_SAMPLES];
+static double rtc_trim[MAX_SAMPLES];
+
+/* Reference time, against which delta times on the RTC scale are measured */
+static time_t rtc_ref;
+
+
+/* System clock (gettimeofday) samples associated with the above
+ samples. */
+static struct timeval system_times[MAX_SAMPLES];
+
+/* Number of samples currently stored. */
+static int n_samples;
+
+/* Number of new samples since last regression */
+static int n_samples_since_regression;
+
+/* Number of runs of residuals in last regression (for logging) */
+static int n_runs;
+
+/* Coefficients */
+/* Whether they are valid */
+static int coefs_valid;
+
+/* Reference time */
+static time_t coef_ref_time;
+/* Number of seconds by which RTC was fast of the system time at coef_ref_time */
+static double coef_seconds_fast;
+
+/* Estimated number of seconds that RTC gains relative to system time
+ for each second of ITS OWN time */
+static double coef_gain_rate;
+
+/* Gain rate saved just before we step the RTC to correct it to the
+ nearest second, so that we can write a useful set of coefs to the
+ RTC data file once we have reacquired its offset after the step */
+static double saved_coef_gain_rate;
+
+/* Filename supplied by config file where RTC coefficients are
+ stored. */
+static char *coefs_file_name;
+
+/* ================================================== */
+/* Coefficients read from file at start of run. */
+
+/* Whether we have tried to load the coefficients */
+static int tried_to_load_coefs = 0;
+
+/* Whether valid coefficients were read */
+static int valid_coefs_from_file = 0;
+
+/* Coefs read in */
+static time_t file_ref_time;
+static double file_ref_offset, file_rate_ppm;
+
+/* ================================================== */
+
+/* Flag to remember whether to assume the RTC is running on UTC */
+static int rtc_on_utc = 0;
+
+/* ================================================== */
+
+static FILE *logfile=NULL;
+static char *logfilename = NULL;
+static unsigned long logwrites=0;
+
+#define RTC_LOG "rtc.log"
+
+/* ================================================== */
+
+static void (*after_init_hook)(void *) = NULL;
+static void *after_init_hook_arg = NULL;
+
+/* ================================================== */
+
+static void
+discard_samples(int new_first)
+{
+ int n_to_save;
+
+ if (!(new_first < n_samples)) {
+ CROAK("new_first should be < n_samples");
+ }
+ if (!(new_first >= 0)) {
+ CROAK("new_first should be non-negative");
+ }
+
+ n_to_save = n_samples - new_first;
+
+ memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
+ memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
+ memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timeval));
+
+ n_samples = n_to_save;
+ return;
+}
+
+/* ================================================== */
+
+#define NEW_FIRST_WHEN_FULL 4
+
+static void
+accumulate_sample(time_t rtc, struct timeval *sys)
+{
+
+ if (n_samples == MAX_SAMPLES) {
+ /* Discard oldest samples */
+ discard_samples(NEW_FIRST_WHEN_FULL);
+ }
+
+ rtc_sec[n_samples] = rtc;
+
+ /* Always use most recent sample as reference */
+ rtc_ref = rtc;
+
+ rtc_trim[n_samples] = 0.0;
+ system_times[n_samples] = *sys;
+ ++n_samples;
+ ++n_samples_since_regression;
+ return;
+
+}
+
+/* ================================================== */
+/* The new_sample flag is to indicate whether to adjust the
+ measurement period depending on the behaviour of the standard
+ deviation. */
+
+static void
+run_regression(int new_sample,
+ int *valid,
+ time_t *ref,
+ double *fast,
+ double *slope)
+{
+ double rtc_rel[MAX_SAMPLES]; /* Relative times on RTC axis */
+ double offsets[MAX_SAMPLES]; /* How much the RTC is fast of the system clock */
+ int i, n;
+ double est_intercept, est_slope;
+ int best_new_start;
+
+ if (n_samples > 0) {
+
+ n = n_samples - 1;
+
+ for (i=0; i<n_samples; i++) {
+ rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
+ offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
+ (1.0e-6 * (double) system_times[i].tv_usec) +
+ rtc_rel[i]);
+
+ }
+
+ if (RGR_FindBestRobustRegression
+ (rtc_rel, offsets,
+ n_samples, 1.0e-9,
+ &est_intercept, &est_slope,
+ &n_runs,
+ &best_new_start)) {
+
+ /* Calculate and store coefficients. We don't do any error
+ bounds processing on any of these. */
+ *valid = 1;
+ *ref = rtc_ref;
+ *fast = est_intercept;
+ *slope = est_slope;
+
+ if (best_new_start > 0) {
+ discard_samples(best_new_start);
+ }
+
+
+ } else {
+ /* Keep existing coefficients. */
+ }
+ } else {
+ /* Keep existing coefficients. */
+ }
+
+}
+
+/* ================================================== */
+
+static void
+slew_samples
+(struct timeval *raw, struct timeval *cooked,
+ double dfreq, double afreq_ppm,
+ double doffset, int is_step_change,
+ void *anything)
+{
+ int i;
+ double elapsed;
+ double new_freq;
+ double old_freq;
+ double delta_time;
+ double old_seconds_fast, old_gain_rate;
+
+ new_freq = 1.0e-6 * afreq_ppm;
+ old_freq = (new_freq - dfreq) / (1.0 - dfreq);
+
+ for (i=0; i<n_samples; i++) {
+ UTI_DiffTimevalsToDouble(&elapsed, cooked, system_times + i);
+
+ delta_time = -(elapsed * dfreq) - doffset;
+
+ UTI_AddDoubleToTimeval(system_times + i, delta_time, system_times + i);
+
+ }
+
+ old_seconds_fast = coef_seconds_fast;
+ old_gain_rate = coef_gain_rate;
+
+ if (coefs_valid) {
+ coef_seconds_fast += doffset;
+ coef_gain_rate = 1.0 - ((1.0 + new_freq) / (1.0 + old_freq)) * (1.0 - coef_gain_rate);
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f new_freq=%.3f old_freq=%.3f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
+ dfreq, doffset, 1.0e6*new_freq, 1.0e6*old_freq,
+ old_seconds_fast, 1.0e6 * old_gain_rate,
+ coef_seconds_fast, 1.0e6 * coef_gain_rate);
+#endif
+
+}
+
+/* ================================================== */
+
+/* Function to convert from a time_t value represenging UTC to the
+ corresponding real time clock 'DMY HMS' form, taking account of
+ whether the user runs his RTC on the local time zone or UTC */
+
+static struct tm *
+rtc_from_t(const time_t *t)
+{
+ if (rtc_on_utc) {
+ return gmtime(t);
+ } else {
+ return localtime(t);
+ }
+}
+
+/* ================================================== */
+
+/* Inverse function to get back from RTC 'DMY HMS' form to time_t UTC
+ form. This essentially uses mktime(), but involves some awful
+ complexity to cope with timezones. The problem is that mktime's
+ behaviour with regard to the daylight saving flag in the 'struct
+ tm' does not seem to be reliable across all systems, unless that
+ flag is set to zero.
+
+ tm_isdst = -1 does not seem to work with all libc's - it is treated
+ as meaning there is DST, or fails completely. (It is supposed to
+ use the timezone info to work out whether summer time is active at
+ the specified epoch).
+
+ tm_isdst = 1 fails if the local timezone has no summer time defined.
+
+ The approach taken is as follows. Suppose the RTC is on localtime.
+ We perform all mktime calls with the tm_isdst field set to zero.
+
+ Let y be the RTC reading in 'DMY HMS' form. Let M be the mktime
+ function with tm_isdst=0 and L be the localtime function.
+
+ We seek x such that y = L(x). Now there will exist a value Z(t)
+ such that M(L(t)) = t + Z(t) for all t, where Z(t) depends on
+ whether daylight saving is active at time t.
+
+ We want L(x) = y. Therefore M(L(x)) = x + Z = M(y). But
+ M(L(M(y))) = M(y) + Z. Therefore x = M(y) - Z = M(y) - (M(L(M(y)))
+ - M(y)).
+
+ The case for the RTC running on UTC is identical but without the
+ potential complication that Z depends on t.
+*/
+
+static time_t
+t_from_rtc(struct tm *stm) {
+ struct tm temp1, temp2;
+ long diff;
+ time_t t1, t2;
+
+ temp1 = *stm;
+ temp1.tm_isdst = 0;
+
+ t1 = mktime(&temp1);
+ if (rtc_on_utc) {
+ temp2 = *gmtime(&t1);
+ } else {
+ temp2 = *localtime(&t1);
+ }
+
+ temp2.tm_isdst = 0;
+ t2 = mktime(&temp2);
+ diff = t2 - t1;
+
+ return t1 - diff;
+}
+
+/* ================================================== */
+
+static void
+setup_config(void)
+{
+ if (CNF_GetRTCOnUTC()) {
+ rtc_on_utc = 1;
+ } else {
+ rtc_on_utc = 0;
+ }
+}
+
+/* ================================================== */
+/* Read the coefficients from the file where they were saved
+ the last time the program was run. */
+
+static void
+read_coefs_from_file(void)
+{
+ FILE *in;
+ char line[256];
+
+ if (!tried_to_load_coefs) {
+
+ valid_coefs_from_file = 0; /* only gets set true if we succeed */
+
+ tried_to_load_coefs = 1;
+
+ in = fopen(coefs_file_name, "r");
+ if (in) {
+ if (fgets(line, sizeof(line), in)) {
+ if (sscanf(line, "%d%ld%lf%lf",
+ &valid_coefs_from_file,
+ &file_ref_time,
+ &file_ref_offset,
+ &file_rate_ppm) == 4) {
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not parse coefficients line from RTC file %s",
+ coefs_file_name);
+ }
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read first line from RTC file %s",
+ coefs_file_name);
+ }
+ fclose(in);
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open RTC file %s for reading",
+ coefs_file_name);
+ }
+ }
+}
+
+/* ================================================== */
+/* Write the coefficients to the file where they will be read
+ the next time the program is run. */
+
+static int
+write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
+{
+ struct stat buf;
+ char *temp_coefs_file_name;
+ FILE *out;
+
+ /* Create a temporary file with a '.tmp' extension. */
+
+ temp_coefs_file_name = (char*) Malloc(strlen(coefs_file_name)+8);
+
+ if(!temp_coefs_file_name) {
+ return RTC_ST_BADFILE;
+ }
+
+ strcpy(temp_coefs_file_name,coefs_file_name);
+ strcat(temp_coefs_file_name,".tmp");
+
+ out = fopen(temp_coefs_file_name, "w");
+ if (!out) {
+ Free(temp_coefs_file_name);
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open temporary RTC file %s.tmp for writing",
+ coefs_file_name);
+ return RTC_ST_BADFILE;
+ }
+
+ /* Gain rate is written out in ppm */
+ fprintf(out, "%1d %ld %.6f %.3f\n",
+ valid,ref_time, offset, 1.0e6 * rate);
+
+ fclose(out);
+
+ /* Clone the file attributes from the existing file if there is one. */
+
+ if (!stat(coefs_file_name,&buf)) {
+ chown(temp_coefs_file_name,buf.st_uid,buf.st_gid);
+ chmod(temp_coefs_file_name,buf.st_mode&0777);
+ }
+
+ /* Rename the temporary file to the correct location (see rename(2) for details). */
+
+ if (rename(temp_coefs_file_name,coefs_file_name)) {
+ unlink(temp_coefs_file_name);
+ Free(temp_coefs_file_name);
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not replace old RTC file %s.tmp with new one %s",
+ coefs_file_name, coefs_file_name);
+ return RTC_ST_BADFILE;
+ }
+
+ Free(temp_coefs_file_name);
+
+ return RTC_ST_OK;
+}
+
+
+/* ================================================== */
+/* file_name is the name of the file where we save the RTC params
+ between executions. Return status is whether we could initialise
+ on this version of the system. */
+
+int
+RTC_Linux_Initialise(void)
+{
+ int major, minor, patch;
+ char *direc;
+
+ /* Check whether we can support the real time clock.
+
+ Linux 1.2.x - haven't checked yet
+
+ Linux 1.3.x - don't know, haven't got a system to look at
+
+ Linux 2.0.x - For x<=31, using any variant of the adjtimex() call
+ sets the kernel into a mode where the RTC was updated every 11
+ minutes. The only way to escape this is to use settimeofday().
+ Since we need to have sole control over the RTC to be able to
+ measure its drift rate, and there is no 'notify' callback to warn
+ you that the kernel is going to do this, I can't see a way to
+ support this.
+
+ Linux 2.0.x - For x>=32 the adjtimex()/RTC behaviour was
+ modified, so that as long as the STA_UNSYNC flag is set the RTC
+ is left alone. This is the mode we exploit here, so that the RTC
+ continues to go its own sweet way, unless we make updates to it
+ from this module.
+
+ Linux 2.1.x - don't know, haven't got a system to look at.
+
+ Linux 2.2.x, 2.3.x and 2.4.x are believed to be OK for all
+ patch levels
+
+ */
+
+ SYS_Linux_GetKernelVersion(&major, &minor, &patch);
+
+ /* Obviously this test can get more elaborate when we know about
+ more system types. */
+ if (major != 2) {
+ return 0;
+ } else {
+ switch (minor) {
+ case 0:
+ if (patch <= 31) {
+ return 0;
+ }
+ break;
+ case 1:
+ return 0;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ break; /* OK for all patch levels */
+ }
+ }
+
+ /* Setup details depending on configuration options */
+ setup_config();
+
+ /* In case it didn't get done by pre-init */
+ coefs_file_name = CNF_GetRtcFile();
+
+ /* Try to open device */
+
+ fd = open (CNF_GetRtcDevice(), O_RDWR);
+ if (fd < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open %s, %s", CNF_GetRtcDevice(), strerror(errno));
+ return 0;
+ }
+
+ n_samples = 0;
+ n_samples_since_regression = 0;
+ n_runs = 0;
+ coefs_valid = 0;
+
+ measurement_period = LOWEST_MEASUREMENT_PERIOD;
+
+ operating_mode = OM_NORMAL;
+
+ /* Register file handler */
+ SCH_AddInputFileHandler(fd, read_from_device, NULL);
+
+ /* Register slew handler */
+ LCL_AddParameterChangeHandler(slew_samples, NULL);
+
+ if (CNF_GetLogRtc()) {
+ direc = CNF_GetLogDir();
+ if (!mkdir_and_parents(direc)) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not create directory %s", direc);
+ logfile = NULL;
+ } else {
+ logfilename = MallocArray(char, 2 + strlen(direc) + strlen(RTC_LOG));
+ strcpy(logfilename, direc);
+ strcat(logfilename, "/");
+ strcat(logfilename, RTC_LOG);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Couldn't open logfile %s for update", logfilename);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+void
+RTC_Linux_Finalise(void)
+{
+ if (timeout_running) {
+ SCH_RemoveTimeout(timeout_id);
+ timeout_running = 0;
+ }
+
+ /* Remove input file handler */
+ if (fd >= 0) {
+ SCH_RemoveInputFileHandler(fd);
+ close(fd);
+
+ /* Save the RTC data */
+ (void) RTC_Linux_WriteParameters();
+
+ }
+
+ if (logfile) {
+ fclose(logfile);
+ }
+
+}
+
+/* ================================================== */
+
+static void
+switch_interrupts(int onoff)
+{
+ int status;
+
+ if (onoff) {
+ status = ioctl(fd, RTC_UIE_ON, 0);
+ if (status < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not start measurement : %s", strerror(errno));
+ return;
+ }
+ } else {
+ status = ioctl(fd, RTC_UIE_OFF, 0);
+ if (status < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not stop measurement : %s", strerror(errno));
+ return;
+ }
+ }
+}
+
+/* ================================================== */
+
+static void
+measurement_timeout(void *any)
+{
+ timeout_running = 0;
+ switch_interrupts(1);
+}
+
+/* ================================================== */
+
+static void
+set_rtc(time_t new_rtc_time)
+{
+ struct tm rtc_tm;
+ struct rtc_time rtc_raw;
+ int status;
+
+ rtc_tm = *rtc_from_t(&new_rtc_time);
+
+ rtc_raw.tm_sec = rtc_tm.tm_sec;
+ rtc_raw.tm_min = rtc_tm.tm_min;
+ rtc_raw.tm_hour = rtc_tm.tm_hour;
+ rtc_raw.tm_mday = rtc_tm.tm_mday;
+ rtc_raw.tm_mon = rtc_tm.tm_mon;
+ rtc_raw.tm_year = rtc_tm.tm_year;
+
+ status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
+ if (status < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time");
+ }
+
+}
+
+/* ================================================== */
+
+static void
+handle_initial_trim(void)
+{
+ double rate;
+ long delta_time;
+ double rtc_error_now, sys_error_now;
+
+ /* The idea is to accumulate some number of samples at 1 second
+ intervals, then do a robust regression fit to this. This
+ should give a good fix on the intercept (=system clock error
+ rel to RTC) at a particular time, removing risk of any
+ particular sample being an outlier. We can then look at the
+ elapsed interval since the epoch recorded in the RTC file,
+ and correct the system time accordingly. */
+
+ run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
+
+ n_samples_since_regression = 0;
+ n_samples = 0;
+
+ read_coefs_from_file();
+
+ if (valid_coefs_from_file) {
+ /* Can process data */
+ delta_time = coef_ref_time - file_ref_time;
+ rate = 1.0e-6 * file_rate_ppm;
+ rtc_error_now = file_ref_offset + rate * (double) delta_time;
+
+ /* sys_error_now is positive if the system clock is fast */
+ sys_error_now = rtc_error_now - coef_seconds_fast;
+
+ LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now);
+ LCL_AccumulateOffset(sys_error_now);
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time");
+ }
+
+ coefs_valid = 0;
+
+ (after_init_hook)(after_init_hook_arg);
+
+ operating_mode = OM_NORMAL;
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+handle_relock_after_trim(void)
+{
+ int valid;
+ time_t ref;
+ double fast, slope;
+
+ run_regression(1, &valid, &ref, &fast, &slope);
+
+ if (valid) {
+ write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not do regression after trim");
+ }
+
+ n_samples = 0;
+ n_samples_since_regression = 0;
+ operating_mode = OM_NORMAL;
+ measurement_period = LOWEST_MEASUREMENT_PERIOD;
+}
+
+/* ================================================== */
+
+/* Day number of 1 Jan 1970 */
+#define MJD_1970 40587
+
+static void
+process_reading(time_t rtc_time, struct timeval *system_time)
+{
+ double rtc_fast;
+
+ accumulate_sample(rtc_time, system_time);
+
+ switch (operating_mode) {
+ case OM_NORMAL:
+
+ if (n_samples_since_regression >= /* 4 */ 1 ) {
+ run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
+ n_samples_since_regression = 0;
+ }
+
+ break;
+ case OM_INITIAL:
+ if (n_samples_since_regression >= 8) {
+ handle_initial_trim();
+ }
+ break;
+ case OM_AFTERTRIM:
+ if (n_samples_since_regression >= 8) {
+ handle_relock_after_trim();
+ }
+ break;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+
+
+ if (logfile) {
+ rtc_fast = (double)(rtc_time - system_time->tv_sec) - 1.0e-6 * (double) system_time->tv_usec;
+
+ if (((logwrites++) % 32) == 0) {
+ fprintf(logfile,
+ "===============================================================================\n"
+ " Date (UTC) Time RTC fast (s) Val Est fast (s) Slope (ppm) Ns Nr Meas\n"
+ "===============================================================================\n");
+ }
+
+ fprintf(logfile, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d\n",
+ UTI_TimeToLogForm(system_time->tv_sec),
+ rtc_fast,
+ coefs_valid,
+ coef_seconds_fast, coef_gain_rate * 1.0e6, n_samples, n_runs, measurement_period);
+
+ fflush(logfile);
+ }
+
+}
+
+/* ================================================== */
+
+static void
+read_from_device(void *any)
+{
+ int status;
+ unsigned long data;
+ struct timeval sys_time;
+ struct rtc_time rtc_raw;
+ struct tm rtc_tm;
+ time_t rtc_t;
+ double read_err;
+ int error = 0;
+
+ status = read(fd, &data, sizeof(data));
+ if (status < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
+ error = 1;
+ goto turn_off_interrupt;
+ }
+
+ if ((data & RTC_UIE) == RTC_UIE) {
+ /* Update interrupt detected */
+
+ /* Read RTC time, sandwiched between two polls of the system clock
+ so we can bound any error. */
+
+ LCL_ReadCookedTime(&sys_time, &read_err);
+
+ status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
+ if (status < 0) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
+ error = 1;
+ goto turn_off_interrupt;
+ }
+
+ /* Convert RTC time into a struct timeval */
+ rtc_tm.tm_sec = rtc_raw.tm_sec;
+ rtc_tm.tm_min = rtc_raw.tm_min;
+ rtc_tm.tm_hour = rtc_raw.tm_hour;
+ rtc_tm.tm_mday = rtc_raw.tm_mday;
+ rtc_tm.tm_mon = rtc_raw.tm_mon;
+ rtc_tm.tm_year = rtc_raw.tm_year;
+
+ rtc_t = t_from_rtc(&rtc_tm);
+
+ if (rtc_t == (time_t)(-1)) {
+ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not convert RTC time to timeval");
+ error = 1;
+ goto turn_off_interrupt;
+ }
+
+ process_reading(rtc_t, &sys_time);
+
+ if (n_samples < 4) {
+ measurement_period = LOWEST_MEASUREMENT_PERIOD;
+ } else if (n_samples < 6) {
+ measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
+ } else if (n_samples < 10) {
+ measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
+ } else if (n_samples < 14) {
+ measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
+ } else {
+ measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
+ }
+
+ }
+
+turn_off_interrupt:
+
+ switch (operating_mode) {
+ case OM_INITIAL:
+ if (error) {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete initial step due to errors");
+ operating_mode = OM_NORMAL;
+ (after_init_hook)(after_init_hook_arg);
+
+ switch_interrupts(0);
+
+ timeout_running = 1;
+ timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
+ }
+
+ break;
+
+ case OM_AFTERTRIM:
+ if (error) {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete after trim relock due to errors");
+ operating_mode = OM_NORMAL;
+
+ switch_interrupts(0);
+
+ timeout_running = 1;
+ timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
+ }
+
+ break;
+
+ case OM_NORMAL:
+ switch_interrupts(0);
+
+ timeout_running = 1;
+ timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
+
+ break;
+ default:
+ CROAK("Impossible");
+ break;
+ }
+
+}
+
+/* ================================================== */
+
+void
+RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
+{
+ after_init_hook = after_hook;
+ after_init_hook_arg = anything;
+
+ operating_mode = OM_INITIAL;
+ timeout_running = 0;
+ switch_interrupts(1);
+
+}
+
+/* ================================================== */
+
+void
+RTC_Linux_StartMeasurements(void)
+{
+ timeout_running = 0;
+ measurement_timeout(NULL);
+}
+
+/* ================================================== */
+
+int
+RTC_Linux_WriteParameters(void)
+{
+ int retval;
+
+ if (fd < 0) {
+ return RTC_ST_NODRV;
+ }
+
+ if (coefs_valid) {
+ retval = write_coefs_to_file(1,coef_ref_time, coef_seconds_fast, coef_gain_rate);
+ } else {
+ /* Don't change the existing file, it may not be 100% valid but is our
+ current best guess. */
+ retval = RTC_ST_OK; /*write_coefs_to_file(0,0,0.0,0.0); */
+ }
+
+ return(retval);
+}
+
+/* ================================================== */
+/* Try to set the system clock from the RTC, in the same manner as
+ /sbin/clock -s -u would do. We're not as picky about OS version
+ etc in this case, since we have fewer requirements regarding the
+ RTC behaviour than we do for the rest of the module. */
+
+void
+RTC_Linux_TimePreInit(void)
+{
+ int fd, status;
+ struct rtc_time rtc_raw;
+ struct tm rtc_tm;
+ time_t rtc_t, estimated_correct_rtc_t;
+ long interval;
+ double accumulated_error = 0.0;
+ struct timeval new_sys_time;
+
+ coefs_file_name = CNF_GetRtcFile();
+
+ setup_config();
+ read_coefs_from_file();
+
+ fd = open(CNF_GetRtcDevice(), O_RDONLY);
+
+ if (fd < 0) {
+ return; /* Can't open it, and won't be able to later */
+ }
+
+ status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
+
+ if (status >= 0) {
+ /* Convert to seconds since 1970 */
+ rtc_tm.tm_sec = rtc_raw.tm_sec;
+ rtc_tm.tm_min = rtc_raw.tm_min;
+ rtc_tm.tm_hour = rtc_raw.tm_hour;
+ rtc_tm.tm_mday = rtc_raw.tm_mday;
+ rtc_tm.tm_mon = rtc_raw.tm_mon;
+ rtc_tm.tm_year = rtc_raw.tm_year;
+
+ rtc_t = t_from_rtc(&rtc_tm);
+
+ if (rtc_t != (time_t)(-1)) {
+
+ /* Work out approximatation to correct time (to about the
+ nearest second) */
+ if (valid_coefs_from_file) {
+ interval = rtc_t - file_ref_time;
+ accumulated_error = file_ref_offset + (double)(interval) * 1.0e-6 * file_rate_ppm;
+
+ /* Correct time */
+ LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
+ accumulated_error);
+ estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
+ } else {
+ estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
+ }
+
+ new_sys_time.tv_sec = estimated_correct_rtc_t;
+ new_sys_time.tv_usec = 0;
+
+ /* Tough luck if this fails */
+ if (settimeofday(&new_sys_time, NULL) < 0) {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not settimeofday");
+ }
+ } else {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
+ }
+ }
+
+ close(fd);
+}
+
+/* ================================================== */
+
+int
+RTC_Linux_GetReport(RPT_RTC_Report *report)
+{
+ report->ref_time = (unsigned long) coef_ref_time;
+ report->n_samples = n_samples;
+ report->n_runs = n_runs;
+ if (n_samples > 1) {
+ report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) +
+ (long)(rtc_trim[n_samples-1] - rtc_trim[0]));
+ } else {
+ report->span_seconds = 0;
+ }
+ report->rtc_seconds_fast = coef_seconds_fast;
+ report->rtc_gain_rate_ppm = 1.0e6 * coef_gain_rate;
+ return 1;
+}
+
+/* ================================================== */
+
+int
+RTC_Linux_Trim(void)
+{
+ struct timeval now;
+ double local_clock_err;
+
+
+ /* Remember the slope coefficient - we won't be able to determine a
+ good one in a few seconds when we determine the new offset! */
+ saved_coef_gain_rate = coef_gain_rate;
+
+ if (fabs(coef_seconds_fast) > 1.0) {
+
+ LOG(LOGS_INFO, LOGF_RtcLinux, "Trimming RTC, error = %.3f seconds", coef_seconds_fast);
+
+ /* Do processing to set clock. Let R be the value we set the
+ RTC to, then in 500ms the RTC ticks (R+1) (see comments in
+ arch/i386/kernel/time.c about the behaviour of the real time
+ clock chip). If S is the system time now, the error at the
+ next RTC tick is given by E = (R+1) - (S+0.5). Ideally we
+ want |E| <= 0.5, which implies R <= S <= R+1, i.e. R is just
+ the rounded down part of S, i.e. the seconds part. */
+
+ LCL_ReadCookedTime(&now, &local_clock_err);
+
+ set_rtc(now.tv_sec);
+
+ /* All old samples will now look bogus under the new
+ regime. */
+ n_samples = 0;
+ operating_mode = OM_AFTERTRIM;
+
+ /* And start rapid sampling, interrupts on now */
+ if (timeout_running) {
+ SCH_RemoveTimeout(timeout_id);
+ timeout_running = 0;
+ }
+ switch_interrupts(1);
+ }
+
+ return 1;
+
+}
+
+/* ================================================== */
+
+void
+RTC_Linux_CycleLogFile(void)
+{
+ if (logfile && logfilename) {
+ fclose(logfile);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_RtcLinux, "Could not reopen logfile %s", logfilename);
+ }
+ logwrites = 0;
+ }
+}
+
+/* ================================================== */
+
+#endif /* defined LINUX */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/rtc_linux.h,v 1.13 2002/02/28 23:27:13 richard Exp $
+
+ ======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ ======================================================================
+
+ */
+
+#ifndef _GOT_RTC_LINUX_H
+#define _GOT_RTC_LINUX_H
+
+#include "reports.h"
+
+#if defined LINUX
+
+extern int RTC_Linux_Initialise(void);
+extern void RTC_Linux_Finalise(void);
+extern void RTC_Linux_TimePreInit(void);
+extern void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything);
+extern void RTC_Linux_StartMeasurements(void);
+
+/* 0=success, 1=no driver, 2=can't write file */
+extern int RTC_Linux_WriteParameters(void);
+
+extern int RTC_Linux_GetReport(RPT_RTC_Report *report);
+extern int RTC_Linux_Trim(void);
+
+extern void RTC_Linux_CycleLogFile(void);
+
+#endif /* defined LINUX */
+
+#endif /* _GOT_RTC_LINUX_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sched.c,v 1.16 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This file contains the scheduling loop and the timeout queue.
+
+ */
+
+#include "sysincl.h"
+
+#include "sched.h"
+#include "memory.h"
+#include "util.h"
+#include "local.h"
+#include "logging.h"
+
+/* ================================================== */
+
+/* Flag indicating that we are initialised */
+static int initialised = 0;
+
+/* ================================================== */
+
+/* Variables to handle the capability to dispatch on particular file
+ handles becoming readable */
+
+/* Each bit set in this fd set corresponds to a read descriptor that
+ we are watching and with which we have a handler associated in the
+ file_handlers array */
+static fd_set read_fds;
+
+/* This is the number of bits that we have set in read_fds */
+static unsigned int n_read_fds;
+
+/* One more than the highest file descriptor that is registered */
+static unsigned int one_highest_fd;
+
+/* This assumes that fd_set is implemented as a fixed size array of
+ bits, possibly embedded inside a record. It might therefore
+ somewhat non-portable. */
+
+#define FD_SET_SIZE (sizeof(fd_set) * 8)
+
+typedef struct {
+ SCH_FileHandler handler;
+ SCH_ArbitraryArgument arg;
+} FileHandlerEntry;
+
+static FileHandlerEntry file_handlers[FD_SET_SIZE];
+
+/* ================================================== */
+
+/* Variables to handler the timer queue */
+
+typedef struct _TimerQueueEntry
+{
+ struct _TimerQueueEntry *next; /* Forward and back links in the list */
+ struct _TimerQueueEntry *prev;
+ struct timeval tv; /* Local system time at which the
+ timeout is to expire. Clearly this
+ must be in terms of what the
+ operating system thinks of as
+ system time, because it will be an
+ argument to select(). Therefore,
+ any fudges etc that our local time
+ driver module would apply to time
+ that we pass to clients etc doesn't
+ apply to this. */
+ SCH_TimeoutID id; /* ID to allow client to delete
+ timeout */
+ SCH_TimeoutClass class; /* The class that the epoch is in */
+ SCH_TimeoutHandler handler; /* The handler routine to use */
+ SCH_ArbitraryArgument arg; /* The argument to pass to the handler */
+
+} TimerQueueEntry;
+
+/* The timer queue. We only use the next and prev entries of this
+ record, these chain to the real entries. */
+static TimerQueueEntry timer_queue;
+static unsigned long n_timer_queue_entries;
+static SCH_TimeoutID next_tqe_id;
+
+/* Pointer to head of free list */
+static TimerQueueEntry *tqe_free_list = NULL;
+
+/* ================================================== */
+
+static int need_to_exit;
+
+/* ================================================== */
+
+static void
+handle_slew(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *anything);
+
+/* ================================================== */
+
+void
+SCH_Initialise(void)
+{
+
+ FD_ZERO(&read_fds);
+ n_read_fds = 0;
+
+ n_timer_queue_entries = 0;
+ next_tqe_id = 0;
+
+ timer_queue.next = &timer_queue;
+ timer_queue.prev = &timer_queue;
+
+ need_to_exit = 0;
+
+ LCL_AddParameterChangeHandler(handle_slew, NULL);
+
+ initialised = 1;
+
+ return;
+}
+
+
+/* ================================================== */
+
+void
+SCH_Finalise(void) {
+ initialised = 0;
+ return; /* Nothing to do for now */
+}
+
+/* ================================================== */
+
+void
+SCH_AddInputFileHandler
+(int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
+{
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ /* Don't want to allow the same fd to register a handler more than
+ once without deleting a previous association - this suggests
+ a bug somewhere else in the program. */
+ if (FD_ISSET(fd, &read_fds)) {
+ CROAK("File handler already registered");
+ }
+
+ ++n_read_fds;
+
+ file_handlers[fd].handler = handler;
+ file_handlers[fd].arg = arg;
+
+ FD_SET(fd, &read_fds);
+
+ if ((fd + 1) > one_highest_fd) {
+ one_highest_fd = fd + 1;
+ }
+
+ return;
+}
+
+
+/* ================================================== */
+
+void
+SCH_RemoveInputFileHandler(int fd)
+{
+ int fds_left, fd_to_check;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ /* Check that a handler was registered for the fd in question */
+ if (!FD_ISSET(fd, &read_fds)) {
+ CROAK("File handler not registered");
+ }
+
+ --n_read_fds;
+
+ FD_CLR(fd, &read_fds);
+
+ /* Find new highest file descriptor */
+ fds_left = n_read_fds;
+ fd_to_check = 0;
+ while (fds_left > 0) {
+ if (FD_ISSET(fd_to_check, &read_fds)) {
+ --fds_left;
+ }
+ ++fd_to_check;
+ }
+
+ one_highest_fd = fd_to_check;
+
+ return;
+
+}
+
+/* ================================================== */
+
+#define TQE_ALLOC_QUANTUM 32
+
+static TimerQueueEntry *
+allocate_tqe(void)
+{
+ TimerQueueEntry *new_block;
+ TimerQueueEntry *result;
+ int i;
+ if (tqe_free_list == NULL) {
+ new_block = MallocArray(TimerQueueEntry, TQE_ALLOC_QUANTUM);
+ for (i=1; i<TQE_ALLOC_QUANTUM; i++) {
+ new_block[i].next = &(new_block[i-1]);
+ }
+ new_block[0].next = NULL;
+ tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
+ }
+
+ result = tqe_free_list;
+ tqe_free_list = tqe_free_list->next;
+ return result;
+}
+
+/* ================================================== */
+
+static void
+release_tqe(TimerQueueEntry *node)
+{
+ node->next = tqe_free_list;
+ tqe_free_list = node;
+ return;
+}
+
+/* ================================================== */
+
+SCH_TimeoutID
+SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
+{
+ TimerQueueEntry *new_tqe;
+ TimerQueueEntry *ptr;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ new_tqe = allocate_tqe();
+
+ new_tqe->id = next_tqe_id++;
+ new_tqe->handler = handler;
+ new_tqe->arg = arg;
+ new_tqe->tv = *tv;
+ new_tqe->class = SCH_ReservedTimeoutValue;
+
+ /* Now work out where to insert the new entry in the list */
+ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
+ if (UTI_CompareTimevals(&new_tqe->tv, &ptr->tv) == -1) {
+ /* If the new entry comes before the current pointer location in
+ the list, we want to insert the new entry just before ptr. */
+ break;
+ }
+ }
+
+ /* At this stage, we want to insert the new entry immediately before
+ the entry identified by 'ptr' */
+
+ new_tqe->next = ptr;
+ new_tqe->prev = ptr->prev;
+ ptr->prev->next = new_tqe;
+ ptr->prev = new_tqe;
+
+ n_timer_queue_entries++;
+
+ return new_tqe->id;
+}
+
+/* ================================================== */
+/* This queues a timeout to elapse at a given delta time relative to
+ the current (raw) time */
+
+SCH_TimeoutID
+SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
+{
+ struct timeval now, then;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ LCL_ReadRawTime(&now);
+ UTI_AddDoubleToTimeval(&now, delay, &then);
+ return SCH_AddTimeout(&then, handler, arg);
+
+}
+
+/* ================================================== */
+
+SCH_TimeoutID
+SCH_AddTimeoutInClass(double min_delay, double separation,
+ SCH_TimeoutClass class,
+ SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
+{
+ TimerQueueEntry *new_tqe;
+ TimerQueueEntry *ptr;
+ struct timeval now;
+ double diff;
+ double new_min_delay;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ LCL_ReadRawTime(&now);
+ new_min_delay = min_delay;
+
+ /* Scan through list for entries in the same class and increase min_delay
+ if necessary to keep at least the separation away */
+ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
+ if (ptr->class == class) {
+ UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
+ if (new_min_delay > diff) {
+ if (new_min_delay - diff < separation) {
+ new_min_delay = diff + separation;
+ }
+ }
+ if (new_min_delay < diff) {
+ if (diff - new_min_delay < separation) {
+ new_min_delay = diff + separation;
+ }
+ }
+ }
+ }
+
+ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
+ UTI_DiffTimevalsToDouble(&diff, &ptr->tv, &now);
+ if (diff > new_min_delay) {
+ break;
+ }
+ }
+
+ /* We have located the insertion point */
+ new_tqe = allocate_tqe();
+
+ new_tqe->id = next_tqe_id++;
+ new_tqe->handler = handler;
+ new_tqe->arg = arg;
+ UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv);
+ new_tqe->class = class;
+
+ new_tqe->next = ptr;
+ new_tqe->prev = ptr->prev;
+ ptr->prev->next = new_tqe;
+ ptr->prev = new_tqe;
+ n_timer_queue_entries++;
+
+ return new_tqe->id;
+}
+
+/* ================================================== */
+
+void
+SCH_RemoveTimeout(SCH_TimeoutID id)
+{
+ TimerQueueEntry *ptr;
+ int ok;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ ok = 0;
+
+ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
+
+ if (ptr->id == id) {
+ /* Found the required entry */
+
+ /* Unlink from the queue */
+ ptr->next->prev = ptr->prev;
+ ptr->prev->next = ptr->next;
+
+ /* Decrement entry count */
+ --n_timer_queue_entries;
+
+ /* Release memory back to the operating system */
+ release_tqe(ptr);
+
+ ok = 1;
+
+ break;
+ }
+ }
+
+ assert(ok);
+
+}
+
+/* ================================================== */
+/* The current time (now) has to be passed in from the
+ caller to avoid race conditions */
+
+static int
+dispatch_timeouts(struct timeval *now) {
+ TimerQueueEntry *ptr;
+ int n_done = 0;
+
+ while ((n_timer_queue_entries > 0) &&
+ (UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) {
+ ptr = timer_queue.next;
+
+ /* Dispatch the handler */
+ (ptr->handler)(ptr->arg);
+
+ /* Increment count of timeouts handled */
+ ++n_done;
+
+ /* Unlink entry from the queue */
+ ptr->prev->next = ptr->next;
+ ptr->next->prev = ptr->prev;
+
+ /* Decrement count of entries in queue */
+ --n_timer_queue_entries;
+
+ /* Delete entry */
+ release_tqe(ptr);
+ }
+
+ return n_done;
+
+}
+
+/* ================================================== */
+
+/* nfh is the number of bits set in fhs */
+
+static void
+dispatch_filehandlers(int nfh, fd_set *fhs)
+{
+ int fh = 0;
+
+ while (nfh > 0) {
+ if (FD_ISSET(fh, fhs)) {
+
+ /* This descriptor can be read from, dispatch its handler */
+ (file_handlers[fh].handler)(file_handlers[fh].arg);
+
+ /* Decrement number of readable files still to find */
+ --nfh;
+ }
+
+ ++fh;
+ }
+
+}
+
+/* ================================================== */
+
+static void
+handle_slew(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *anything)
+{
+ TimerQueueEntry *ptr;
+ struct timeval T1;
+
+ if (is_step_change) {
+ /* We're not interested in anything else - it won't affect the
+ functionality of timer event dispatching. If a step change
+ occurs, just shift all the timeouts by the offset */
+
+ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
+ UTI_AddDoubleToTimeval(&ptr->tv, -doffset, &T1);
+ ptr->tv = T1;
+ }
+
+ }
+}
+
+/* ================================================== */
+
+void
+SCH_MainLoop(void)
+{
+ fd_set rd, wr, ex;
+ int status;
+ struct timeval tv, *ptv;
+ struct timeval now;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ while (!need_to_exit) {
+
+ /* Copy current set of read file descriptors */
+ memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
+
+ /* Blank the write and exception descriptors - we aren't very
+ interested */
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+
+ /* Try to dispatch any timeouts that have already gone by, and
+ keep going until all are done. (The earlier ones may take so
+ long to do that the later ones come around by the time they are
+ completed). */
+
+ do {
+ LCL_ReadRawTime(&now);
+ } while (dispatch_timeouts(&now) > 0);
+
+ /* Check whether there is a timeout and set it up */
+ if (n_timer_queue_entries > 0) {
+
+ UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
+ ptv = &tv;
+
+ } else {
+ ptv = NULL;
+ }
+
+ /* if there are no file descriptors being waited on and no
+ timeout set, this is clearly ridiculous, so stop the run */
+
+ if (!ptv && (n_read_fds == 0)) {
+ LOG_FATAL(LOGF_Scheduler, "No descriptors or timeout to wait for");
+ }
+
+ status = select(one_highest_fd, &rd, &wr, &ex, ptv);
+
+ if (status < 0) {
+ CROAK("Status < 0 after select");
+ } else if (status > 0) {
+ /* A file descriptor is ready to read */
+
+ dispatch_filehandlers(status, &rd);
+
+ } else {
+ if (status != 0) {
+ CROAK("Unexpected value from select");
+ }
+
+ /* No descriptors readable, timeout must have elapsed.
+ Therefore, tv must be non-null */
+ if (!ptv) {
+ CROAK("No descriptors or timeout?");
+ }
+
+ /* There's nothing to do here, since the timeouts
+ will be dispatched at the top of the next loop
+ cycle */
+
+ }
+ }
+
+ return;
+
+}
+
+/* ================================================== */
+
+void
+SCH_QuitProgram(void)
+{
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+ need_to_exit = 1;
+}
+
+/* ================================================== */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sched.h,v 1.10 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Exported header file for sched.c
+ */
+
+#ifndef GOT_SCHED_H
+#define GOT_SCHED_H
+
+#include "sysincl.h"
+
+typedef unsigned long SCH_TimeoutID;
+
+typedef unsigned long SCH_TimeoutClass;
+static const SCH_TimeoutClass SCH_ReservedTimeoutValue = 0;
+static const SCH_TimeoutClass SCH_NtpSamplingClass = 1;
+static const SCH_TimeoutClass SCH_NtpBroadcastClass = 2;
+
+typedef void* SCH_ArbitraryArgument;
+typedef void (*SCH_FileHandler)(SCH_ArbitraryArgument);
+typedef void (*SCH_TimeoutHandler)(SCH_ArbitraryArgument);
+
+/* Exported functions */
+
+/* Initialisation function for the module */
+extern void SCH_Initialise(void);
+
+/* Finalisation function for the module */
+extern void SCH_Finalise(void);
+
+/* Register a handler for when select goes true on a file descriptor */
+extern void SCH_AddInputFileHandler
+(int fd, /* The file descriptor */
+ SCH_FileHandler, /* The handler routine */
+ SCH_ArbitraryArgument /* An arbitrary passthrough argument to the handler */
+);
+extern void SCH_RemoveInputFileHandler(int fd);
+
+/* This queues a timeout to elapse at a given (raw) local time */
+extern SCH_TimeoutID SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler, SCH_ArbitraryArgument);
+
+/* This queues a timeout to elapse at a given delta time relative to the current (raw) time */
+extern SCH_TimeoutID SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler, SCH_ArbitraryArgument);
+
+/* This queues a timeout in a particular class, ensuring that the
+ expiry time is at least a given separation away from any other
+ timeout in the same class */
+extern SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation,
+ SCH_TimeoutClass class,
+ SCH_TimeoutHandler handler, SCH_ArbitraryArgument);
+
+/* The next one probably ought to return a status code */
+extern void SCH_RemoveTimeout(SCH_TimeoutID);
+
+extern void SCH_MainLoop(void);
+
+extern void SCH_QuitProgram(void);
+
+#endif /* GOT_SCHED_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sources.c,v 1.31 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ The routines in this file manage the complete pool of sources that
+ we might be synchronizing to. This includes NTP sources and others
+ (e.g. local reference clocks, eyeball + wristwatch etc).
+
+ */
+
+#include "sysincl.h"
+
+#include "sources.h"
+#include "sourcestats.h"
+#include "memory.h"
+#include "ntp.h" /* For NTP_Leap */
+#include "local.h"
+#include "reference.h"
+#include "util.h"
+#include "conf.h"
+#include "logging.h"
+#include "reports.h"
+#include "nameserv.h"
+#include "mkdirpp.h"
+
+/* ================================================== */
+/* Flag indicating that we are initialised */
+static int initialised = 0;
+
+/* ================================================== */
+/* Structure used to hold info for selecting between sources */
+struct SelectInfo {
+ int stratum;
+ int select_ok;
+ double variance;
+ double root_delay;
+ double root_dispersion;
+ double root_distance;
+ double best_offset;
+ double lo_limit;
+ double hi_limit;
+};
+
+/* ================================================== */
+/* This enum contains the flag values that are used to label
+ each source */
+typedef enum {
+ SRC_OK, /* OK so far */
+ SRC_UNREACHABLE, /* Source is not reachable */
+ SRC_BAD_STATS, /* Stats driver could not supply valid
+ data */
+ SRC_FALSETICKER, /* Source is found to be a falseticker */
+ SRC_JITTERY, /* Source scatter worse than other's dispersion */
+ SRC_SELECTABLE, /* Source is acceptable candidate */
+ SRC_SYNC /* Current synchronisation source */
+} SRC_Status;
+
+/* ================================================== */
+/* Define the instance structure used to hold information about each
+ source */
+struct SRC_Instance_Record {
+ SST_Stats stats;
+ NTP_Leap leap_status; /* Leap status */
+ int index; /* Index back into the array of source */
+ unsigned long ref_id; /* The reference ID of this source
+ (i.e. its IP address, NOT the
+ reference _it_ is sync'd to) */
+
+ /* Flag indicating that we are receiving packets with valid headers
+ from this source and can use it as a reference */
+ int reachable;
+
+ /* Flag indicating the status of the source */
+ SRC_Status status;
+
+ struct SelectInfo sel_info;
+};
+
+/* ================================================== */
+/* Structure used to build the sort list for finding falsetickers */
+struct Sort_Element {
+ int index;
+ double offset;
+ enum {LOW=-1, CENTRE=0, HIGH=1} tag;
+};
+
+/* ================================================== */
+/* Table of sources */
+static struct SRC_Instance_Record **sources;
+static struct Sort_Element *sort_list;
+static int *sel_sources;
+static int n_sources; /* Number of sources currently in the table */
+static int max_n_sources; /* Capacity of the table */
+
+#define INVALID_SOURCE (-1)
+static int selected_source_index; /* Which source index is currently
+ selected (set to INVALID_SOURCE
+ if no current valid reference) */
+
+/* ================================================== */
+/* Forward prototype */
+
+static void
+slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double afreq,
+ double doffset, int is_step_change, void *anything);
+
+/* ================================================== */
+/* Initialisation function */
+void SRC_Initialise(void) {
+ sources = NULL;
+ sort_list = NULL;
+ n_sources = 0;
+ max_n_sources = 0;
+ selected_source_index = INVALID_SOURCE;
+ initialised = 1;
+
+ LCL_AddParameterChangeHandler(slew_sources, NULL);
+
+ return;
+}
+
+/* ================================================== */
+/* Finalisation function */
+void SRC_Finalise(void)
+{
+ LCL_RemoveParameterChangeHandler(slew_sources, NULL);
+ initialised = 0;
+ return;
+}
+
+/* ================================================== */
+/* Function to create a new instance. This would be called by one of
+ the individual source-type instance creation routines. */
+
+SRC_Instance SRC_CreateNewInstance(unsigned long ref_id)
+{
+ SRC_Instance result;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ result = MallocNew(struct SRC_Instance_Record);
+ result->stats = SST_CreateInstance(ref_id);
+
+ if (n_sources == max_n_sources) {
+ /* Reallocate memory */
+ max_n_sources += 32;
+ if (sources) {
+ sources = ReallocArray(struct SRC_Instance_Record *, max_n_sources, sources);
+ sort_list = ReallocArray(struct Sort_Element, 3*max_n_sources, sort_list);
+ sel_sources = ReallocArray(int, max_n_sources, sel_sources);
+ } else {
+ sources = MallocArray(struct SRC_Instance_Record *, max_n_sources);
+ sort_list = MallocArray(struct Sort_Element, 3*max_n_sources);
+ sel_sources = MallocArray(int, max_n_sources);
+ }
+ }
+
+ sources[n_sources] = result;
+ result->index = n_sources;
+ result->leap_status = LEAP_Normal;
+ result->ref_id = ref_id;
+ result->reachable = 0;
+ result->status = SRC_BAD_STATS;
+
+ n_sources++;
+
+ return result;
+}
+
+/* ================================================== */
+/* Function to get rid of a source when it is being unconfigured.
+ This may cause the current reference source to be reselected, if this
+ was the reference source or contributed significantly to a
+ falseticker decision. */
+
+void SRC_DestroyInstance(SRC_Instance instance)
+{
+ int dead_index, i;
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ if (instance->index == selected_source_index) {
+ instance->reachable = 0;
+ SRC_SelectSource(0);
+ }
+
+ SST_DeleteInstance(instance->stats);
+ dead_index = instance->index;
+ for (i=dead_index; i<n_sources-1; i++) {
+ sources[i] = sources[i+1];
+ sources[i]->index = i;
+ }
+ --n_sources;
+ Free(instance);
+
+ if (selected_source_index > dead_index) {
+ --selected_source_index;
+ }
+}
+
+/* ================================================== */
+/* Function to get the range of frequencies, relative to the given
+ source, that we believe the local clock lies within. The return
+ values are in terms of the number of seconds fast (+ve) or slow
+ (-ve) relative to the source that the local clock becomes after a
+ given amount of local time has elapsed.
+
+ Suppose the initial offset relative to the source is U (fast +ve,
+ slow -ve) and a time interval T elapses measured in terms of the
+ local clock. Then the error relative to the source at the end of
+ the interval should lie in the interval [U+T*lo, U+T*hi]. */
+
+void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
+{
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ SST_GetFrequencyRange(instance->stats, lo, hi);
+ return;
+}
+
+/* ================================================== */
+
+/* This function is called by one of the source drivers when it has
+ a new sample that is to be accumulated.
+
+ This function causes the frequency estimation to be re-run for the
+ designated source, and the clock selection procedure to be re-run
+ afterwards.
+
+ Parameters are described in sources.h
+
+ */
+
+void SRC_AccumulateSample
+(SRC_Instance inst,
+ struct timeval *sample_time,
+ double offset,
+ double peer_delay,
+ double peer_dispersion,
+ double root_delay,
+ double root_dispersion,
+ int stratum,
+ NTP_Leap leap_status)
+{
+
+ if (!initialised) {
+ CROAK("Should be initialised");
+ }
+
+ inst->leap_status = leap_status;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
+ UTI_IPToDottedQuad(inst->ref_id), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
+#endif
+
+ /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
+ IS FLIPPED */
+ SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
+ SST_DoNewRegression(inst->stats);
+ /* And redo clock selection */
+ SRC_SelectSource(inst->ref_id);
+
+ return;
+}
+
+/* ================================================== */
+
+void
+SRC_SetReachable(SRC_Instance inst)
+{
+ inst->reachable = 1;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Sources, "%s", UTI_IPToDottedQuad(inst->ref_id));
+#endif
+
+ /* Don't do selection at this point, though - that will come about
+ in due course when we get some useful data from the source */
+}
+
+/* ================================================== */
+
+void
+SRC_UnsetReachable(SRC_Instance inst)
+{
+ inst->reachable = 0;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_Sources, "%s%s", UTI_IPToDottedQuad(inst->ref_id),
+ (inst->index == selected_source_index) ? "(REF)":"");
+#endif
+
+ /* If this was the previous reference source, we have to reselect! */
+
+ if (inst->index == selected_source_index) {
+ SRC_SelectSource(0);
+ }
+
+}
+
+/* ================================================== */
+
+static int
+compare_sort_elements(const void *a, const void *b)
+{
+ const struct Sort_Element *u = (const struct Sort_Element *) a;
+ const struct Sort_Element *v = (const struct Sort_Element *) b;
+
+ if (u->offset < v->offset) {
+ return -1;
+ } else if (u->offset > v->offset) {
+ return +1;
+ } else if (u->tag < v->tag) {
+ return -1;
+ } else if (u->tag > v->tag) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+/* This function selects the current reference from amongst the pool
+ of sources we are holding.
+
+ Updates are only made to the local reference if match_addr is zero or is
+ equal to the selected reference source address */
+
+void
+SRC_SelectSource(unsigned long match_addr)
+{
+ int i, j, index;
+ struct timeval now;
+ double local_clock_err;
+ int src_select_ok;
+ double src_offset, src_offset_sd, src_frequency, src_skew;
+ double src_accrued_dispersion;
+ int n_endpoints, j1, j2;
+ double best_lo, best_hi;
+ int depth, best_depth;
+ int n_sel_sources;
+ double distance, min_distance;
+ int stratum, min_stratum;
+ int min_distance_index;
+ struct SelectInfo *si;
+ double total_root_dispersion;
+ int n_reachable_sources;
+
+ NTP_Leap leap_status = LEAP_Normal;
+
+ if (n_sources == 0) {
+ /* In this case, we clearly cannot synchronise to anything */
+ if (selected_source_index != INVALID_SOURCE) {
+ LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no sources");
+ }
+ selected_source_index = INVALID_SOURCE;
+ REF_SetUnsynchronised();
+ return;
+ }
+
+ LCL_ReadCookedTime(&now, &local_clock_err);
+
+ /* Step 1 - build intervals about each source */
+ n_endpoints = 0;
+ n_reachable_sources = 0;
+ for (i=0; i<n_sources; i++) {
+
+ if (sources[i]->reachable) {
+
+ ++n_reachable_sources;
+
+ si = &(sources[i]->sel_info);
+ SST_GetSelectionData(sources[i]->stats, &now,
+ &(si->stratum),
+ &(si->best_offset),
+ &(si->root_delay),
+ &(si->root_dispersion),
+ &(si->variance),
+ &(si->select_ok));
+
+ /* Eventually this might be a flag indicating whether the get
+ selection data call was successful. For now it always is. */
+ src_select_ok = 1;
+
+ si->root_distance = si->root_dispersion + 0.5 * fabs(si->root_delay);
+ si->lo_limit = si->best_offset - si->root_distance;
+ si->hi_limit = si->best_offset + si->root_distance;
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "%s off=%f dist=%f lo=%f hi=%f",
+ UTI_IPToDottedQuad(sources[i]->ref_id),
+ si->best_offset, si->root_distance,
+ si->lo_limit, si->hi_limit);
+#endif
+
+ if (src_select_ok) {
+
+ sources[i]->status = SRC_OK; /* For now */
+
+ /* Otherwise it will be hard to pick this one later! However,
+ this test might be too strict, we might want to dump it */
+ j1 = n_endpoints;
+ j2 = j1 + 1;
+
+ sort_list[j1].index = i;
+ sort_list[j1].offset = si->lo_limit;
+ sort_list[j1].tag = LOW;
+
+ sort_list[j2].index = i;
+ sort_list[j2].offset = si->hi_limit;
+ sort_list[j2].tag = HIGH;
+
+ n_endpoints += 2;
+
+ } else {
+ sources[i]->status = SRC_BAD_STATS;
+ }
+ } else {
+ /* If the source is not reachable, there is no way we will pick
+ it. */
+ sources[i]->status = SRC_UNREACHABLE;
+ }
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "n_endpoints=%d", n_endpoints);
+#endif
+
+ /* Now sort the endpoint list */
+ if (n_endpoints > 0) {
+
+ /* Sort the list into order */
+ qsort((void *) sort_list, n_endpoints, sizeof(struct Sort_Element), compare_sort_elements);
+
+ /* Now search for the interval which is contained in the most
+ individual source intervals. Any source which overlaps this
+ will be a candidate.
+
+ If we get a case like
+
+ <----------------------->
+ <-->
+ <-->
+ <===========>
+
+ we will build the interval as shown with '=', whereas with an extra source we get
+ <----------------------->
+ <------->
+ <-->
+ <-->
+ <==>
+
+ The first case is just bad luck - we need extra sources to
+ detect the falseticker, so just make an arbitrary choice based
+ on stratum & stability etc.
+ */
+
+ depth = best_depth = 0;
+ best_lo = best_hi = 0.0;
+
+ for (i=0; i<n_endpoints; i++) {
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d t=%f tag=%d addr=%s", i, sort_list[i].offset, sort_list[i].tag,
+ UTI_IPToDottedQuad(sources[sort_list[i].index]->ref_id));
+#endif
+ switch(sort_list[i].tag) {
+ case LOW:
+ depth++;
+ if (depth > best_depth) {
+ best_depth = depth;
+ best_lo = sort_list[i].offset;
+ }
+ break;
+
+ case CENTRE:
+ CROAK("CENTRE cannot occur");
+ break;
+
+ case HIGH:
+ if (depth == best_depth) {
+ best_hi = sort_list[i].offset;
+ }
+ depth--;
+ break;
+
+ }
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "best_depth=%d best_lo=%f best_hi=%f",
+ best_depth, best_lo, best_hi);
+#endif
+
+ if (best_depth <= n_reachable_sources/2) {
+ /* Could not even get half the reachable sources to agree -
+ clearly we can't synchronise.
+
+ srcs #to agree
+ 1 1
+ 2 2
+ 3 2
+ 4 3 etc
+
+ */
+
+ if (selected_source_index != INVALID_SOURCE) {
+ LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no majority");
+ }
+ selected_source_index = INVALID_SOURCE;
+ REF_SetUnsynchronised();
+
+ /* .. and mark all sources as falsetickers (so they appear thus
+ on the outputs from the command client) */
+
+ for (i=0; i<n_sources; i++) {
+ sources[i]->status = SRC_FALSETICKER;
+ }
+
+ } else {
+
+ /* We have our interval, now work out which source are in it,
+ i.e. build list of admissible sources. */
+
+ n_sel_sources = 0;
+ for (i=0; i<n_sources; i++) {
+ if (sources[i]->status == SRC_OK) {
+ /* This should be the same condition to get into the endpoint
+ list */
+ /* Check if source's interval contains the best interval, or
+ is wholly contained within it */
+ if (((sources[i]->sel_info.lo_limit <= best_lo) &&
+ (sources[i]->sel_info.hi_limit >= best_hi)) ||
+ ((sources[i]->sel_info.lo_limit >= best_lo) &&
+ (sources[i]->sel_info.hi_limit <= best_hi))) {
+
+ sel_sources[n_sel_sources++] = i;
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is valid", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+#endif
+ } else {
+ sources[i]->status = SRC_FALSETICKER;
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is a falseticker", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+#endif
+ }
+ }
+ }
+
+ /* We now have a list of indices for the sources which pass the
+ false-ticker test. Now go on to reject those whose variance is
+ greater than the minimum distance of any other */
+
+ /* Find minimum distance */
+ index = sel_sources[0];
+ min_distance = sources[index]->sel_info.root_distance;
+ for (i=1; i<n_sel_sources; i++) {
+ index = sel_sources[i];
+ distance = sources[index]->sel_info.root_distance;
+ if (distance < min_distance) {
+ min_distance = distance;
+ }
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "min_distance=%f", min_distance);
+#endif
+
+ /* Now go through and prune any sources that have excessive
+ variance */
+ for (i=0; i<n_sel_sources; i++) {
+ index = sel_sources[i];
+ if (sources[index]->sel_info.variance > min_distance) {
+ sel_sources[i] = INVALID_SOURCE;
+ sources[index]->status = SRC_JITTERY;
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s has too much variance", i, UTI_IPToDottedQuad(sources[i]->ref_id));
+#endif
+ }
+ }
+
+ /* Now crunch the list and mark all sources as selectable */
+ for (i=j=0; i<n_sel_sources; i++) {
+ index = sel_sources[i];
+ if (index != INVALID_SOURCE) {
+ sources[index]->status = SRC_SELECTABLE;
+ sel_sources[j++] = sel_sources[i];
+ index++;
+ }
+ }
+ n_sel_sources = j;
+
+ /* Now find minimum stratum. If none are left now,
+ tough. RFC1305 is not so harsh on pruning sources due to
+ excess variance, which prevents this from happening */
+
+ if (n_sel_sources > 0) {
+ index = sel_sources[0];
+ min_stratum = sources[index]->sel_info.stratum;
+ for (i=1; i<n_sel_sources; i++) {
+ index = sel_sources[i];
+ stratum = sources[index]->sel_info.stratum;
+ if (stratum < min_stratum) min_stratum = stratum;
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "min_stratum=%d", min_stratum);
+#endif
+
+ /* Does the current source have this stratum and is it still a
+ survivor? */
+
+ if ((selected_source_index == INVALID_SOURCE) ||
+ (sources[selected_source_index]->status != SRC_SELECTABLE) ||
+ (sources[selected_source_index]->sel_info.stratum > min_stratum)) {
+
+ /* We have to elect a new synchronisation source */
+ min_distance_index = INVALID_SOURCE;
+ for (i=0; i<n_sel_sources; i++) {
+ index = sel_sources[i];
+ if (sources[index]->sel_info.stratum == min_stratum) {
+ if ((min_distance_index == INVALID_SOURCE) ||
+ (sources[index]->sel_info.root_distance < min_distance)) {
+ min_distance = sources[index]->sel_info.root_distance;
+ min_distance_index = index;
+ }
+ }
+ }
+
+ selected_source_index = min_distance_index;
+ LOG(LOGS_INFO, LOGF_Sources, "Selected source %s",
+ UTI_IPToDottedQuad(sources[selected_source_index]->ref_id));
+
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "new_sel_index=%d", min_distance_index);
+#endif
+ } else {
+ /* We retain the existing sync source, see p40 of RFC1305b.ps */
+#if 0
+ LOG(LOGS_INFO, LOGF_Sources, "existing reference retained", min_distance_index);
+#endif
+
+ }
+
+ sources[selected_source_index]->status = SRC_SYNC;
+
+ /* Now just use the statistics of the selected source for
+ trimming the local clock */
+
+ LCL_ReadCookedTime(&now, &local_clock_err);
+
+ SST_GetTrackingData(sources[selected_source_index]->stats, &now,
+ &src_offset, &src_offset_sd,
+ &src_accrued_dispersion,
+ &src_frequency, &src_skew);
+
+ total_root_dispersion = (src_accrued_dispersion +
+ sources[selected_source_index]->sel_info.root_dispersion);
+
+ if ((match_addr == 0) ||
+ (match_addr == sources[selected_source_index]->ref_id)) {
+
+ REF_SetReference(min_stratum, leap_status,
+ sources[selected_source_index]->ref_id,
+ &now,
+ src_offset,
+ src_frequency,
+ src_skew,
+ sources[selected_source_index]->sel_info.root_delay,
+ total_root_dispersion);
+ }
+
+ } else {
+ if (selected_source_index != INVALID_SOURCE) {
+ LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no selectable sources");
+ }
+ selected_source_index = INVALID_SOURCE;
+ REF_SetUnsynchronised();
+
+ }
+
+
+ }
+
+ } else {
+ /* No sources provided valid endpoints */
+ if (selected_source_index != INVALID_SOURCE) {
+ LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no reachable sources");
+ }
+ selected_source_index = INVALID_SOURCE;
+ REF_SetUnsynchronised();
+ }
+
+}
+
+/* ================================================== */
+
+double
+SRC_PredictOffset(SRC_Instance inst, struct timeval *when)
+{
+ return SST_PredictOffset(inst->stats, when);
+}
+
+/* ================================================== */
+
+double
+SRC_MinRoundTripDelay(SRC_Instance inst)
+{
+ return SST_MinRoundTripDelay(inst->stats);
+}
+
+/* ================================================== */
+/* This routine is registered as a callback with the local clock
+ module, to be called whenever the local clock changes frequency or
+ is slewed. It runs through all the existing source statistics, and
+ adjusts them to make them look as though they were sampled under
+ the new regime. */
+
+static void
+slew_sources(struct timeval *raw,
+ struct timeval *cooked,
+ double dfreq,
+ double afreq,
+ double doffset,
+ int is_step_change,
+ void *anything)
+{
+ int i;
+
+ for (i=0; i<n_sources; i++) {
+ SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset);
+ }
+
+}
+
+/* ================================================== */
+/* This is called to dump out the source measurement registers */
+
+void
+SRC_DumpSources(void)
+{
+ FILE *out;
+ int direc_len;
+ char *filename;
+ unsigned int a, b, c, d;
+ int i;
+ char *direc;
+
+ direc = CNF_GetDumpDir();
+ direc_len = strlen(direc);
+ filename = MallocArray(char, direc_len+24); /* a bit of slack */
+ if (mkdir_and_parents(direc)) {
+ for (i=0; i<n_sources; i++) {
+ a = (sources[i]->ref_id) >> 24;
+ b = ((sources[i]->ref_id) >> 16) & 0xff;
+ c = ((sources[i]->ref_id) >> 8) & 0xff;
+ d = ((sources[i]->ref_id)) & 0xff;
+
+ sprintf(filename, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
+ out = fopen(filename, "w");
+ if (!out) {
+ LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
+ } else {
+ SST_SaveToFile(sources[i]->stats, out);
+ fclose(out);
+ }
+ }
+ } else {
+ LOG(LOGS_ERR, LOGF_Sources, "Could not create directory %s", direc);
+ }
+ Free(filename);
+}
+
+/* ================================================== */
+
+void
+SRC_ReloadSources(void)
+{
+ FILE *in;
+ char *filename;
+ unsigned int a, b, c, d;
+ int i;
+ char *dumpdir;
+ int dumpdirlen;
+
+ for (i=0; i<n_sources; i++) {
+ a = (sources[i]->ref_id) >> 24;
+ b = ((sources[i]->ref_id) >> 16) & 0xff;
+ c = ((sources[i]->ref_id) >> 8) & 0xff;
+ d = ((sources[i]->ref_id)) & 0xff;
+
+ dumpdir = CNF_GetDumpDir();
+ dumpdirlen = strlen(dumpdir);
+ filename = MallocArray(char, dumpdirlen+24);
+ sprintf(filename, "%s/%d.%d.%d.%d.dat", dumpdir, a, b, c, d);
+ in = fopen(filename, "r");
+ if (!in) {
+ LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
+ } else {
+ if (SST_LoadFromFile(sources[i]->stats, in)) {
+ /* We might want to use SST_DoUpdateRegression here, but we
+ need to check it has the same functionality */
+ SST_DoNewRegression(sources[i]->stats);
+ } else {
+ LOG(LOGS_WARN, LOGF_Sources, "Problem loading from file %s", filename);
+ }
+ fclose(in);
+ }
+ Free(filename);
+ }
+}
+
+/* ================================================== */
+
+int
+SRC_IsSyncPeer(SRC_Instance inst)
+{
+ if (inst->index == selected_source_index) {
+ return 1;
+ } else {
+ return 0;
+ }
+
+}
+
+/* ================================================== */
+
+int
+SRC_ReadNumberOfSources(void)
+{
+ return n_sources;
+}
+
+/* ================================================== */
+
+int
+SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
+{
+ SRC_Instance src;
+ if ((index >= n_sources) || (index < 0)) {
+ return 0;
+ } else {
+ src = sources[index];
+ report->ip_addr = src->ref_id;
+ switch (src->status) {
+ case SRC_SYNC:
+ report->state = RPT_SYNC;
+ break;
+ case SRC_JITTERY:
+ report->state = RPT_JITTERY;
+ break;
+ case SRC_UNREACHABLE:
+ report->state = RPT_UNREACH;
+ break;
+ case SRC_FALSETICKER:
+ report->state = RPT_FALSETICKER;
+ break;
+ default:
+ report->state = RPT_OTHER;
+ break;
+ }
+ /* Call stats module to fill out estimates */
+ SST_DoSourceReport(src->stats, report, now);
+
+ return 1;
+ }
+
+}
+
+/* ================================================== */
+
+int
+SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report)
+{
+ SRC_Instance src;
+
+ if ((index >= n_sources) || (index < 0)) {
+ return 0;
+ } else {
+ src = sources[index];
+ report->ip_addr = src->ref_id;
+ SST_DoSourcestatsReport(src->stats, report);
+ return 1;
+ }
+}
+
+/* ================================================== */
+
+SRC_Skew_Direction SRC_LastSkewChange(SRC_Instance inst)
+{
+ SRC_Skew_Direction result = SRC_Skew_Nochange;
+
+ switch (SST_LastSkewChange(inst->stats)) {
+ case SST_Skew_Decrease:
+ result = SRC_Skew_Decrease;
+ break;
+ case SST_Skew_Nochange:
+ result = SRC_Skew_Nochange;
+ break;
+ case SST_Skew_Increase:
+ result = SRC_Skew_Increase;
+ break;
+ }
+
+ return result;
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sources.h,v 1.15 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the header for the module that manages the collection of all
+ sources that we are making measurements from. This include all NTP
+ servers & peers, locally connected reference sources, eye/wristwatch
+ drivers etc */
+
+#ifndef GOT_SOURCES_H
+#define GOT_SOURCES_H
+
+#include "sysincl.h"
+
+#include "ntp.h"
+#include "reports.h"
+
+/* This datatype is used to hold information about sources. The
+ instance must be passed when calling many of the interface
+ functions */
+
+typedef struct SRC_Instance_Record *SRC_Instance;
+
+/* Initialisation function */
+extern void SRC_Initialise(void);
+
+/* Finalisation function */
+extern void SRC_Finalise(void);
+
+/* Function to create a new instance. This would be called by one of
+ the individual source-type instance creation routines. */
+
+extern SRC_Instance SRC_CreateNewInstance(unsigned long ref_id);
+
+/* Function to get rid of a source when it is being unconfigured.
+ This may cause the current reference source to be reselected, if this
+ was the reference source or contributed significantly to a
+ falseticker decision. */
+
+extern void SRC_DestroyInstance(SRC_Instance instance);
+
+
+/* Function to get the range of frequencies, relative to the given
+ source, that we believe the local clock lies within. The return
+ values are in terms of the number of seconds fast (+ve) or slow
+ (-ve) relative to the source that the local clock becomes after a
+ given amount of local time has elapsed.
+
+ Suppose the initial offset relative to the source is U (fast +ve,
+ slow -ve) and a time interval T elapses measured in terms of the
+ local clock. Then the error relative to the source at the end of
+ the interval should lie in the interval [U+T*lo, U+T*hi]. */
+
+extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi);
+
+/* This function is called by one of the source drivers when it has
+ a new sample that is to be accumulated.
+
+ This function causes the frequency estimation to be re-run for the
+ designated source, and the clock selection procedure to be re-run
+ afterwards.
+
+ sample_time is the local time at which the sample is to be
+ considered to have been made, in terms of doing a regression fit of
+ offset against local time.
+
+ offset is the offset at the time, in seconds. Positive indicates
+ that the local clock is SLOW relative to the source, negative
+ indicates that the local clock is FAST relative to it.
+
+ root_delay and root_dispersion are in seconds, and are as per
+ RFC1305. root_dispersion only includes the peer's root dispersion
+ + local sampling precision + skew dispersion accrued during the
+ measurement. It is the job of the source statistics algorithms +
+ track.c to add on the extra dispersion due to the residual standard
+ deviation of the offsets from this source after regression, to form
+ the root_dispersion field in the packets transmitted to clients or
+ peers.
+
+ stratum is the stratum of the source that supplied the sample.
+
+ */
+
+extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
+
+/* This routine indicates that packets with valid headers are being
+ received from the designated source */
+extern void SRC_SetReachable(SRC_Instance instance);
+
+/* This routine indicates that we are no longer receiving packets with
+ valid headers from the designated source */
+extern void SRC_UnsetReachable(SRC_Instance instance);
+
+/* This routine is used to select the best source from amongst those
+ we currently have valid data on, and use it as the tracking base
+ for the local time. If match_addr is zero it means we must start
+ tracking the (newly) selected reference unconditionally, otherwise
+ it is equal to the address we should track if it turns out to be
+ the best reference. (This avoids updating the frequency tracking
+ for every sample from other sources - only the ones from the
+ selected reference make a difference) */
+extern void SRC_SelectSource(unsigned long match_addr);
+
+/* Predict the offset of the local clock relative to a given source at
+ a given local cooked time. Positive indicates local clock is FAST
+ relative to reference. */
+extern double SRC_PredictOffset(SRC_Instance inst, struct timeval *when);
+
+/* Return the minimum peer delay amongst the previous samples
+ currently held in the register */
+extern double SRC_MinRoundTripDelay(SRC_Instance inst);
+
+extern void SRC_DumpSources(void);
+
+extern void SRC_ReloadSources(void);
+
+extern int SRC_IsSyncPeer(SRC_Instance inst);
+extern int SRC_ReadNumberOfSources(void);
+extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);
+
+extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report);
+
+typedef enum {
+ SRC_Skew_Decrease,
+ SRC_Skew_Nochange,
+ SRC_Skew_Increase
+} SRC_Skew_Direction;
+
+extern SRC_Skew_Direction SRC_LastSkewChange(SRC_Instance inst);
+
+#endif /* GOT_SOURCES_H */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sourcestats.c,v 1.39 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This file contains the routines that do the statistical
+ analysis on the samples obtained from the sources,
+ to determined frequencies and error bounds. */
+
+#include "sysincl.h"
+
+#include "sourcestats.h"
+#include "memory.h"
+#include "regress.h"
+#include "util.h"
+#include "conf.h"
+#include "logging.h"
+#include "local.h"
+#include "mkdirpp.h"
+
+/* ================================================== */
+/* Define the maxumum number of samples that we want
+ to store per source */
+#define MAX_SAMPLES 64
+
+/* This is the assumed worst case bound on an unknown frequency,
+ 2000ppm, which would be pretty bad */
+#define WORST_CASE_FREQ_BOUND (2000.0/1.0e6)
+
+/* Day number of 1 Jan 1970 */
+#define MJD_1970 40587
+
+/* ================================================== */
+/* File to which statistics are logged, NULL if none */
+static FILE *logfile = NULL;
+static char *logfilename = NULL;
+static unsigned long logwrites = 0;
+
+#define STATISTICS_LOG "statistics.log"
+
+/* ================================================== */
+/* This data structure is used to hold the history of data from the
+ source */
+
+struct SST_Stats_Record {
+
+ /* Reference ID of source, used for logging to statistics log */
+ unsigned long refid;
+
+ /* Number of samples currently stored. sample[n_samples-1] is the
+ newest. The samples are expected to be sorted in order, but that
+ probably doesn't matter. */
+ int n_samples;
+
+ /* The index in the registers of the best individual sample that we
+ are holding, in terms of the minimum root distance at the present
+ time */
+ int best_single_sample;
+
+ /* This is the estimated offset (+ve => local fast) at a particular time */
+ double estimated_offset;
+ double estimated_offset_sd;
+ struct timeval offset_time;
+
+ /* Number of runs of the same sign amongst the residuals */
+ int nruns;
+
+ /* This value contains the estimated frequency. This is the number
+ of seconds that the local clock gains relative to the reference
+ source per unit local time. (Positive => local clock fast,
+ negative => local clock slow) */
+ double estimated_frequency;
+
+ /* This is the assumed worst case bounds on the estimated frequency.
+ We assume that the true frequency lies within +/- half this much
+ about estimated_frequency */
+ double skew;
+
+ /* This is the direction the skew went in at the last sample */
+ SST_Skew_Direction skew_dirn;
+
+ /* This is the estimated residual variance of the data points */
+ double variance;
+
+ /* This array contains the sample epochs, in terms of the local
+ clock. */
+ struct timeval sample_times[MAX_SAMPLES];
+
+ /* This is an array of offsets, in seconds, corresponding to the
+ sample times. In this module, we use the convention that
+ positive means the local clock is FAST of the source and negative
+ means it is SLOW. This is contrary to the convention in the NTP
+ stuff; that part of the code is written to correspond with
+ RFC1305 conventions. */
+ double offsets[MAX_SAMPLES];
+
+ /* This is an array of the offsets as originally measured. Local
+ clock fast of real time is indicated by positive values. This
+ array is not slewed to adjust the readings when we apply
+ adjustments to the local clock, as is done for the array
+ 'offset'. */
+ double orig_offsets[MAX_SAMPLES];
+
+ /* This is an array of peer delays, in seconds, being the roundtrip
+ measurement delay to the peer */
+ double peer_delays[MAX_SAMPLES];
+
+ /* This is an array of peer dispersions, being the skew and local
+ precision dispersion terms from sampling the peer */
+ double peer_dispersions[MAX_SAMPLES];
+
+ /* This array contains the root delays of each sample, in seconds */
+ double root_delays[MAX_SAMPLES];
+
+ /* This array contains the root dispersions of each sample at the
+ time of the measurements */
+ double root_dispersions[MAX_SAMPLES];
+
+ /* This array contains the weights to be used in the regression
+ analysis for each of the samples. */
+ double weights[MAX_SAMPLES];
+
+ /* This array contains the strata that were associated with the sources
+ at the times the samples were generated */
+ int strata[MAX_SAMPLES];
+
+};
+
+/* ================================================== */
+
+void
+SST_Initialise(void)
+{
+ char *direc;
+
+ if (CNF_GetLogStatistics()) {
+ direc = CNF_GetLogDir();
+ if (!mkdir_and_parents(direc)) {
+ LOG(LOGS_ERR, LOGF_SourceStats, "Could not create directory %s", direc);
+ logfile = NULL;
+ } else {
+ logfilename = MallocArray(char, 2 + strlen(direc) + strlen(STATISTICS_LOG));
+ strcpy(logfilename, direc);
+ strcat(logfilename, "/");
+ strcat(logfilename, STATISTICS_LOG);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_SourceStats, "Couldn't open logfile %s for update", logfilename);
+ }
+ }
+ }
+}
+
+/* ================================================== */
+
+void
+SST_Finalise(void)
+{
+ if (logfile) {
+ fclose(logfile);
+ }
+}
+
+/* ================================================== */
+/* This function creates a new instance of the statistics handler */
+
+SST_Stats
+SST_CreateInstance(unsigned long refid)
+{
+ SST_Stats inst;
+ inst = MallocNew(struct SST_Stats_Record);
+ inst->refid = refid;
+ inst->n_samples = 0;
+ inst->estimated_frequency = 0;
+ inst->skew = 2000.0e-6;
+ inst->skew_dirn = SST_Skew_Nochange;
+ inst->estimated_offset = 0.0;
+ inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
+ inst->variance = 16.0;
+ inst->nruns = 0;
+ return inst;
+}
+
+/* ================================================== */
+/* This function deletes an instance of the statistics handler. */
+
+void
+SST_DeleteInstance(SST_Stats inst)
+{
+ Free(inst);
+ return;
+}
+
+/* ================================================== */
+
+static void
+move_stats_entry(SST_Stats inst, int src, int dest)
+{
+ inst->sample_times[dest] = inst->sample_times[src];
+ inst->offsets[dest] = inst->offsets[src];
+ inst->orig_offsets[dest] = inst->orig_offsets[src];
+ inst->peer_delays[dest] = inst->peer_delays[src];
+ inst->peer_dispersions[dest] = inst->peer_dispersions[src];
+ inst->root_delays[dest] = inst->root_delays[src];
+ inst->root_dispersions[dest] = inst->root_dispersions[src];
+ inst->weights[dest] = inst->weights[src];
+ inst->strata[dest] = inst->strata[src];
+}
+
+/* ================================================== */
+/* This function is called to prune the register down when it is full.
+ For now, just discard the oldest sample. */
+
+static void
+prune_register(SST_Stats inst, int new_oldest, int *bad_points)
+{
+ int i, j;
+
+ if (!(new_oldest < inst->n_samples)) {
+ CROAK("new_oldest should be < n_samples");
+ }
+
+ for (i=0, j=new_oldest; j<inst->n_samples; j++) {
+ if (!bad_points || !bad_points[j]) {
+ if (j != i) {
+ move_stats_entry(inst, j, i);
+ }
+ i++;
+ }
+ }
+ inst->n_samples = i;
+
+}
+
+/* ================================================== */
+
+void
+SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
+ double offset,
+ double peer_delay, double peer_dispersion,
+ double root_delay, double root_dispersion,
+ int stratum)
+{
+ int n;
+#if 0
+ double root_distance;
+#endif
+
+ if (inst->n_samples == MAX_SAMPLES) {
+ prune_register(inst, 1, NULL);
+ }
+
+ n = inst->n_samples;
+
+ inst->sample_times[n] = *sample_time;
+ inst->offsets[n] = offset;
+ inst->orig_offsets[n] = offset;
+ inst->peer_delays[n] = peer_delay;
+ inst->peer_dispersions[n] = peer_dispersion;
+ inst->root_delays[n] = root_delay;
+ inst->root_dispersions[n] = root_dispersion;
+
+#if 0
+ /* The weight is worked out when we run the regression algorithm */
+ root_distance = root_dispersion + 0.5 * fabs(root_delay);
+
+ /* For now, this is the formula for the weight functions */
+ inst->weights[n] = root_distance * root_distance;
+#endif
+
+ inst->strata[n] = stratum;
+
+ ++inst->n_samples;
+
+}
+
+/* ================================================== */
+/* This function is used by both the regression routines to find the
+ time interval between each historical sample and the most recent
+ one */
+
+static void
+convert_to_intervals(SST_Stats inst, double *times_back)
+{
+ struct timeval *newest_tv;
+ int i;
+
+ newest_tv = &(inst->sample_times[inst->n_samples - 1]);
+ for (i=0; i<inst->n_samples; i++) {
+ /* The entries in times_back[] should end up negative */
+ UTI_DiffTimevalsToDouble(&(times_back[i]), &(inst->sample_times[i]), newest_tv);
+ }
+}
+
+/* ================================================== */
+
+static void
+find_best_sample_index(SST_Stats inst, double *times_back)
+{
+ /* With the value of skew that has been computed, see which of the
+ samples offers the tightest bound on root distance */
+
+ double root_distance, best_root_distance;
+ double elapsed;
+ int i, n, best_index;
+
+ n = inst->n_samples - 1;
+ best_root_distance = inst->root_dispersions[n] + 0.5 * fabs(inst->root_delays[n]);
+ best_index = n;
+#if 0
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d brd=%f", n, best_root_distance);
+#endif
+ for (i=0; i<n; i++) {
+ elapsed = -times_back[i];
+#if 0
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d i=%d latest=[%s] doing=[%s] elapsed=%f", n, i,
+ UTI_TimevalToString(&(inst->sample_times[n])),
+ UTI_TimevalToString(&(inst->sample_times[i])),
+ elapsed);
+#endif
+
+ /* Because the loop does not consider the most recent sample, this assertion must hold */
+ if (elapsed <= 0.0) {
+ LOG(LOGS_ERR, LOGF_SourceStats, "Elapsed<0! n=%d i=%d latest=[%s] doing=[%s] elapsed=%f",
+ n, i,
+ UTI_TimevalToString(&(inst->sample_times[n])),
+ UTI_TimevalToString(&(inst->sample_times[i])),
+ elapsed);
+
+ elapsed = fabs(elapsed);
+ }
+
+ root_distance = inst->root_dispersions[i] + elapsed * inst->skew + 0.5 * fabs(inst->root_delays[i]);
+ if (root_distance < best_root_distance) {
+ best_root_distance = root_distance;
+ best_index = i;
+ }
+ }
+
+ inst->best_single_sample = best_index;
+
+#if 0
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d best_index=%d", n, best_index);
+#endif
+
+ return;
+}
+
+/* ================================================== */
+
+/* This defines the assumed ratio between the standard deviation of
+ the samples and the peer distance as measured from the round trip
+ time. E.g. a value of 4 means that we think the standard deviation
+ is a quarter of the peer distance */
+
+#define SD_TO_DIST_RATIO 8.0
+
+/* ================================================== */
+/* This function runs the linear regression operation on the data. It
+ finds the set of most recent samples that give the tightest
+ confidence interval for the frequency, and truncates the register
+ down to that number of samples */
+
+void
+SST_DoNewRegression(SST_Stats inst)
+{
+ double times_back[MAX_SAMPLES];
+ double peer_distances[MAX_SAMPLES];
+
+ int bad_points[MAX_SAMPLES];
+ int degrees_of_freedom;
+ int best_start;
+ double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd;
+ int i, nruns;
+ double min_distance;
+ double sd_weight;
+ double old_skew, old_freq, stress;
+
+ int regression_ok;
+
+ convert_to_intervals(inst, times_back);
+
+ if (inst->n_samples > 0) {
+ for (i=0; i<inst->n_samples; i++) {
+ peer_distances[i] = 0.5 * fabs(inst->peer_delays[i]) + inst->peer_dispersions[i];
+ }
+
+ min_distance = peer_distances[0];
+ for (i=1; i<inst->n_samples; i++) {
+ if (peer_distances[i] < min_distance) {
+ min_distance = peer_distances[i];
+ }
+ }
+
+ /* And now, work out the weight vector */
+
+ for (i=0; i<inst->n_samples; i++) {
+ sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / min_distance;
+ inst->weights[i] = sd_weight * sd_weight;
+ }
+ }
+
+ regression_ok = RGR_FindBestRegression(times_back, inst->offsets, inst->weights,
+ inst->n_samples,
+ &est_intercept, &est_slope, &est_var,
+ &est_intercept_sd, &est_slope_sd,
+ &best_start, &nruns, °rees_of_freedom);
+
+ /* This is a legacy of when the regression routine found outliers
+ for us. We don't use it anymore. */
+ memset((void *) bad_points, 0, MAX_SAMPLES * sizeof(int));
+
+ if (regression_ok) {
+
+ old_skew = inst->skew;
+ old_freq = inst->estimated_frequency;
+
+ inst->estimated_frequency = est_slope;
+ inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
+ inst->estimated_offset = est_intercept;
+ inst->offset_time = inst->sample_times[inst->n_samples - 1];
+ inst->estimated_offset_sd = est_intercept_sd;
+ inst->variance = est_var;
+ inst->nruns = nruns;
+
+ stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
+
+ if (best_start > 0) {
+ /* If we are throwing old data away, retain the current
+ assumptions about the skew */
+ inst->skew_dirn = SST_Skew_Nochange;
+ } else {
+ if (inst->skew < old_skew) {
+ inst->skew_dirn = SST_Skew_Decrease;
+ } else {
+ inst->skew_dirn = SST_Skew_Increase;
+ }
+ }
+
+ if (logfile) {
+
+ if (((logwrites++) % 32) == 0) {
+ fprintf(logfile,
+ "==============================================================================================================\n"
+ " Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr\n"
+ "==============================================================================================================\n");
+ }
+
+
+ fprintf(logfile, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d\n",
+ UTI_TimeToLogForm(inst->offset_time.tv_sec),
+ UTI_IPToDottedQuad(inst->refid),
+ sqrt(inst->variance),
+ inst->estimated_offset,
+ inst->estimated_offset_sd,
+ inst->estimated_frequency,
+ inst->skew,
+ stress,
+ inst->n_samples,
+ best_start, nruns);
+
+ fflush(logfile);
+ }
+
+ prune_register(inst, best_start, bad_points);
+
+ } else {
+#if 0
+ LOG(LOGS_INFO, LOGF_SourceStats, "too few points (%d) for regression", inst->n_samples);
+#endif
+ inst->estimated_frequency = 0.0;
+ inst->skew = WORST_CASE_FREQ_BOUND;
+ }
+
+ find_best_sample_index(inst, times_back);
+
+}
+
+/* ================================================== */
+/* This function does a simple regression on what is in the register,
+ without trying to optimise the error bounds on the frequency by
+ deleting old samples */
+
+void
+SST_DoUpdateRegression(SST_Stats inst)
+{
+ double times_back[MAX_SAMPLES];
+ double freq_error_bound;
+ double est_intercept, est_slope, est_var_base, est_intercept_sd, est_slope_sd;
+
+ convert_to_intervals(inst, times_back);
+
+ if (inst->n_samples >= 3) { /* Otherwise, we're wasting our time - we
+ can't do a useful linear regression
+ with less than 3 points */
+
+ RGR_WeightedRegression(times_back, inst->offsets, inst->weights,
+ inst->n_samples,
+ &est_intercept, &est_slope, &est_var_base,
+ &est_intercept_sd, &est_slope_sd);
+
+ freq_error_bound = est_slope_sd * RGR_GetTCoef(inst->n_samples - 2);
+
+ inst->estimated_frequency = est_slope;
+ inst->skew = freq_error_bound;
+
+ } else {
+ inst->estimated_frequency = 0.0;
+ inst->skew = WORST_CASE_FREQ_BOUND;
+ }
+
+ find_best_sample_index(inst, times_back);
+
+}
+
+/* ================================================== */
+
+void
+SST_GetReferenceData(SST_Stats inst, struct timeval *now,
+ int *stratum, double *offset,
+ double *root_delay, double *root_dispersion,
+ double *frequency, double *skew)
+{
+
+ double elapsed;
+ int n;
+
+ *frequency = inst->estimated_frequency;
+ *skew = inst->skew;
+
+ n = inst->best_single_sample;
+
+ UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->sample_times[n]));
+ *root_delay = inst->root_delays[n];
+ *root_dispersion = inst->root_dispersions[n] + elapsed * inst->skew;
+ *offset = inst->offsets[n] + elapsed * inst->estimated_frequency;
+ *stratum = inst->strata[n];
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d freq=%f skew=%f del=%f disp=%f ofs=%f str=%d",
+ n, *frequency, *skew, *root_delay, *root_dispersion, *offset, *stratum);
+#endif
+
+ return;
+}
+
+/* ================================================== */
+/* Return the assumed worst case range of values that this source's
+ frequency lies within. Frequency is defined as the amount of time
+ the local clock gains relative to the source per unit local clock
+ time. */
+void
+SST_GetFrequencyRange(SST_Stats inst,
+ double *lo, double *hi)
+{
+ double freq, skew;
+ freq = inst->estimated_frequency;
+ skew = inst->skew;
+ *lo = freq - skew;
+ *hi = freq + skew;
+}
+
+/* ================================================== */
+
+/* ================================================== */
+
+void
+SST_GetSelectionData(SST_Stats inst, struct timeval *now,
+ int *stratum,
+ double *best_offset, double *best_root_delay,
+ double *best_root_dispersion,
+ double *variance, int *average_ok)
+{
+ double average_offset;
+ double sample_elapsed;
+ double elapsed;
+ int n;
+ double peer_distance;
+
+ n = inst->best_single_sample;
+ *stratum = inst->strata[n];
+ *variance = inst->variance;
+
+ peer_distance = inst->peer_dispersions[n] + 0.5 * fabs(inst->peer_delays[n]);
+ UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->offset_time));
+
+ UTI_DiffTimevalsToDouble(&sample_elapsed, now, &(inst->sample_times[n]));
+ *best_offset = inst->offsets[n] + sample_elapsed * inst->estimated_frequency;
+ *best_root_delay = inst->root_delays[n];
+ *best_root_dispersion = inst->root_dispersions[n] + sample_elapsed * inst->skew;
+
+ average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
+ if (fabs(average_offset - *best_offset) <= peer_distance) {
+ *average_ok = 1;
+ } else {
+ *average_ok = 0;
+ }
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d off=%f del=%f dis=%f var=%f pdist=%f avoff=%f avok=%d",
+ n, *best_offset, *best_root_delay, *best_root_dispersion, *variance,
+ peer_distance, average_offset, *average_ok);
+#endif
+
+ return;
+}
+
+/* ================================================== */
+
+void
+SST_GetTrackingData(SST_Stats inst, struct timeval *now,
+ double *average_offset, double *offset_sd,
+ double *accrued_dispersion,
+ double *frequency, double *skew)
+{
+ int n;
+ double peer_distance;
+ double elapsed_offset, elapsed_sample;
+
+ n = inst->best_single_sample;
+
+ *frequency = inst->estimated_frequency;
+ *skew = inst->skew;
+
+ peer_distance = inst->peer_dispersions[n] + 0.5 * fabs(inst->peer_delays[n]);
+ UTI_DiffTimevalsToDouble(&elapsed_offset, now, &(inst->offset_time));
+ *average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed_offset;
+ *offset_sd = inst->estimated_offset_sd + elapsed_offset * inst->skew;
+
+ UTI_DiffTimevalsToDouble(&elapsed_sample, now, &(inst->sample_times[n]));
+ *accrued_dispersion = inst->skew * elapsed_sample;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) pdist=%f avoff=%f offsd=%f accrdis=%f",
+ n, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, peer_distance, *average_offset, *offset_sd, *accrued_dispersion);
+#endif
+
+}
+
+/* ================================================== */
+
+void
+SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset)
+{
+ int n, i;
+ double elapsed;
+ double delta_time;
+ struct timeval *sample, prev;
+ double prev_offset, prev_freq;
+
+ n = inst->n_samples;
+
+ for (i=0; i<n; i++) {
+ sample = &(inst->sample_times[i]);
+ prev = *sample;
+#if 0
+ UTI_AdjustTimeval(sample, when, sample, dfreq, doffset);
+ /* Can't easily use this because we need to slew offset */
+#endif
+ UTI_DiffTimevalsToDouble(&elapsed, when, sample);
+ delta_time = elapsed * dfreq - doffset;
+ UTI_AddDoubleToTimeval(sample, delta_time, sample);
+ prev_offset = inst->offsets[i];
+ inst->offsets[i] += delta_time;
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_SourceStats, "i=%d old_st=[%s] new_st=[%s] old_off=%f new_off=%f",
+ i, UTI_TimevalToString(&prev), UTI_TimevalToString(sample),
+ prev_offset, inst->offsets[i]);
+#endif
+ }
+
+ /* Do a half-baked update to the regression estimates */
+ UTI_DiffTimevalsToDouble(&elapsed, when, &(inst->offset_time));
+ prev = inst->offset_time;
+ delta_time = elapsed * dfreq - doffset;
+ UTI_AddDoubleToTimeval(&(inst->offset_time), delta_time, &(inst->offset_time));
+ prev_offset = inst->estimated_offset;
+ prev_freq = inst->estimated_frequency;
+ inst->estimated_offset += delta_time;
+ inst->estimated_frequency -= dfreq;
+
+#ifdef TRACEON
+ LOG(LOGS_INFO, LOGF_SourceStats, "old_off_time=[%s] new=[%s] old_off=%f new_off=%f old_freq=%.3fppm new_freq=%.3fppm",
+ UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)),
+ prev_offset, inst->estimated_offset,
+ 1.0e6*prev_freq, 1.0e6*inst->estimated_frequency);
+#endif
+
+ return;
+}
+
+/* ================================================== */
+
+double
+SST_PredictOffset(SST_Stats inst, struct timeval *when)
+{
+ double elapsed;
+
+ if (inst->n_samples < 3) {
+ /* We don't have any useful statistics, and presumably the poll
+ interval is minimal. We can't do any useful prediction other
+ than use the latest sample */
+ return inst->offsets[inst->n_samples - 1];
+ } else {
+ UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time);
+ return inst->estimated_offset + elapsed * inst->estimated_frequency;
+ }
+
+}
+
+/* ================================================== */
+
+double
+SST_MinRoundTripDelay(SST_Stats inst)
+{
+ double min_delay, delay;
+ int i;
+
+ if (inst->n_samples == 0) {
+ return DBL_MAX;
+ } else {
+ min_delay = fabs(inst->peer_delays[0]);
+ for (i=1; i<inst->n_samples; i++) {
+ delay = fabs(inst->peer_delays[i]);
+ if (delay < min_delay) {
+ min_delay = delay;
+ }
+ }
+ return min_delay;
+ }
+}
+
+/* ================================================== */
+/* This is used to save the register to a file, so that we can reload
+ it after restarting the daemon */
+
+void
+SST_SaveToFile(SST_Stats inst, FILE *out)
+{
+ int i;
+
+ fprintf(out, "%d\n", inst->n_samples);
+
+ for(i=0; i<inst->n_samples; i++) {
+
+ fprintf(out, "%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n",
+ (unsigned long) inst->sample_times[i].tv_sec,
+ (unsigned long) inst->sample_times[i].tv_usec,
+ inst->offsets[i],
+ inst->orig_offsets[i],
+ inst->peer_delays[i],
+ inst->peer_dispersions[i],
+ inst->root_delays[i],
+ inst->root_dispersions[i],
+ inst->weights[i],
+ inst->strata[i]);
+
+ }
+}
+
+/* ================================================== */
+/* This is used to reload samples from a file */
+
+int
+SST_LoadFromFile(SST_Stats inst, FILE *in)
+{
+ int i, line_number;
+ char line[1024];
+ unsigned long sec, usec;
+
+ if (fgets(line, sizeof(line), in) &&
+ (sscanf(line, "%d", &inst->n_samples) == 1)) {
+
+ line_number = 2;
+
+ for (i=0; i<inst->n_samples; i++) {
+ if (!fgets(line, sizeof(line), in) ||
+ (sscanf(line, "%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n",
+ &(sec), &(usec),
+ &(inst->offsets[i]),
+ &(inst->orig_offsets[i]),
+ &(inst->peer_delays[i]),
+ &(inst->peer_dispersions[i]),
+ &(inst->root_delays[i]),
+ &(inst->root_dispersions[i]),
+ &(inst->weights[i]),
+ &(inst->strata[i])) != 10)) {
+
+ /* This is the branch taken if the read FAILED */
+
+ LOG(LOGS_WARN, LOGF_SourceStats, "Failed to read data from line %d of dump file", line_number);
+ inst->n_samples = 0; /* Load abandoned if any sign of corruption */
+ return 0;
+ } else {
+
+ /* This is the branch taken if the read is SUCCESSFUL */
+ inst->sample_times[i].tv_sec = sec;
+ inst->sample_times[i].tv_usec = usec;
+
+ line_number++;
+ }
+ }
+ } else {
+ LOG(LOGS_WARN, LOGF_SourceStats, "Could not read number of samples from dump file");
+ inst->n_samples = 0; /* Load abandoned if any sign of corruption */
+ return 0;
+ }
+
+ return 1;
+
+}
+
+/* ================================================== */
+
+void
+SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now)
+{
+ int n, nb;
+ double est_offset, est_err, elapsed, sample_elapsed;
+ struct timeval ago;
+
+ if (inst->n_samples > 0) {
+ n = inst->n_samples - 1;
+ report->orig_latest_meas = (long)(0.5 + 1.0e6 * inst->orig_offsets[n]);
+ report->latest_meas = (long)(0.5 + 1.0e6 * inst->offsets[n]);
+ report->latest_meas_err = (unsigned long)(0.5 + 1.0e6 * (0.5*inst->root_delays[n] + inst->root_dispersions[n]));
+ report->stratum = inst->strata[n];
+
+ UTI_DiffTimevals(&ago, now, &inst->sample_times[n]);
+ report->latest_meas_ago = ago.tv_sec;
+
+ if (inst->n_samples > 3) {
+ UTI_DiffTimevalsToDouble(&elapsed, now, &inst->offset_time);
+ nb = inst->best_single_sample;
+ UTI_DiffTimevalsToDouble(&sample_elapsed, now, &(inst->sample_times[nb]));
+ est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency;
+ est_err = (inst->estimated_offset_sd +
+ sample_elapsed * inst->skew +
+ (0.5*inst->root_delays[nb] + inst->root_dispersions[nb]));
+ report->est_offset = (long)(0.5 + 1.0e6 * est_offset);
+ report->est_offset_err = (unsigned long) (0.5 + 1.0e6 * est_err);
+ report->resid_freq = (long) (0.5 * 1.0e9 * inst->estimated_frequency);
+ report->resid_skew = (unsigned long) (0.5 + 1.0e9 * inst->skew);
+ } else {
+ report->est_offset = report->latest_meas;
+ report->est_offset_err = report->latest_meas_err;
+ report->resid_freq = 0;
+ report->resid_skew = 0;
+ }
+ } else {
+ report->orig_latest_meas = 0;
+ report->latest_meas = 0;
+ report->latest_meas_err = 0;
+ report->stratum = 0;
+ report->est_offset = 0;
+ report->est_offset_err = 0;
+ report->resid_freq = 0;
+ report->resid_skew = 0;
+ }
+}
+
+
+/* ================================================== */
+
+SST_Skew_Direction SST_LastSkewChange(SST_Stats inst)
+{
+ return inst->skew_dirn;
+}
+
+/* ================================================== */
+
+void
+SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report)
+{
+ double dspan;
+ int n;
+
+ report->n_samples = inst->n_samples;
+ report->n_runs = inst->nruns;
+
+ if (inst->n_samples > 1) {
+ n = inst->n_samples - 1;
+ UTI_DiffTimevalsToDouble(&dspan, &inst->sample_times[n], &inst->sample_times[0]);
+ report->span_seconds = (unsigned long) (dspan + 0.5);
+ } else {
+ report->span_seconds = 0;
+ }
+
+ report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency;
+ report->skew_ppm = 1.0e6 * inst->skew;
+ report->sd_us = 1.0e6 * sqrt(inst->variance);
+}
+
+/* ================================================== */
+
+void
+SST_CycleLogFile(void)
+{
+ if (logfile && logfilename) {
+ fclose(logfile);
+ logfile = fopen(logfilename, "a");
+ if (!logfile) {
+ LOG(LOGS_WARN, LOGF_SourceStats, "Could not reopen logfile %s", logfilename);
+ }
+ logwrites = 0;
+ }
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sourcestats.h,v 1.13 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for module that deals with the measurements and statistics of
+ each of the sources. */
+
+#ifndef GOT_SOURCESTATS_H
+#define GOT_SOURCESTATS_H
+
+#include "sysincl.h"
+
+#include "reports.h"
+
+typedef struct SST_Stats_Record *SST_Stats;
+
+/* Init and fini functions */
+extern void SST_Initialise(void);
+extern void SST_Finalise(void);
+
+/* This function creates a new instance of the statistics handler */
+extern SST_Stats SST_CreateInstance(unsigned long refid);
+
+/* This function deletes an instance of the statistics handler. */
+extern void SST_DeleteInstance(SST_Stats inst);
+
+/* This function accumulates a single sample into the statistics handler
+
+ sample_time is the epoch at which the sample is to be considered to
+ have been made.
+
+ offset is the offset of the local clock relative to the source in
+ seconds. Positive indicates that the local clock if FAST (contrary
+ to the NTP parts of the software)
+
+ root_distance is the Lambda+Delta/2 term in RFC1305, but excluding
+ the extra dispersion due to the residual standard deviation after
+ we have done the regression fit.
+
+ stratum is the stratum of the source from which the sample came.
+ */
+
+extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
+
+/* This function runs the linear regression operation on the data. It
+ finds the set of most recent samples that give the tightest
+ confidence interval for the frequency, and truncates the register
+ down to that number of samples. */
+extern void SST_DoNewRegression(SST_Stats inst);
+
+/* This function does a simple regression on what is in the register,
+ without trying to optimise the error bounds on the frequency by
+ deleting old samples */
+extern void SST_DoUpdateRegression(SST_Stats inst);
+
+/* Return the assumed worst case range of values that this source's
+ frequency lies within. Frequency is defined as the amount of time
+ the local clock gains relative to the source per unit local clock
+ time. */
+extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
+
+/* Get data needed for selection */
+extern void
+SST_GetSelectionData(SST_Stats inst, struct timeval *now,
+ int *stratum,
+ double *best_offset, double *best_root_delay,
+ double *best_root_dispersion,
+ double *variance,
+ int *average_ok);
+
+/* Get data needed when setting up tracking on this source */
+extern void
+SST_GetTrackingData(SST_Stats inst, struct timeval *now,
+ double *average_offset, double *offset_sd,
+ double *accrued_dispersion,
+ double *frequency, double *skew);
+
+/* Get parameters for using this source as the reference */
+extern void
+SST_GetReferenceData(SST_Stats inst, struct timeval *now,
+ int *stratum, double *offset,
+ double *root_delay, double *root_dispersion,
+ double *frequency, double *skew);
+
+
+
+/* This routine is called when the local machine clock parameters are
+ changed. It adjusts all existing samples that we are holding for
+ each peer so that it looks like they were made under the new clock
+ regime rather than the old one.
+
+ when = cooked local time when the change occurs
+
+ dfreq = delta frequency. positive means the clock has been adjusted
+ because it was previously gaining time relative to the external
+ reference(s).
+
+ doffset = offset slewed onto local clock. positive => local clock
+ has been made fast by that amount.
+
+*/
+
+extern void SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset);
+
+
+/* Predict the offset of the local clock relative to a given source at
+ a given local cooked time. Positive indicates local clock is FAST
+ relative to reference. */
+extern double SST_PredictOffset(SST_Stats inst, struct timeval *when);
+
+/* Find the minimum round trip delay in the register */
+extern double SST_MinRoundTripDelay(SST_Stats inst);
+
+extern void SST_SaveToFile(SST_Stats inst, FILE *out);
+
+extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
+
+extern void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now);
+
+extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report);
+
+typedef enum {
+ SST_Skew_Decrease,
+ SST_Skew_Nochange,
+ SST_Skew_Increase
+} SST_Skew_Direction;
+
+extern SST_Skew_Direction SST_LastSkewChange(SST_Stats inst);
+
+extern void SST_CycleLogFile(void);
+
+#endif /* GOT_SOURCESTATS_H */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/srcparams.h,v 1.10 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file defining parameters that can be set on a per source basis
+ */
+
+#ifndef GOT_SRCPARAMS_H
+#define GOT_SRCPARAMS_H
+
+typedef struct {
+ int minpoll;
+ int maxpoll;
+ int online;
+ int auto_offline;
+ int presend_minpoll;
+ unsigned long authkey;
+ double max_delay;
+ double max_delay_ratio;
+} SourceParameters;
+
+#define INACTIVE_AUTHKEY 0UL
+
+#endif /* GOT_SRCPARAMS_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/strerror.c,v 1.8 2002/02/28 23:27:14 richard Exp $
+
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Replacement strerror function for systems that don't have it
+ */
+
+#ifdef SUNOS
+
+#include <errno.h>
+extern char *sys_errlist[];
+
+char *strerror(int n) {
+ return sys_errlist[n];
+}
+
+#endif /* SUNOS */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys.c,v 1.11 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This file contains all the conditionally compiled bits that pull
+ in the various operating-system specific modules
+ */
+
+#include "sys.h"
+
+#if defined (LINUX)
+#include "sys_linux.h"
+#endif
+
+#if defined (SOLARIS)
+#include "sys_solaris.h"
+#endif
+
+#if defined (SUNOS)
+#include "sys_sunos.h"
+#endif
+
+#if defined (__NetBSD__)
+#include "sys_netbsd.h"
+#endif
+
+/* ================================================== */
+
+void
+SYS_Initialise(void)
+{
+
+#if defined(LINUX)
+ SYS_Linux_Initialise();
+#endif
+
+#if defined(SOLARIS)
+ SYS_Solaris_Initialise();
+#endif
+
+#if defined(SUNOS)
+ SYS_SunOS_Initialise();
+#endif
+
+#if defined(__NetBSD__)
+ SYS_NetBSD_Initialise();
+#endif
+
+}
+
+/* ================================================== */
+
+void
+SYS_Finalise(void)
+{
+
+#if defined(LINUX)
+ SYS_Linux_Finalise();
+#endif
+
+#if defined(SOLARIS)
+ SYS_Solaris_Finalise();
+#endif
+
+#if defined(SUNOS)
+ SYS_SunOS_Finalise();
+#endif
+
+#if defined(__NetBSD__)
+ SYS_NetBSD_Finalise();
+#endif
+
+ return;
+}
+
+/* ================================================== */
+/* ================================================== */
+/* ================================================== */
+
+
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys.h,v 1.7 2002/02/28 23:27:14 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the header for the file that links in the operating system-
+ specific parts of the software
+
+*/
+
+#ifndef GOT_SYS_H
+#define GOT_SYS_H
+
+/* Called at the start of the run to do initialisation */
+extern void SYS_Initialise(void);
+
+/* Called at the end of the run to do final clean-up */
+extern void SYS_Finalise(void);
+
+#endif /* GOT_SYS_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_linux.c,v 1.41 2003/07/01 20:56:23 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is the module specific to the Linux operating system.
+
+ */
+
+#ifdef LINUX
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/utsname.h>
+
+#include "localp.h"
+#include "sys_linux.h"
+#include "sched.h"
+#include "util.h"
+#include "conf.h"
+#include "logging.h"
+#include "wrap_adjtimex.h"
+
+static long current_tick;
+
+/* This is the value of tick, in seconds, including the current vernier
+ frequency term */
+static double current_total_tick;
+
+/* This is the uncompensated system tick value */
+static int nominal_tick;
+
+/* This is the scaling required to go between absolute ppm and the
+ scaled ppm used as an argument to adjtimex. Because chronyd is to an extent
+ 'closed loop' maybe it doesn't matter if this is wrongly determined, UNLESS
+ the system's ppm error is close to a multiple of HZ, in which case the
+ relationship between changing the frequency and changing the value of 'tick'
+ will be wrong. This would (I imagine) cause the system to thrash between
+ two states.
+
+ However..., if this effect was not corrected, and the system is left offline
+ for a long period, a substantial error would build up. e.g. with HZ==100,
+ the correction required is 128/128.125, giving a drift of about 84 seconds
+ per day). */
+static double freq_scale;
+
+/* The HZ value from the kernel header file (may be over-ridden from config
+ file, e.g. if chronyd binary is moved to a box whose kernel was built with a
+ different HZ value). */
+static int hz;
+static double dhz; /* And dbl prec version of same for arithmetic */
+
+
+/* ================================================== */
+
+/* The operating system kernel version */
+static int version_major;
+static int version_minor;
+static int version_patchlevel;
+
+/* Flag indicating whether adjtimex() with txc.modes equal to zero
+returns the remaining time adjustment or not. If not we have to read
+the outstanding adjustment by setting it to zero, examining the return
+value and setting the outstanding adjustment back again. */
+
+static int have_readonly_adjtime;
+
+/* ================================================== */
+
+static void handle_end_of_slew(void *anything);
+
+/* ================================================== */
+
+inline static int
+round(double x) {
+ int y;
+ y = (int)(x + 0.5);
+ while ((double)y < x - 0.5) y++;
+ while ((double)y > x + 0.5) y--;
+ return y;
+}
+
+/* ================================================== */
+/* Amount of outstanding offset to process */
+static double offset_register;
+
+/* Flag set true if a fast slew (one done by altering tick) is being
+ run at the moment */
+static int fast_slewing;
+
+/* The amount by which the fast slew was supposed to slew the clock */
+static double fast_slew_wanted;
+
+/* The value programmed into the kernel's 'tick' variable whilst
+ slewing a large offset */
+static long slewing_tick;
+
+/* The timeval (raw) at which a fast slew was started. We need to
+ know this for two reasons. First, if we want to change the
+ frequency midway through, we will want to abort the slew and return
+ the unprocessed portion to the offset register to start again
+ later. Second, when the end of the slew expires, we need to know
+ precisely how long we have been slewing for, so that we can negate
+ the excess and slew it back the other way. */
+static struct timeval slew_start_tv;
+
+/* This is the ID returned to use by the scheduler's timeout handler.
+ We need this if we subsequently wish to abort a slew, because we will have to
+ dequeue the timeout */
+static SCH_TimeoutID slew_timeout_id;
+
+/* The adjustment that we apply to 'tick', in seconds, whilst applying
+ a fast slew */
+static double delta_total_tick;
+
+/* Max amount of time that we wish to slew by using adjtime (or its
+ equivalent). If more than this is outstanding, we alter the value
+ of tick instead, for a set period. Set this according to the
+ amount of time that a dial-up clock might need to be shifted
+ assuming it is resync'ed about once per day. (TBC) */
+#define MAX_ADJUST_WITH_ADJTIME (0.2)
+
+/* The amount by which we alter 'tick' when doing a large slew */
+static int slew_delta_tick;
+
+/* The maximum amount by which 'tick' can be biased away from 'nominal_tick'
+ (sys_adjtimex() in the kernel bounds this to 10%) */
+static int max_tick_bias;
+
+/* ================================================== */
+/* This routine stops a fast slew, determines how long the slew has
+ been running for, and consequently how much adjustment has actually
+ been applied. It can be used both when a slew finishes naturally
+ due to a timeout, and when a slew is being aborted. */
+
+static void
+stop_fast_slew(void)
+{
+ struct timeval T1, T1d, T1a;
+ struct timezone tz;
+ double end_window;
+ double fast_slew_done;
+ double slew_duration;
+ double introduced_dispersion;
+
+ /* Should never get here unless this is true */
+ if (!fast_slewing) {
+ CROAK("Should be fast slewing");
+ }
+
+ /* Now set the thing off */
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in stop_fast_slew");
+ }
+
+ if (TMX_SetTick(current_tick) < 0) {
+ CROAK("adjtimex() failed in stop_fast_slew");
+ }
+
+ if (gettimeofday(&T1d, &tz) < 0) {
+ CROAK("gettimeofday() failed in stop_fast_slew");
+ }
+
+ fast_slewing = 0;
+
+ UTI_AverageDiffTimevals(&T1, &T1d, &T1a, &end_window);
+ UTI_DiffTimevalsToDouble(&slew_duration, &T1a, &slew_start_tv);
+
+ /* Compute the dispersion we have introduced by changing tick this
+ way. If the two samples of gettimeofday differ, there is an
+ uncertainty window wrt when the frequency change actually applies
+ from. We handle this by adding dispersion to all statistics held
+ at higher levels in the system. */
+
+ introduced_dispersion = end_window * delta_total_tick;
+ lcl_InvokeDispersionNotifyHandlers(introduced_dispersion);
+
+ fast_slew_done = delta_total_tick * slew_duration /
+ (current_total_tick + delta_total_tick);
+
+ offset_register += (fast_slew_wanted + fast_slew_done);
+
+}
+
+
+/* ================================================== */
+/* This routine is called to start a clock offset adjustment */
+
+static void
+initiate_slew(void)
+{
+ double dseconds;
+ long tick_adjust;
+ long offset;
+ struct timeval T0, T0d, T0a;
+ struct timeval end_of_slew;
+ struct timezone tz;
+ double start_window;
+ double introduced_dispersion;
+
+ /* Don't want to get here if we already have an adjust on the go! */
+ if (fast_slewing) {
+ CROAK("Should not be fast slewing");
+ }
+
+ if (offset_register == 0.0) {
+ return;
+ }
+
+ if (fabs(offset_register) < MAX_ADJUST_WITH_ADJTIME) {
+ /* Use adjtime to do the shift */
+ offset = (long)(0.5 + 1.0e6*(-offset_register));
+
+ if (TMX_ApplyOffset(&offset) < 0) {
+ CROAK("adjtimex() failed in initiate_slew");
+ }
+
+ offset_register = 0.0;
+
+ } else {
+
+ /* If the system clock has a high drift rate, the combination of
+ current_tick + slew_delta_tick could be outside the range that adjtimex
+ will accept. To prevent this, the tick adjustment that is used to slew
+ an error off the clock is clamped according to what tick_adjust is.
+ */
+
+ long min_allowed_tick, max_allowed_tick;
+
+ min_allowed_tick = nominal_tick - max_tick_bias;
+ max_allowed_tick = nominal_tick + max_tick_bias;
+
+ if (offset_register > 0) {
+ slewing_tick = current_tick - slew_delta_tick;
+ if (slewing_tick <= min_allowed_tick) {
+ slewing_tick = min_allowed_tick + 1;
+ }
+ } else {
+ slewing_tick = current_tick + slew_delta_tick;
+ if (slewing_tick >= max_allowed_tick) {
+ slewing_tick = max_allowed_tick - 1;
+ }
+ }
+
+ tick_adjust = slewing_tick - current_tick;
+
+ delta_total_tick = (double) tick_adjust / 1.0e6;
+ dseconds = - offset_register * (current_total_tick + delta_total_tick) / delta_total_tick;
+
+ /* Now set the thing off */
+ if (gettimeofday(&T0, &tz) < 0) {
+ CROAK("gettimeofday() failed in initiate_slew");
+ }
+
+ if (TMX_SetTick(slewing_tick) < 0) {
+ LOG(LOGS_INFO, LOGF_SysLinux, "c_t=%ld ta=%ld sl_t=%ld dtt=%e",
+ current_tick, tick_adjust, slewing_tick, delta_total_tick);
+ CROAK("adjtimex() failed to start big slew");
+ }
+
+ if (gettimeofday(&T0d, &tz) < 0) {
+ CROAK("gettimeofday() failed in initiate_slew");
+ }
+
+ /* Now work out the uncertainty in when we actually started the
+ slew. */
+
+ UTI_AverageDiffTimevals(&T0, &T0d, &T0a, &start_window);
+
+ /* Compute the dispersion we have introduced by changing tick this
+ way. If the two samples of gettimeofday differ, there is an
+ uncertainty window wrt when the frequency change actually applies
+ from. We handle this by adding dispersion to all statistics held
+ at higher levels in the system. */
+
+ introduced_dispersion = start_window * delta_total_tick;
+ lcl_InvokeDispersionNotifyHandlers(introduced_dispersion);
+
+ fast_slewing = 1;
+ slew_start_tv = T0a;
+
+ /* Set up timeout for end of slew */
+ UTI_AddDoubleToTimeval(&T0a, dseconds, &end_of_slew);
+
+ slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
+
+ fast_slew_wanted = offset_register;
+ offset_register = 0.0;
+
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+/* This is the callback routine invoked by the scheduler at the end of
+ a slew. */
+
+static void
+handle_end_of_slew(void *anything)
+{
+ stop_fast_slew();
+ initiate_slew(); /* To do any fine trimming required */
+}
+
+/* ================================================== */
+/* This routine is used to abort a slew that is in progress, if any */
+
+static void
+abort_slew(void)
+{
+ if (fast_slewing) {
+ stop_fast_slew();
+ SCH_RemoveTimeout(slew_timeout_id);
+ }
+}
+
+/* ================================================== */
+/* This routine accrues an offset into the offset register, and starts
+ a slew if required.
+
+ The offset argument is measured in seconds. Positive means the
+ clock needs to be slewed backwards (i.e. is currently fast of true
+ time) */
+
+static void
+accrue_offset(double offset)
+{
+ long toffset;
+
+ /* Add the new offset to the register */
+ offset_register += offset;
+
+ /* Cancel any standard adjtime that is running */
+ toffset = 0;
+ if (TMX_ApplyOffset(&toffset) < 0) {
+ CROAK("adjtimex() failed in accrue_offset");
+ }
+
+ offset_register -= (double) toffset / 1.0e6;
+
+ if (!fast_slewing) {
+ initiate_slew();
+ } /* Otherwise, when the fast slew completes, any other stuff
+ in the offset register will be applied */
+
+}
+
+/* ================================================== */
+/* Positive means currently fast of true time, i.e. jump backwards */
+
+static void
+apply_step_offset(double offset)
+{
+ struct timeval old_time, new_time;
+ struct timezone tz;
+
+ if (fast_slewing) {
+ abort_slew();
+ }
+
+ if (gettimeofday(&old_time, &tz) < 0) {
+ CROAK("gettimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+
+ if (settimeofday(&new_time, &tz) < 0) {
+ CROAK("settimeofday in apply_step_offset");
+ }
+
+ initiate_slew();
+
+}
+
+/* ================================================== */
+/* This call sets the Linux kernel frequency to a given value in parts
+ per million relative to the nominal running frequency. Nominal is taken to
+ be tick=10000, freq=0 (for a HZ==100 system, other values otherwise). The
+ convention is that this is called with a positive argument if the local
+ clock runs fast when uncompensated. */
+
+static void
+set_frequency(double freq_ppm) {
+
+ long required_tick;
+ double required_freq; /* what we use */
+ double scaled_freq; /* what adjtimex & the kernel use */
+ int required_delta_tick;
+ int neg; /* True if estimate is that local clock runs slow,
+ i.e. positive frequency correction required */
+
+
+ /* If we in the middle of slewing the time by having the value of
+ tick altered, we have to stop doing that, because the timeout
+ expiry etc will change if we don't. */
+
+ if (fast_slewing) {
+ abort_slew();
+ }
+
+ if (freq_ppm < 0.0) {
+ neg = 1;
+ freq_ppm = -freq_ppm;
+ } else {
+ neg = 0;
+ }
+
+ required_delta_tick = round(freq_ppm / dhz);
+ required_freq = freq_ppm - dhz * (double) required_delta_tick;
+
+ if (neg) {
+ /* Uncompensated local clock runs slow */
+ required_tick = nominal_tick + required_delta_tick;
+ scaled_freq = freq_scale * required_freq;
+ } else {
+ /* Uncompensated local clock runs fast */
+ required_tick = nominal_tick - required_delta_tick;
+ scaled_freq = -freq_scale * required_freq;
+ }
+
+ if (TMX_SetFrequency(scaled_freq, required_tick) < 0) {
+ char buffer[1024];
+ sprintf(buffer, "adjtimex failed for set_frequency, freq_ppm=%10.4e scaled_freq=%10.4e required_tick=%ld",
+ freq_ppm, scaled_freq, required_tick);
+ CROAK(buffer);
+ }
+
+ current_tick = required_tick;
+ current_total_tick = ((double)current_tick + required_freq/dhz) / 1.0e6 ;
+
+ initiate_slew(); /* Restart any slews that need to be restarted */
+
+ return;
+
+}
+
+/* ================================================== */
+/* Read the ppm frequency from the kernel */
+
+static double
+read_frequency(void)
+{
+ double tick_term;
+ double unscaled_freq;
+ double freq_term;
+
+ if (TMX_GetFrequency(&unscaled_freq) < 0) {
+ CROAK("adjtimex failed in read_frequency");
+ }
+
+ /* Use current_tick here rather than txc.tick, otherwise we're
+ thrown off course when doing a fast slew (in which case, txc.tick
+ is nowhere near the nominal value) */
+ tick_term = dhz * (double)(nominal_tick - current_tick);
+ freq_term = unscaled_freq / freq_scale;
+
+#if 0
+ LOG(LOGS_INFO, LOGF_SysLinux, "txc.tick=%ld txc.freq=%ld tick_term=%f freq_term=%f",
+ txc.tick, txc.freq, tick_term, freq_term);
+#endif
+
+ return tick_term - freq_term;
+
+}
+
+/* ================================================== */
+/* Given a raw time, determine the correction in seconds to generate
+ the 'cooked' time. The correction has to be added to the
+ raw time */
+
+static void
+get_offset_correction(struct timeval *raw,
+ double *corr)
+{
+
+ /* Correction is given by these things :
+ 1. Any value in offset register
+ 2. Amount of fast slew remaining
+ 3. Any amount of adjtime correction remaining */
+
+
+ double adjtime_left;
+ double fast_slew_duration;
+ double fast_slew_achieved;
+ double fast_slew_remaining;
+ long offset;
+
+ if (have_readonly_adjtime) {
+ if (TMX_GetOffsetLeft(&offset) < 0) {
+ CROAK("adjtimex() failed in get_offset_correction");
+ }
+
+ adjtime_left = (double)offset / 1.0e6;
+ } else {
+ offset = 0;
+ if (TMX_ApplyOffset(&offset) < 0) {
+ CROAK("adjtimex() failed in get_offset_correction");
+ }
+
+ adjtime_left = (double)offset / 1.0e6;
+
+ /* txc.offset still set from return value of last call */
+ if (TMX_ApplyOffset(&offset) < 0) {
+ CROAK("adjtimex() failed in get_offset_correction");
+ }
+ }
+
+ if (fast_slewing) {
+ UTI_DiffTimevalsToDouble(&fast_slew_duration, raw, &slew_start_tv);
+ fast_slew_achieved = delta_total_tick * fast_slew_duration /
+ (current_total_tick + delta_total_tick);
+ fast_slew_remaining = fast_slew_wanted + fast_slew_achieved;
+ } else {
+ fast_slew_remaining = 0.0;
+ }
+
+ *corr = - (offset_register + fast_slew_remaining) + adjtime_left;
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+immediate_step(void)
+{
+ struct timeval old_time, new_time;
+ struct timezone tz;
+ long offset;
+
+ if (fast_slewing) {
+ abort_slew();
+ }
+
+ offset = 0;
+ if (TMX_ApplyOffset(&offset) < 0) {
+ CROAK("adjtimex() failed in immediate_step");
+ }
+
+ offset_register -= (double) offset / 1.0e6;
+
+ if (gettimeofday(&old_time, &tz) < 0) {
+ CROAK("gettimeofday() failed in immediate_step");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset_register, &new_time);
+
+ if (settimeofday(&new_time, &tz) < 0) {
+ CROAK("settimeofday() failed in immediate_step");
+ }
+
+ offset_register = 0.0;
+
+ return;
+}
+
+/* ================================================== */
+
+/* Estimate the value of HZ given the value of txc.tick that chronyd finds when
+ * it starts. The only credible values are 100 (Linux/x86) or powers of 2.
+ * Also, the bounds checking inside the kernel's adjtimex system call enforces
+ * a +/- 10% movement of tick away from the nominal value 1e6/HZ. */
+
+static void
+guess_hz_and_shift_hz(int tick, int *hz, int *shift_hz)
+{
+ int i, tick_lo, tick_hi, ihz;
+ double tick_nominal;
+ /* Pick off the hz=100 case first */
+ if (tick >= 9000 && tick <= 11000) {
+ *hz = 100;
+ *shift_hz = 7;
+ return;
+ }
+
+ for (i=4; i<16; i++) { /* surely 16 .. 32768 is a wide enough range? */
+ ihz = 1 << i;
+ tick_nominal = 1.0e6 / (double) ihz;
+ tick_lo = (int)(0.5 + tick_nominal*2.0/3.0);
+ tick_hi = (int)(0.5 + tick_nominal*4.0/3.0);
+
+ if (tick_lo < tick && tick <= tick_hi) {
+ *hz = ihz;
+ *shift_hz = i;
+ return;
+ }
+ }
+
+ /* oh dear. doomed. */
+ *hz = 0;
+ *shift_hz = 0;
+ return;
+}
+
+/* ================================================== */
+/* Compute the scaling to use on any frequency we set, according to
+ the vintage of the Linux kernel being used. */
+
+static void
+get_version_specific_details(void)
+{
+ int major, minor, patch;
+ int shift_hz;
+ double dshift_hz;
+ double basic_freq_scale; /* what to use if HZ!=100 */
+ int config_hz, set_config_hz; /* values of HZ from conf file */
+ int set_config_freq_scale;
+ double config_freq_scale;
+ double calculated_freq_scale;
+ struct tmx_params tmx_params;
+ struct utsname uts;
+
+ TMX_ReadCurrentParams(&tmx_params);
+
+ guess_hz_and_shift_hz(tmx_params.tick, &hz, &shift_hz);
+
+ if (!shift_hz) {
+ LOG_FATAL(LOGF_SysLinux, "Can't determine hz (txc.tick=%ld txc.freq=%ld (%.8f) txc.offset=%ld)",
+ tmx_params.tick, tmx_params.freq, tmx_params.dfreq, tmx_params.offset);
+ } else {
+ LOG(LOGS_INFO, LOGF_SysLinux, "Initial txc.tick=%ld txc.freq=%ld (%.8f) txc.offset=%ld => hz=%d shift_hz=%d",
+ tmx_params.tick, tmx_params.freq, tmx_params.dfreq, tmx_params.offset, hz, shift_hz);
+ }
+
+ CNF_GetLinuxHz(&set_config_hz, &config_hz);
+ if (set_config_hz) hz = config_hz;
+ /* (If true, presumably freq_scale will be overridden anyway, making shift_hz
+ redundant too.) */
+
+ dhz = (double) hz;
+ dshift_hz = (double)(1UL << shift_hz);
+ basic_freq_scale = dshift_hz / dhz;
+ nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
+ slew_delta_tick = nominal_tick / 12;
+ max_tick_bias = nominal_tick / 10;
+
+ /* The basic_freq_scale comes from:
+ * the kernel increments the usec counter HZ times per second (if the timer
+ interrupt period were perfect)
+ * the following code in the kernel
+
+ time_adj (+/-)= ltemp >>
+ (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE);
+
+ causes the adjtimex 'freq' value to be divided down by 1<<SHIFT_HZ.
+
+ The net effect is that we have to scale up the value we want by the
+ reciprocal of all this, i.e. multiply by (1<<SHIFT_HZ)/HZ.
+
+ If HZ==100, this code in the kernel comes into play too:
+#if HZ == 100
+ * Compensate for (HZ==100) != (1 << SHIFT_HZ).
+ * Add 25% and 3.125% to get 128.125; => only 0.125% error (p. 14)
+ *
+ if (time_adj < 0)
+ time_adj -= (-time_adj >> 2) + (-time_adj >> 5);
+ else
+ time_adj += (time_adj >> 2) + (time_adj >> 5);
+#endif
+
+ Special case that later.
+ */
+
+ if (uname(&uts) < 0) {
+ LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry.");
+ }
+ if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) {
+ LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry");
+ }
+
+ LOG(LOGS_INFO, LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
+
+ version_major = major;
+ version_minor = minor;
+ version_patchlevel = patch;
+
+ switch (major) {
+ case 1:
+ /* Does Linux v1.x even support HZ!=100? */
+ switch (minor) {
+ case 2:
+ if (patch == 13) {
+ freq_scale = (hz==100) ? (128.0 / 100.0) : basic_freq_scale ; /* I _think_! */
+ have_readonly_adjtime = 1;
+ } else {
+ LOG_FATAL(LOGF_SysLinux, "Kernel version not supported yet, sorry.");
+ }
+ break;
+ case 3:
+ /* I guess the change from the 1.2.x scaling to the 2.0.x
+ scaling must have happened during 1.3 development. I
+ haven't a clue where though, until someone looks it
+ up. */
+ LOG_FATAL(LOGF_SysLinux, "Kernel version not supported yet, sorry.");
+ break;
+ default:
+ LOG_FATAL(LOGF_SysLinux, "Kernel version not supported yet, sorry.");
+ break;
+ }
+ break;
+ case 2:
+ switch (minor) {
+ case 0:
+ if (patch < 32) {
+ freq_scale = (hz==100) ? (128.0 / 125.0) : basic_freq_scale;
+ have_readonly_adjtime = 1;
+ } else if (patch >= 32) {
+ freq_scale = (hz==100) ? (128.0 / 128.125) : basic_freq_scale;
+
+ /* The functionality in kernel/time.c in the kernel source
+ was modified with regard to what comes back in the
+ txc.offset field on return from adjtimex. If txc.modes
+ was ADJ_OFFSET_SINGLESHOT on entry, the outstanding
+ adjustment is returned, however the running offset will
+ be modified to the passed value. */
+ have_readonly_adjtime = 0;
+ }
+ break;
+ case 1:
+ /* I know that earlier 2.1 kernels were like 2.0.31, hence
+ the settings below. However, the 2.0.32 behaviour may
+ have been added late in the 2.1 series, however I have no
+ idea at which patch level. Leave it like this until
+ someone supplies some info. */
+ freq_scale = (hz==100) ? (128.0 / 125.0) : basic_freq_scale;
+ have_readonly_adjtime = 0; /* For safety ! */
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ /* These seem to be like 2.0.32 */
+ freq_scale = (hz==100) ? (128.0 / 128.125) : basic_freq_scale;
+ have_readonly_adjtime = 0;
+ break;
+ default:
+ LOG_FATAL(LOGF_SysLinux, "Kernel version not supported yet, sorry.");
+ }
+ break;
+ default:
+ LOG_FATAL(LOGF_SysLinux, "Kernel's major version not supported yet, sorry");
+ break;
+ }
+
+ /* Override freq_scale if it appears in conf file */
+ CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale);
+ calculated_freq_scale = freq_scale;
+ if (set_config_freq_scale) freq_scale = config_freq_scale;
+
+ LOG(LOGS_INFO, LOGF_SysLinux, "calculated_freq_scale=%.8f freq_scale=%.8f",
+ calculated_freq_scale, freq_scale);
+
+}
+
+/* ================================================== */
+/* Set denorms to flush to zero instead of trapping. */
+
+#if defined(__SH5__)
+static void enable_flush_denorms(void)
+{
+ float fpscr;
+ unsigned long ifpscr;
+ asm volatile("fgetscr %0" : "=f" (fpscr));
+ asm volatile("fmov.sl %1, %0" : "=r" (ifpscr) : "f" (fpscr));
+ ifpscr |= 0x40000;
+ asm volatile("fmov.ls %1, %0" : "=f" (fpscr) : "r" (ifpscr));
+ asm volatile("fputscr %0" : : "f" (fpscr));
+ return;
+}
+#endif
+
+/* ================================================== */
+/* Initialisation code for this module */
+
+void
+SYS_Linux_Initialise(void)
+{
+ offset_register = 0.0;
+ fast_slewing = 0;
+
+#if defined(__SH5__)
+ enable_flush_denorms();
+#endif
+
+ get_version_specific_details();
+
+ current_tick = nominal_tick;
+ current_total_tick = 1.0 / dhz;
+
+ lcl_RegisterSystemDrivers(read_frequency, set_frequency,
+ accrue_offset, apply_step_offset,
+ get_offset_correction, immediate_step);
+}
+
+/* ================================================== */
+/* Finalisation code for this module */
+
+void
+SYS_Linux_Finalise(void)
+{
+ /* Must *NOT* leave a fast slew running - clock would drift way off
+ if the daemon is not restarted */
+ abort_slew();
+}
+
+/* ================================================== */
+
+void
+SYS_Linux_GetKernelVersion(int *major, int *minor, int *patchlevel)
+{
+ *major = version_major;
+ *minor = version_minor;
+ *patchlevel = version_patchlevel;
+}
+
+/* ================================================== */
+
+#endif /* LINUX */
+
+/* vim:ts=8
+ * */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_linux.h,v 1.8 2002/02/28 23:27:15 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ The header file for the linux driver
+ */
+
+#ifndef GOT_SYS_LINUX_H
+#define GOT_SYS_LINUX_H
+
+extern void SYS_Linux_Initialise(void);
+
+extern void SYS_Linux_Finalise(void);
+
+extern void SYS_Linux_GetKernelVersion(int *major, int *minor, int *patchlevel);
+
+#endif /* GOT_SYS_LINUX_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_netbsd.c,v 1.2 2002/02/17 22:13:49 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2001
+ * Copyright (C) J. Hannken-Illjes 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Driver file for the NetBSD operating system.
+ */
+
+#ifdef __NetBSD__
+
+#include <kvm.h>
+#include <nlist.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "sys_netbsd.h"
+#include "localp.h"
+#include "logging.h"
+#include "util.h"
+
+/* ================================================== */
+
+/* This register contains the number of seconds by which the local
+ clock was estimated to be fast of reference time at the epoch when
+ gettimeofday() returned T0 */
+
+static double offset_register;
+
+/* This register contains the epoch to which the offset is referenced */
+
+static struct timeval T0;
+
+/* This register contains the current estimate of the system
+ frequency, in absolute (NOT ppm) */
+
+static double current_freq;
+
+/* This register contains the number of seconds of adjustment that
+ were passed to adjtime last time it was called. */
+
+static double adjustment_requested;
+
+/* Kernel parameters to calculate adjtime error. */
+
+static int kern_tickadj;
+static long kern_bigadj;
+
+/* ================================================== */
+
+static void
+clock_initialise(void)
+{
+ struct timeval newadj, oldadj;
+ struct timezone tz;
+
+ offset_register = 0.0;
+ adjustment_requested = 0.0;
+ current_freq = 0.0;
+
+ if (gettimeofday(&T0, &tz) < 0) {
+ CROAK("gettimeofday() failed in clock_initialise()");
+ }
+
+ newadj.tv_sec = 0;
+ newadj.tv_usec = 0;
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in clock_initialise");
+ }
+
+}
+
+/* ================================================== */
+
+static void
+clock_finalise(void)
+{
+ /* Nothing to do yet */
+
+}
+
+/* ================================================== */
+
+static void
+start_adjust(void)
+{
+ struct timeval newadj, oldadj;
+ struct timeval T1;
+ struct timezone tz;
+ double elapsed, accrued_error;
+ double adjust_required;
+ struct timeval exact_newadj;
+ long delta, tickdelta;
+ double rounding_error;
+ double old_adjust_remaining;
+
+ /* Determine the amount of error built up since the last adjustment */
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in start_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ accrued_error = elapsed * current_freq;
+
+ adjust_required = - (accrued_error + offset_register);
+
+ UTI_DoubleToTimeval(adjust_required, &exact_newadj);
+
+ /* At this point, we need to round the required adjustment the
+ same way the kernel does. */
+
+ delta = exact_newadj.tv_sec * 1000000 + exact_newadj.tv_usec;
+ if (delta > kern_bigadj || delta < -kern_bigadj)
+ tickdelta = 10 * kern_tickadj;
+ else
+ tickdelta = kern_tickadj;
+ if (delta % tickdelta)
+ delta = delta / tickdelta * tickdelta;
+ newadj.tv_sec = 0;
+ newadj.tv_usec = delta;
+ UTI_NormaliseTimeval(&newadj);
+
+ /* Add rounding error back onto offset register. */
+ UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in start_adjust");
+ }
+
+ UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
+
+ offset_register = rounding_error - old_adjust_remaining;
+
+ T0 = T1;
+ UTI_TimevalToDouble(&newadj, &adjustment_requested);
+
+}
+
+/* ================================================== */
+
+static void
+stop_adjust(void)
+{
+ struct timeval T1;
+ struct timezone tz;
+ struct timeval zeroadj, remadj;
+ double adjustment_remaining, adjustment_achieved;
+ double elapsed, elapsed_plus_adjust;
+
+ zeroadj.tv_sec = 0;
+ zeroadj.tv_usec = 0;
+
+ if (adjtime(&zeroadj, &remadj) < 0) {
+ CROAK("adjtime() failed in stop_adjust");
+ }
+
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in stop_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+
+ adjustment_achieved = adjustment_requested - adjustment_remaining;
+ elapsed_plus_adjust = elapsed - adjustment_achieved;
+
+ offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
+
+ adjustment_requested = 0.0;
+ T0 = T1;
+
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ slew backwards */
+
+static void
+accrue_offset(double offset)
+{
+ stop_adjust();
+ offset_register += offset;
+ start_adjust();
+
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ step backwards */
+
+static void
+apply_step_offset(double offset)
+{
+ struct timeval old_time, new_time, T1;
+ struct timezone tz;
+
+ stop_adjust();
+
+ if (gettimeofday(&old_time, &tz) < 0) {
+ CROAK("gettimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+
+ if (settimeofday(&new_time, &tz) < 0) {
+ CROAK("settimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&T0, offset, &T1);
+ T0 = T1;
+
+ start_adjust();
+
+}
+
+/* ================================================== */
+
+static void
+set_frequency(double new_freq_ppm)
+{
+ stop_adjust();
+ current_freq = new_freq_ppm * 1.0e-6;
+ start_adjust();
+}
+
+/* ================================================== */
+
+static double
+read_frequency(void)
+{
+ return current_freq * 1.0e6;
+}
+
+/* ================================================== */
+
+static void
+get_offset_correction(struct timeval *raw,
+ double *corr)
+{
+ stop_adjust();
+ *corr = -offset_register;
+ start_adjust();
+}
+
+/* ================================================== */
+
+void
+SYS_NetBSD_Initialise(void)
+{
+ static struct nlist nl[] = {
+ {"_tickadj"},
+ {"_bigadj"},
+ {NULL}
+ };
+
+ kvm_t *kt;
+ FILE *fp;
+
+ kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
+ if (!kt) {
+ CROAK("Cannot open kvm\n");
+ }
+
+ if (kvm_nlist(kt, nl) < 0) {
+ CROAK("Cannot read kernel symbols\n");
+ }
+
+ if (kvm_read(kt, nl[0].n_value, (char *)(&kern_tickadj), sizeof(int)) < 0) {
+ CROAK("Cannot read from _tickadj\n");
+ }
+
+ if (kvm_read(kt, nl[1].n_value, (char *)(&kern_bigadj), sizeof(long)) < 0) {
+ CROAK("Cannot read from _bigadj\n");
+ }
+
+ kvm_close(kt);
+
+ clock_initialise();
+
+ lcl_RegisterSystemDrivers(read_frequency, set_frequency,
+ accrue_offset, apply_step_offset,
+ get_offset_correction, NULL /* immediate_step */);
+
+}
+
+/* ================================================== */
+
+void
+SYS_NetBSD_Finalise(void)
+{
+ clock_finalise();
+}
+
+/* ================================================== */
+
+
+#endif /* __NetBSD__ */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_netbsd.h,v 1.2 2002/02/17 22:13:49 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2001
+ * Copyright (C) J. Hannken-Illjes 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for NetBSD driver
+ */
+
+#ifndef GOT_SYS_NETBSD_H
+#define GOT_SYS_NETBSD_H
+
+void SYS_NetBSD_Initialise(void);
+
+void SYS_NetBSD_Finalise(void);
+
+#endif
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_solaris.c,v 1.18 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Driver file for Solaris operating system
+ */
+
+#ifdef SOLARIS
+
+#include <kvm.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#include <stdio.h>
+
+#include "sys_solaris.h"
+#include "localp.h"
+#include "sched.h"
+#include "logging.h"
+#include "util.h"
+
+/* ================================================== */
+
+/* This register contains the number of seconds by which the local
+ clock was estimated to be fast of reference time at the epoch when
+ gettimeofday() returned T0 */
+
+static double offset_register;
+
+/* This register contains the epoch to which the offset is referenced */
+
+static struct timeval T0;
+
+/* This register contains the current estimate of the system
+ frequency, in absolute (NOT ppm) */
+
+static double current_freq;
+
+/* This register contains the number of seconds of adjustment that
+ were passed to adjtime last time it was called. */
+
+static double adjustment_requested;
+
+/* ================================================== */
+/* On Solaris 2.5 & 2.5.1, passing an argument of zero as the new
+ delta to adjtime does not zero out the adjustment - the remaining
+ adjustment is returned as the old delta arg, but the adjustment keeps
+ running. To get round this, we set adjustments of +/-1us when we
+ really want zero. Alternate adjustments are used to avoid a drift
+ from building up. */
+
+static struct timeval zeroes[2] = {
+ {0, 1},
+ {-1, 999999}
+};
+
+static int index=0;
+
+/* If 1, need to run dosynctodr(). If 0, don't */
+static int need_dosynctodr = -1;
+
+
+#define GET_ZERO (zeroes[index^=1])
+
+/* ================================================== */
+
+static void
+clock_initialise(void)
+{
+ struct timeval newadj, oldadj;
+ struct timezone tz;
+
+ offset_register = 0.0;
+ adjustment_requested = 0.0;
+ current_freq = 0.0;
+
+ if (gettimeofday(&T0, &tz) < 0) {
+ CROAK("gettimeofday() failed in clock_initialise()");
+ }
+
+ newadj = GET_ZERO;
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in clock_initialise");
+ }
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in clock_initialise");
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+clock_finalise(void)
+{
+ /* Nothing to do yet */
+
+ return;
+
+}
+
+/* ================================================== */
+
+static void
+start_adjust(void)
+{
+ struct timeval newadj, oldadj;
+ struct timeval T1;
+ struct timezone tz;
+ double elapsed, accrued_error;
+ double adjust_required;
+ struct timeval exact_newadj;
+ double rounding_error;
+ double old_adjust_remaining;
+
+ /* Determine the amount of error built up since the last adjustment */
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in start_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ accrued_error = elapsed * current_freq;
+
+ adjust_required = - (accrued_error + offset_register);
+
+ UTI_DoubleToTimeval(adjust_required, &exact_newadj);
+
+ /* At this point, we will need to call the adjustment rounding
+ algorithm in the system-specific layer. For now, just assume the
+ adjustment can be applied exactly. */
+
+ newadj = exact_newadj;
+
+ /* Want to *add* rounding error back onto offset register */
+ UTI_DiffTimevalsToDouble(&rounding_error, &exact_newadj, &newadj);
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in start_adjust");
+ }
+
+ UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
+
+ offset_register = rounding_error - old_adjust_remaining;
+
+ T0 = T1;
+ UTI_TimevalToDouble(&newadj, &adjustment_requested);
+
+}
+
+/* ================================================== */
+
+static void
+stop_adjust(void)
+{
+ struct timeval T1;
+ struct timezone tz;
+ struct timeval zeroadj, remadj;
+ double adjustment_remaining, adjustment_achieved;
+ double elapsed, elapsed_plus_adjust;
+
+
+ zeroadj = GET_ZERO;
+
+ if (adjtime(&zeroadj, &remadj) < 0) {
+ CROAK("adjtime() failed in stop_adjust");
+ }
+
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in stop_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+
+ adjustment_achieved = adjustment_requested - adjustment_remaining;
+ elapsed_plus_adjust = elapsed - adjustment_achieved;
+
+ offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
+
+ adjustment_requested = 0.0;
+ T0 = T1;
+
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ slew backwards */
+
+static void
+accrue_offset(double offset)
+{
+ stop_adjust();
+ offset_register += offset;
+ start_adjust();
+ return;
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ step backwards */
+
+static void
+apply_step_offset(double offset)
+{
+ struct timeval old_time, new_time, rounded_new_time, T1;
+ double rounding_error;
+ struct timezone tz;
+
+ stop_adjust();
+ if (gettimeofday(&old_time, &tz) < 0) {
+ CROAK("gettimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+
+ /* The settimeofday function (on Solaris 2.5/Sparc20 at least) does
+ not work quite as we would want. The time we want to set is
+ rounded to the nearest second and that time is used. Also, the
+ clock appears to start from that second boundary plus about 4ms.
+ For now we'll tolerate this small error. */
+
+ rounded_new_time.tv_usec = 0;
+ if (new_time.tv_usec >= 500000) {
+ rounded_new_time.tv_sec = new_time.tv_sec + 1;
+ } else {
+ rounded_new_time.tv_sec = new_time.tv_sec;
+ }
+
+ UTI_DiffTimevalsToDouble(&rounding_error, &rounded_new_time, &new_time);
+
+ if (settimeofday(&new_time, &tz) < 0) {
+ CROAK("settimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&T0, offset, &T1);
+ T0 = T1;
+
+ offset_register += rounding_error;
+
+ start_adjust();
+}
+
+/* ================================================== */
+
+static void
+set_frequency(double new_freq_ppm)
+{
+ stop_adjust();
+ current_freq = new_freq_ppm * 1.0e-6;
+ start_adjust();
+}
+
+/* ================================================== */
+
+static double
+read_frequency(void)
+{
+ return current_freq * 1.0e6;
+}
+
+/* ================================================== */
+
+static void
+get_offset_correction(struct timeval *raw,
+ double *corr)
+{
+ stop_adjust();
+ *corr = -offset_register;
+ start_adjust();
+ return;
+}
+
+/* ================================================== */
+
+static void
+immediate_step(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+/* Interval in seconds between adjustments to cancel systematic drift */
+#define DRIFT_REMOVAL_INTERVAL (4.0)
+
+static int drift_removal_running = 0;
+static SCH_TimeoutID drift_removal_id;
+
+/* ================================================== */
+/* This is the timer callback routine which is called periodically to
+ invoke a time adjustment to take out the machine's drift.
+ Otherwise, times reported through this software (e.g. by running
+ ntpdate from another machine) show the machine being correct (since
+ they correct for drift build-up), but any program on this machine
+ that reads the system time will be given an erroneous value, the
+ degree of error depending on how long it is since
+ get_offset_correction was last called. */
+
+static void
+drift_removal_timeout(SCH_ArbitraryArgument not_used)
+{
+ stop_adjust();
+ start_adjust();
+ drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+}
+
+/* ================================================== */
+
+static void
+check_need_dosynctodr(void)
+{
+ struct utsname name;
+ int result;
+ int major, minor, veryminor, n_fields;
+
+ result = uname(&name);
+ if (result < 0) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot use uname to detect Solaris version");
+ need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
+ return;
+ }
+
+ n_fields = sscanf(name.release, "%d.%d.%d\n", &major, &minor, &veryminor);
+
+ if (n_fields < 2) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris version doesn't appear to be of the form X.Y[.Z]");
+ need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
+ return;
+ }
+
+ if (major != 5) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris major version doesn't appear to be 5");
+ need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
+ return;
+ }
+
+ /* The 'rule of thumb' is that from Solaris 2.6 onwards, dosynctodr() doesn't
+ * need to be called, and in fact it is counter-productive to do so. For
+ * earlier versions, it is required. */
+
+ if (minor < 6) {
+ need_dosynctodr = 1;
+ } else {
+ need_dosynctodr = 0;
+ }
+
+}
+
+/* ================================================== */
+
+static void
+set_dosynctodr(unsigned long on_off)
+{
+ static struct nlist nl[] = {
+ {"dosynctodr"},
+ {NULL}
+ };
+
+ kvm_t *kt;
+ unsigned long read_back;
+
+ if (on_off!=1 && on_off!=0) {
+ CROAK("on_off should be 0 or 1");
+ }
+
+ kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
+ if (!kt) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot open kvm to change dosynctodr");
+ return;
+ }
+
+ if (kvm_nlist(kt, nl) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read dosynctodr in nlist");
+ kvm_close(kt);
+ return;
+ }
+
+ if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot write to dosynctodr");
+ kvm_close(kt);
+ return;
+ }
+
+ if (kvm_read(kt, nl[0].n_value, (char *)(&read_back), sizeof(unsigned long)) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read from dosynctodr");
+ kvm_close(kt);
+ return;
+ }
+
+ kvm_close(kt);
+
+ if (read_back != on_off) {
+ CROAK("read_back should equal on_off");
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_SysSolaris, "Set value of dosynctodr to %d", on_off);
+#endif
+
+}
+
+/* ================================================== */
+
+void
+SYS_Solaris_Initialise(void)
+{
+
+ check_need_dosynctodr();
+
+ /* Need to do KVM stuff to turn off dosynctodr. */
+
+ clock_initialise();
+
+ lcl_RegisterSystemDrivers(read_frequency, set_frequency,
+ accrue_offset, apply_step_offset,
+ get_offset_correction, NULL /* immediate_step */);
+
+ /* Turn off the kernel switch that keeps the system clock in step
+ with the non-volatile clock */
+ if (need_dosynctodr) {
+ set_dosynctodr(0);
+ }
+
+ drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+ drift_removal_running = 1;
+}
+
+/* ================================================== */
+
+void
+SYS_Solaris_Finalise(void)
+{
+
+ if (drift_removal_running) {
+ SCH_RemoveTimeout(drift_removal_id);
+ }
+
+ clock_finalise();
+
+ /* When exiting, we want to return the machine to its 'autonomous'
+ tracking mode */
+ if (need_dosynctodr) {
+ set_dosynctodr(1);
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+#endif /* SOLARIS */
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_solaris.h,v 1.7 2002/02/28 23:27:15 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for Solaris driver
+ */
+
+#ifndef GOT_SYS_SOLARIS_H
+#define GOT_SYS_SOLARIS_H
+
+void SYS_Solaris_Initialise(void);
+
+void SYS_Solaris_Finalise(void);
+
+#endif
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_sunos.c,v 1.18 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Driver file for the SunOS 4.1.x operating system.
+ */
+
+#ifdef SUNOS
+
+#include <kvm.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "sys_sunos.h"
+#include "localp.h"
+#include "logging.h"
+#include "util.h"
+#include "sched.h"
+
+/* ================================================== */
+
+/* This register contains the number of seconds by which the local
+ clock was estimated to be fast of reference time at the epoch when
+ gettimeofday() returned T0 */
+
+static double offset_register;
+
+/* This register contains the epoch to which the offset is referenced */
+
+static struct timeval T0;
+
+/* This register contains the current estimate of the system
+ frequency, in absolute (NOT ppm) */
+
+static double current_freq;
+
+/* This register contains the number of seconds of adjustment that
+ were passed to adjtime last time it was called. */
+
+static double adjustment_requested;
+
+/* Eventually, this needs to be a user-defined parameter - e.g. user
+ might want 5 to get much finer resolution like xntpd. We stick
+ with a reasonable number so that slewing can work.
+
+ This value has to be a factor of 1 million, otherwise the noddy
+ method we use for rounding an adjustment to the nearest multiple of
+ this value won't work!!
+
+ */
+static unsigned long our_tickadj = 100;
+
+/* ================================================== */
+
+static void
+clock_initialise(void)
+{
+ struct timeval newadj, oldadj;
+ struct timezone tz;
+
+ offset_register = 0.0;
+ adjustment_requested = 0.0;
+ current_freq = 0.0;
+
+ if (gettimeofday(&T0, &tz) < 0) {
+ CROAK("gettimeofday() failed in clock_initialise()");
+ }
+
+ newadj.tv_sec = 0;
+ newadj.tv_usec = 0;
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in clock_initialise");
+ }
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in clock_initialise");
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+static void
+clock_finalise(void)
+{
+ /* Nothing to do yet */
+
+ return;
+
+}
+
+/* ================================================== */
+
+static void
+start_adjust(void)
+{
+ struct timeval newadj, oldadj;
+ struct timeval T1;
+ struct timezone tz;
+ double elapsed, accrued_error;
+ double adjust_required;
+ struct timeval exact_newadj;
+ double rounding_error;
+ double old_adjust_remaining;
+ long remainder, multiplier;
+
+ /* Determine the amount of error built up since the last adjustment */
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in start_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ accrued_error = elapsed * current_freq;
+
+ adjust_required = - (accrued_error + offset_register);
+
+ UTI_DoubleToTimeval(adjust_required, &exact_newadj);
+
+ /* At this point, we need to round the required adjustment to the
+ closest multiple of _tickadj --- because SunOS can't process
+ other adjustments exactly and will silently discard the residual.
+ Obviously such behaviour can't be tolerated for us. */
+
+ newadj = exact_newadj;
+ remainder = newadj.tv_usec % our_tickadj;
+ multiplier = newadj.tv_usec / our_tickadj;
+ if (remainder >= (our_tickadj >> 1)) {
+ newadj.tv_usec = (multiplier + 1) * our_tickadj;
+ } else {
+ newadj.tv_usec = multiplier * our_tickadj;
+ }
+
+ UTI_NormaliseTimeval(&newadj);
+
+ /* Want to *add* rounding error back onto offset register. Note
+ that the exact adjustment was the offset register *negated* */
+ UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ CROAK("adjtime() failed in start_adjust");
+ }
+
+ UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
+
+ offset_register = rounding_error - old_adjust_remaining;
+
+ T0 = T1;
+ UTI_TimevalToDouble(&newadj, &adjustment_requested);
+
+}
+
+/* ================================================== */
+
+static void
+stop_adjust(void)
+{
+ struct timeval T1;
+ struct timezone tz;
+ struct timeval zeroadj, remadj;
+ double adjustment_remaining, adjustment_achieved;
+ double gap;
+ double elapsed, elapsed_plus_adjust;
+
+ zeroadj.tv_sec = 0;
+ zeroadj.tv_usec = 0;
+
+ if (adjtime(&zeroadj, &remadj) < 0) {
+ CROAK("adjtime() failed in stop_adjust");
+ }
+
+ if (gettimeofday(&T1, &tz) < 0) {
+ CROAK("gettimeofday() failed in stop_adjust");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+
+ adjustment_achieved = adjustment_requested - adjustment_remaining;
+ elapsed_plus_adjust = elapsed - adjustment_achieved;
+
+ offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
+
+ adjustment_requested = 0.0;
+ T0 = T1;
+
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ slew backwards */
+
+static void
+accrue_offset(double offset)
+{
+ stop_adjust();
+ offset_register += offset;
+ start_adjust();
+ return;
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ step backwards */
+
+static void
+apply_step_offset(double offset)
+{
+ struct timeval old_time, new_time, T1;
+ struct timezone tz;
+
+ stop_adjust();
+ if (gettimeofday(&old_time, &tz) < 0) {
+ CROAK("gettimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+
+ if (settimeofday(&new_time, &tz) < 0) {
+ CROAK("settimeofday in apply_step_offset");
+ }
+
+ UTI_AddDoubleToTimeval(&T0, offset, &T1);
+ T0 = T1;
+
+ start_adjust();
+
+}
+
+/* ================================================== */
+
+static void
+set_frequency(double new_freq_ppm)
+{
+ stop_adjust();
+ current_freq = new_freq_ppm * 1.0e-6;
+ start_adjust();
+}
+
+/* ================================================== */
+
+static double
+read_frequency(void)
+{
+ return current_freq * 1.0e6;
+}
+
+/* ================================================== */
+
+static void
+get_offset_correction(struct timeval *raw,
+ double *corr)
+{
+ stop_adjust();
+ *corr = -offset_register;
+ start_adjust();
+ return;
+}
+
+/* ================================================== */
+
+static void
+immediate_step(void)
+{
+ return;
+}
+
+/* ================================================== */
+
+/* Interval in seconds between adjustments to cancel systematic drift */
+#define DRIFT_REMOVAL_INTERVAL (4.0)
+
+static int drift_removal_running = 0;
+static SCH_TimeoutID drift_removal_id;
+
+/* ================================================== */
+/* This is the timer callback routine which is called periodically to
+ invoke a time adjustment to take out the machine's drift.
+ Otherwise, times reported through this software (e.g. by running
+ ntpdate from another machine) show the machine being correct (since
+ they correct for drift build-up), but any program on this machine
+ that reads the system time will be given an erroneous value, the
+ degree of error depending on how long it is since
+ get_offset_correction was last called. */
+
+static void
+drift_removal_timeout(SCH_ArbitraryArgument not_used)
+{
+ stop_adjust();
+ start_adjust();
+ drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+}
+
+/* ================================================== */
+
+static void
+setup_kernel(unsigned long on_off)
+{
+ static struct nlist nl[] = {
+ {"_dosynctodr"},
+ {"_tick"},
+ {"_tickadj"},
+ {NULL}
+ };
+
+ kvm_t *kt;
+ unsigned long read_back;
+ unsigned long our_tick = 10000;
+ unsigned long default_tickadj = 625;
+
+ if (on_off!=1 && on_off!=0) {
+ CROAK("on_off should be 0 or 1");
+ }
+
+ kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
+ if (!kt) {
+ LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot open kvm");
+ return;
+ }
+
+ if (kvm_nlist(kt, nl) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot read kernel symbols");
+ kvm_close(kt);
+ return;
+ }
+
+ if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _dosynctodr");
+ kvm_close(kt);
+ return;
+ }
+
+ if (kvm_write(kt, nl[1].n_value, (char *)(&our_tick), sizeof(unsigned long)) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tick");
+ kvm_close(kt);
+ return;
+ }
+
+ if (kvm_write(kt, nl[2].n_value,
+ (char *)(&(on_off ? default_tickadj : our_tickadj)),
+ sizeof(unsigned long)) < 0) {
+ LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tickadj");
+ kvm_close(kt);
+ return;
+ }
+
+ kvm_close(kt);
+
+#if 0
+ LOG(LOGS_INFO, LOGF_SysSunOS, "Set value of _dosynctodr to %d", on_off);
+#endif
+
+}
+
+/* ================================================== */
+
+void
+SYS_SunOS_Initialise(void)
+{
+
+ /* Need to do KVM stuff to turn off dosynctodr. */
+
+ clock_initialise();
+
+ lcl_RegisterSystemDrivers(read_frequency, set_frequency,
+ accrue_offset, apply_step_offset,
+ get_offset_correction, NULL /* immediate_step */);
+
+ /* Turn off the kernel switch that keeps the system clock in step
+ with the non-volatile clock */
+ setup_kernel(0);
+
+ drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+ drift_removal_running = 1;
+
+}
+
+/* ================================================== */
+
+void
+SYS_SunOS_Finalise(void)
+{
+
+ if (drift_removal_running) {
+ SCH_RemoveTimeout(drift_removal_id);
+ }
+
+ /* Turn dosynctodr back on?? */
+
+ clock_finalise();
+
+ /* When exiting, we want to return the machine to its 'autonomous'
+ tracking mode */
+ setup_kernel(1);
+
+ return;
+}
+
+/* ================================================== */
+
+
+#endif /* SUNOS */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sys_sunos.h,v 1.7 2002/02/28 23:27:15 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for Solaris driver
+ */
+
+#ifndef GOT_SYS_SUNOS_H
+#define GOT_SYS_SUNOS_H
+
+void SYS_SunOS_Initialise(void);
+
+void SYS_SunOS_Finalise(void);
+
+#endif
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/sysincl.h,v 1.10 2003/04/10 21:28:11 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This file includes all system header files that the software
+ requires. This allows us to isolate system dependencies to this file
+ alone.
+ */
+
+#ifndef GOT_SYSINCL_H
+#define GOT_SYSINCL_H
+
+#if defined (SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(__NetBSD__)
+
+#if !defined(__NetBSD__)
+#include <alloca.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <malloc.h>
+#include <math.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <time.h>
+
+#if HAS_STDINT_H
+#include <stdint.h>
+#elif defined(HAS_INTTYPES_H)
+#include <inttypes.h>
+#else
+/* Tough */
+#endif
+
+/* One or other of these to make getsid() visible */
+#define __EXTENSIONS__ 1
+#define __USE_XOPEN_EXTENDED 1
+
+#include <unistd.h>
+
+#endif
+
+#if defined (SOLARIS) || defined(SUNOS)
+/* Only needed on these platforms, and doesn't exist on some Linux
+ versions. */
+#include <nlist.h>
+#endif
+
+#if defined (HAS_NO_BZERO)
+#define bzero(ptr,n) memset(ptr,0,n)
+#endif /* HAS_NO_BZERO */
+
+#if defined (WINNT)
+
+/* Designed to work with the GCC from the GNAT-3.10 for Win32
+ distribution */
+
+#define Win32_Winsock
+#include <assert.h>
+#include <ctype.h>
+
+#if 1
+/* Cheat and inline the necessary bits from <errno.h>. We don't
+ include it directly because it redefines some EXXX constants that
+ conflict with <windows32/sockets.h> (included by <windows.h>) */
+
+int* _errno();
+int* __doserrno();
+
+#define errno (*_errno())
+#define _doserrno (*__doserrno())
+
+#define ENOENT 2
+#else
+
+#include <errno.h>
+#endif
+
+
+#include <float.h>
+#include <math.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <windows.h>
+#endif
+
+#endif /* GOT_SYSINCL_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/util.c,v 1.19 2003/03/24 23:35:43 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Various utility functions
+ */
+
+#include "sysincl.h"
+
+#include "util.h"
+#include "logging.h"
+
+/* ================================================== */
+
+INLINE_STATIC void
+UTI_TimevalToDouble(struct timeval *a, double *b)
+{
+ *b = (double)(a->tv_sec) + 1.0e-6 * (double)(a->tv_usec);
+
+}
+
+/* ================================================== */
+
+INLINE_STATIC void
+UTI_DoubleToTimeval(double a, struct timeval *b)
+{
+ long int_part, frac_part;
+ int_part = (long)(a);
+ frac_part = (long)(0.5 + 1.0e6 * (a - (double)(int_part)));
+ b->tv_sec = int_part;
+ b->tv_usec = frac_part;
+ UTI_NormaliseTimeval(b);
+}
+
+/* ================================================== */
+
+INLINE_STATIC int
+UTI_CompareTimevals(struct timeval *a, struct timeval *b)
+{
+ if (a->tv_sec < b->tv_sec) {
+ return -1;
+ } else if (a->tv_sec > b->tv_sec) {
+ return +1;
+ } else {
+ if (a->tv_sec != b->tv_sec) {
+ CROAK("a->tv_sec != b->tv_sec");
+ }
+ if (a->tv_usec < b->tv_usec) {
+ return -1;
+ } else if (a->tv_usec > b->tv_usec) {
+ return +1;
+ } else {
+ if (a->tv_usec != b->tv_usec) {
+ CROAK("a->tv_usec != b->tv_usec");
+ }
+ return 0;
+ }
+ }
+ CROAK("Impossible"); /* Shouldn't be able to fall through. */
+}
+
+/* ================================================== */
+
+INLINE_STATIC void
+UTI_NormaliseTimeval(struct timeval *x)
+{
+ while (x->tv_usec >= 1000000) {
+ ++x->tv_sec;
+ x->tv_usec -= 1000000;
+ }
+
+ while (x->tv_usec < 0) {
+ --x->tv_sec;
+ x->tv_usec += 1000000;
+ }
+
+}
+
+/* ================================================== */
+
+INLINE_STATIC void
+UTI_DiffTimevals(struct timeval *result,
+ struct timeval *a,
+ struct timeval *b)
+{
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ result->tv_usec = a->tv_usec - b->tv_usec;
+
+ /* Correct microseconds field to bring it into the range
+ [0,1000000) */
+
+ while (result->tv_usec < 0) {
+ result->tv_usec += 1000000;
+ --result->tv_sec;
+ }
+
+ while (result->tv_usec > 999999) {
+ result->tv_usec -= 1000000;
+ ++result->tv_sec;
+ }
+
+ return;
+}
+
+/* ================================================== */
+
+/* Calculate result = a - b and return as a double */
+INLINE_STATIC void
+UTI_DiffTimevalsToDouble(double *result,
+ struct timeval *a,
+ struct timeval *b)
+{
+ *result = (double)(a->tv_sec - b->tv_sec) +
+ (double)(a->tv_usec - b->tv_usec) * 1.0e-6;
+}
+
+/* ================================================== */
+
+INLINE_STATIC void
+UTI_AddDoubleToTimeval(struct timeval *start,
+ double increment,
+ struct timeval *end)
+{
+ long int_part, frac_part;
+
+ /* Don't want to do this by using (long)(1000000 * increment), since
+ that will only cope with increments up to +/- 2148 seconds, which
+ is too marginal here. */
+
+ int_part = (long) increment;
+ frac_part = (long) (0.5 + 1.0e6 * (increment - (double)int_part));
+
+ end->tv_sec = int_part + start->tv_sec;
+ end->tv_usec = frac_part + start->tv_usec;
+
+ UTI_NormaliseTimeval(end);
+}
+
+/* ================================================== */
+
+/* Calculate the average and difference (as a double) of two timevals */
+INLINE_STATIC void
+UTI_AverageDiffTimevals (struct timeval *earlier,
+ struct timeval *later,
+ struct timeval *average,
+ double *diff)
+{
+ struct timeval tvdiff;
+ struct timeval tvhalf;
+
+ UTI_DiffTimevals(&tvdiff, later, earlier);
+ *diff = (double)tvdiff.tv_sec + 1.0e-6 * (double)tvdiff.tv_usec;
+
+ if (*diff < 0.0) {
+ /* Either there's a bug elsewhere causing 'earlier' and 'later' to
+ be backwards, or something wierd has happened. Maybe when we
+ change the frequency on Linux? */
+
+ /* This seems to be fairly benign, so don't bother logging it */
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Util, "Earlier=[%s] Later=[%s]",
+ UTI_TimevalToString(earlier), UTI_TimevalToString(later));
+#endif
+
+ /* Assume the required behaviour is to treat it as zero */
+ *diff = 0.0;
+ }
+
+ tvhalf.tv_sec = tvdiff.tv_sec / 2;
+ tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2);
+
+ average->tv_sec = earlier->tv_sec + tvhalf.tv_sec;
+ average->tv_usec = earlier->tv_usec + tvhalf.tv_usec;
+
+ /* Bring into range */
+ UTI_NormaliseTimeval(average);
+
+ while (average->tv_usec >= 1000000) {
+ ++average->tv_sec;
+ average->tv_usec -= 1000000;
+ }
+
+ while (average->tv_usec < 0) {
+ --average->tv_sec;
+ average->tv_usec += 1000000;
+ }
+
+}
+
+/* ================================================== */
+
+#define POOL_ENTRIES 16
+#define BUFFER_LENGTH 64
+static char buffer_pool[POOL_ENTRIES][BUFFER_LENGTH];
+static int pool_ptr = 0;
+
+#define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)])
+
+/* ================================================== */
+/* Convert a timeval into a temporary string, largely for diagnostic
+ display */
+
+char *
+UTI_TimevalToString(struct timeval *tv)
+{
+ char buffer[64], *result;
+ struct tm stm;
+ stm = *gmtime((time_t *) &(tv->tv_sec));
+ strftime(buffer, sizeof(buffer), "%a %x %X", &stm);
+ result = NEXT_BUFFER;
+ sprintf(result, "%s.%06ld", buffer, (unsigned long)(tv->tv_usec));
+ return result;
+}
+
+/* ================================================== */
+#define JAN_1970 0x83aa7e80UL
+
+inline static void
+int64_to_timeval(NTP_int64 *src,
+ struct timeval *dest)
+{
+ dest->tv_sec = ntohl(src->hi) - JAN_1970;
+
+ /* Until I invent a slick way to do this, just do it the obvious way */
+ dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296);
+}
+
+/* ================================================== */
+/* Convert an NTP timestamp into a temporary string, largely
+ for diagnostic display */
+
+char *
+UTI_TimestampToString(NTP_int64 *ts)
+{
+ struct timeval tv;
+ int64_to_timeval(ts, &tv);
+ return UTI_TimevalToString(&tv);
+}
+
+/* ================================================== */
+
+char *
+UTI_IPToDottedQuad(unsigned long ip)
+{
+ unsigned long a, b, c, d;
+ char *result;
+ a = (ip>>24) & 0xff;
+ b = (ip>>16) & 0xff;
+ c = (ip>> 8) & 0xff;
+ d = (ip>> 0) & 0xff;
+ result = NEXT_BUFFER;
+ sprintf(result, "%ld.%ld.%ld.%ld", a, b, c, d);
+ return result;
+}
+
+/* ================================================== */
+
+char *
+UTI_TimeToLogForm(time_t t)
+{
+ struct tm stm;
+ char *result;
+
+ result = NEXT_BUFFER;
+
+ stm = *gmtime(&t);
+ strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", &stm);
+
+ return result;
+}
+
+/* ================================================== */
+
+void
+UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double dfreq, double doffset)
+{
+ double elapsed, delta_time;
+
+ UTI_DiffTimevalsToDouble(&elapsed, when, old_tv);
+ delta_time = elapsed * dfreq - doffset;
+ UTI_AddDoubleToTimeval(old_tv, delta_time, new_tv);
+}
+
+/* ================================================== */
+
+/* Seconds part of RFC1305 timestamp correponding to the origin of the
+ struct timeval format. */
+#define JAN_1970 0x83aa7e80UL
+
+void
+UTI_TimevalToInt64(struct timeval *src,
+ NTP_int64 *dest)
+{
+ unsigned long usec = src->tv_usec;
+ unsigned long sec = src->tv_sec;
+
+ /* Recognize zero as a special case - it always signifies
+ an 'unknown' value */
+ if (!usec && !sec) {
+ dest->hi = dest->lo = 0;
+ } else {
+ dest->hi = htonl(src->tv_sec + JAN_1970);
+
+ /* This formula gives an error of about 0.1us worst case */
+ dest->lo = htonl(4295 * usec - (usec>>5) - (usec>>9));
+ }
+}
+
+/* ================================================== */
+
+void
+UTI_Int64ToTimeval(NTP_int64 *src,
+ struct timeval *dest)
+{
+ /* As yet, there is no need to check for zero - all processing that
+ has to detect that case is in the NTP layer */
+
+ dest->tv_sec = ntohl(src->hi) - JAN_1970;
+
+ /* Until I invent a slick way to do this, just do it the obvious way */
+ dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296);
+}
+
+/* ================================================== */
+/* Force a core dump and exit without doing abort() or assert(0).
+ These do funny things with the call stack in the core file that is
+ generated, which makes diagnosis difficult. */
+
+int
+croak(const char *file, int line, const char *msg)
+{
+ int a;
+ LOG(LOGS_ERR, LOGF_Util, "Unexpected condition [%s] at %s:%d, core dumped",
+ msg, file, line);
+ a = * (int *) 0;
+ return a; /* Can't happen - this stops the optimiser optimising the
+ line above */
+}
+
+/* ================================================== */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/util.h,v 1.14 2003/04/01 20:07:20 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Various utility functions
+ */
+
+#ifndef GOT_UTIL_H
+#define GOT_UTIL_H
+
+#include "sysincl.h"
+
+#include "ntp.h"
+
+/* Convert a timeval into a floating point number of seconds */
+extern void UTI_TimevalToDouble(struct timeval *a, double *b);
+
+/* Convert a number of seconds expressed in floating point into a
+ timeval */
+extern void UTI_DoubleToTimeval(double a, struct timeval *b);
+
+/* Returns -1 if a comes earlier than b, 0 if a is the same time as b,
+ and +1 if a comes after b */
+extern int UTI_CompareTimevals(struct timeval *a, struct timeval *b);
+
+/* Normalise a struct timeval, by adding or subtracting seconds to bring
+ its microseconds field into range */
+extern void UTI_NormaliseTimeval(struct timeval *x);
+
+/* Calculate result = a - b */
+extern void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b);
+
+/* Calculate result = a - b and return as a double */
+extern void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b);
+
+/* Add a double increment to a timeval to get a new one. 'start' is
+ the starting time, 'end' is the result that we return. This is
+ safe to use if start and end are the same */
+extern void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end);
+
+/* Calculate the average and difference (as a double) of two timevals */
+extern void UTI_AverageDiffTimevals(struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff);
+
+/* Convert a timeval into a temporary string, largely for diagnostic
+ display */
+extern char *UTI_TimevalToString(struct timeval *tv);
+
+/* Convert an NTP timestamp into a temporary string, largely for
+ diagnostic display */
+extern char *UTI_TimestampToString(NTP_int64 *ts);
+
+/* Convert an IP address to dotted quad notation, for diagnostics */
+extern char *UTI_IPToDottedQuad(unsigned long ip);
+
+extern char *UTI_TimeToLogForm(time_t t);
+
+/* Adjust time following a frequency/offset change */
+extern void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double dfreq, double doffset);
+
+
+extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest);
+
+extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest);
+
+/* Like assert(0) */
+
+#if defined(LINUX) && defined(__alpha__)
+#define CROAK(message) assert(0) /* Added JGH Feb 24 2001 FIXME */
+#else
+extern int croak(const char *file, int line, const char *msg);
+#define CROAK(message) croak(__FILE__, __LINE__, message);
+#endif
+
+#if defined (INLINE_UTILITIES)
+#define INLINE_STATIC inline static
+#include "util.c"
+#else
+#define INLINE_STATIC
+#endif /* defined (INLINE_UTILITIES) */
+
+#endif /* GOT_UTIL_H */
--- /dev/null
+#ifndef VERSION_H
+#define VERSION_H
+#define PROGRAM_VERSION_STRING "$Name: $"
+#endif /* VERSION_H */
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/wrap_adjtimex.c,v 1.9 2002/11/19 21:33:42 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This is a wrapper around the Linux adjtimex system call. It isolates the
+ inclusion of <linux/adjtimex.h> from the need to include other header files,
+ many of which conflict with those in <linux/...> on some recent distributions
+ (as of Jul 2000) using kernels around 2.2.16 onwards.
+
+ */
+
+#ifdef LINUX
+
+#define _LOOSE_KERNEL_NAMES
+
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <asm/param.h>
+
+#include "wrap_adjtimex.h"
+
+/* This doesn't seem to be in any include files !! */
+
+extern int adjtimex(struct timex *);
+
+int
+TMX_SetTick(long tick)
+{
+ struct timex txc;
+ txc.modes = ADJ_TICK;
+ txc.tick = tick;
+
+ return adjtimex(&txc);
+}
+
+int
+TMX_ApplyOffset(long *offset)
+{
+ struct timex txc;
+ int result;
+
+ txc.modes = ADJ_OFFSET_SINGLESHOT;
+ txc.offset = *offset;
+ result = adjtimex(&txc);
+ *offset = txc.offset;
+ return result;
+}
+
+int
+TMX_SetFrequency(double freq, long tick)
+{
+ struct timex txc;
+
+ txc.modes = ADJ_TICK | ADJ_FREQUENCY | ADJ_STATUS;
+
+ txc.freq = (long)(freq * (double)(1 << SHIFT_USEC));
+ txc.tick = tick;
+ txc.status = STA_UNSYNC; /* Prevent any of the FLL/PLL stuff coming
+ up */
+
+ return adjtimex(&txc);
+}
+
+int
+TMX_GetFrequency(double *freq)
+{
+ struct timex txc;
+ int result;
+ txc.modes = 0; /* pure read */
+ result = adjtimex(&txc);
+ *freq = txc.freq / (double)(1 << SHIFT_USEC);
+ return result;
+}
+
+int
+TMX_GetOffsetLeft(long *offset)
+{
+ struct timex txc;
+ int result;
+ txc.modes = 0; /* pure read */
+ result = adjtimex(&txc);
+ *offset = txc.offset;
+ return result;
+}
+
+int
+TMX_ReadCurrentParams(struct tmx_params *params)
+{
+ struct timex txc;
+ int result;
+
+ txc.modes = 0; /* pure read */
+ result = adjtimex(&txc);
+
+ params->tick = txc.tick;
+ params->offset = txc.offset;
+ params->freq = txc.freq;
+ params->dfreq = txc.freq / (double)(1 << SHIFT_USEC);
+ params->maxerror = txc.maxerror;
+ params->esterror = txc.esterror;
+
+ params->sta_pll = (txc.status & STA_PLL);
+ params->sta_ppsfreq = (txc.status & STA_PPSFREQ);
+ params->sta_ppstime = (txc.status & STA_PPSTIME);
+ params->sta_fll = (txc.status & STA_FLL);
+ params->sta_ins = (txc.status & STA_INS);
+ params->sta_del = (txc.status & STA_DEL);
+ params->sta_unsync = (txc.status & STA_UNSYNC);
+ params->sta_freqhold = (txc.status & STA_FREQHOLD);
+ params->sta_ppssignal = (txc.status & STA_PPSSIGNAL);
+ params->sta_ppsjitter = (txc.status & STA_PPSJITTER);
+ params->sta_ppswander = (txc.status & STA_PPSWANDER);
+ params->sta_ppserror = (txc.status & STA_PPSERROR);
+ params->sta_clockerr = (txc.status & STA_CLOCKERR);
+
+ params->constant = txc.constant;
+ params->precision = txc.precision;
+ params->tolerance = txc.tolerance;
+ params->ppsfreq = txc.ppsfreq;
+ params->jitter = txc.jitter;
+ params->shift = txc.shift;
+ params->stabil = txc.stabil;
+ params->jitcnt = txc.jitcnt;
+ params->calcnt = txc.calcnt;
+ params->errcnt = txc.errcnt;
+ params->stbcnt = txc.stbcnt;
+
+ return result;
+}
+
+#endif
+
--- /dev/null
+/*
+ $Header: /cvs/src/chrony/wrap_adjtimex.h,v 1.6 2002/11/19 21:33:42 richard Exp $
+
+ =======================================================================
+
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ The header file for the adjtimex wrapper
+ */
+
+#ifndef GOT_WRAP_ADJTIMEX_H
+#define GOT_WRAP_ADJTIMEX_H
+
+/* Cut-down version of struct timex */
+struct tmx_params {
+ long tick;
+ long offset;
+ long freq;
+ double dfreq;
+ long maxerror;
+ long esterror;
+
+ unsigned sta_pll:1;
+ unsigned sta_ppsfreq:1;
+ unsigned sta_ppstime:1;
+ unsigned sta_fll:1;
+ unsigned sta_ins:1;
+ unsigned sta_del:1;
+ unsigned sta_unsync:1;
+ unsigned sta_freqhold:1;
+ unsigned sta_ppssignal:1;
+ unsigned sta_ppsjitter:1;
+ unsigned sta_ppswander:1;
+ unsigned sta_ppserror:1;
+ unsigned sta_clockerr:1;
+
+ int status;
+ long constant;
+ long precision;
+ long tolerance;
+ long ppsfreq;
+ long jitter;
+ int shift;
+ long stabil;
+ long jitcnt;
+ long calcnt;
+ long errcnt;
+ long stbcnt;
+};
+
+int TMX_SetTick(long tick);
+int TMX_ApplyOffset(long *offset);
+int TMX_SetFrequency(double freq, long tick);
+int TMX_GetFrequency(double *freq);
+int TMX_GetOffsetLeft(long *offset);
+int TMX_ReadCurrentParams(struct tmx_params *params);
+
+#endif /* GOT_WRAP_ADJTIMEX_H */
+