Equivalent to V1.19.99.1

This is a verbatim copy of the files at that stage of the repository that was
built from the CVS import.  It allows future development to see a bit of recent
history, but without carrying around the baggage going back to 1997.  If that
is really required, git grafts can be used.
This commit is contained in:
Richard P. Curnow
2006-01-19 21:34:28 +00:00
commit 8884034104
110 changed files with 34864 additions and 0 deletions

339
COPYING Normal file
View File

@@ -0,0 +1,339 @@
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.
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.

89
INSTALL Normal file
View File

@@ -0,0 +1,89 @@
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.

161
Makefile.in Normal file
View File

@@ -0,0 +1,161 @@
##################################################
#
# $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

267
NEWS Normal file
View File

@@ -0,0 +1,267 @@
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.

243
README Normal file
View File

@@ -0,0 +1,243 @@
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

688
acquire.c Normal file
View File

@@ -0,0 +1,688 @@
/*
$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;
}
/* ================================================== */

47
acquire.h Normal file
View File

@@ -0,0 +1,47 @@
/*
$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 */

46
addressing.h Normal file
View File

@@ -0,0 +1,46 @@
/*
$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 */

385
addrfilt.c Normal file
View File

@@ -0,0 +1,385 @@
/*
$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 */

77
addrfilt.h Normal file
View File

@@ -0,0 +1,77 @@
/*
$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 */

159
broadcast.c Normal file
View File

@@ -0,0 +1,159 @@
/*
$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;
}

39
broadcast.h Normal file
View File

@@ -0,0 +1,39 @@
/*
$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 */

24
build_kit Executable file
View File

@@ -0,0 +1,24 @@
#!/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");

594
candm.h Normal file
View File

@@ -0,0 +1,594 @@
/*
$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 */

65
chrony.1 Normal file
View File

@@ -0,0 +1,65 @@
.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.

49
chrony.conf.5 Normal file
View File

@@ -0,0 +1,49 @@
.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.

29
chrony.lsm Normal file
View File

@@ -0,0 +1,29 @@
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

52
chrony.spec.sample Normal file
View File

@@ -0,0 +1,52 @@
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

3979
chrony.texi Normal file

File diff suppressed because it is too large Load Diff

56
chronyc.1 Normal file
View File

@@ -0,0 +1,56 @@
.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.

111
chronyd.8 Normal file
View File

@@ -0,0 +1,111 @@
.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.

2589
client.c Normal file

File diff suppressed because it is too large Load Diff

392
clientlog.c Normal file
View File

@@ -0,0 +1,392 @@
/*
$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;
}
}

91
clientlog.h Normal file
View File

@@ -0,0 +1,91 @@
/*
$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 */

2119
cmdmon.c Normal file

File diff suppressed because it is too large Load Diff

41
cmdmon.h Normal file
View File

@@ -0,0 +1,41 @@
/*
$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 */

163
cmdparse.c Normal file
View File

@@ -0,0 +1,163 @@
/*
$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;
}
/* ================================================== */

60
cmdparse.h Normal file
View File

@@ -0,0 +1,60 @@
/*
$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 */

1235
conf.c Normal file

File diff suppressed because it is too large Load Diff

74
conf.h Normal file
View File

@@ -0,0 +1,74 @@
/*
$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 */

313
configure vendored Executable file
View File

@@ -0,0 +1,313 @@
#!/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

339
contrib/DNSchrony/COPYING Normal file
View File

@@ -0,0 +1,339 @@
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.
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.

583
contrib/DNSchrony/DNSchrony.pl Executable file
View File

@@ -0,0 +1,583 @@
#!/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);
}

21
contrib/DNSchrony/DNSchronyADD Executable file
View File

@@ -0,0 +1,21 @@
#!/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"

View File

@@ -0,0 +1,7 @@
#!/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

View File

@@ -0,0 +1,7 @@
#!/usr/bin/bash
# $1 is chrony password.
CHRONYPASSWORD=$1 \
/usr/local/bin/DNSchrony.pl -ulS

166
contrib/DNSchrony/README Normal file
View File

@@ -0,0 +1,166 @@
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

View File

@@ -0,0 +1,22 @@
#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

114
contrib/andrew_bishop_1 Normal file
View File

@@ -0,0 +1,114 @@
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/

95
contrib/andrew_bishop_2 Normal file
View File

@@ -0,0 +1,95 @@
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/

65
contrib/erik_bryer_1 Normal file
View File

@@ -0,0 +1,65 @@
#!/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

100
contrib/ken_gillett_1 Normal file
View File

@@ -0,0 +1,100 @@
#!/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

162
contrib/stephan_boettcher_1 Normal file
View File

@@ -0,0 +1,162 @@
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

View File

@@ -0,0 +1,118 @@
> 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";
}
############################################################

View File

@@ -0,0 +1,289 @@
#######################################################################
# $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
#######################################################################

View File

@@ -0,0 +1,27 @@
#######################################################################
# $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.

384
faq.txt Normal file
View File

@@ -0,0 +1,384 @@
@@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 &quot;chrony&quot; 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 &lt;Return&gt;, 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&lt;-&gt;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

140
faqgen.pl Normal file
View File

@@ -0,0 +1,140 @@
#!/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;
}
#}}}

2019
getdate.c Normal file

File diff suppressed because it is too large Load Diff

28
getdate.h Normal file
View File

@@ -0,0 +1,28 @@
/* 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 */

243
keys.c Normal file
View File

@@ -0,0 +1,243 @@
/*
$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;
}
}
}

44
keys.h Normal file
View File

@@ -0,0 +1,44 @@
/*
$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 */

574
local.c Normal file
View File

@@ -0,0 +1,574 @@
/*
$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;
}
/* ================================================== */

184
local.h Normal file
View File

@@ -0,0 +1,184 @@
/*
$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 */

73
localp.h Normal file
View File

@@ -0,0 +1,73 @@
/*
$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 */

216
logging.c Normal file
View File

@@ -0,0 +1,216 @@
/*
$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
}
/* ================================================== */

97
logging.h Normal file
View File

@@ -0,0 +1,97 @@
/*
$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 */

311
main.c Normal file
View File

@@ -0,0 +1,311 @@
/*
$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;
}
/* ================================================== */

39
main.h Normal file
View File

@@ -0,0 +1,39 @@
/*
$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 */

331
manual.c Normal file
View File

@@ -0,0 +1,331 @@
/*
$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;
}
/* ================================================== */

49
manual.h Normal file
View File

@@ -0,0 +1,49 @@
/*
$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 */

322
md5.c Normal file
View File

@@ -0,0 +1,322 @@
/*
***********************************************************************
** 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) ********************************
*/

60
md5.h Normal file
View File

@@ -0,0 +1,60 @@
/*
***********************************************************************
** 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) ********************************
*/

43
memory.h Normal file
View File

@@ -0,0 +1,43 @@
/*
$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 */

135
mkdirpp.c Normal file
View File

@@ -0,0 +1,135 @@
/*
$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

35
mkdirpp.h Normal file
View File

@@ -0,0 +1,35 @@
/*
$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

92
nameserv.c Normal file
View File

@@ -0,0 +1,92 @@
/*
$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;
}
}
}
/* ================================================== */

42
nameserv.h Normal file
View File

@@ -0,0 +1,42 @@
/*
$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 */

116
ntp.h Normal file
View File

@@ -0,0 +1,116 @@
/*
$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 */

1867
ntp_core.c Normal file

File diff suppressed because it is too large Load Diff

105
ntp_core.h Normal file
View File

@@ -0,0 +1,105 @@
/*
$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 */

297
ntp_io.c Normal file
View File

@@ -0,0 +1,297 @@
/*
$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));
}

53
ntp_io.h Normal file
View File

@@ -0,0 +1,53 @@
/*
$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 */

499
ntp_sources.c Normal file
View File

@@ -0,0 +1,499 @@
/*
$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;
}
/* ================================================== */

99
ntp_sources.h Normal file
View File

@@ -0,0 +1,99 @@
/*
$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 */

240
pktlength.c Normal file
View File

@@ -0,0 +1,240 @@
/*
$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;
}
/* ================================================== */

42
pktlength.h Normal file
View File

@@ -0,0 +1,42 @@
/*
$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 */

709
reference.c Normal file
View File

@@ -0,0 +1,709 @@
/*
$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;
}
}
/* ================================================== */

147
reference.h Normal file
View File

@@ -0,0 +1,147 @@
/*
$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 */

628
regress.c Normal file
View File

@@ -0,0 +1,628 @@
/*
$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;
}
/* ================================================== */

108
regress.h Normal file
View File

@@ -0,0 +1,108 @@
/*
$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 */

121
reports.h Normal file
View File

@@ -0,0 +1,121 @@
/*
$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 */

222
rtc.c Normal file
View File

@@ -0,0 +1,222 @@
/*
$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)();
}
}
/* ================================================== */

52
rtc.h Normal file
View File

@@ -0,0 +1,52 @@
/*
$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 */

1174
rtc_linux.c Normal file

File diff suppressed because it is too large Load Diff

53
rtc_linux.h Normal file
View File

@@ -0,0 +1,53 @@
/*
$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 */

601
sched.c Normal file
View File

@@ -0,0 +1,601 @@
/*
$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;
}
/* ================================================== */

83
sched.h Normal file
View File

@@ -0,0 +1,83 @@
/*
$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 */

938
sources.c Normal file
View File

@@ -0,0 +1,938 @@
/*
$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;
}
/* ================================================== */

155
sources.h Normal file
View File

@@ -0,0 +1,155 @@
/*
$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 */

931
sourcestats.c Normal file
View File

@@ -0,0 +1,931 @@
/*
$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, &degrees_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;
}
}
/* ================================================== */

156
sourcestats.h Normal file
View File

@@ -0,0 +1,156 @@
/*
$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 */

47
srcparams.h Normal file
View File

@@ -0,0 +1,47 @@
/*
$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 */

41
strerror.c Normal file
View File

@@ -0,0 +1,41 @@
/*
$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 */

104
sys.c Normal file
View File

@@ -0,0 +1,104 @@
/*
$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;
}
/* ================================================== */
/* ================================================== */
/* ================================================== */

42
sys.h Normal file
View File

@@ -0,0 +1,42 @@
/*
$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 */

850
sys_linux.c Normal file
View File

@@ -0,0 +1,850 @@
/*
$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
* */

40
sys_linux.h Normal file
View File

@@ -0,0 +1,40 @@
/*
$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 */

326
sys_netbsd.c Normal file
View File

@@ -0,0 +1,326 @@
/*
$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__ */

39
sys_netbsd.h Normal file
View File

@@ -0,0 +1,39 @@
/*
$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

Some files were not shown because too many files have changed in this diff Show More