chrony-2.1.1/0000755000000000000000000000000012542263375011524 5ustar rootrootchrony-2.1.1/COPYING0000644000000000000000000004310312542263351012552 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 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. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. , 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 Lesser General Public License instead of this License. chrony-2.1.1/Makefile.in0000644000000000000000000001215212542263351013564 0ustar rootroot################################################## # # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ======================================================================= # # Makefile template SYSCONFDIR=@SYSCONFDIR@ BINDIR=@BINDIR@ SBINDIR=@SBINDIR@ MANDIR=@MANDIR@ INFODIR=@INFODIR@ DOCDIR=@DOCDIR@ LOCALSTATEDIR=@LOCALSTATEDIR@ CHRONYVARDIR=@CHRONYVARDIR@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ DESTDIR= HASH_OBJ = @HASH_OBJ@ OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o mkdirpp.o \ reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \ sys.o smooth.o tempcomp.o util.o $(HASH_OBJ) EXTRA_OBJS=@EXTRA_OBJECTS@ CLI_OBJS = client.o nameserv.o getdate.o cmdparse.o \ pktlength.o util.o $(HASH_OBJ) ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS) LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ EXTRA_LIBS=@EXTRA_LIBS@ EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@ # Until we have a main procedure we can link, just build object files # to test compilation all : chronyd chronyc chronyd : $(OBJS) $(EXTRA_OBJS) $(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS) chronyc : $(CLI_OBJS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) client.o : client.c $(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $< $(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ)) $(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $< distclean : clean -rm -f .DS_Store -rm -f Makefile -rm -f chrony.conf.5 chrony.texi chronyc.1 chronyd.8 clean : -rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt -rm -rf .deps -rm -rf *.dSYM getdate.c : bison -o getdate.c getdate.y # This can be used to force regeneration of getdate.c getdate : bison -o getdate.c getdate.y # For install, don't use the install command, because its switches # seem to vary between systems. install: chronyd chronyc chrony.txt [ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR) [ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR) [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) [ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR) [ -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)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR) [ -d $(DESTDIR)$(CHRONYVARDIR) ] || mkdir -p $(DESTDIR)$(CHRONYVARDIR) if [ -f $(DESTDIR)$(SBINDIR)/chronyd ]; then rm -f $(DESTDIR)$(SBINDIR)/chronyd ; fi if [ -f $(DESTDIR)$(BINDIR)/chronyc ]; then rm -f $(DESTDIR)$(BINDIR)/chronyc ; fi cp chronyd $(DESTDIR)$(SBINDIR)/chronyd chmod 755 $(DESTDIR)$(SBINDIR)/chronyd cp chronyc $(DESTDIR)$(BINDIR)/chronyc chmod 755 $(DESTDIR)$(BINDIR)/chronyc cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt cp COPYING $(DESTDIR)$(DOCDIR)/COPYING chmod 644 $(DESTDIR)$(DOCDIR)/COPYING cp README $(DESTDIR)$(DOCDIR)/README chmod 644 $(DESTDIR)$(DOCDIR)/README cp chronyc.1 $(DESTDIR)$(MANDIR)/man1 chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1 cp chronyd.8 $(DESTDIR)$(MANDIR)/man8 chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8 cp chrony.conf.5 $(DESTDIR)$(MANDIR)/man5 chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5 %.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c $< %.s : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -S $< check : chronyd chronyc cd test/simulation && ./run install-docs : docs [ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR) cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt cp chrony.html $(DESTDIR)$(DOCDIR)/chrony.html chmod 644 $(DESTDIR)$(DOCDIR)/chrony.html [ -d $(DESTDIR)$(INFODIR) ] || mkdir -p $(DESTDIR)$(INFODIR) cp chrony.info* $(DESTDIR)$(INFODIR) chmod 644 $(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 command -v texi2html > /dev/null 2>&1 && texi2html chrony.texi || \ makeinfo --no-split --html --number-sections -o chrony.html chrony.texi chrony.info : chrony.texi makeinfo chrony.texi Makefile : Makefile.in configure @echo @echo Makefile needs to be regenerated, run ./configure @echo @exit 1 .deps: @mkdir .deps .deps/%.d: %.c | .deps @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ -include $(ALL_OBJS:%.o=.deps/%.d) chrony-2.1.1/NEWS0000644000000000000000000005435312542263351012227 0ustar rootrootNew in version 2.1.1 ==================== Bug fixes --------- * Fix clock stepping by integer number of seconds on Linux New in version 2.1 ================== Enhancements ------------ * Add support for Mac OS X * Try to replace unreachable and falseticker servers/peers specified by name like pool sources * Add leaponly option to smoothtime directive to allow synchronised leap smear between multiple servers * Use specific reference ID when smoothing served time * Add smoothing command to report time smoothing status * Add smoothtime command to activate or reset time smoothing Bug fixes --------- * Fix crash in source selection with preferred sources * Fix resetting of time smoothing * Include packet precision in peer dispersion * Fix crash in chronyc on invalid command syntax New in version 2.0 ================== Enhancements ------------ * Update to NTP version 4 (RFC 5905) * Add pool directive to specify pool of NTP servers * Add leapsecmode directive to select how to correct clock for leap second * Add smoothtime directive to smooth served time and enable leap smear * Add minsources directive to set required number of selectable sources * Add minsamples and maxsamples options for all sources * Add tempcomp configuration with list of points * Allow unlimited number of NTP sources, refclocks and keys * Allow unreachable sources to remain selected * Improve source selection * Handle offline sources as unreachable * Open NTP server port only when necessary (client access is allowed by allow directive/command or peer/broadcast is configured) * Change default bindcmdaddress to loopback address * Change default maxdelay to 3 seconds * Change default stratumweight to 0.001 * Update adjtimex synchronisation status * Use system headers for adjtimex * Check for memory allocation errors * Reduce memory usage * Add configure options to compile without NTP, cmdmon, refclock support * Extend makestep command to set automatic clock stepping Bug fixes --------- * Add sanity checks for time and frequency offset * Don't report synchronised status during leap second * Don't combine reference clocks with close NTP sources * Fix accepting requests from configured sources * Fix initial fallback drift setting New in version 1.31.1 ===================== Security fixes -------------- * Protect authenticated symmetric NTP associations against DoS attacks (CVE-2015-1853) * Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821) * Fix initialization of reply slots for authenticated commands (CVE-2015-1822) New in version 1.31 =================== Enhancements ------------ * Support operation in other NTP eras (next era begins in 2036), NTP time is mapped to [-50, +86] years around build date by default * Restore time from driftfile with -s when RTC is missing/unsupported * Close connected client sockets when not waiting for reply * Use one client socket with random port when acquisitionport is 0 * Use NTP packets instead of UDP echo for presend * Don't adjust polling interval when sending fails * Allow binding to addresses that don't exist yet * Ignore measurements around leap second * Improve detection of unexpected time jumps * Include example of logrotate configuration, systemd services and NetworkManager dispatcher script Bug fixes --------- * Reconnect client sockets for each request to follow changes in network configuration automatically * Restart timer when polling interval is changed on reset New in version 1.30 =================== Enhancements ------------ * Add asynchronous name resolving with POSIX threads * Add PTP hardware clock (PHC) refclock driver * Add new generic clock driver to slew by adjusting frequency only (without kernel PLL or adjtime) and use it on Linux * Add rtcautotrim directive to trim RTC automatically * Add hwclockfile directive to share RTC LOCAL/UTC setting with hwclock * Add maxslewrate directive to set maximum allowed slew rate * Add maxdispersion option for refclocks * Add -q/-Q options to set clock/print offset once and exit * Allow directives to be specified on chronyd command line * Replace frequency scaling in Linux driver with retaining of tick * Try to detect unexpected forward time jumps and reset state * Exit with non-zero code when maxchange limit is reached * Improve makestep to not start and stop slew unnecessarily * Change default corrtimeratio to 3.0 to improve frequency accuracy * Announce leap second only on last day of June and December * Use separate connected client sockets for each NTP server * Remove separate NTP implementation used for initstepslew * Limit maximum minpoll set by KoD RATE to default maxpoll * Don't send NTP requests with unknown key * Print warning when source is added with unknown key * Take leap second in PPS refclock from locked source * Make reading of RTC for initial trim more reliable * Don't create cmdmon sockets when cmdport is 0 * Add configure option to set default user to drop root privileges * Add configure option to compile with debug messages * Print debug messages when -d is used more than once * Change format of messages written to terminal with -d * Write fatal messages also to stderr with -n * Use IP_RECVERR socket option in chronyc to not wait unnecessarily * Shorten default chronyc timeout for localhost * Change default hostname in chronyc from localhost to 127.0.0.1 * Print error message on invalid syntax with all chronyc commands * Include simulation test suite using clknetsim Bug fixes --------- * Fix crash when selecting with multiple preferred sources * Fix frequency calculation with large frequency offsets * Fix code writing drift and RTC files to compile correctly * Fix -4/-6 options in chronyc to not reset hostname set by -h * Fix refclock sample validation with sub-second polling interval * Set stratum correctly with non-PPS SOCK refclock and local stratum * Modify dispersion accounting in refclocks to prevent PPS getting stuck with large dispersion and not accepting new samples New in version 1.29.1 ===================== Security fixes -------------- * Modify chronyc protocol to prevent amplification attacks (CVE-2014-0021) (incompatible with previous protocol version, chronyc supports both) New in version 1.29 =================== Security fixes -------------- * Fix crash when processing crafted commands (CVE-2012-4502) (possible with IP addresses allowed by cmdallow and localhost) * Don't send uninitialized data in SUBNETS_ACCESSED and CLIENT_ACCESSES replies (CVE-2012-4503) (not used by chronyc) Other changes ------------- * Drop support for SUBNETS_ACCESSED and CLIENT_ACCESSES commands New in version 1.28 =================== * Combine sources to improve accuracy * Make config and command parser strict * Add -a option to chronyc to authenticate automatically * Add -R option to ignore initstepslew and makestep directives * Add generatecommandkey, minsamples, maxsamples and user directives * Improve compatibility with NTPv1 and NTPv2 clients * Create sockets only in selected family with -4/-6 option * Treat address bind errors as non-fatal * Extend tracking log * Accept float values as initstepslew threshold * Allow hostnames in offline, online and burst commands * Fix and improve peer polling * Fix crash in config parsing with too many servers * Fix crash with duplicated initstepslew address * Fix delta calculation with extreme frequency offsets * Set local stratum correctly * Remove unnecessary adjtimex calls * Set paths in documentation by configure * Update chrony.spec New in version 1.27 =================== * Support for stronger keys via NSS or libtomcrypt library * Support reading leap second data from tz database * Support for precise clock stepping on Linux * Support for nanoseconds in SHM refclock * Make offset corrections smoother on Linux * Make transmit timestamps random below clock precision * Add corrtimeratio and maxchange directives * Extend tracking, sources and activity reports * Wait in foreground process until daemon is fully initialized * Fix crash with slow name resolving * Fix iburst with jittery sources * Fix offset stored in rtc data right after trimrtc * Fix crash and hang with RTC or manual samples * Don't use readonly adjtime on Linux kernels before 2.6.28 * Changed chronyc protocol, incompatible with older versions New in version 1.26 =================== * Add compatibility with Linux 3.0 and later * Use proper source address in NTP replies on multihomed IPv6 hosts * Accept NTP packets with versions 4, 3 and 2 * Cope with unexpected backward time jumps * Don't reset kernel frequency on start without drift file * Retry on permanent DNS error by default * Add waitsync command New in version 1.25 =================== * Improve accuracy with NTP sources * Improve accuracy with reference clocks * Improve polling interval adjustment * Improve stability with temporary asymmetric delays * Improve source selection * Improve initial synchronisation * Add delayed server name resolving * Add temperature compensation * Add nanosecond slewing to Linux driver * Add fallback drifts * Add iburst, minstratum, maxdelaydevratio, polltarget, prefer, noselect options * Add rtcsync directive to enable Linux 11-minute mode * Add reselectdist, stratumweight, logbanner, maxclockerror, include directives * Add -n option to not detach daemon from terminal * Fix pidfile directive * Fix name resolving with disabled IPv6 support * Fix reloading sample histories with reference clocks * Fix crash with auto_offline option * Fix online command on auto_offline sources * Fix file descriptor leaks * Increase burst polling interval and stop on KoD RATE * Set maxupdateskew to 1000 ppm by default * Require password for clients command * Update drift file at most once per hour * Use system headers for Linux RTC support * Reduce default chronyc timeout and make it configurable * Avoid large values in chronyc sources and sourcestats output * Add reselect command to force reselecting best source * Add -m option to allow multiple commands on command line New in version 1.24 =================== Security fixes -------------- * Don't reply to invalid cmdmon packets (CVE-2010-0292) * Limit client log memory size (CVE-2010-0293) * Limit rate of syslog messages (CVE-2010-0294) Bug fixes/Enhancements ---------------------- * Support for reference clocks (SHM, SOCK, PPS drivers) * IPv6 support * Linux capabilities support (to drop root privileges) * Memory locking support on Linux * Real-time scheduler support on Linux * Leap second support on Linux * Support for editline library * Support for new Linux readonly adjtime * NTP client support for KoD RATE * Read kernel timestamps for received NTP packets * Reply to NTP requests with correct address on multihomed hosts * Retry name resolving after temporary failure * Fix makestep command, make it available on all systems * Add makestep directive for automatic clock stepping * Don't require _bigadj kernel symbol on NetBSD * Avoid blocking read in Linux RTC driver * Support for Linux on S/390 and PowerPC * Fix various bugs on 64-bit systems * Fix valgrind errors and compiler warnings * Improve configure to support common options and variables * Improve status checking and printing in chronyc * Return non-zero exit code on errors in chronyc * Reduce request timeout in chronyc * Print estimated offset in sourcestats * Changed chronyc protocol, incompatible with older versions New in version 1.23 =================== * Support for MIPS, x86_64, sparc, alpha, arm, FreeBSD * Fix serious sign-extension error in handling IP addresses * RTC support can be excluded at compile time * Make sources gcc-4 compatible * Fix various compiler warnings * Handle fluctuations in peer distance better. * Fixed handling of stratum zero. * Fix various problems for 64-bit systems * Flush chronyc output streams after each command, to allow it to be driven through pipes * Manpage improvements Version 1.22 ============ This release number was claimed by a release that Mandriva made to patch important bugs in 1.21. The official numbering has jumped to 1.23 as a consequence. New in version 1.21 =================== * Don't include Linux kernel header files any longer : allows chrony to compile on recent distros. * Stop trying to use RTC if continuous streams of error messages would occur (Linux with HPET). 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) * Add detection of Linux 2.5 * Allow arbitrary value of HZ in Linux kernel * Fix for chrony.spec on SuSE (Paul Elliot) * Fix handling of initstepslew if no servers are listed (John Hasler) * Fix install rule in Makefile if chronyd is in use (Juliusz Chroboczek) * Replace sprintf by snprintf to remove risk of buffer overrun (John Hasler) * Add --help to configure script 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. chrony-2.1.1/README0000644000000000000000000001626412542263351012407 0ustar rootrootThis is the README for chrony. What is chrony? =============== chrony is a versatile implementation of the Network Time Protocol (NTP). It can synchronize the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network. It is designed to perform well in a wide range of conditions, including intermittent network connections, heavily congested networks, changing temperatures (ordinary computer clocks are sensitive to temperature), and systems that do not run continuosly, or run on a virtual machine. Typical accuracy between two machines on a LAN is in tens, or a few hundreds, of microseconds; over the Internet, accuracy is typically within a few milliseconds. With a good hardware reference clock sub-microsecond accuracy is possible. Two programs are included in chrony, chronyd is a daemon that can be started at boot time and chronyc is a command-line interface program which can be used to monitor chronyd's performance and to change various operating parameters whilst it is running. What will chrony run on? ======================== The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and Solaris. Closely related systems may work too. Any other system will likely 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. 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. 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.tuxfamily.org/ 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 with the subject "subscribe" to chrony-announce-request@chrony.tuxfamily.org These messages will be copied to chrony-users (see below). 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 a message with the subject "subscribe" to chrony-users-request@chrony.tuxfamily.org or chrony-dev-request@chrony.tuxfamily.org as applicable. License ======= chrony is distributed under the GNU General Public License version 2. Author ====== Richard P. Curnow Maintainers =========== Miroslav Lichvar Acknowledgements ================ The following people have provided patches and other major contributions to the program : Benny Lyne Amorsen Patch to add minstratum option Andrew Bishop 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 Entries in contrib directory Erik Bryer Entries in contrib directory Bryan Christianson Support for Mac OS X Juliusz Chroboczek Fix install rule in Makefile if chronyd file is in use. Paul Elliott DNSchrony (in contrib directory), a tool for handling NTP servers with variable IP addresses. Mike Fleetwood Fixes for compiler warnings Alexander Gretencord Changes to installation directory system to make it easier for package builders. Walter Haidinger 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 Port to NetBSD John Hasler Project and website at tuxfamily.org Changes to support 64 bit machines (i.e. those where sizeof(unsigned long) > 4) Bug fix to initstepslew directive Fix to remove potential buffer overrun errors. Memory locking and real-time scheduler support Fix fault where chronyd enters an endless loop Tjalling Hattink Fix scheduler to allow stepping clock from timeout handler Patch to take leap second in PPS refclock from locked source Patch to make reading of RTC for initial trim more reliable Liam Hatton Advice on configuring for Linux on PPC Jachym Holecek Patch to make Linux real time clock work with devfs Håkan Johansson Patch to avoid large values in sources and sourcestats output Jim Knoble Fixes for compiler warnings Antti Jrvinen Advice on configuring for BSD/386 Miroslav Lichvar Reference clock support IPv6 support Linux capabilities support Leap second support Improved source selection Improved sample history trimming Improved polling interval adjustment Improved stability with temporary asymmetric delays Temperature compensation Many other bug fixes and improvements Victor Moroz Patch to support Linux with HZ!=100 Kalle Olavi Niemitalo acquisitionport support Frank Otto Handling arbitrary HZ values Andreas Piesk Patch to make chronyc use the readline library if available Timo Teras Patch to reply correctly on multihomed hosts Wolfgang Weisselberg Entries in contrib directory Ralf Wildenhues Many robustness and security improvements Ulrich Windl for the Providing me with information about the Linux 2.2 kernel functionality compared to 2.0. Doug Woodward 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. chrony-2.1.1/addressing.h0000644000000000000000000000310712542263351014013 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Types used for addressing sources etc */ #ifndef GOT_ADDRESSING_H #define GOT_ADDRESSING_H #include "sysincl.h" /* This type is used to represent an IPv4 address or IPv6 address. All parts are in HOST order, NOT network order. */ #define IPADDR_UNSPEC 0 #define IPADDR_INET4 1 #define IPADDR_INET6 2 typedef struct { union { uint32_t in4; uint8_t in6[16]; } addr; uint16_t family; } IPAddr; typedef struct { IPAddr ip_addr; unsigned short port; } NTP_Remote_Address; typedef struct { IPAddr ip_addr; int sock_fd; } NTP_Local_Address; #endif /* GOT_ADDRESSING_H */ chrony-2.1.1/addrfilt.c0000644000000000000000000002720612542263351013462 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005 * Copyright (C) Miroslav Lichvar 2009, 2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #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<addr.in6[i * 4 + 0] << 24 | ip->addr.in6[i * 4 + 1] << 16 | ip->addr.in6[i * 4 + 2] << 8 | ip->addr.in6[i * 4 + 3]; } /* ================================================== */ inline static uint32_t get_subnet(uint32_t *addr, unsigned int where) { int off; off = where / 32; where %= 32; return (addr[off] >> (32 - NBITS - where)) & ((1UL << NBITS) - 1); } /* ================================================== */ ADF_AuthTable ADF_CreateTable(void) { ADF_AuthTable result; result = MallocNew(struct ADF_AuthTableInst); /* Default is that nothing is allowed */ result->base4.state = DENY; result->base4.extended = NULL; result->base6.state = DENY; result->base6.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; iextended[i]); close_node(child_node); } Free(node->extended); node->extended = NULL; } } /* ================================================== */ /* 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 = MallocArray(struct _TableNode, TABLE_SIZE); for (i=0; iextended[i]); child_node->state = AS_PARENT; child_node->extended = NULL; } } } /* ================================================== */ static ADF_Status set_subnet(TableNode *start_node, uint32_t *ip, int ip_len, int subnet_bits, State new_state, int delete_children) { int bits_to_go, bits_consumed; uint32_t subnet; TableNode *node; bits_consumed = 0; bits_to_go = subnet_bits; node = start_node; if ((subnet_bits < 0) || (subnet_bits > 32 * ip_len)) { return ADF_BADSUBNET; } else { if ((bits_to_go & (NBITS-1)) == 0) { while (bits_to_go > 0) { subnet = get_subnet(ip, bits_consumed); if (!(node->extended)) { open_node(node); } node = &(node->extended[subnet]); bits_to_go -= NBITS; bits_consumed += 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(ip, bits_consumed); if (!(node->extended)) { open_node(node); } node = &(node->extended[subnet]); bits_to_go -= NBITS; bits_consumed += NBITS; } /* How many subnet entries to set : 1->8, 2->4, 3->2 */ N = 1 << (NBITS-bits_to_go); subnet = get_subnet(ip, bits_consumed) & ~(N - 1); assert(subnet + N <= TABLE_SIZE); if (!(node->extended)) { open_node(node); } for (i=subnet, j=0; jextended[i]); if (delete_children) { close_node(this_node); } this_node->state = new_state; } } return ADF_SUCCESS; } } /* ================================================== */ static ADF_Status set_subnet_(ADF_AuthTable table, IPAddr *ip_addr, int subnet_bits, State new_state, int delete_children) { uint32_t ip6[4]; switch (ip_addr->family) { case IPADDR_INET4: return set_subnet(&table->base4, &ip_addr->addr.in4, 1, subnet_bits, new_state, delete_children); case IPADDR_INET6: split_ip6(ip_addr, ip6); return set_subnet(&table->base6, ip6, 4, subnet_bits, new_state, delete_children); case IPADDR_UNSPEC: /* Apply to both, subnet_bits has to be 0 */ if (subnet_bits != 0) return ADF_BADSUBNET; memset(ip6, 0, sizeof (ip6)); if (set_subnet(&table->base4, ip6, 1, 0, new_state, delete_children) == ADF_SUCCESS && set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS) return ADF_SUCCESS; break; } return ADF_BADSUBNET; } ADF_Status ADF_Allow(ADF_AuthTable table, IPAddr *ip, int subnet_bits) { return set_subnet_(table, ip, subnet_bits, ALLOW, 0); } /* ================================================== */ ADF_Status ADF_AllowAll(ADF_AuthTable table, IPAddr *ip, int subnet_bits) { return set_subnet_(table, ip, subnet_bits, ALLOW, 1); } /* ================================================== */ ADF_Status ADF_Deny(ADF_AuthTable table, IPAddr *ip, int subnet_bits) { return set_subnet_(table, ip, subnet_bits, DENY, 0); } /* ================================================== */ ADF_Status ADF_DenyAll(ADF_AuthTable table, IPAddr *ip, int subnet_bits) { return set_subnet_(table, ip, subnet_bits, DENY, 1); } /* ================================================== */ void ADF_DestroyTable(ADF_AuthTable table) { close_node(&table->base4); close_node(&table->base6); Free(table); } /* ================================================== */ static int check_ip_in_node(TableNode *start_node, uint32_t *ip) { uint32_t subnet; int bits_consumed = 0; int result = 0; int finished = 0; TableNode *node; State state=DENY; node = start_node; do { if (node->state != AS_PARENT) { state = node->state; } if (node->extended) { subnet = get_subnet(ip, bits_consumed); node = &(node->extended[subnet]); bits_consumed += NBITS; } 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, IPAddr *ip_addr) { uint32_t ip6[4]; switch (ip_addr->family) { case IPADDR_INET4: return check_ip_in_node(&table->base4, &ip_addr->addr.in4); case IPADDR_INET6: split_ip6(ip_addr, ip6); return check_ip_in_node(&table->base6, ip6); } return 0; } /* ================================================== */ static int is_any_allowed(TableNode *node, State parent) { State state; int i; state = node->state != AS_PARENT ? node->state : parent; assert(state != AS_PARENT); if (node->extended) { for (i = 0; i < TABLE_SIZE; i++) { if (is_any_allowed(&node->extended[i], state)) return 1; } } else if (state == ALLOW) { return 1; } return 0; } /* ================================================== */ int ADF_IsAnyAllowed(ADF_AuthTable table, int family) { switch (family) { case IPADDR_INET4: return is_any_allowed(&table->base4, AS_PARENT); case IPADDR_INET6: return is_any_allowed(&table->base6, AS_PARENT); default: return 0; } } /* ================================================== */ #if defined TEST static void print_node(TableNode *node, uint32_t *addr, int ip_len, int shift, int subnet_bits) { uint32_t new_addr[4]; int i; TableNode *sub_node; for (i=0; i> 24) & 255), ((addr[0] >> 16) & 255), ((addr[0] >> 8) & 255), ((addr[0] ) & 255)); else { for (i=0; i<4; i++) { if (addr[i]) printf("%d.%d.%d.%d", ((addr[i] >> 24) & 255), ((addr[i] >> 16) & 255), ((addr[i] >> 8) & 255), ((addr[i] ) & 255)); putchar(i < 3 ? ':' : '\0'); } } printf("/%d : %s\n", 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[0] = addr[0]; new_addr[1] = addr[1]; new_addr[2] = addr[2]; new_addr[3] = addr[3]; new_addr[ip_len - 1 - shift / 32] |= ((uint32_t)i << (shift % 32)); print_node(sub_node, new_addr, ip_len, shift - 4, subnet_bits + 4); } } } static void print_table(ADF_AuthTable table) { uint32_t addr[4]; memset(addr, 0, sizeof (addr)); printf("IPv4 table:\n"); print_node(&table->base4, addr, 1, 28, 0); memset(addr, 0, sizeof (addr)); printf("IPv6 table:\n"); print_node(&table->base6, addr, 4, 124, 0); } /* ================================================== */ int main (int argc, char **argv) { IPAddr ip; ADF_AuthTable table; table = ADF_CreateTable(); ip.family = IPADDR_INET4; ip.addr.in4 = 0x7e800000; ADF_Allow(table, &ip, 9); ip.addr.in4 = 0x7ecc0000; ADF_Deny(table, &ip, 14); #if 0 ip.addr.in4 = 0x7f000001; ADF_Deny(table, &ip, 32); ip.addr.in4 = 0x7f000000; ADF_Allow(table, &ip, 8); #endif printf("allowed: %d\n", ADF_IsAllowed(table, &ip)); ip.addr.in4 ^= 1; printf("allowed: %d\n", ADF_IsAllowed(table, &ip)); ip.family = IPADDR_INET6; memcpy(ip.addr.in6, "abcdefghijklmnop", 16); ADF_Deny(table, &ip, 66); ADF_Allow(table, &ip, 59); memcpy(ip.addr.in6, "xbcdefghijklmnop", 16); ADF_Deny(table, &ip, 128); ip.addr.in6[15] ^= 3; ADF_Allow(table, &ip, 127); printf("allowed: %d\n", ADF_IsAllowed(table, &ip)); ip.addr.in4 ^= 1; printf("allowed: %d\n", ADF_IsAllowed(table, &ip)); print_table(table); ADF_DestroyTable(table); return 0; } #endif /* defined TEST */ chrony-2.1.1/addrfilt.h0000644000000000000000000000541012542263351013460 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module for providing an authorisation filter on IP addresses */ #ifndef GOT_ADDRFILT_H #define GOT_ADDRFILT_H #include "addressing.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, IPAddr *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, IPAddr *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, IPAddr *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, IPAddr *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, IPAddr *ip); /* Check if at least one address from a given family is allowed by the rules in the table */ extern int ADF_IsAnyAllowed(ADF_AuthTable table, int family); #endif /* GOT_ADDRFILT_H */ chrony-2.1.1/array.c0000644000000000000000000000560012542263351013001 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Functions implementing an array with automatic memory allocation. */ #include "config.h" #include "sysincl.h" #include "array.h" #include "memory.h" struct ARR_Instance_Record { void *data; unsigned int elem_size; unsigned int used; unsigned int allocated; }; ARR_Instance ARR_CreateInstance(unsigned int elem_size) { ARR_Instance array; assert(elem_size > 0); array = MallocNew(struct ARR_Instance_Record); array->data = NULL; array->elem_size = elem_size; array->used = 0; array->allocated = 0; return array; } void ARR_DestroyInstance(ARR_Instance array) { Free(array->data); Free(array); } static void realloc_array(ARR_Instance array, unsigned int min_size) { size_t data_size; assert(min_size <= 2 * min_size); if (array->allocated >= min_size && array->allocated <= 2 * min_size) return; if (array->allocated < min_size) { while (array->allocated < min_size) array->allocated = array->allocated ? 2 * array->allocated : 1; } else { array->allocated = min_size; } data_size = (size_t)array->elem_size * array->allocated; assert(data_size / array->elem_size == array->allocated); array->data = Realloc(array->data, data_size); } void * ARR_GetNewElement(ARR_Instance array) { array->used++; realloc_array(array, array->used); return ARR_GetElement(array, array->used - 1); } void * ARR_GetElement(ARR_Instance array, unsigned int index) { assert(index < array->used); return (void *)((char *)array->data + (size_t)index * array->elem_size); } void * ARR_GetElements(ARR_Instance array) { return array->data; } void ARR_AppendElement(ARR_Instance array, void *element) { void *e; e = ARR_GetNewElement(array); memcpy(e, element, array->elem_size); } void ARR_SetSize(ARR_Instance array, unsigned int size) { realloc_array(array, size); array->used = size; } unsigned int ARR_GetSize(ARR_Instance array) { return array->used; } chrony-2.1.1/array.h0000644000000000000000000000366212542263351013014 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for array functions. */ #ifndef GOT_ARRAY_H #define GOT_ARRAY_H typedef struct ARR_Instance_Record *ARR_Instance; /* Create a new array with given element size */ extern ARR_Instance ARR_CreateInstance(unsigned int elem_size); /* Destroy the array */ extern void ARR_DestroyInstance(ARR_Instance array); /* Return pointer to a new element added to the end of the array */ extern void *ARR_GetNewElement(ARR_Instance array); /* Return element with given index */ extern void *ARR_GetElement(ARR_Instance array, unsigned int index); /* Return pointer to the internal array of elements */ extern void *ARR_GetElements(ARR_Instance array); /* Add a new element to the end of the array */ extern void ARR_AppendElement(ARR_Instance array, void *element); /* Set the size of the array */ extern void ARR_SetSize(ARR_Instance array, unsigned int size); /* Return current size of the array */ extern unsigned int ARR_GetSize(ARR_Instance array); #endif chrony-2.1.1/candm.h0000644000000000000000000003663012542263351012761 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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" #include "addressing.h" #include "hash.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 REQ_MODIFY_MINSTRATUM 45 #define REQ_MODIFY_POLLTARGET 46 #define REQ_MODIFY_MAXDELAYDEVRATIO 47 #define REQ_RESELECT 48 #define REQ_RESELECTDISTANCE 49 #define REQ_MODIFY_MAKESTEP 50 #define REQ_SMOOTHING 51 #define REQ_SMOOTHTIME 52 #define N_REQUEST_TYPES 53 /* 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 /* Structure used to exchange timevals independent on size of time_t */ typedef struct { uint32_t tv_sec_high; uint32_t tv_sec_low; uint32_t tv_nsec; } Timeval; /* This is used in tv_sec_high for 32-bit timestamps */ #define TV_NOHIGHSEC 0x7fffffff /* 32-bit floating-point format consisting of 7-bit signed exponent and 25-bit signed coefficient without hidden bit. The result is calculated as: 2^(exp - 25) * coef */ typedef struct { int32_t f; } Float; /* 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 { int32_t EOR; } REQ_Null; typedef struct { IPAddr mask; IPAddr address; int32_t EOR; } REQ_Online; typedef struct { IPAddr mask; IPAddr address; int32_t EOR; } REQ_Offline; typedef struct { IPAddr mask; IPAddr address; int32_t n_good_samples; int32_t n_total_samples; int32_t EOR; } REQ_Burst; typedef struct { IPAddr address; int32_t new_minpoll; int32_t EOR; } REQ_Modify_Minpoll; typedef struct { IPAddr address; int32_t new_maxpoll; int32_t EOR; } REQ_Modify_Maxpoll; typedef struct { int32_t pad; int32_t EOR; } REQ_Dump; typedef struct { IPAddr address; Float new_max_delay; int32_t EOR; } REQ_Modify_Maxdelay; typedef struct { IPAddr address; Float new_max_delay_ratio; int32_t EOR; } REQ_Modify_Maxdelayratio; typedef struct { IPAddr address; Float new_max_delay_dev_ratio; int32_t EOR; } REQ_Modify_Maxdelaydevratio; typedef struct { IPAddr address; int32_t new_min_stratum; int32_t EOR; } REQ_Modify_Minstratum; typedef struct { IPAddr address; int32_t new_poll_target; int32_t EOR; } REQ_Modify_Polltarget; typedef struct { Float new_max_update_skew; int32_t EOR; } REQ_Modify_Maxupdateskew; typedef struct { int32_t limit; Float threshold; int32_t EOR; } REQ_Modify_Makestep; typedef struct { Timeval ts; int32_t EOR; } REQ_Logon; typedef 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 index; int32_t EOR; } REQ_Source_Data; typedef struct { IPAddr ip; int32_t subnet_bits; int32_t EOR; } REQ_Allow_Deny; typedef struct { IPAddr ip; int32_t EOR; } REQ_Ac_Check; /* Flags used in NTP source requests */ #define REQ_ADDSRC_ONLINE 0x1 #define REQ_ADDSRC_AUTOOFFLINE 0x2 #define REQ_ADDSRC_IBURST 0x4 #define REQ_ADDSRC_PREFER 0x8 #define REQ_ADDSRC_NOSELECT 0x10 typedef struct { IPAddr ip_addr; uint32_t port; int32_t minpoll; int32_t maxpoll; int32_t presend_minpoll; uint32_t authkey; Float max_delay; Float max_delay_ratio; uint32_t flags; int32_t EOR; } REQ_NTP_Source; typedef struct { IPAddr ip_addr; int32_t EOR; } REQ_Del_Source; typedef struct { Float dfreq; int32_t EOR; } REQ_Dfreq; typedef struct { int32_t sec; int32_t usec; int32_t EOR; } REQ_Doffset; typedef struct { uint32_t index; int32_t EOR; } REQ_Sourcestats; /* This is based on the response size rather than the request size */ #define MAX_CLIENT_ACCESSES 8 typedef struct { uint32_t first_index; uint32_t n_indices; int32_t EOR; } REQ_ClientAccessesByIndex; typedef struct { int32_t index; int32_t EOR; } REQ_ManualDelete; typedef struct { Float distance; int32_t EOR; } REQ_ReselectDistance; #define REQ_SMOOTHTIME_RESET 0 #define REQ_SMOOTHTIME_ACTIVATE 1 typedef struct { int32_t option; int32_t EOR; } REQ_SmoothTime; /* ================================================== */ #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) Version 4 : IPv6 addressing added, 64-bit time values, sourcestats and tracking reports extended, added flags to NTP source request, trimmed source report, replaced fixed-point format with floating-point and used also instead of integer microseconds, new commands: modify stratum, modify polltarget, modify maxdelaydevratio, reselect, reselectdistance Version 5 : auth data moved to the end of the packet to allow hashes with different sizes, extended sources, tracking and activity reports, dropped subnets accessed and client accesses Version 6 : added padding to requests to prevent amplification attack, changed maximum number of samples in manual list to 16, new commands: modify makestep, smoothing report, smoothtime command */ #define PROTO_VERSION_NUMBER 6 /* The oldest protocol versions that are compatible enough with the current version to report a version mismatch for the server and the client */ #define PROTO_VERSION_MISMATCH_COMPAT_SERVER 5 #define PROTO_VERSION_MISMATCH_COMPAT_CLIENT 4 /* The first protocol version using padding in requests */ #define PROTO_VERSION_PADDING 6 /* The maximum length of padding in request packet, currently defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */ #define MAX_PADDING_LENGTH 396 /* ================================================== */ 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) */ union { REQ_Null null; 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_Maxdelaydevratio modify_maxdelaydevratio; REQ_Modify_Minstratum modify_minstratum; REQ_Modify_Polltarget modify_polltarget; REQ_Modify_Maxupdateskew modify_maxupdateskew; REQ_Modify_Makestep modify_makestep; REQ_Logon logon; REQ_Settime settime; REQ_Local local; REQ_Manual manual; REQ_Source_Data source_data; REQ_Allow_Deny allow_deny; REQ_Ac_Check ac_check; REQ_NTP_Source ntp_source; REQ_Del_Source del_source; REQ_Dfreq dfreq; REQ_Doffset doffset; REQ_Sourcestats sourcestats; REQ_ClientAccessesByIndex client_accesses_by_index; REQ_ManualDelete manual_delete; REQ_ReselectDistance reselect_distance; REQ_SmoothTime smoothtime; } data; /* Command specific parameters */ /* The following fields only set the maximum size of the packet. There are no holes between them and the actual data. */ /* Padding used to prevent traffic amplification */ uint8_t padding[MAX_PADDING_LENGTH]; /* Authentication data */ uint8_t auth[MAX_HASH_LENGTH]; } CMD_Request; /* ================================================== */ /* Authority codes for command types */ #define PERMIT_OPEN 0 #define PERMIT_LOCAL 1 #define PERMIT_AUTH 2 /* ================================================== */ /* 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 RPY_SMOOTHING 13 #define N_REPLY_TYPES 14 /* 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 /* Deprecated */ #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 #define STT_INVALIDAF 17 #define STT_BADPKTVERSION 18 #define STT_BADPKTLENGTH 19 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_CANDIDATE 4 #define RPY_SD_ST_OUTLIER 5 #define RPY_SD_FLAG_NOSELECT 0x1 #define RPY_SD_FLAG_PREFER 0x2 typedef struct { IPAddr ip_addr; int16_t poll; uint16_t stratum; uint16_t state; uint16_t mode; uint16_t flags; uint16_t reachability; uint32_t since_sample; Float orig_latest_meas; Float latest_meas; Float latest_meas_err; int32_t EOR; } RPY_Source_Data; typedef struct { uint32_t ref_id; IPAddr ip_addr; uint16_t stratum; uint16_t leap_status; Timeval ref_time; Float current_correction; Float last_offset; Float rms_offset; Float freq_ppm; Float resid_freq_ppm; Float skew_ppm; Float root_delay; Float root_dispersion; Float last_update_interval; int32_t EOR; } RPY_Tracking; typedef struct { uint32_t ref_id; IPAddr ip_addr; uint32_t n_samples; uint32_t n_runs; uint32_t span_seconds; Float sd; Float resid_freq_ppm; Float skew_ppm; Float est_offset; Float est_offset_err; int32_t EOR; } RPY_Sourcestats; typedef struct { Timeval ref_time; uint16_t n_samples; uint16_t n_runs; uint32_t span_seconds; Float rtc_seconds_fast; Float rtc_gain_rate_ppm; int32_t EOR; } RPY_Rtc; typedef struct { uint32_t centiseconds; Float dfreq_ppm; Float new_afreq_ppm; int32_t EOR; } RPY_ManualTimestamp; typedef struct { IPAddr 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_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]; int32_t EOR; } RPY_ClientAccessesByIndex; #define MAX_MANUAL_LIST_SAMPLES 16 typedef struct { Timeval when; Float slewed_offset; Float orig_offset; Float residual; } RPY_ManualListSample; typedef struct { uint32_t n_samples; RPY_ManualListSample samples[MAX_MANUAL_LIST_SAMPLES]; int32_t EOR; } RPY_ManualList; typedef struct { int32_t online; int32_t offline; int32_t burst_online; int32_t burst_offline; int32_t unresolved; int32_t EOR; } RPY_Activity; #define RPY_SMT_FLAG_ACTIVE 0x1 #define RPY_SMT_FLAG_LEAPONLY 0x2 typedef struct { uint32_t flags; Float offset; Float freq_ppm; Float wander_ppm; Float last_update_ago; Float remaining_time; int32_t EOR; } RPY_Smoothing; 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 pad1; /* Padding for compatibility and 4 byte alignment */ uint16_t pad2; uint16_t pad3; 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) */ 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_ClientAccessesByIndex client_accesses_by_index; RPY_ManualList manual_list; RPY_Activity activity; RPY_Smoothing smoothing; } data; /* Reply specific parameters */ /* authentication of the packet, there is no hole after the actual data from the data union, this field only sets the maximum auth size */ uint8_t auth[MAX_HASH_LENGTH]; } CMD_Reply; /* ================================================== */ #endif /* GOT_CANDM_H */ chrony-2.1.1/chrony.conf.5.in0000644000000000000000000000412312542263372014442 0ustar rootroot.TH chrony.conf 5 "June 2015" "chrony 2.1.1" "Configuration Files" .SH NAME chrony.conf \- chronyd configuration file .SH SYNOPSIS .B @SYSCONFDIR@/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@SYSCONFDIR@/chrony.conf\fR. Assuming that your NTP servers are called `foo.example.net', `bar.example.net' and `baz.example.net', your \fBchrony.conf\fR file could contain as a minimum .EX server foo.example.net server bar.example.net server baz.example.net .EE 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 may be particularly useful : `driftfile', `makestep', `rtcsync'. Also, the `iburst' server option is useful to speed up the initial synchronization. The smallest useful configuration file would look something like .EX server foo.example.net iburst server bar.example.net iburst server baz.example.net iburst driftfile @CHRONYVARDIR@/drift makestep 10 3 rtcsync .EE When using a pool of NTP servers (one name is used for multiple servers which may change over time), it's better to specify them with the `pool' directive instead of multiple `server' directives. The configuration file could in this case look like .EX pool pool.ntp.org iburst driftfile @CHRONYVARDIR@/drift makestep 10 3 rtcsync .EE .SH "SEE ALSO" .BR chronyc(1), .BR chronyd(8) .I http://chrony.tuxfamily.org/ .SH AUTHOR Richard Curnow This man-page was written by Jan Schaumann as part of "The Missing Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details. The complete chrony documentation is supplied in texinfo format. chrony-2.1.1/chrony.texi.in0000644000000000000000000060624412542263351014334 0ustar rootroot\input texinfo @c {{{ Main header stuff @afourwide @paragraphindent 0 @setfilename chrony.info @settitle User guide for the chrony suite @c @setchapternewpage off @ifinfo @dircategory Net Utilities @direntry * chrony: (chrony). How to use chronyd and chronyc * chronyd: (chrony)Starting chronyd. Reference for chronyd * chronyc: (chrony)Running chronyc. Reference for chronyc @end direntry @end ifinfo @titlepage @sp 10 @title The chrony suite @subtitle This manual describes how to use @subtitle the programs chronyd and chronyc @author Richard P. Curnow @page @vskip 0pt plus 1filll Copyright @copyright{} 1997-1999 Richard P. Curnow Copyright @copyright{} 2009-2015 Miroslav Lichvar @end titlepage @c }}} @c {{{ Top node @node Top @top @menu * Introduction:: What the chrony suite does * Installation:: How to compile and install the software * Typical scenarios:: How to configure the software for some common cases * Usage reference:: Reference manual * GPL:: The GNU General Public License @end menu @c }}} @c {{{ Ch:Introduction @c {{{ Chapter top @node Introduction @chapter Introduction @menu * Overview:: What the programs do * Acknowledgements:: Credit where credit is due * Availability:: Where to get the software * Other time synchronisation packages:: Comparision with other software * Distribution and warranty:: There is no warranty * Bug reporting:: How to report bugs and make suggestions * Contributing:: Areas where contributions are particularly welcome @end menu @c }}} @c {{{ S:Overview @node Overview @section Overview chrony is a versatile implementation of the Network Time Protocol (NTP). It can synchronize the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network. It is designed to perform well in a wide range of conditions, including intermittent network connections, heavily congested networks, changing temperatures (ordinary computer clocks are sensitive to temperature), and systems that do not run continuosly, or run on a virtual machine. Typical accuracy between two machines on a LAN is in tens, or a few hundreds, of microseconds; over the Internet, accuracy is typically within a few milliseconds. With a good hardware reference clock sub-microsecond accuracy is possible. Two programs are included in chrony, @code{chronyd} is a daemon that can be started at boot time and @code{chronyc} is a command-line interface program which can be used to monitor @code{chronyd}'s performance and to change various operating parameters whilst it is running. The IP addresses from which @code{chronyc} clients may connect can be tightly controlled. The default is just the computer that @code{chronyd} itself is running on. @c }}} @c {{{ S:Acknowledgments @node Acknowledgements @section Acknowledgements The @code{chrony} suite makes use of the algorithm known as @emph{RSA Data Security, Inc. MD5 Message-Digest Algorithm} for authenticating messages between different machines on the network. In writing the @code{chronyd} program, extensive use has been made of RFC 1305 and RFC 5905, written by David Mills. The source code of the NTP reference implementation has been used to check details of the protocol. @c }}} @c {{{ S:Availability @node Availability @section Availability @menu * Getting the software:: Where can I get the software from? * Platforms:: Which platforms will it run on? @end menu @node Getting the software @subsection Getting the software Links on @uref{http://chrony.tuxfamily.org, the chrony home page} describe how to obtain the software. @node Platforms @subsection Platforms Although most of the program is portable between Unix-like systems, there are parts that have to be tailored to each specific vendor's system. These are the parts that interface with the operating system's facilities for adjusting the system clock; different operating systems may provide different function calls to achieve this, and even where the same function is used it may have different quirks in its behaviour. The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and Solaris. Closely related systems may work too. Porting the software to other systems (particularly to those supporting an @code{adjtime} system call) should not be difficult, however it requires access to such systems to test out the driver. @c }}} @c {{{ S:Other programs @node Other time synchronisation packages @section Relationship to other software packages @menu * Comparison with ntpd:: * Comparison with timed:: @end menu @node Comparison with ntpd @subsection ntpd The `reference' implementation of the Network Time Protocol is the program @code{ntpd}, available via @uref{http://www.ntp.org/, The NTP home page}. One of the main differences between @code{ntpd} and @code{chronyd} is in the algorithms used to control the computer's clock. Things @code{chronyd} can do better than @code{ntpd}: @itemize @bullet @item @code{chronyd} can perform usefully in an environment where access to the time reference is intermittent. @code{ntpd} needs regular polling of the reference to work well. @item @code{chronyd} can usually synchronise the clock faster and with better time accuracy. @item @code{chronyd} quickly adapts to sudden changes in the rate of the clock (e.g. due to changes in the temperature of the crystal oscillator). @code{ntpd} may need a long time to settle down again. @item @code{chronyd} can perform well even when the network is congested for longer periods of time. @item @code{chronyd} in the default configuration never steps the time to not upset other running programs. @code{ntpd} can be configured to never step the time too, but it has to use a different means of adjusting the clock, which has some disadvantages. @item @code{chronyd} can adjust the rate of the clock on Linux in a larger range, which allows it to operate even on machines with broken or unstable clock (e.g. in some virtual machines). @end itemize Things @code{chronyd} can do that @code{ntpd} can't: @itemize @bullet @item @code{chronyd} provides support for isolated networks whether the only method of time correction is manual entry (e.g. by the administrator looking at a clock). @code{chronyd} can look at the errors corrected at different updates to work out the rate at which the computer gains or loses time, and use this estimate to trim the computer clock subsequently. @item @code{chronyd} provides support to work out the gain or loss rate of the `real-time clock', i.e. the clock that maintains the time when the computer is turned off. It can use this data when the system boots to set the system time from a corrected version of the real-time clock. These real-time clock facilities are only available on Linux, so far. @end itemize Things @code{ntpd} can do that @code{chronyd} can't: @itemize @bullet @item @code{ntpd} supports all operating modes from RFC 5905, including broadcast, multicast and manycast client / server. It supports the orphan mode and it also supports authentication based on public-key cryptography described in RFC 5906. @item @code{ntpd} has been ported to more types of computer / operating system. @item @code{ntpd} includes drivers for many reference clocks. @code{chronyd} relies on other programs (e.g. gpsd) to access the data from the reference clocks. @end itemize @node Comparison with timed @subsection timed @code{timed} is a program that is part of the BSD networking suite. It uses broadcast packets to find all machines running the daemon within a subnet. The machines elect a master which periodically measures the system clock offsets of the other computers using ICMP timestamps. Corrections are sent to each member as a result of this process. Problems that may arise with @code{timed} are : @itemize @bullet @item Because it uses broadcasts, it is not possible to isolate its functionality to a particular group of computers; there is a risk of upsetting other computers on the same network (e.g. where a whole company is on the same subnet but different departments are independent from the point of view of administering their computers.) @item The update period appears to be 10 minutes. Computers can build up significant offsets relative to each other in that time. If a computer can estimate its rate of drift it can keep itself closer to the other computers between updates by adjusting its clock every few seconds. @code{timed} does not seem to do this. @item @code{timed} does not have any integrated capability for feeding real-time into its estimates, or for estimating the average rate of time loss/gain of the machines relative to real-time (unless one of the computers in the group has access to an external reference and is always appointed as the `master'). @end itemize @code{timed} does have the benefit over @code{chronyd} that for isolated networks of computers, they will track the `majority vote' time. For such isolated networks, @code{chronyd} requires one computer to be the `master' with the others slaved to it. If the master has a particular defective clock, the whole set of computers will tend to slip relative to real time (but they @emph{will} stay accurate relative to one another). @c }}} @c {{{ S:Rights + warranty @node Distribution and warranty @section Distribution rights and (lack of) warranty Chrony may be distributed in accordance with the GNU General Public License version 2, reproduced in @xref{GPL}. @c }}} @c {{{ S:Bug reporting + suggestions @node Bug reporting @section Bug reporting and suggestions If you think you've found a bug in chrony, or have a suggestion, please let us know. You can join chrony users mailing list by sending a message with the subject subscribe to @email{chrony-users-request@@chrony.tuxfamily.org}. Only subscribers can post to the list. When you are reporting a bug, please send us all the information you can. Unfortunately, chrony has proven to be one of those programs where it is very difficult to reproduce bugs in a different environment. So we may have to interact with you quite a lot to obtain enough extra logging and tracing to pin-point the problem in some cases. Please be patient and plan for this! Of course, if you can debug the problem yourself and send us a source code patch to fix it, we will be very grateful! @c }}} @c {{{ S:Contributions @node Contributing @section Contributions Although chrony is now a fairly mature and established project, there are still areas that could be improved. If you can program in C and have some expertise in these areas, you might be able to fill the gaps. Particular areas that need addressing are : @enumerate @item Porting to other Unices This involves creating equivalents of sys_solaris.c, sys_linux.c etc for the new system. @item Porting to Windows NT A small amount of work on this was done under Cygwin. Only the sorting out of the include files has really been achieved so far. The two main areas still to address are @enumerate @item The system clock driver. @item How to make chronyd into an NT service (i.e. what to replace fork(), setsid() etc with so that chronyd can be automatically started in the system bootstrap. @end enumerate @item More drivers for reference clock support @end enumerate @c }}} @c }}} @c {{{ Ch:Installation @node Installation @chapter Installation @c {{{ main introduction text The software is distributed as source code which has to be compiled. The source code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. After unpacking the source code, change directory into it, and type @example ./configure @end example This is a shell script that automatically determines the system type. There is a single optional parameter, @code{--prefix} which indicates the directory tree where the software should be installed. For example, @example ./configure --prefix=/opt/free @end example will install the @code{chronyd} daemon into /opt/free/sbin and the @code{chronyc} control program into /opt/free/bin. The default value for the prefix is /usr/local. The configure script assumes you want to use gcc as your compiler. If you want to use a different compiler, you can configure this way: @example CC=cc CFLAGS=-O ./configure --prefix=/opt/free @end example for Bourne-family shells, or @example setenv CC cc setenv CFLAGS -O ./configure --prefix=/opt/free @end example for C-family shells. If the software cannot (yet) be built on your system, an error message will be shown. Otherwise, @file{Makefile} will be generated. If editline or readline library is available, chronyc will be built with line editing support. If you don't want this, specify the --disable-readline flag to configure. Please refer to @pxref{line editing support} for more information. If a @file{timepps.h} header is available (e.g. from the @uref{http://linuxpps.org/, LinuxPPS project}), @code{chronyd} will be built with PPS API reference clock driver. If the header is installed in a location that isn't normally searched by the compiler, you can add it to the searched locations by setting @code{CPPFLAGS} variable to @code{-I/path/to/timepps}. Now type @example make @end example to build the programs. If you want to build the manual in plain text, HTML and info versions, type @example make docs @end example Once the programs have been successfully compiled, they need to be installed in their target locations. This step normally needs to be performed by the superuser, and requires the following command to be entered. @example make install @end example This will install the binaries, plain text manual and manpages. To install the HTML and info versions of the manual as well, enter the command @example make install-docs @end example If you want chrony to appear in the top level info directory listing, you need to run the @command{install-info} command manually after this step. @command{install-info} takes 2 arguments. The first is the path to the @file{chrony.info} file you have just installed. This will be the argument you gave to --prefix when you configured (@file{/usr/local} by default), with @file{/share/info/chrony.info} on the end. The second argument is the location of the file called @file{dir}. This will typically be @file{/usr/share/info/dir}. So the typical command line would be @example install-info /usr/local/share/info/chrony.info /usr/share/info/dir @end example Now that the software is successfully installed, the next step is to set up a configuration file. The default location of the file is @file{@SYSCONFDIR@/chrony.conf}. Several examples of configuration with comments are included in the examples directory. Suppose you want to use public NTP servers from the pool.ntp.org project as your time reference. A minimal useful configuration file could be @example pool pool.ntp.org iburst makestep 10 3 rtcsync @end example Then, @code{chronyd} can be run. @c }}} @menu * line editing support:: If libraries are in a non-standard place * package builders:: Extra options useful to package builders @end menu @c {{{ line editing support @node line editing support @section Support for line editing libraries Chronyc can be built with support for line editing, this allows you to use the cursor keys to replay and edit old commands. Two libraries are supported which provide such functionality, editline and GNU readline. Please note that readline since version 6.0 is licensed under GPLv3+ which is incompatible with chrony's license GPLv2. You should use editline instead if you don't want to use older readline versions. The configure script will automatically enable the line editing support if one of the supported libraries is available. If they are both available, the editline library will be used. If you don't want to use it (in which case chronyc will use a minimal command line interface), invoke configure like this: @example ./configure --disable-readline other-options... @end example If you have editline, readline or ncurses installed in locations that aren't normally searched by the compiler and linker, you need to use extra options: @table @samp @item --with-readline-includes=directory_name This defines the name of the directory above the one where @file{readline.h} is. @file{readline.h} is assumed to be in @file{editline} or @file{readline} subdirectory of the named directory. @item --with-readline-library=directory_name This defines the directory containing the @file{libedit.a} or @file{libedit.so} file, or @file{libreadline.a} or @file{libreadline.so} file. @item --with-ncurses-library=directory_name This defines the directory containing the @file{libncurses.a} or @file{libncurses.so} file. @end table @c }}} @c {{{ @node package builders @section Extra options for package builders The configure and make procedures have some extra options that may be useful if you are building a distribution package for chrony. The --infodir=DIR option to configure specifies an install directory for the info files. This overrides the @file{info} subdirectory of the argument to the --prefix option. For example, you might use @example ./configure --prefix=/usr --infodir=/usr/share/info @end example The --mandir=DIR option to configure specifies an install directory for the man pages. This overrides the @file{man} subdirectory of the argument to the --prefix option. @example ./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man @end example to set both options together. The final option is the DESTDIR option to the make command. For example, you could use the commands @example ./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man make all docs make install DESTDIR=./tmp cd tmp tar cvf - . | gzip -9 > chrony.tar.gz @end example to build a package. When untarred within the root directory, this will install the files to the intended final locations. @c }}} @c }}} @c {{{ Ch:Typical operating scenarios @c {{{ Chapter top @node Typical scenarios @chapter Typical operating scenarios @menu * Computers on the net:: Your computer is on the Internet most of the time (or on a private network with NTP servers) * Infrequent connection:: You connect to the Internet sometimes (e.g. via a modem) * Isolated networks:: You have an isolated network with no reference clocks * Dial-up home PCs:: Additional considerations if you turn your computer off when it's not in use * Configuration options overview:: Overview of some configuration options @end menu @c }}} @c {{{ S:Permanent connection @node Computers on the net @section Computers connected to the internet In this section we discuss how to configure chrony for computers that are connected to the Internet (or to any network containing true NTP servers which ultimately derive their time from a reference clock) permanently or most of the time. To operate in this mode, you will need to know the names of the NTP server machines you wish to use. You may be able to find names of suitable servers by one of the following methods: @itemize @bullet @item Your institution may already operate servers on its network. Contact your system administrator to find out. @item Your ISP probably has one or more NTP servers available for its customers. @item Somewhere under the NTP homepage there is a list of public stratum 1 and stratum 2 servers. You should find one or more servers that are near to you --- check that their access policy allows you to use their facilities. @item Use public servers from @uref{http://www.pool.ntp.org/, the pool.ntp.org project}. @end itemize Assuming that you have found some servers, you need to set up a configuration file to run chrony. The (compiled-in) default location for this file is @file{@SYSCONFDIR@/chrony.conf}. Assuming that your NTP servers are called @code{foo.example.net}, @code{bar.example.net} and @code{baz.example.net}, your @file{chrony.conf} file could contain as a minimum @example server foo.example.net server bar.example.net server baz.example.net @end example However, you will probably want to include some of the other directives described later. The following directives may be particularly useful : @code{driftfile}, @code{makestep}, @code{rtcsync}. Also, the @code{iburst} server option is useful to speed up the initial synchronization. The smallest useful configuration file would look something like @example server foo.example.net iburst server bar.example.net iburst server baz.example.net iburst driftfile @CHRONYVARDIR@/drift makestep 10 3 rtcsync @end example When using a pool of NTP servers (one name is used for multiple servers which may change over time), it's better to specify them with the @code{pool} directive instead of multiple @code{server} directives. The configuration file could in this case look like @example pool pool.ntp.org iburst driftfile @CHRONYVARDIR@/drift makestep 10 3 rtcsync @end example @c }}} @c {{{ S:Infrequent connection @node Infrequent connection @section Infrequent connection to true NTP servers In this section we discuss how to configure chrony for computers that have occasional connections to the internet. @menu * Configuration for infrequent connections:: How to set up the @code{@SYSCONFDIR@/chrony.conf} file * Advising chronyd of internet availability:: How to tell chronyd when the link is available @end menu @node Configuration for infrequent connections @subsection Setting up the configuration file for infrequent connections As in the previous section, you will need access to NTP servers on the internet. The same remarks apply for how to find them. In this case, you will need some additional configuration to tell @code{chronyd} when the connection to the internet goes up and down. This saves the program from continuously trying to poll the servers when they are inaccessible. Again, assuming that your NTP servers are called @code{foo.example.net}, @code{bar.example.net} and @code{baz.example.net}, your @file{chrony.conf} file would need to contain something like @example server foo.example.net server bar.example.net server baz.example.net @end example However, your computer will keep trying to contact the servers to obtain timestamps, even whilst offline. If you operate a dial-on-demand system, things are even worse, because the link to the internet will keep getting established. For this reason, it would be better to specify this part of your configuration file in the following way: @example server foo.example.net offline server bar.example.net offline server baz.example.net offline @end example The @code{offline} keyword indicates that the servers start in an offline state, and that they should not be contacted until @code{chronyd} receives notification that the link to the internet is present. In order to notify @code{chronyd} of the presence of the link, you will need to be able to log in to it with the program @code{chronyc}. To do this, @code{chronyd} needs to be configured with an administrator password. The password is read from a file specified by the @code{keyfile} directive. The @code{generatecommandkey} directive can be used to generate a random password automatically on the first @code{chronyd} start. The smallest useful configuration file would look something like @example server foo.example.net offline server bar.example.net offline server baz.example.net offline keyfile @SYSCONFDIR@/chrony.keys generatecommandkey driftfile @CHRONYVARDIR@/drift makestep 10 3 @end example The next section describes how to tell @code{chronyd} when the internet link goes up and down. @node Advising chronyd of internet availability @subsection How to tell chronyd when the internet link is available. To use this option, you will need to configure a command key in @code{chronyd's} configuration file @file{@SYSCONFDIR@/chrony.conf}, as described in the previous section. To tell @code{chronyd} when to start and finish sampling the servers, the @code{online} and @code{offline} commands of chronyc need to be used. To give an example of their use, we assume that @code{pppd} is the program being used to connect to the internet, and that chronyc has been installed at its default location @file{@BINDIR@/chronyc}. We also assume that the command key has been set up as described in the previous section. In the file @file{/etc/ppp/ip-up} we add the command sequence @example @BINDIR@/chronyc -a online @end example and in the file @file{/etc/ppp/ip-down} we add the sequence @example @BINDIR@/chronyc -a offline @end example @code{chronyd's} polling of the servers will now only occur whilst the machine is actually connected to the Internet. @c }}} @c {{{ S:Isolated networks @node Isolated networks @section Isolated networks In this section we discuss how to configure chrony for computers that never have network conectivity to any computer which ultimately derives its time from a reference clock. In this situation, one computer is selected to be the master timeserver. The other computers are either direct clients of the master, or clients of clients. The rate value in the master's drift file needs to be set to the average rate at which the master gains or loses time. @code{chronyd} includes support for this, in the form of the @code{manual} directive in the configuration file and the @code{settime} command in the @code{chronyc} program. If the master is rebooted, @code{chronyd} can re-read the drift rate from the drift file. However, the master has no accurate estimate of the current time. To get around this, the system can be configured so that the master can initially set itself to a `majority-vote' of selected clients' times; this allows the clients to `flywheel' the master across its outage. A typical configuration file for the master (called @code{master}) might be (assuming the clients are in the 192.168.165.x subnet and that the master's address is 192.168.169.170) @example driftfile @CHRONYVARDIR@/drift generatecommandkey keyfile @SYSCONFDIR@/chrony.keys initstepslew 10 client1 client3 client6 local stratum 8 manual allow 192.168.165 @end example For the clients that have to resynchronise the master when it restarts, the configuration file might be @example server master driftfile @CHRONYVARDIR@/drift logdir /var/log/chrony log measurements statistics tracking keyfile @SYSCONFDIR@/chrony.keys generatecommandkey local stratum 10 initstepslew 20 master allow 192.168.169.170 @end example The rest of the clients would be the same, except that the @code{local} and @code{allow} directives are not required. @c }}} @c {{{ S:Dial-up home PCs @node Dial-up home PCs @section The home PC with a dial-up connection @menu * Dial-up overview:: General discussion of how the software operates in this mode * Dial-up configuration:: Typical configuration files @end menu @node Dial-up overview @subsection Assumptions/how the software works This section considers the home computer which has a dial-up connection. It assumes that Linux is run exclusively on the computer. Dual-boot systems may work; it depends what (if anything) the other system does to the system's real-time clock. Much of the configuration for this case is discussed earlier (@pxref{Infrequent connection}). This section addresses specifically the case of a computer which is turned off between 'sessions'. In this case, @code{chronyd} relies on the computer's real-time clock (RTC) to maintain the time between the periods when it is powered up. The arrangement is shown in the figure below. @example @group trim if required PSTN +---------------------------+ +----------+ | | | | v | | | +---------+ +-------+ +-----+ +---+ | System's| measure error/ |chronyd| |modem| |ISP| |real-time|------------------->| |-------| | | | | clock | drift rate +-------+ +-----+ +---+ +---------+ ^ | | | | +---------------------------+ --o-----o--- set time at boot up | +----------+ |NTP server| +----------+ @end group @end example When the computer is connected to the Internet (via the modem), @code{chronyd} has access to external NTP servers which it makes measurements from. These measurements are saved, and straight-line fits are performed on them to provide an estimate of the computer's time error and rate of gaining/losing time. When the computer is taken offline from the Internet, the best estimate of the gain/loss rate is used to free-run the computer until it next goes online. Whilst the computer is running, @code{chronyd} makes measurements of the real-time clock (RTC) (via the @file{/dev/rtc} interface, which must be compiled into the kernel). An estimate is made of the RTC error at a particular RTC second, and the rate at which the RTC gains or loses time relative to true time. On 2.6 and later kernels, if your motherboard has a HPET, you need to enable the @samp{HPET_EMULATE_RTC} option in your kernel configuration. Otherwise, chrony will not be able to interact with the RTC device and will give up using it. When the computer is powered down, the measurement histories for all the NTP servers are saved to files (if the @code{dumponexit} directive is specified in the configuration file), and the RTC tracking information is also saved to a file (if the @code{rtcfile} directive has been specified). These pieces of information are also saved if the @code{dump} and @code{writertc} commands respectively are issued through @code{chronyc}. When the computer is rebooted, @code{chronyd} reads the current RTC time and the RTC information saved at the last shutdown. This information is used to set the system clock to the best estimate of what its time would have been now, had it been left running continuously. The measurement histories for the servers are then reloaded. The next time the computer goes online, the previous sessions' measurements can contribute to the line-fitting process, which gives a much better estimate of the computer's gain/loss rate. One problem with saving the measurements and RTC data when the machine is shut down is what happens if there is a power failure; the most recent data will not be saved. Although @code{chronyd} is robust enough to cope with this, some performance may be lost. (The main danger arises if the RTC has been changed during the session, with the @code{trimrtc} command in @code{chronyc}. Because of this, @code{trimrtc} will make sure that a meaningful RTC file is saved out after the change is completed). The easiest protection against power failure is to put the @code{dump} and @code{writertc} commands in the same place as the @code{offline} command is issued to take @code{chronyd} offline; because @code{chronyd} free-runs between online sessions, no parameters will change significantly between going offline from the Internet and any power failure. A final point regards home computers which are left running for extended periods and where it is desired to spin down the hard disc when it is not in use (e.g. when not accessed for 15 minutes). @code{chronyd} has been planned so it supports such operation; this is the reason why the RTC tracking parameters are not saved to disc after every update, but only when the user requests such a write, or during the shutdown sequence. The only other facility that will generate periodic writes to the disc is the @code{log rtc} facility in the configuration file; this option should not be used if you want your disc to spin down. @node Dial-up configuration @subsection Typical configuration files. To illustrate how a dial-up home computer might be configured, example configuration files are shown in this section. For the @file{@SYSCONFDIR@/chrony.conf} file, the following can be used as an example. @example server foo.example.net maxdelay 0.4 offline server bar.example.net maxdelay 0.4 offline server baz.example.net maxdelay 0.4 offline logdir /var/log/chrony log statistics measurements tracking driftfile @CHRONYVARDIR@/drift keyfile @SYSCONFDIR@/chrony.keys generatecommandkey makestep 10 3 maxupdateskew 100.0 dumponexit dumpdir @CHRONYVARDIR@ rtcfile @CHRONYVARDIR@/rtc @end example @code{pppd} is used for connecting to the internet. This runs two scripts @file{/etc/ppp/ip-up} and @file{/etc/ppp/ip-down} when the link goes online and offline respectively. The relevant part of the @file{/etc/ppp/ip-up} file is @example @BINDIR@/chronyc -a online @end example and the relevant part of the @file{/etc/ppp/ip-down} script is @example @BINDIR@/chronyc -a -m offline dump writertc @end example To start @code{chronyd} during the boot sequence, the following is in @file{/etc/rc.d/rc.local} (this is a Slackware system) @example if [ -f @SBINDIR@/chronyd -a -f @SYSCONFDIR@/chrony.conf ]; then @SBINDIR@/chronyd -r -s echo "Start chronyd" fi @end example The placement of this command may be important on some systems. In particular, @code{chronyd} may need to be started before any software that depends on the system clock not jumping or moving backwards, depending on the directives in @code{chronyd's} configuration file. For the system shutdown, @code{chronyd} should receive a SIGTERM several seconds before the final SIGKILL; the SIGTERM causes the measurement histories and RTC information to be saved out. @c }}} @c {{{ S:Other config options @node Configuration options overview @section Other important configuration options The most common option to include in the configuration file is the @code{driftfile} option. One of the major tasks of @code{chronyd} is to work out how fast or how slow the system clock runs relative to real time - e.g. in terms of seconds gained or lost per day. Measurements over a long period are usually required to refine this estimate to an acceptable degree of accuracy. Therefore, it would be bad if @code{chronyd} had to work the value out each time it is restarted, because the system clock would not run so accurately whilst the determination is taking place. To avoid this problem, @code{chronyd} allows the gain or loss rate to be stored in a file, which can be read back in when the program is restarted. This file is called the drift file, and might typically be stored in @file{@CHRONYVARDIR@/drift}. By specifying an option like the following @example driftfile @CHRONYVARDIR@/drift @end example in the configuration file (@file{@SYSCONFDIR@/chrony.conf}), the drift file facility will be activated. @c }}} @c }}} @c {{{ Ch:Usage reference @node Usage reference @chapter Usage reference @c {{{ Chapter top @menu * Starting chronyd:: Command line options for the daemon * Configuration file:: Format of the configuration file * Running chronyc:: The run-time configuration program @end menu @c }}} @c {{{ S:Starting chronyd @node Starting chronyd @section Starting chronyd If @code{chronyd} has been installed to its default location @file{@SBINDIR@/chronyd}, starting it is simply a matter of entering the command @example @SBINDIR@/chronyd @end example Information messages and warnings will be logged to syslog. If no configuration commands are specified on the command line, @code{chronyd} will read the commands from the configuration file (default @file{@SYSCONFDIR@/chrony.conf}). The command line options supported are as follows: @table @code @item -n When run in this mode, the program will not detach itself from the terminal. @item -d When run in this mode, the program will not detach itself from the terminal, and all messages will be sent to the terminal instead of to syslog. When @code{chronyd} was compiled with debugging support, this option can be used twice to print also debugging messages. @item -f This option can be used to specify an alternate location for the configuration file (default @file{@SYSCONFDIR@/chrony.conf}). @item -r This option will reload sample histories for each of the servers and refclocks being used. These histories are created by using the @code{dump} command in @code{chronyc}, or by setting the @code{dumponexit} directive in the configuration file. This option is useful if you want to stop and restart @code{chronyd} briefly for any reason, e.g. to install a new version. However, it only makes sense on systems where the kernel can maintain clock compensation whilst not under @code{chronyd's} control. The only version where this happens so far is Linux. On other systems this option should not be used. @item -R When this option is used, the @code{initstepslew} directive and the @code{makestep} directive used with a positive limit will be ignored. This option is useful when restarting @code{chronyd} and can be used in conjunction with the `-r' option. @item -s This option will set the system clock from the computer's real-time clock. This is analogous to supplying the `-s' flag to the @file{/sbin/hwclock} program during the Linux boot sequence. Support for real-time clocks is limited at present - the criteria are described in the section on the @code{rtcfile} directive (@pxref{rtcfile directive}). If used in conjunction with the `-r' flag, @code{chronyd} will attempt to preserve the old samples after setting the system clock from the real time clock (RTC). This can be used to allow @code{chronyd} to perform long term averaging of the gain or loss rate across system reboots, and is useful for dial-up systems that are shut down when not in use. For this to work well, it relies on @code{chronyd} having been able to determine accurate statistics for the difference between the RTC and system clock last time the computer was on. If @code{chronyd} doesn't support the RTC on your computer or there is no RTC installed, the system clock will be set with this option forward to the time of the last modification of the drift file (specified by the @code{driftfile} directive) to restore the system time at which @code{chronyd} was previously stopped. @item -u This option sets the name of the user to which will @code{chronyd} switch to drop root privileges if compiled with Linux capabilities support (default @code{@DEFAULT_USER@}). @item -q When run in this mode, @code{chronyd} will set the system clock once and exit. It will not detach from the terminal. @item -Q This option is similar to `-q', but it will only print the offset and not correct the clock. @item -v This option displays @code{chronyd's} version number to the terminal and exits. @item -P This option will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). This mode is supported only on Linux. @item -m This option will lock chronyd into RAM so that it will never be paged out. This mode is only supported on Linux. @item -4 With this option hostnames will be resolved only to IPv4 addresses and only IPv4 sockets will be created. @item -6 With this option hostnames will be resolved only to IPv6 addresses and only IPv6 sockets will be created. @end table On systems that support an @file{/etc/rc.local} file for starting programs at boot time, @code{chronyd} can be started from there. On systems with a System V style initialisation, a suitable start/stop script might be as shown below. This might be placed in the file @file{/etc/rc2.d/S83chrony}. @example @group #!/bin/sh # This file should have uid root, gid sys and chmod 744 # killproc() @{ # kill the named process(es) pid=`/usr/bin/ps -e | /usr/bin/grep -w $1 | /usr/bin/sed -e 's/^ *//' -e 's/ .*//'` [ "$pid" != "" ] && kill $pid @} case "$1" in 'start') if [ -f /opt/free/sbin/chronyd -a -f @SYSCONFDIR@/chrony.conf ]; then /opt/free/sbin/chronyd fi ;; 'stop') killproc chronyd ;; *) echo "Usage: /etc/rc2.d/S83chrony @{ start | stop @}" ;; esac @end group @end example (In both cases, you may want to bear in mind that @code{chronyd} can step the time when it starts. There may be other programs started at boot time that could be upset by this, so you may need to consider the ordering carefully. However, @code{chronyd} will need to start after daemons providing services that it may require, e.g. the domain name service.) @c }}} @c {{{ S:chronyd configuration file @node Configuration file @section The chronyd configuration file @c {{{ section top The configuration file is normally called @file{@SYSCONFDIR@/chrony.conf}; in fact, this is the compiled-in default. However, other locations can be specified with a command line option. Each command in the configuration file is placed on a separate line. The following sections describe each of the commands in turn. The directives can occur in any order in the file and they are not case-sensitive. The configuration commands can also be specified directly on the @code{chronyd} command line, each argument is parsed as a line and the configuration file is ignored. @menu * comments in config file:: How to write a comment * acquisitionport directive:: Set NTP client port * allow directive:: Give access to NTP clients * bindacqaddress directive:: Limit network interface used by NTP client * bindaddress directive:: Limit network interface used by NTP server * bindcmdaddress directive:: Limit network interface used for commands * broadcast directive:: Make chronyd act as an NTP broadcast server * clientloglimit directive:: Set client log memory limit * cmdallow directive:: Give control access to chronyc on other computers * cmddeny directive:: Deny control access to chronyc on other computers * cmdport directive:: Set port to use for runtime commanding * combinelimit directive:: Limit sources included in combining algorithm * commandkey directive:: Set runtime command key * corrtimeratio directive:: Set correction time ratio * deny directive:: Deny access to NTP clients * driftfile directive:: Specify location of file containing drift data * dumpdir directive:: Specify directory for dumping measurements * dumponexit directive:: Dump measurements when daemon exits * fallbackdrift directive:: Specify fallback drift intervals * generatecommandkey directive:: Generate command key automatically * hwclockfile directive:: Specify location of hwclock's adjtime file * include directive:: Include a configuration file * initstepslew directive:: Trim the system clock on boot-up * keyfile directive:: Specify location of file containing keys * leapsecmode directive:: Select leap second handling mode * leapsectz directive:: Read leap second data from tz database * local directive:: Allow unsynchronised machine to act as server * lock_all directive:: Require that chronyd be locked into RAM * log directive:: Make daemon log certain sets of information * logbanner directive:: Specify how often is banner written to log files * logchange directive:: Generate syslog messages if large offsets occur * logdir directive:: Specify directory for logging * mailonchange directive:: Send email if a clock correction above a threshold occurs * makestep directive:: Step system clock if large correction is needed * manual directive:: Allow manual entry using chronyc's settime cmd * maxchange directive:: Set maximum allowed offset * maxclockerror directive:: Set maximum frequency error of local clock * maxsamples directive:: Set maximum number of samples per source * maxslewrate directive:: Set maximum slew rate * maxupdateskew directive:: Stop bad estimates upsetting machine clock * minsamples directive:: Set minimum number of samples per source * minsources directive:: Set minimum number of selectable sources to update clock * noclientlog directive:: Prevent chronyd from gathering data about clients * peer directive:: Specify an NTP peer * pidfile directive:: Specify the file where chronyd's pid is written * pool directive:: Specify an NTP pool * port directive:: Set NTP server port * refclock directive:: Specify a reference clock * reselectdist directive:: Set improvement in distance needed to reselect a source * rtcautotrim directive:: Specify threshold at which RTC is trimmed automatically * rtcdevice directive:: Specify name of enhanced RTC device (if not /dev/rtc) * rtcfile directive:: Specify the file where real-time clock data is stored * rtconutc directive:: Specify that the real time clock keeps UTC not local time * rtcsync directive:: Specify that RTC should be automatically synchronised by kernel * sched_priority directive:: Require real-time scheduling and specify a priority for it * server directive:: Specify an NTP server * smoothtime directive:: Smooth served time to keep clients close together * stratumweight directive:: Specify how important is stratum when selecting source * tempcomp directive:: Specify temperature sensor and compensation coefficients * user directive:: Specify user for dropping root privileges @end menu @c }}} @c {{{ comments in config file @node comments in config file @subsection Comments in the configuration file The configuration file may contain comment lines. A comment line is any line that starts with zero or more spaces followed by any one of the following characters: @itemize @item ! @item ; @item # @item % @end itemize Any line with this format will be ignored. @c }}} @c {{{ acquisitionport directive @node acquisitionport directive @subsection acquisitionport By default, @code{chronyd} uses a separate client socket for each configured server and their source port is chosen arbitrarily by the operating system. However, you can use the @code{acquisitionport} directive to explicitly specify a port and use only one socket (per IPv4/IPv6 address family) for all configured servers. This may be useful for getting through firewalls. If set to 0, the source port of the socket will be chosen arbitrarily. It may be set to the same port as used by the NTP server (@pxref{port directive}) to use only one socket for all NTP packets. An example of the @code{acquisitionport} command is @example acquisitionport 1123 @end example This would change the source port used for client requests to udp/1123. You could then persuade the firewall administrator to let that port through. @c }}} @c {{{ allow @node allow directive @subsection allow The @code{allow} command is used to designate a particular subnet from which NTP clients are allowed to access the computer as an NTP server. The default is that no clients are allowed access, i.e. @code{chronyd} operates purely as an NTP client. If the @code{allow} directive is used, @code{chronyd} will be both a client of its servers, and a server to other clients. Examples of use of the command are as follows: @example allow foo.example.net allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 2001:db8::/32 allow 0/0 allow ::/0 allow @end example The first command allows the named node to be an NTP client of this computer. The second command allows any node with an IPv4 address of the form 1.2.x.y (with x and y arbitrary) to be an NTP client of this computer. Likewise, the third command allows any node with an IPv4 address of the form 3.4.5.x to have client NTP access. The fourth and fifth forms allow access from any node with an IPv4 address of the form 6.7.8.x, 6.7.9.x, 6.7.10.x or 6.7.11.x (with x arbitrary), i.e. the value 22 is the number of bits defining the specified subnet. (In the fifth form, the final byte is ignored). The sixth form is used for IPv6 addresses. The seventh and eighth forms allow access by any IPv4 and IPv6 node respectively. The ninth forms allows access by any node (IPv4 or IPv6). A second form of the directive, @code{allow all}, has a greater effect, depending on the ordering of directives in the configuration file. To illustrate the effect, consider the two examples @example allow 1.2.3.4 deny 1.2.3 allow 1.2 @end example and @example allow 1.2.3.4 deny 1.2.3 allow all 1.2 @end example In the first example, the effect is the same regardles of what order the three directives are given in. So the 1.2.x.y subnet is allowed access, except for the 1.2.3.x subnet, which is denied access, however the host 1.2.3.4 is allowed access. In the second example, the @code{allow all 1.2} directives overrides the effect of @emph{any} previous directive relating to a subnet within the specified subnet. Within a configuration file this capability is probably rather moot; however, it is of greater use for reconfiguration at run-time via @code{chronyc} (@pxref{allow all command}). Note, if the @code{initstepslew} directive (@pxref{initstepslew directive}) is used in the configuration file, each of the computers listed in that directive must allow client access by this computer for it to work. @c }}} @c {{{ bindacqaddress @node bindacqaddress directive @subsection bindacqaddress The @code{bindacqaddress} directive sets the network interface to which will @code{chronyd} bind its NTP client sockets. The syntax is similar to the @code{bindaddress} and @code{bindcmdaddress} directives. For each of IPv4 and IPv6 protocols, only one @code{bindacqaddress} directive can be specified. @c }}} @c {{{ bindaddress @node bindaddress directive @subsection bindaddress The @code{bindaddress} directive allows you to restrict the network interface to which @code{chronyd} will listen for NTP requests. This provides an additional level of access restriction above that available through the @code{deny} mechanism. Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with an internet connection. The ethernet interface's IP address is 192.168.1.1. Suppose you want to block all access through the internet connection. You could add the line @example bindaddress 192.168.1.1 @end example to the configuration file. For each of IPv4 and IPv6 protocols, only one @code{bindaddress} directive can be specified. Therefore, it's not useful on computers which should serve NTP on multiple network interfaces. @c }}} @c {{{ bindcmdaddress @node bindcmdaddress directive @subsection bindcmdaddress The @code{bindcmdaddress} directive allows you to specify the network interface to which @code{chronyd} will listen for command packets (issued by @code{chronyc}). This provides an additional level of access restriction above that available through @code{cmddeny} mechanism. By default, @code{chronyd} binds to the loopback interface (with addresses @code{127.0.0.1} and @code{::1}). This blocks all access except from localhost. To listen for command packets on all interfaces, you can add the lines @example bindcmdaddress 0.0.0.0 bindcmdaddress :: @end example to the configuration file. For each of IPv4 and IPv6 protocols, only one @code{bindcmdaddress} directive can be specified. @c }}} @c {{{ broadcast directive @node broadcast directive @subsection broadcast The @code{broadcast} directive is used to declare a broadcast address to which chronyd should send packets in NTP broadcast mode (i.e. make chronyd act as a broadcast server). Broadcast clients on that subnet will be able to synchronise. The syntax is as follows @example broadcast 30 192.168.1.255 broadcast 60 192.168.2.255 12123 broadcast 60 ff02::101 @end example In the first example, the destination port defaults to 123/udp (the normal NTP port). In the second example, the destionation port is specified as 12123. The first parameter in each case (30 or 60 respectively) is the interval in seconds between broadcast packets being sent. The second parameter in each case is the broadcast address to send the packet to. This should correspond to the broadcast address of one of the network interfaces on the computer where chronyd is running. You can have more than 1 @code{broadcast} directive if you have more than 1 network interface onto which you wish to send NTP broadcast packets. @code{chronyd} itself cannot currently act as a broadcast client; it must always be configured as a point-to-point client by defining specific NTP servers and peers. This broadcast server feature is intended for providing a time source to other NTP software (e.g. various MS Windows clients). If ntpd is used as the broadcast client, it will try to use a point-to-point client/server NTP access to measure the round-trip delay. Thus, the broadcast subnet should also be the subject of an @code{allow} directive (@pxref{allow directive}). @c }}} @c {{{ clientloglimit @node clientloglimit directive @subsection clientloglimit This directive specifies the maximum size of the memory allocated to log client accesses. When the limit is reached, only information for clients that have already been logged will be updated. If 0 is specified, the memory size will be unlimited. The default is 524288 bytes. An example of the use of this directive is @example clientloglimit 1048576 @end example @c }}} @c {{{ cmdallow @node cmdallow directive @subsection cmdallow This is similar to the @code{allow} directive (@pxref{allow directive}), except that it allows control access (rather than NTP client access) to a particular subnet or host. (By 'control access' is meant that chronyc can be run on those hosts and successfully connect to chronyd on this computer.) The syntax is identical to the @code{allow} directive. There is also a @code{cmdallow all} directive with similar behaviour to the @code{allow all} directive (but applying to control access in this case, of course). Note that @code{chronyd} has to be configured with the @code{bindcmdaddress} directive to not listen only on the loopback interface to actually allow remote access. @c }}} @c {{{ cmddeny @node cmddeny directive @subsection cmddeny This is similar to the @code{cmdallow} directive (@pxref{cmdallow directive}), except that it denies control access to a particular subnet or host, rather than allowing it. The syntax is identical. There is also a @code{cmddeny all} directive with similar behaviour to the @code{cmdallow all} directive. @c }}} @c {{{ cmdport @node cmdport directive @subsection cmdport The @code{cmdport} directive allows the port that is used for run-time command and monitoring (via the program @code{chronyc}) to be altered from its default (323/udp). If set to 0, @code{chronyd} will not open the port, this is useful to disable the @code{chronyc} access completely. An example shows the syntax @example cmdport 257 @end example This would make @code{chronyd} use 257/udp as its command port. (@code{chronyc} would need to be run with the @code{-p 257} switch to inter-operate correctly). @c }}} @c {{{ combinelimit @node combinelimit directive @subsection combinelimit When @code{chronyd} has multiple sources available for synchronization, it has to select one source as the synchronization source. The measured offsets and frequencies of the system clock relative to the other sources, however, can be combined with the selected source to improve the accuracy of the system clock. The @code{combinelimit} directive limits which sources are included in the combining algorithm. Their synchronization distance has to be shorter than the distance of the selected source multiplied by the value of the limit. Also, their measured frequencies have to be close to the frequency of the selected source. By default, the limit is 3. Setting the limit to 0 effectively disables the source combining algorithm and only the selected source will be used to control the system clock. The syntax is @example combinelimit @end example @c }}} @c {{{ commandkey @node commandkey directive @subsection commandkey The commandkey command is used to set the key number used for authenticating user commands via the chronyc program at run time. This allows certain actions of the chronyc program to be restricted to administrators. An example of the commandkey command is @example commandkey 20 @end example By default, the key number is 0. In the key file (see the keyfile command) there should be a line of the form @example 20 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC @end example When running the chronyc program to perform run-time configuration, the command @example password HEX:B028F91EA5C38D06C2E140B26C7F41EC @end example must be entered before any commands affecting the operation of the daemon can be entered, or chronyc must be started with the `-a' option to run the password command automatically. @c }}} @c {{{ corrtimeratio @node corrtimeratio directive @subsection corrtimeratio When @code{chronyd} makes a time correction, it controls how quickly the system clock is slewed (so far only on Linux). This rate affects the frequency error of the system clock. The @code{corrtimeratio} directive sets the ratio between the duration in which the clock is slewed for an average correction according to the source history and the interval in which the corrections are done (usually the NTP polling interval). Corrections larger than the average take less time and smaller corrections take more time, the amount of the correction and the correction time are inversely proportional. Increasing @code{corrtimeratio} improves the overall frequency error of the system clock, but increases the overall time error as the corrections take longer. By default, the ratio is set to 3, the time accuracy of the clock is preferred over its frequency accuracy. The syntax is @example corrtimeratio 100 @end example The maximum allowed slew rate can be set by the @code{maxslewrate} directive (@pxref{maxslewrate directive}. The current remaining correction is shown in the @code{tracking} report (@pxref{tracking command}) as the @code{System time} value. @c }}} @c {{{ deny @node deny directive @subsection deny This is similar to the @code{allow} directive (@pxref{allow directive}), except that it denies NTP client access to a particular subnet or host, rather than allowing it. The syntax is identical. There is also a @code{deny all} directive with similar behaviour to the @code{allow all} directive. @c }}} @c {{{ driftfile @node driftfile directive @subsection driftfile One of the main activities of the @code{chronyd} program is to work out the rate at which the system clock gains or loses time relative to real time. Whenever @code{chronyd} computes a new value of the gain/loss rate, it is desirable to record it somewhere. This allows @code{chronyd} to begin compensating the system clock at that rate whenever it is restarted, even before it has had a chance to obtain an equally good estimate of the rate during the new run. (This process may take many minutes, at least). The driftfile command allows a file to be specified into which @code{chronyd} can store the rate information. Two parameters are recorded in the file. The first is the rate at which the system clock gains or loses time, expressed in parts per million, with gains positive. Therefore, a value of 100.0 indicates that when the system clock has advanced by a second, it has gained 100 microseconds on reality (so the true time has only advanced by 999900 microseconds). The second is an estimate of the error bound around the first value in which the true rate actually lies. An example of the driftfile command is @example driftfile @CHRONYVARDIR@/drift @end example @c }}} @c {{{ dumpdir @node dumpdir directive @subsection dumpdir To compute the rate of gain or loss of time, @code{chronyd} has to store a measurement history for each of the time sources it uses. Certain systems (so far only Linux) have operating system support for setting the rate of gain or loss to compensate for known errors. (On other systems, @code{chronyd} must simulate such a capability by periodically slewing the system clock forwards or backwards by a suitable amount to compensate for the error built up since the previous slew). For such systems, it is possible to save the measurement history across restarts of @code{chronyd} (assuming no changes are made to the system clock behaviour whilst it is not running). If this capability is to be used (via the dumponexit command in the configuration file, or the dump command in chronyc), the dumpdir command should be used to define the directory where the measurement histories are saved. An example of the command is @example dumpdir @CHRONYVARDIR@ @end example A source whose reference id (the IP address for IPv4 sources) is 1.2.3.4 would have its measurement history saved in the file @file{/var/lib/chrony/1.2.3.4.dat}. @c }}} @c {{{ dumponexit @node dumponexit directive @subsection dumponexit If this command is present, it indicates that @code{chronyd} should save the measurement history for each of its time sources recorded whenever the program exits. (See the dumpdir command above). @c }}} @c {{{ fallbackdrift @node fallbackdrift directive @subsection fallbackdrift Fallback drifts are long-term averages of the system clock drift calculated over exponentially increasing intervals. They are used when the clock is no longer synchronised to avoid quickly drifting away from true time if there was a short-term deviation in the drift before the synchronisation was lost. The directive specifies the minimum and maximum interval since last clock update to switch between fallback drifts. They are defined as a power of 2 (in seconds). The syntax is as follows @example fallbackdrift 16 19 @end example In this example, the minimum interval is 16 (18 hours) and maximum interval is 19 (6 days). The system clock frequency will be set to the first fallback 18 hours after last clock update, to the second after 36 hours, etc. This might be a good setting to cover daily and weekly temperature fluctuations. By default (or if the specified maximum or minimum is 0), no fallbacks are used and the clock frequency changes only with new measurements from NTP, reference clocks or manual input. @c }}} @c {{{ generatecommandkey @node generatecommandkey directive @subsection generatecommandkey With this directive, if the command key is not found on start in the file specified by the @code{keyfile} directive, @code{chronyd} will generate a new command key from the /dev/urandom file and write it to the key file. The generated key will use SHA1 if @code{chronyd} is compiled with the support, otherwise MD5 will be used. @c }}} @c {{{ hwclockfile @node hwclockfile directive @subsection hwclockfile The @code{hwclockfile} directive sets the location of the adjtime file which is used by the @file{/sbin/hwclock} program. With this directive, @code{chronyd} will parse the file to find out if the RTC keeps local time or UTC. It overrides the @code{rtconutc} directive (@pxref{rtconutc directive}). An example of the command is @example hwclockfile /etc/adjtime @end example @c }}} @c {{{ include @node include directive @subsection include The @code{include} directive includes a specified configuration file. This is useful when maintaining configuration on multiple hosts to keep the differences in a separate file. @example include @SYSCONFDIR@/chrony/local.conf @end example @c }}} @c {{{ initstepslew @node initstepslew directive @subsection initstepslew In normal operation, @code{chronyd} slews the time when it needs to adjust the system clock. For example, to correct a system clock which is 1 second slow, @code{chronyd} slightly increases the amount by which the system clock is advanced on each clock interrupt, until the error is removed. (Actually, this is done by calling the @code{adjtime()} or similar system function which does it for us.) Note that at no time does time run backwards with this method. On most Unix systems it is not desirable to step the system clock, because many programs rely on time advancing monotonically forwards. When the @code{chronyd} daemon is initially started, it is possible that the system clock is considerably in error. Attempting to correct such an error by slewing may not be sensible, since it may take several hours to correct the error by this means. The purpose of the @code{initstepslew} directive is to allow @code{chronyd} to make a rapid measurement of the system clock error at boot time, and to correct the system clock by stepping before normal operation begins. Since this would normally be performed only at an appropriate point in the system boot sequence, no other software should be adversely affected by the step. If the correction required is less than a specified threshold, a slew is used instead. This makes it easier to restart @code{chronyd} whilst the system is in normal operation. The @code{initstepslew} directive takes a threshold and a list of NTP servers as arguments. Each of the servers is rapidly polled several times, and a majority voting mechanism used to find the most likely range of system clock error that is present. A step (or slew) is applied to the system clock to correct this error. @code{chronyd} then enters its normal operating mode. An example of use of the command is @example initstepslew 30 foo.example.net bar.example.net @end example where 2 NTP servers are used to make the measurement. The @code{30} indicates that if the system's error is found to be 30 seconds or less, a slew will be used to correct it; if the error is above 30 seconds, a step will be used. The @code{initstepslew} directive can also be used in an isolated LAN environment, where the clocks are set manually. The most stable computer is chosen as the master, and the other computers are slaved to it. If each of the slaves is configured with the local option (see below), the master can be set up with an @code{initstepslew} directive which references some or all of the slaves. Then, if the master machine has to be rebooted, the slaves can be relied on to 'flywheel' the time for the master. The @code{initstepslew} directive is functionally similar to a combination of the @code{makestep} and @code{server} directives with the @code{iburst} option. The main difference is that the @code{initstepslew} servers are used only before normal operation begins and that the foreground @code{chronyd} process waits for @code{initstepslew} to finish before exiting. This is useful to prevent programs started in the boot sequence after @code{chronyd} from reading the clock before it's stepped. @c }}} @c {{{ keyfile @node keyfile directive @subsection keyfile This command is used to specify the location of the file containing ID/key pairs for the following 2 uses: @itemize @bullet @item Authentication of NTP packets. @item Authentication of administrator commands entered via chronyc. @end itemize The format of the command is shown in the example below @example keyfile @SYSCONFDIR@/chrony.keys @end example The argument is simply the name of the file containing the ID/key pairs. The format of the file is shown below @example 10 tulip 11 hyacinth 20 MD5 ASCII:crocus 25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c ... @end example Each line consists of an ID, a name of authentication hash function (optional) and a password. The ID can be any unsigned integer in the range 0 through 2**32-1, but ID of 0 can be used only for the command key and not for the NTP authentication. The hash function is MD5 by default, depending on how was @code{chronyd} compiled other allowed hash functions may be SHA1, SHA256, SHA384, SHA512, RMD128, RMD160, RMD256, RMD320, TIGER and WHIRLPOOL. The password can be encoded as a string of characters not containing a space with optional @code{ASCII:} prefix or as a hexadecimal number with @code{HEX:} prefix. The password is used with the hash function to generate and verify a message authentication code (MAC) in NTP and command packets. For maximum security, it's recommended to use SHA1 or stronger hash function. The passwords should be random and they should be as long as the output size of the configured hash function, e.g. 160 bits with SHA1. The ID for the chronyc authentication key is specified with the commandkey command (see earlier). The command key can be generated automatically on start with the @code{generatecommandkey} directive. @c }}} @c {{{ leapsecmode @node leapsecmode directive @subsection leapsecmode A leap second is an adjustment that is occasionally applied to UTC to keep it close to the mean solar time. When a leap second is inserted, the last day of June or December has an extra second 23:59:60. For computer clocks that is a problem. The Unix time is defined as number of seconds since 00:00:00 UTC on 1 January 1970 without leap seconds. The system clock cannot have time 23:59:60, every minute has 60 seconds and every day has 86400 seconds by definition. The inserted leap second is skipped and the clock is suddenly ahead of UTC by one second. The @code{leapsecmode} directive selects how that error is corrected. There are four options: @table @code @item system When inserting a leap second, the kernel steps the system clock backwards by one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it steps forward by one second when the clock gets to 23:59:59 UTC. This is the default mode when the system driver supports leap seconds (currently Linux only). @item step This is similar to the @code{system} mode, except the clock is stepped by @code{chronyd} instead of the kernel. It can be useful to avoid bugs in the kernel code that would be executed in the @code{system} mode. This is the default mode when the system driver doesn't support leap seconds. @item slew The clock is corrected by slewing started at 00:00:00 UTC when a leap second is inserted or 23:59:59 UTC when a leap second is deleted. This may be preferred over the @code{system} and @code{step} modes when applications running on the system are sensitive to jumps in the system time and it's acceptable that the clock will be off for a longer time. On Linux with the default @code{maxslewrate} value (@pxref{maxslewrate directive}) the correction takes 12 seconds. @item ignore No correction is applied to the clock for the leap second. The clock will be corrected later in normal operation when new measurements are made and the estimated offset includes the one second error. @end table An example of the command is @example leapsecmode slew @end example When serving time to NTP clients that can't be configured to correct their clocks for a leap second by slewing or they would correct them at slightly different rates when it's necessary to keep them close together, the @code{slew} mode can be combined with the @code{smoothtime} directive (@pxref{smoothtime directive}) to enable a server leap smear. When smearing a leap second, the leap status is suppressed on the server and the served time is corrected slowly be slewing instead of stepping. The clients don't need any special configuration as they don't know there is any leap second and they follow the server time which eventually brings them back to UTC. Care must be taken to ensure they use for synchronization only NTP servers which smear the leap second in exactly the same way. This feature needs to be used carefully, because the server is intentionally not serving its best estimate of the true time. A recommended configuration to enable a server leap smear is: @example leapsecmode slew maxslewrate 1000 smoothtime 400 0.001 leaponly @end example The first directive is necessary to disable the clock step which would reset the smoothing process. The second directive limits the slewing rate of the local clock to 1000 ppm, which improves the stability of the smoothing process when the local correction starts and ends. The third directive enables the server time smoothing process. It will start when the clock gets to 00:00:00 UTC and it will take 17 hours 34 minutes to finish. The frequency offset will be changing by 0.001 ppm per second and will reach maximum of 31.623 ppm. The @code{leaponly} option makes the duration of the leap smear constant and allows the clients to safely synchronise with multiple identically configured leap smearing servers. @c }}} @c {{{ leapsectz @node leapsectz directive @subsection leapsectz This directive is used to set the name of the timezone in the system tz database which @code{chronyd} can use to find out when will the next leap second occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid on Jun 30 and Dec 31 in the timezone. A useful timezone is @code{right/UTC}. This is mainly useful with reference clocks which don't provide the leap second information. It is not necessary to restart @code{chronyd} if the tz database is updated with a new leap second at least 12 hours before the event. An example of the command is @example leapsectz right/UTC @end example The following shell command verifies that the timezone contains leap seconds and can be used with this directive @example $ TZ=right/UTC date -d 'Dec 31 2008 23:59:60' Wed Dec 31 23:59:60 UTC 2008 @end example @c }}} @c {{{ local @node local directive @subsection local The local keyword is used to allow @code{chronyd} to appear synchronised to real time (from the viewpoint of clients polling it), even if it has no current synchronisation source. This option is normally used on computers in an isolated network, where several computers are required to synchronise to one other, this being the "master" which is kept vaguely in line with real time by manual input. An example of the command is @example local stratum 10 @end example The value 10 may be substituted with other values in the range 1 through 15. Stratum 1 indicates a computer that has a true real-time reference directly connected to it (e.g. GPS, atomic clock etc) – such computers are expected to be very close to real time. Stratum 2 computers are those which have a stratum 1 server; stratum 3 computers have a stratum 2 server and so on. A large value of 10 indicates that the clock is so many hops away from a reference clock that its time is fairly unreliable. Put another way, if the computer ever has access to another computer which is ultimately synchronised to a reference clock, it will almost certainly be at a stratum less than 10. Therefore, the choice of a high value like 10 for the local command prevents the machine's own time from ever being confused with real time, were it ever to leak out to clients that have visibility of real servers. @c }}} @c {{{ lock_all @node lock_all directive @subsection lock_all The @code{lock_all} directive will lock chronyd into RAM so that it will never be paged out. This mode is only supported on Linux. This directive uses the Linux mlockall() system call to prevent @code{chronyd} from ever being swapped out. This should result in lower and more consistent latency. It should not have significant impact on performance as @code{chronyd's} memory usage is modest. The mlockall man page has more details. @c }}} @c {{{ log @node log directive @subsection log @c {{{ section top The log command indicates that certain information is to be logged. @table @code @item measurements This option logs the raw NTP measurements and related information to a file called measurements.log. @item statistics This option logs information about the regression processing to a file called statistics.log. @item tracking This option logs changes to the estimate of the system's gain or loss rate, and any slews made, to a file called tracking.log. @item rtc This option logs information about the system's real-time clock. @item refclocks This option logs the raw and filtered reference clock measurements to a file called refclocks.log. @item tempcomp This option logs the temperature measurements and system rate compensations to a file called tempcomp.log. @end table The files are written to the directory specified by the logdir command. An example of the command is @example log measurements statistics tracking @end example @menu * measurements log:: The format of the measurements log * statistics log:: The format of the statistics log * tracking log:: The format of the tracking log * RTC log:: The format of the RTC log * refclocks log:: The format of the refclocks log * tempcomp log:: The format of the tempcomp log @end menu @c }}} @c {{{ measurements.log @node measurements log @subsubsection Measurements log file format An example line (which actually appears as a single line in the file) from the measurements log file is shown below. @example 2014-10-13 05:40:50 158.152.1.76 N 2 111 111 1111 10 10 1.0 \ -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [2014-10-13] @item Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. @item IP address of server/peer from which measurement comes [158.152.1.76] @item Leap status (@code{N} means normal, @code{+} means that the last minute of the current month has 61 seconds, @code{-} means that the last minute of the month has 59 seconds, @code{?} means the remote computer is not currently synchronised.) [N] @item Stratum of remote computer. [2] @item RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111] @item RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111] @item Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, against defined parameters, and a test for synchronisation loop (1=pass, 0=fail) [1111] @item Local poll [10] @item Remote poll [10] @item `Score' (an internal score within each polling level used to decide when to increase or decrease the polling level. This is adjusted based on number of measurements currently being used for the regression algorithm). [1.0] @item The estimated local clock error (`theta' in RFC 5905). Positive indicates that the local clock is slow of the remote source. [-4.966e-03]. @item The peer delay (`delta' in RFC 5905). [2.296e-01] @item The peer dispersion (`epsilon' in RFC 5905). [1.577e-05] @item The root delay (`DELTA' in RFC 5905). [1.615e-01] @item The root dispersion (`EPSILON' in RFC 5905). [7.446e-03] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c {{{ statistics.log @node statistics log @subsubsection Statistics log file format An example line (which actually appears as a single line in the file) from the statistics log file is shown below. @example 1998-07-22 05:40:50 158.152.1.76 6.261e-03 -3.247e-03 \ 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [1998-07-22] @item Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. @item IP address of server/peer from which measurement comes [158.152.1.76] @item The estimated standard deviation of the measurements from the source (in seconds). [6.261e-03] @item The estimated offset of the source (in seconds, positive means the local clock is estimated to be fast, in this case). [-3.247e-03] @item The estimated standard deviation of the offset estimate (in seconds). [2.220e-03] @item The estimated rate at which the local clock is gaining or losing time relative to the source (in seconds per second, positive means the local clock is gaining). This is relative to the compensation currently being applied to the local clock, @emph{not} to the local clock without any compensation. [1.874e-06] @item The estimated error in the rate value (in seconds per second). [1.080e-06]. @item The ration of |old_rate - new_rate| / old_rate_error. Large values indicate the statistics are not modelling the source very well. [7.8e-02] @item The number of measurements currently being used for the regression algorithm. [16] @item The new starting index (the oldest sample has index 0; this is the method used to prune old samples when it no longer looks like the measurements fit a linear model). [0, i.e. no samples discarded this time] @item The number of runs. The number of runs of regression residuals with the same sign is computed. If this is too small it indicates that the measurements are no longer represented well by a linear model and that some older samples need to be discarded. The number of runs for the data that is being retained is tabulated. Values of approximately half the number of samples are expected. [8] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c {{{ tracking.log @node tracking log @subsubsection Tracking log file format An example line (which actually appears as a single line in the file) from the tracking log file is shown below. @example 2012-02-23 05:40:50 158.152.1.76 3 340.529 1.606 1.046e-03 N \ 4 6.849e-03 -4.670e-04 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [2012-02-03] @item Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. @item The IP address of the server/peer to which the local system is synchronised. [158.152.1.76] @item The stratum of the local system. [3] @item The local system frequency (in ppm, positive means the local system runs fast of UTC). [340.529] @item The error bounds on the frequency (in ppm) [1.606] @item The estimated local offset at the epoch (which is rapidly corrected by slewing the local clock. (In seconds, positive indicates the local system is fast of UTC). [1.046e-3] @item Leap status (@code{N} means normal, @code{+} means that the last minute of this month has 61 seconds, @code{-} means that the last minute of the month has 59 seconds, @code{?} means the clock is not currently synchronised.) [N] @item The number of combined sources. [4] @item The estimated standard deviation of the combined offset (in seconds). [6.849e-03] @item The remaining offset correction from the previous update (in seconds, positive means the system clock is slow of UTC). [-4.670e-04] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c {{{ rtc.log @node RTC log @subsubsection Real-time clock log file format An example line (which actually appears as a single line in the file) from the measurements log file is shown below. @example 1998-07-22 05:40:50 -0.037360 1 -0.037434\ -37.948 12 5 120 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [1998-07-22] @item Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. @item The measured offset between the system's real time clock and the system (@code{gettimeofday()}) time. In seconds, positive indicates that the RTC is fast of the system time. [-0.037360]. @item Flag indicating whether the regression has produced valid coefficients. (1 for yes, 0 for no). [1] @item Offset at the current time predicted by the regression process. A large difference between this value and the measured offset tends to indicate that the measurement is an outlier with a serious measurement error. [-0.037434]. @item The rate at which the RTC is losing or gaining time relative to the system clock. In ppm, with positive indicating that the RTC is gaining time. [-37.948] @item The number of measurements used in the regression. [12] @item The number of runs of regression residuals of the same sign. Low values indicate that a straight line is no longer a good model of the measured data and that older measurements should be discarded. [5] @item The measurement interval used prior to the measurement being made (in seconds). [120] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c {{{ refclocks.log @node refclocks log @subsubsection Refclocks log file format An example line (which actually appears as a single line in the file) from the refclocks log file is shown below. @example 2009-11-30 14:33:27.000000 PPS2 7 N 1 4.900000e-07 -6.741777e-07 1.000e-06 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [2009-11-30] @item Hour:Minute:Second.Microsecond [14:33:27.000000]. Note that the date/time pair is expressed in UTC, not the local time zone. @item Reference ID of refclock from which measurement comes. [PPS2] @item Sequence number of driver poll within one polling interval for raw samples, or @code{-} for filtered samples. [7] @item Leap status (@code{N} means normal, @code{+} means that the last minute of the current month has 61 seconds, @code{-} means that the last minute of the month has 59 seconds). [N] @item Flag indicating whether the sample comes from PPS source. (1 for yes, 0 for no, or @code{-} for filtered sample). [1] @item Local clock error measured by refclock driver, or @code{-} for filtered sample. [4.900000e-07] @item Local clock error with applied corrections. Positive indicates that the local clock is slow. [-6.741777e-07] @item Assumed dispersion of the sample. [1.000e-06] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c {{{ tempcomp.log @node tempcomp log @subsubsection Tempcomp log file format An example line (which actually appears as a single line in the file) from the tempcomp log file is shown below. @example 2010-04-19 10:39:48 2.8000e+04 3.6600e-01 @end example The columns are as follows (the quantities in square brackets are the values from the example line above) : @enumerate 1 @item Date [2010-04-19] @item Hour:Minute:Second [10:39:48]. Note that the date/time pair is expressed in UTC, not the local time zone. @item Temperature read from tempcomp file. [2.8000e+04] @item Applied compensation in ppm, positive means the system clock is running faster than it would be without the compensation. [3.6600e-01] @end enumerate A banner is periodically written to the log file to indicate the meanings of the columns. @c }}} @c }}} @c {{{ logbanner @node logbanner directive @subsection logbanner A banner is periodically written to the log files enabled by the @code{log} directive to indicate the meanings of the columns. The @code{logbanner} directive specifies after how many entries in the log file should be the banner written. The default is 32, and 0 can be used to disable it entirely. @c }}} @c {{{ logchange @node logchange directive @subsection logchange This directive forces @code{chronyd} to send a message to syslog if it makes a system clock adjustment larger than a threshold value. An example of use is @example logchange 0.5 @end example which would cause a syslog message to be generated a system clock error of over 0.5 seconds starts to be compensated. Clock errors detected either via NTP packets or via timestamps entered via the @code{settime} command of @code{chronyc} are logged. This directive assumes that syslog messages are appearing where somebody can see them. This allows that person to see if a large error has arisen, e.g. because of a fault, or because of faulty timezone handling, for example when summer time (daylight saving) starts or ends. @c }}} @c {{{ logdir @node logdir directive @subsection logdir This directive allows the directory where log files are written to be specified. An example of the use of this directive is @example logdir /var/log/chrony @end example @c }}} @c {{{ mailonchange @node mailonchange directive @subsection mailonchange This directive defines an email address to which mail should be sent if chronyd applies a correction exceeding a particular threshold to the system clock. An example of use of this directive is @example mailonchange root@@localhost 0.5 @end example This would send a mail message to root if a change of more than 0.5 seconds were applied to the system clock. @c }}} @c {{{ makestep @node makestep directive @subsection makestep Normally chronyd will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, the system clock may be so far adrift that this slewing process would take a very long time to correct the system clock. This directive forces @code{chronyd} to step system clock if the adjustment is larger than a threshold value, but only if there were no more clock updates since @code{chronyd} was started than a specified limit (a negative value can be used to disable the limit). This is particularly useful when using reference clocks, because the @code{initstepslew} directive (@pxref{initstepslew directive}) works only with NTP sources. An example of the use of this directive is @example makestep 1000 10 @end example This would step system clock if the adjustment is larger than 1000 seconds, but only in the first ten clock updates. @c }}} @c {{{ manual @node manual directive @subsection manual The @code{manual} directive enables support at run-time for the @code{settime} command in chronyc (@pxref{settime command}). If no @code{manual} directive is included, any attempt to use the @code{settime} command in chronyc will be met with an error message. Note that the @code{settime} command can be enabled at run-time using the @code{manual} command in chronyc (@pxref{manual command}). (The idea of the two commands is that the @code{manual} command controls the manual clock driver's behaviour, whereas the @code{settime} command allows samples of manually entered time to be provided). @c }}} @c {{{ maxchange @node maxchange directive @subsection maxchange This directive sets the maximum allowed offset corrected on a clock update. The check is performed only after the specified number of updates to allow a large initial adjustment of the system clock. When an offset larger than the specified maximum occurs, it will be ignored for the specified number of times and then @code{chronyd} will give up and exit (a negative value can be used to never exit). In both cases a message is sent to syslog. An example of the use of this directive is @example maxchange 1000 1 2 @end example After the first clock update, @code{chronyd} will check the offset on every clock update, it will ignore two adjustments larger than 1000 seconds and exit on another one. @c }}} @c {{{ maxclockerror @node maxclockerror directive @subsection maxclockerror The @code{maxclockerror} directive sets the maximum assumed frequency error of the local clock. This is a frequency stability of the clock, not an absolute frequency error. By default, the maximum assumed error is set to 1 ppm. The syntax is @example maxclockerror @end example Typical values for might be 10 for a low quality clock to 0.1 for a high quality clock using a temperature compensated crystal oscillator. @c }}} @c {{{ maxsamples @node maxsamples directive @subsection maxsamples The @code{maxsamples} directive sets the default maximum number of samples @code{chronyd} should keep for each source. This setting can be overriden for individual sources in the @code{server} and @code{refclock} directives (@pxref{server directive}, @pxref{refclock directive}). The default value is 0, which disables the configurable limit. The useful range is 4 to 64. The syntax is @example maxsamples @end example @c }}} @c {{{ maxslewrate @node maxslewrate directive @subsection maxslewrate The @code{maxslewrate} directive sets the maximum rate at which @code{chronyd} is allowed to slew the time. It limits the slew rate controlled by the correction time ratio (@pxref{corrtimeratio directive}) and is effective only on systems where @code{chronyd} is able to control the rate (so far only Linux). By default, the maximum slew rate is 83333.333 ppm (one twelfth). The syntax is @example maxslewrate @end example @c }}} @c {{{ maxupdateskew @node maxupdateskew directive @subsection maxupdateskew One of @code{chronyd's} tasks is to work out how fast or slow the computer's clock runs relative to its reference sources. In addition, it computes an estimate of the error bounds around the estimated value. If the range of error is too large, it probably indicates that the measurements have not settled down yet, and that the estimated gain or loss rate is not very reliable. The @code{maxupdateskew} parameter allows the threshold for determining whether an estimate may be so unreliable that it should not be used. By default, the threshold is 1000 ppm. The syntax is @example maxupdateskew @end example Typical values for might be 100 for a dial-up connection to servers over a phone line, and 5 or 10 for a computer on a LAN. It should be noted that this is not the only means of protection against using unreliable estimates. At all times, @code{chronyd} keeps track of both the estimated gain or loss rate, and the error bound on the estimate. When a new estimate is generated following another measurement from one of the sources, a weighted combination algorithm is used to update the master estimate. So if @code{chronyd} has an existing highly-reliable master estimate and a new estimate is generated which has large error bounds, the existing master estimate will dominate in the new master estimate. @c }}} @c {{{ minsamples @node minsamples directive @subsection minsamples The @code{minsamples} directive sets the default minimum number of samples @code{chronyd} should keep for each source. This setting can be overriden for individual sources in the @code{server} and @code{refclock} directives (@pxref{server directive}, @pxref{refclock directive}). The default value is 0. The useful range is 4 to 64. The syntax is @example minsamples @end example @c }}} @c {{{ minsources @node minsources directive @subsection minsources The @code{minsources} directive sets the minimum number of sources that need to be considered as selectable in the source selection algorithm before the local clock is updated. The default value is 1. Setting this option to a larger number can be used to improve the reliability. More sources will have to agree with each other and the clock will not be updated when only one source (which could be serving wrong time) is reachable. The syntax is @example minsources @end example @c }}} @c {{{ noclientlog @node noclientlog directive @subsection noclientlog This directive, which takes no arguments, specifies that client accesses are not to be logged. Normally they are logged, allowing statistics to be reported using the @code{clients} command in @code{chronyc}. @c }}} @c {{{ peer @node peer directive @subsection peer The syntax of this directive is identical to that for the @code{server} directive (@pxref{server directive}), except that it is used to specify an NTP peer rather than an NTP server. Please note that NTP peers that are not configured with a key to enable authentication are vulnerable to a denial-of-service attack. An attacker knowing that NTP hosts A and B are peering with each other can send a packet with random timestamps to host A with source address of B which will set the NTP state variables on A to the values sent by the attacker. Host A will then send on its next poll to B a packet with originate timestamp that doesn't match the transmit timestamp of B and the packet will be dropped. If the attacker does this periodically for both hosts, they won't be able to synchronize to each other. This attack can be prevented by enabling authentication with the key option, or using the @code{server} directive on both sides to specify the other host as a server instead of peer, the only drawback is that it will double the network traffic between the two hosts. @c }}} @c {{{ pidfile @node pidfile directive @subsection pidfile chronyd always writes its process ID (pid) to a file, and checks this file on startup to see if another chronyd may already be running on the system. By default, the file used is @code{/var/run/chronyd.pid}. The @code{pidfile} directive allows the name to be changed, e.g. @example pidfile /var/tmp/chronyd.pid @end example @c }}} @c {{{ pool @node pool directive @subsection pool The syntax of this directive is similar to that for the @code{server} directive (@pxref{server directive}), except that it is used to specify a pool of NTP servers rather than a single NTP server. The pool name is expected to resolve to multiple addresses which may change over time. All options valid in the @code{server} directive can be used in this directive too. There is one option specific to @code{pool} directive: @code{maxsources} sets the maximum number of sources that can be used from the pool, the default value is 4. On start, when the pool name is resolved, @code{chronyd} will add up to 16 sources, one for each resolved address. When the number of sources from which at least one valid reply was received reaches @code{maxsources}, the other sources will be removed. When a pool source is unreachable or marked as falseticker, @code{chronyd} will try to replace the source with a newly resolved address of the pool. An example of the pool directive is @example pool pool.ntp.org iburst maxsources 3 @end example @c }}} @c {{{ port @node port directive @subsection port This option allows you to configure the port on which @code{chronyd} will listen for NTP requests. The port will be open only when an address is allowed by the @code{allow} directive or command, an NTP peer is configured, or the broadcast server mode is enabled. The compiled in default is udp/123, the standard NTP port. If set to 0, @code{chronyd} will never open the server port and will operate strictly in a client-only mode. The source port used in NTP client requests can be set by the @code{acquisitionport} directive. An example of the port command is @example port 11123 @end example This would change the NTP port served by @code{chronyd} on the computer to udp/11123. @c }}} @c {{{ refclock @node refclock directive @subsection refclock Reference clocks allows very accurate synchronisation and @code{chronyd} can function as a stratum 1 server. They are specified by the @code{refclock} directive. It has two mandatory parameters, a refclock driver name and a driver specific parameter. There are currently four drivers included: @table @code @item PPS PPSAPI (pulse per second) driver. The parameter is the path to a PPS device. Assert events are used by default. Driver option @code{:clear} can be appended to the path if clear events should be used instead. As PPS refclock gets only sub-second time information, it needs another source (NTP or non-PPS refclock) or local directive (@pxref{local directive}) enabled to work. For example: @example refclock PPS /dev/pps0 lock NMEA refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect @end example @item SHM NTP shared memory driver. This driver uses a shared memory segment to receive data from another daemon which communicates with an actual reference clock. The parameter is the number of a shared memory segment, usually 0, 1, 2 or 3. For example: @example refclock SHM 1 poll 3 refid GPS1 @end example A driver option in form @code{:perm=NNN} can be appended to the segment number to create the segment with permissions other than the default @code{0600}. Some examples of applications that can be used as SHM sources are @uref{http://catb.org/gpsd/, @code{gpsd}}, @code{shmpps} and @uref{http://www.buzzard.me.uk/jonathan/radioclock.html, @code{radioclk}}. @item SOCK Unix domain socket driver. It is similar to the SHM driver, but uses a different format and uses a socket instead of shared memory. It does not require polling and it supports transmitting of PPS data. The parameter is a path to the socket which will be created by @code{chronyd} and used to receive the messages. The format of messages sent over the socket is described in the @code{refclock_sock.c} file. Recent versions of the @code{gpsd} daemon include support for the SOCK protocol. The path where the socket should be created is described in the @code{gpsd(8)} man page. For example: @example refclock SOCK /var/run/chrony.ttyS0.sock @end example @item PHC PTP hardware clock (PHC) driver. The parameter is the path to the device of the PTP clock, which can be synchronised by a PTP daemon (e.g. @code{ptp4l} from the @uref{http://linuxptp.sourceforge.net/, Linux PTP project}. The PTP clocks are typically kept in TAI instead of UTC. The @code{offset} option can be used to compensate for the current UTC/TAI offset. For example: @example refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -35 @end example @end table The @code{refclock} command also supports a number of subfields (which may be defined in any order): @table @code @item poll Timestamps produced by refclock drivers are not used immediately, but they are stored and processed by a median filter in the polling interval specified by this option. This is defined as a power of 2 and may be negative to specify a sub-second interval. The default is 4 (16 seconds). A shorter interval allows @code{chronyd} to react faster to changes in clock frequency, but it may decrease the accuracy if the source is too noisy. @item dpoll Some drivers don't listen for external events and try to produce samples in their own polling interval. This is defined as a power of 2 and may be negative to specify a sub-second interval. The default is 0 (1 second). @item refid This option is used to specify a reference id of the refclock, as up to four ASCII characters. By default, first three characters from driver name and the number of the refclock are used as refid. Each refclock must have an unique refid. @item filter This option sets the length of the median filter which is used to reduce noise. With each poll about 40 percent of the stored samples is discarded and one final sample is calculated as average of the remaining samples. If the length is 4 or above, at least 4 samples have to be collected between polls. For lengths below 4, the filter has to be full. The default is 64. @item rate PPS signal frequency (in Hz). This option only controls how the received pulses are aligned. To actually receive more than one pulse per second, a negative @code{dpoll} has to be specified (-3 for 5Hz signal). The default is 1. @item lock This option can be used to lock a PPS refclock to another refclock whose reference id is specified by this option. In this mode received pulses are aligned directly to unfiltered samples from the refclock. By default, pulses are aligned to local clock, but only when it is well synchronised. @item offset This option can be used to compensate a constant error. The specified offset (in seconds) is applied to all samples produced by the refclock. The default is 0.0. @item delay This option sets the NTP delay of the source (in seconds). Half of this value is included in the maximum assumed error which is used in the source selection algorithm. Increasing the delay is useful to avoid having no majority in the algorithm or to make it prefer other sources. The default is 1e-9 (1 nanosecond). @item precision Refclock precision (in seconds). The default is 1e-6 (1 microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC refclocks. @item maxdispersion Maximum allowed dispersion for filtered samples (in seconds). Samples with larger estimated dispersion are ignored. By default, this limit is disabled. @item prefer Prefer this source over sources without prefer option. @item noselect Never select this source. This is useful for monitoring or with sources which are not very accurate, but are locked with a PPS refclock. @item minsamples Set the minimum number of samples kept for this source. This overrides the @code{minsamples} directive (@pxref{minsamples directive}). @item maxsamples Set the maximum number of samples kept for this source. This overrides the @code{maxsamples} directive (@pxref{maxsamples directive}). @end table @c }}} @c {{{ reselectdist @node reselectdist directive @subsection reselectdist When @code{chronyd} selects synchronisation source from available sources, it will prefer the one with minimum synchronisation distance. However, to avoid frequent reselecting when there are sources with similar distance, a fixed distance is added to the distance for sources that are currently not selected. This can be set with the @code{reselectdist} option. By default, the distance is 100 microseconds. The syntax is @example reselectdist @end example @c }}} @c {{{ rtcautotrim @node rtcautotrim directive @subsection rtcautotrim The @code{rtcautotrim} directive is used to keep the real time clock (RTC) close to the system clock automatically. When the system clock is synchronized and the estimated error between the two clocks is larger than the specified threshold, @code{chronyd} will trim the RTC as if the @code{trimrtc} (@pxref{trimrtc command}) command was issued. This directive is effective only with the @code{rtcfile} directive. An example of the use of this directive is @example rtcautotrim 30 @end example This would set the threshold error to 30 seconds. @c }}} @c {{{ rtcdevice @node rtcdevice directive @subsection rtcdevice The @code{rtcdevice} directive defines the name of the device file for accessing the real time clock. By default this is @code{/dev/rtc}, unless the directive is used to set a different value. This applies to Linux systems with devfs. An example of use is @example rtcdevice /dev/misc/rtc @end example @c }}} @c {{{ rtcfile @node rtcfile directive @subsection rtcfile The @code{rtcfile} directive defines the name of the file in which @code{chronyd} can save parameters associated with tracking the accuracy of the system's real-time clock (RTC). The syntax is illustrated in the following example @example rtcfile @CHRONYVARDIR@/rtc @end example @code{chronyd} saves information in this file when it exits and when the @code{writertc} command is issued in @code{chronyc}. The information saved is the RTC's error at some epoch, that epoch (in seconds since January 1 1970), and the rate at which the RTC gains or loses time. So far, the support for real-time clocks is limited - their code is even more system-specific than the rest of the software. You can only use the real time clock facilities (the @code{rtcfile} directive and the @code{-s} command line option to @code{chronyd}) if the following three conditions apply: @enumerate 1 @item You are running Linux version 2.2.x or later. @item You have compiled the kernel with extended real-time clock support (i.e. the @file{/dev/rtc} device is capable of doing useful things). @item You don't have other applications that need to make use of @file{/dev/rtc} at all. @end enumerate @c }}} @c {{{ rtconutc @node rtconutc directive @subsection rtconutc @code{chronyd} assumes by default that the real time clock (RTC) keeps local time (including any daylight saving changes). This is convenient on PCs running Linux which are dual-booted with DOS or Windows. NOTE : IF YOU KEEP THE REAL TIME CLOCK ON LOCAL TIME AND YOUR COMPUTER IS OFF WHEN DAYLIGHT SAVING (SUMMER TIME) STARTS OR ENDS, THE COMPUTER'S SYSTEM TIME WILL BE ONE HOUR IN ERROR WHEN YOU NEXT BOOT AND START CHRONYD. An alternative is for the RTC to keep Universal Coordinated Time (UTC). This does not suffer from the 1 hour problem when daylight saving starts or ends. If the @code{rtconutc} directive appears, it means the RTC is required to keep UTC. The directive takes no arguments. It is equivalent to specifying the @code{-u} switch to the Linux @file{/sbin/hwclock} program. Note that this setting is overriden when the @code{hwclockfile} directive (@pxref{hwclockfile directive}) is used. @c }}} @c {{{ rtcsync @node rtcsync directive @subsection rtcsync The @code{rtcsync} directive will enable a kernel mode where the system time is copied to the real time clock (RTC) every 11 minutes. This directive is supported only on Linux and cannot be used when the normal RTC tracking is enabled, i.e. when the @code{rtcfile} directive is used. @c }}} @c {{{ sched_priority @node sched_priority directive @subsection sched_priority The @code{sched_priority} directive will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). This mode is supported only on Linux. This directive uses the Linux sched_setscheduler() system call to instruct the kernel to use the SCHED_FIFO first-in, first-out real-time scheduling policy for @code{chronyd} with the specified priority. This means that whenever @code{chronyd} is ready to run it will run, interrupting whatever else is running unless it is a higher priority real-time process. This should not impact performance as @code{chronyd's} resource requirements are modest, but it should result in lower and more consistent latency since @code{chronyd} will not need to wait for the scheduler to get around to running it. You should not use this unless you really need it. The sched_setscheduler man page has more details. @c }}} @c {{{ server @node server directive @subsection server The @code{server} directive allows NTP servers to be specified. The client/server relationship is strictly hierarchical : a client may synchronise its system time to that of the server, but the server's system time will never be influenced by that of a client. The @code{server} directive is immediately followed by either the name of the server, or its IP address. The server command also supports a number of subfields (which may be defined in any order): @table @code @item port This option allows the UDP port on which the server understands NTP requests to be specified. For normal servers this option should not be required (the default is 123, the standard NTP port). @item minpoll Although @code{chronyd} will trim the rate at which it samples the server during normal operation, the user may wish to constrain the minimum polling interval. This is always defined as a power of 2, so @code{minpoll 5} would mean that the polling interval cannot drop below 32 seconds. The default is 6 (64 seconds). @item maxpoll In a similar way, the user may wish to constrain the maximum polling interval. Again this is specified as a power of 2, @code{maxpoll 9} indicates that the polling interval must stay at or below 512 seconds. The default is 10 (1024 seconds). @item maxdelay @code{chronyd} uses the network round-trip delay to the server to determine how accurate a particular measurement is likely to be. Long round-trip delays indicate that the request, or the response, or both were delayed. If only one of the messages was delayed the measurement error is likely to be substantial. For small variations in round trip delay, @code{chronyd} uses a weighting scheme when processing the measurements. However, beyond a certain level of delay the measurements are likely to be so corrupted as to be useless. (This is particularly so on dial-up or other slow links, where a long delay probably indicates a highly asymmetric delay caused by the response waiting behind a lot of packets related to a download of some sort). If the user knows that round trip delays above a certain level should cause the measurement to be ignored, this level can be defined with the maxdelay command. For example, @code{maxdelay 0.3} would indicate that measurements with a round-trip delay of 0.3 seconds or more should be ignored. The default value is 3 seconds. @item maxdelayratio This option is similar to the maxdelay option above. @code{chronyd} keeps a record of the minimum round-trip delay amongst the previous measurements that it has buffered. If a measurement has a round trip delay that is greater than the maxdelayratio times the minimum delay, it will be rejected. @item maxdelaydevratio If a measurement has ratio of the increase in round-trip delay from the minimum delay amongst the previous measurements to the standard deviation of the previous measurements that is greater than maxdelaydevratio, it will be rejected. The default is 10.0. @item presend If the timing measurements being made by @code{chronyd} are the only network data passing between two computers, you may find that some measurements are badly skewed due to either the client or the server having to do an ARP lookup on the other party prior to transmitting a packet. This is more of a problem with long sampling intervals, which may be similar in duration to the lifetime of entries in the ARP caches of the machines. In order to avoid this problem, the @code{presend} option may be used. It takes a single integer argument, which is the smallest polling interval for which an extra pair of NTP packets will be exchanged between the client and the server prior to the actual measurement. For example, with the following option included in a @code{server} directive : @example presend 9 @end example when the polling interval is 512 seconds or more, an extra NTP client packet will be sent to the server a short time (currently 4 seconds) before making the actual measurement. @item key The NTP protocol supports the inclusion of checksums in the packets, to prevent computers having their system time upset by rogue packets being sent to them. The checksums are generated as a function of a password, using the cryptographic hash function set in the key file. The association between key numbers and passwords is contained in the keys file, defined by the keyfile command. If the key option is present, @code{chronyd} will attempt to use authenticated packets when communicating with this server. The key number used will be the single argument to the key option (an unsigned integer in the range 1 through 2**32-1). The server must have the same password for this key number configured, otherwise no relationship between the computers will be possible. @item offline If the server will not be reachable when @code{chronyd} is started, the offline option may be specified. @code{chronyd} will not try to poll the server until it is enabled to do so (by using the online option of @code{chronyc}). @item auto_offline If this option is set, the server will be assumed to have gone offline when 2 requests have been sent to it without receiving a response. This option avoids the need to run the @code{offline} (@pxref{offline command}) command from chrony when disconnecting the dial-up link. (It will still be necessary to use chronyc's @code{online} (@pxref{online command}) command when the link has been established, to enable measurements to start.) @item iburst On start, make four measurements over a short duration (rather than the usual periodic measurements). @item minstratum When the synchronisation source is selected from available sources, sources with lower stratum are normally preferred. This option can be used to increase stratum of the source to the specified minimum, so @code{chronyd} will avoid selecting that source. This is useful with low stratum sources that are known to be unrealiable or inaccurate and which should be used only when other sources are unreachable. @item polltarget Target number of measurements to use for the regression algorithm which @code{chronyd} will try to maintain by adjusting polling interval between @code{minpoll} and @code{maxpoll}. A higher target makes @code{chronyd} prefer shorter polling intervals. The default is 6 and a useful range is 6 to 60. @item version This option sets the NTP version number used in packets sent to the server. This can be useful when the server runs an old NTP implementation that doesn't respond to newer versions. The default version number is 4. @item prefer Prefer this source over sources without prefer option. @item noselect Never select this source. This is particularly useful for monitoring. @item minsamples Set the minimum number of samples kept for this source. This overrides the @code{minsamples} directive (@pxref{minsamples directive}). @item maxsamples Set the maximum number of samples kept for this source. This overrides the @code{maxsamples} directive (@pxref{maxsamples directive}). @end table @c }}} @c {{{ smoothtime @node smoothtime directive @subsection smoothtime The @code{smoothtime} directive can be used to enable smoothing of the time that @code{chronyd} serves to its clients to make it easier for them to track it and keep their clocks close together even when large offset or frequency corrections are applied to the server's clock, for example after being offline for a longer time. BE WARNED - the server is intentionally not serving its best estimate of the true time. If a large offset has been accumulated, it may take a very long time to smooth it out. This directive should be used only when the clients are not configured to poll also another NTP server, because they could reject this server as a falseticker or fail to select a source completely. The smoothing process is implemented with a quadratic spline function with two or three pieces. It's independent from any slewing applied to the local system clock, but the accumulated offset and frequency will be reset when the clock is corrected by stepping, e.g. by the @code{makestep} directive or command. The process can be reset without stepping the clock by the @code{smoothtime reset} command (@pxref{smoothtime command}). The first two arguments of the directive are the maximum frequency offset of the smoothed time to the tracked NTP time (in ppm) and the maximum rate at which the frequency offset is allowed to change (in ppm per second). @code{leaponly} is an optional third argument which enables a mode where only leap seconds are smoothed out and normal offset/frequency changes are ignored. The @code{leaponly} option is useful in a combination with the @code{leapsecmode slew} option (@pxref{leapsecmode directive}) to allow clients use multiple time smoothing servers safely. The smoothing process is activated automatically when 1/10000 of the estimated skew of the local clock falls below the maximum rate of frequency change. It can be also activated manually by the @code{smoothtime activate} command, which is particularly useful when the clock is synchronized only with manual input and the skew is always larger than the threshold. The @code{smoothing} command (@pxref{smoothing command}) can be used to monitor the process. An example suitable for clients using @code{ntpd} and 1024 second polling interval could be @example smoothtime 400 0.001 @end example An example suitable for clients using @code{chronyd} on Linux could be @example smoothtime 50000 0.01 @end example @c }}} @c {{{ stratumweight @node stratumweight directive @subsection stratumweight The @code{stratumweight} directive sets how much distance should be added per stratum to the synchronisation distance when @code{chronyd} selects the synchronisation source from available sources. The syntax is @example stratumweight @end example By default, the weight is 0.001 seconds. This means that stratum of the sources in the selection process matters only when the differences between the distances are in milliseconds. @c }}} @c {{{ tempcomp @node tempcomp directive @subsection tempcomp Normally, changes in the rate of drift of the system clock are caused mainly by changes in the temperature of the crystal oscillator on the mainboard. If there are temperature measurements available from a sensor close to the oscillator, the @code{tempcomp} directive can be used to compensate for the changes in the temperature and improve the stability and accuracy of the clock. The result depends on many factors, including the resolution of the sensor, the amount of noise in the measurements, the polling interval of the time source, the compensation update interval, how well is the compensation specified, and how close is the sensor to the oscillator. When it's working well, the frequency reported in the @file{tracking.log} file is more stable and the maximum reached offset is smaller. There are two forms of the directive. The first one has six parameters: a path to the file containing the current temperature from the sensor (in text format), the compensation update interval (in seconds), and temperature coefficients T0, k0, k1, k2. The frequency compensation is calculated (in ppm) as @code{k0 + (T - T0) * k1 + (T - T0)^2 * k2} The result has to be between -10 ppm and 10 ppm, otherwise the measurement is considered invalid and will be ignored. The k0 coefficient can be used to get the results in that range. An example of use is @example tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0 @end example The measured temperature will be read from the file in the Linux sysfs filesystem every 30 seconds. When the temperature is 26000 (26 degrees Celsius), the frequency correction will be zero. When it is 27000 (27 degrees Celsius), the clock will be set to run 0.183ppm faster, etc. The second form has three parameters, the path to the sensor file, the update interval and a path to a file containing a list of (temperature, compensation) points, from which the compensation is linearly interpolated or extrapolated. An example is @example tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp @end example where the @file{chrony.tempcomp} file could have @example 20000 1.0 21000 0.64 22000 0.36 23000 0.16 24000 0.04 25000 0.0 26000 0.04 27000 0.16 28000 0.36 29000 0.64 30000 1.0 @end example Valid measurements with corresponding compensations are logged to the @file{tempcomp.log} file if enabled by the @code{log tempcomp} directive. @c }}} @c {{{ user @node user directive @subsection user The @code{user} directive sets the name of the user to which will @code{chronyd} switch on initialisation to drop root privileges. So far, it works only on Linux when compiled with capabilities support. Setting the name to root will disable it. The default value is @code{@DEFAULT_USER@}. @c }}} @c }}} @c {{{ S:Running chronyc @node Running chronyc @section Running chronyc @c {{{ Section top Chronyc is the program that can be used to reconfigure options within the @code{chronyd} program whilst it is running. Chronyc can also be used to generate status reports about the operation of @code{chronyd}. @menu * Chronyc basic use:: How to run chronyc * Chronyc command line options:: Chrony's command line options * Security with chronyc:: How chronyd restricts access * Chronyc command reference:: All the commands chronyc supports @end menu @c }}} @c {{{ SS:Chronyc basic use @node Chronyc basic use @subsection Basic use The program chronyc is run by entering @example chronyc @end example at the command line. The prompt @code{chronyc} is displayed whilst chronyc is expecting input from the user, when it is being run from a terminal. If chronyc's input or output are redirected from/to a file, the prompt is now shown. When you are finished entering commands, the commands @code{exit} or @code{quit} will terminate the program. (Entering @key{Control-D} will also terminate the program.) @c }}} @c {{{ SS:Command line options @node Chronyc command line options @subsection Command line options Chronyc supports the following command line options. @table @code @item -v Displays the version number of chronyc on the terminal, and exists. @item -h This option allows the user to specify which host running the @code{chronyd} program is to be contacted. This allows for remote configuration, without having to ssh to the other host first. The default is to contact @code{chronyd} running on the same host as that where chronyc is being run. @item -p This option allows the user to specify the UDP port number which the target @code{chronyd} is using for its command & monitoring connections. This defaults to the compiled-in default; there would rarely be a need to change this. @item -n This option disables resolving IP addresses to hostnames. @item -4 With this option hostnames will be resolved only to IPv4 addresses. @item -6 With this option hostnames will be resolved only to IPv6 addresses. @item -m With this option multiple commands can be specified on the command line. Each argument will be interpreted as a whole command. @item -f This option can be used to specify an alternate location of the @code{chronyd} configuration file (default @file{@SYSCONFDIR@/chrony.conf}). The configuration file is needed for the `-a' option. @item -a With this option @code{chronyc} will try to authenticate automatically on start. It will read the configuration file, read the command key from the keyfile and run the authhash and password commands. @end table @c }}} @c {{{ SS:Security with chronyc @node Security with chronyc @subsection Security with chronyc Many of the commands available through chronyc have a fair amount of power to reconfigure the run-time behaviour of @code{chronyd}. Consequently, @code{chronyc} is quite dangerous for the integrity of the target system's clock performance. Having access to @code{chronyd} via chronyc is more or less equivalent to being able to modify @code{chronyd's} configuration file (typically @file{@SYSCONFDIR@/chrony.conf}) and to restart @code{chronyd}. Chronyc also provides a number of monitoring (as opposed to commanding) commands, which will not affect the behaviour of @code{chronyd}. However, you may still want to restrict access to these commands. In view of this, access to some of the capabilities of chronyc will usually be tightly controlled. There are two mechanisms supported: @enumerate 1 @item The set of hosts from which @code{chronyd} will accept commands can be restricted. By default, commands will only be accepted from the same host that @code{chronyd} is running on. @item Any command that actually reconfigures some aspect of @code{chronyd's} behaviour requires the user of chronyc to know a password. This password is specified in @code{chronyd's} keys file (@pxref{keyfile directive}) and specified via the commandkey option in its configuration file (@pxref{commandkey directive}). @end enumerate Only the following commands can be used @emph{without} providing a password: @itemize @bullet @item @code{activity} @item @code{authhash} @item @code{dns} @item @code{exit} @item @code{help} @item @code{password} @item @code{quit} @item @code{rtcdata} @item @code{smoothing} @item @code{sources} @item @code{sourcestats} @item @code{tracking} @item @code{waitsync} @end itemize All other commands require a password to have been specified previously, because they affect @code{chronyd's} operation. @c }}} @c {{{ SS:Chronyc command reference @node Chronyc command reference @subsection Command reference @c {{{ Top/menu This section describes each of the commands available within the chronyc program. Chronyc offers the user a simple command-line driven interface. @menu * accheck command:: Verifying NTP client access * activity command:: Check how many NTP servers/peers are online/offline * add peer command:: Add a new NTP peer * add server command:: Add a new NTP server * allow all command:: Allowing NTP client access * allow command:: Allowing NTP client access * authhash command:: Set the command authentication hash function * burst command:: Initiating a rapid set of measurements * clients command:: Show clients that have accessed the server * cmdaccheck command:: Verifying command client access * cmdallow all command:: Allowing command client access * cmdallow command:: Allowing command client access * cmddeny all command:: Denying command client access * cmddeny command:: Denying command client access * cyclelogs command:: Close and re-open open log files * delete command:: Remove an NTP server or peer * deny all command:: Denying NTP client access * deny command :: Denying NTP client access * dns command:: Configure how are hostnames and IP addresses resolved * dump command:: Dump measurement histories to files * exit command:: Exit from chronyc * help command:: Generate help summary * local command:: Let computer be a server when it is unsynchronised * makestep command:: Correct the system clock by stepping instead of slewing * manual command:: Enable/disable/configure options for settime * maxdelay command:: Set max measurement delay for a source * maxdelaydevratio command:: Set max measurement delay for a source as ratio to deviation * maxdelayratio command:: Set max measurement delay for a source as ratio * maxpoll command:: Set maximum polling interval for a source * maxupdateskew command:: Set safety threshold for clock gain/loss rate * minpoll command:: Set minimum polling interval for a source * minstratum command:: Set minimum stratum for a source * offline command:: Warn that connectivity to a source will be lost * online command:: Warn that connectivity to a source has been restored * password command:: Provide password needed for most commands * polltarget command:: Set poll target for a source * quit command:: Exit from chronyc * reselect command:: Reselect synchronisation source * reselectdist command:: Set improvement in distance needed to reselect a source * retries command:: Set maximum number of retries * rtcdata command:: Display RTC parameters * settime command:: Provide a manual input of the current time * smoothing command:: Display current time smoothing state * smoothtime command:: Reset/activate server time smoothing * sources command:: Display information about the current set of sources * sourcestats command:: Display the rate & offset estimation performance of sources * timeout command:: Set initial response timeout * tracking command:: Display system clock performance * trimrtc command:: Correct the RTC time to the current system time * waitsync command:: Wait until synchronised * writertc command:: Write the RTC parameters to file @end menu @c }}} @c {{{ accheck @node accheck command @subsubsection accheck This command allows you to check whether client NTP access is allowed from a particular host. Examples of use, showing a named host and a numeric IP address, are as follows: @example accheck foo.example.net accheck 1.2.3.4 accheck 2001:db8::1 @end example This command can be used to examine the effect of a series of @code{allow}, @code{allow all}, @code{deny} and @code{deny all} commands specified either via chronyc, or in @code{chronyd's} configuration file. @c }}} @c {{{ activity command @node activity command @subsubsection activity This command reports the number of servers/peers that are online and offline. If the auto_offline option is used in specifying some of the servers/peers, the @code{activity} command may be useful for detecting when all of them have entered the offline state after the PPP link has been disconnected. The report shows the number of servers/peers in 5 states: @itemize @item @code{online} : the server/peer is currently online (i.e. assumed by chronyd to be reachable) @item @code{offline} : the server/peer is currently offline (i.e. assumed by chronyd to be unreachable, and no measurements from it will be attempted.) @item @code{burst_online} : a burst command has been initiated for the server/peer and is being performed; after the burst is complete, the server/peer will be returned to the online state. @item @code{burst_offline} : a burst command has been initiated for the server/peer and is being performed; after the burst is complete, the server/peer will be returned to the offline state. @item @code{unresolved} : the name of the server/peer wasn't resolved to an address yet; this server is not visible in the @code{sources} and @code{sourcestats} reports. @end itemize @c }}} @c {{{ add peer @node add peer command @subsubsection add peer The @code{add peer} command allows a new NTP peer to be added whilst @code{chronyd} is running. Following the words @code{add peer}, the syntax of the following parameters and options is similar to that for the @code{peer} directive in the configuration file (@pxref{peer directive}). The following peer options can be set in the command: @code{port}, @code{minpoll}, @code{maxpoll}, @code{presend}, @code{maxdelayratio}, @code{maxdelay}, @code{key} An example of using this command is shown below. @example add peer foo.example.net minpoll 6 maxpoll 10 key 25 @end example @c }}} @c {{{ add server @node add server command @subsubsection add server The @code{add server} command allows a new NTP server to be added whilst @code{chronyd} is running. Following the words @code{add server}, the syntax of the following parameters and options is similar to that for the @code{server} directive in the configuration file (@pxref{server directive}). The following server options can be set in the command: @code{port}, @code{minpoll}, @code{maxpoll}, @code{presend}, @code{maxdelayratio}, @code{maxdelay}, @code{key} An example of using this command is shown below. @example add server foo.example.net minpoll 6 maxpoll 10 key 25 @end example @c }}} @c {{{ allow all @node allow all command @subsubsection allow all The effect of the allow command is identical to the @code{allow all} directive in the configuration file (@pxref{allow directive}). @c }}} @c {{{ allow @node allow command @subsubsection allow The effect of the allow command is identical to the @code{allow} directive in the configuration file (@pxref{allow directive}). The syntax is illustrated in the following examples: @example allow foo.example.net allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 2001:db8:789a::/48 allow 0/0 allow ::/0 allow @end example The effect of each of these examples is the same as that of the @code{allow} directive in the configuration file. @c }}} @c {{{ authhash @node authhash command @subsubsection authhash This command selects the hash function used for authenticating user commands. For successful authentication the hash function has to be the same as the function specified for the command key in the keys file on the server (@pxref{keyfile directive}). It needs to be selected before the @code{password} command is used. The default hash function is MD5. An example is @example authhash SHA1 @end example The authhash command is run automatically on start if @code{chronyc} was started with the `-a' option. @c }}} @c {{{ burst @node burst command @subsubsection burst The @code{burst} command tells @code{chronyd} to make a set of measurements to each of its NTP sources over a short duration (rather than the usual periodic measurements that it makes). After such a burst, @code{chronyd} will revert to the previous state for each source. This might be either online, if the source was being periodically measured in the normal way, or offline, if the source had been indicated as being offline. (Switching a source between the online and offline states is described in @ref{online command}, @ref{offline command}). The syntax of the burst command is as follows @example burst / [/] burst / [/] burst / [
] @end example The mask and masked-address arguments are optional, in which case @code{chronyd} will initiate a burst for all of its currently defined sources. The arguments have the following meaning and format. @table @code @item n-good-measurements This defines the number of good measurements that @code{chronyd} will want to obtain from each source. A measurement is good if it passes certain tests, for example, the round trip time to the source must be acceptable. (This allows @code{chronyd} to reject measurements that are likely to be bogus.) @item max-measurements This defines the maximum number of measurements that @code{chronyd} will attempt to make, even if the required number of good measurements has not been obtained. @item mask This is an IP address with which the IP address of each of @code{chronyd}'s sources is to be masked. @item masked-address This is an IP address. If the masked IP address of a source matches this value then the burst command is applied to that source. @item masked-bits This can be used with @code{masked-address} for CIDR notation, which is a shorter alternative to the form with mask. @item address This is an IP address or a hostname. The burst command is applied only to that source. @end table If no mask or masked address arguments are provided, every source will be matched. An example of the two-argument form of the command is @example burst 2/10 @end example This will cause @code{chronyd} to attempt to get two good measurements from each source, stopping after two have been obtained, but in no event will it try more than ten probes to the source. Examples of the four-argument form of the command are @example burst 2/10 255.255.0.0/1.2.0.0 burst 2/10 2001:db8:789a::/48 @end example In the first case, the two out of ten sampling will only be applied to sources whose IPv4 addresses are of the form @code{1.2.x.y}, where x and y are arbitrary. In the second case, the sampling will be applied to sources whose IPv6 addresses have first 48 bits equal to @code{2001:db8:789a}. Example of the three-argument form of the command is @example burst 2/10 foo.example.net @end example @c }}} @c {{{ clients @node clients command @comment node-name, next, previous, up @subsubsection clients This command shows a list of all clients that have accessed the server, through either the NTP or command/monitoring ports. There are no arguments. An example of the output is @example Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC ========================= ====== ====== ====== ====== ====== ==== ==== localhost 0 0 15 1 0 29y 0 aardvark.xxx 4 0 0 0 0 49 29y badger.xxx 4 0 0 0 0 6 29y @end example Each row shows the data for a single host. Only hosts that have passed the host access checks (set with the @code{allow}, @code{deny}, @code{cmdallow} and @code{cmddeny} commands or configuration file directives) are logged. The columns are as follows: @enumerate 1 @item The hostname of the client @item The number of times the client has accessed the server using an NTP client mode packet. @item The number of times the client has accessed the server using an NTP symmetric active mode packet. @item The number of authenticated command packets that have been processed from the client (i.e. those following a successful @code{password} command). @item The number of unauthenticated command packets that have been processed from the client. @item The number of bad command packets received from the client (not all forms of bad packet are logged). @item Time since the last NTP packet was received @item Time since the last command packet was received @end enumerate The last two entries will be shown as the time since 1970 if no packet of that type has ever been received. @c }}} @c {{{ cmdaccheck @node cmdaccheck command @subsubsection cmdaccheck This command is similar to the @code{accheck} command, except that it is used to check whether command access is permitted from a named host. Examples of use are as follows: @example cmdaccheck foo.example.net cmdaccheck 1.2.3.4 cmdaccheck 2001:db8::1 @end example @c }}} @c {{{ cmdallow all @node cmdallow all command @subsubsection cmdallow all This is similar to the @code{allow all} command, except that it is used to@c {{{ allow particular hosts or subnets to use the chronyc program to interact@c }}} with @code{chronyd} on the current host. @c }}} @c {{{ cmdallow @node cmdallow command @subsubsection cmdallow This is similar to the @code{allow} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cmddeny all @node cmddeny all command @subsubsection cmddeny all This is similar to the @code{deny all} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cmddeny @node cmddeny command @subsubsection cmddeny This is similar to the @code{deny} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cyclelogs @node cyclelogs command @subsubsection cyclelogs The @code{cyclelogs} command causes all of @code{chronyd's} open log files to be closed and re-opened. This allows them to be renamed so that they can be periodically purged. An example of how to do this is shown below. @example % mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log % chronyc -a cyclelogs % ls -l /var/log/chrony -rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log -rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log % rm -f measurements1.log @end example @c }}} @c {{{ delete @node delete command @subsubsection delete The @code{delete} command allows an NTP server or peer to be removed from the current set of sources. The syntax is illustrated in the examples below. @example delete foo.example.net delete 1.2.3.4 delete 2001:db8::1 @end example There is one parameter, the name or IP address of the server or peer to be deleted. @c }}} @c {{{ deny all @node deny all command @subsubsection deny all The effect of the allow command is identical to the @code{deny all} directive in the configuration file (@pxref{deny directive}). @c }}} @c {{{ deny @node deny command @subsubsection deny The effect of the allow command is identical to the @code{deny} directive in the configuration file (@pxref{deny directive}). The syntax is illustrated in the following examples: @example deny foo.example.net deny 1.2 deny 3.4.5 deny 6.7.8/22 deny 6.7.8.9/22 deny 2001:db8:789a::/48 deny 0/0 deny ::/0 deny @end example @c }}} @c {{{ dns @node dns command @subsubsection dns The @code{dns} command configures how are hostnames and IP addresses resolved in @code{chronyc}. IP addresses can be resolved to hostnames when printing results of @code{sources}, @code{sourcestats}, @code{tracking} and @code{clients} commands. Hostnames are resolved in commands that take an address as argument. There are five forms of the command: @table @code @item dns -n Disables resolving IP addresses to hostnames. Raw IP addresses will be displayed. @item dns +n Enables resolving IP addresses to hostnames. This is the default unless @code{chronyc} was started with @code{-n} option. @item dns -4 Resolves hostnames only to IPv4 addresses. @item dns -6 Resolves hostnames only to IPv6 addresses. @item dns -46 Resolves hostnames to both address families. This is the default unless @code{chronyc} was started with @code{-4} or @code{-6} option. @end table @c }}} @c {{{ dump @node dump command @subsubsection dump The @code{dump} command causes @code{chronyd} to write its current history of measurements for each of its sources to dump files, either for inspection or to support the @code{-r} option when @code{chronyd} is restarted. The @code{dump} command is somewhat equivalent to the @code{dumponexit} directive in the chrony configuration file. @xref{dumponexit directive}. To use the @code{dump}, you probably want to configure the name of the directory into which the dump files will be written. This can only be done in the configuration file, see @ref{dumpdir directive}. @c }}} @c {{{ exit @node exit command @subsubsection exit The exit command exits from chronyc and returns the user to the shell (same as the quit command). @c }}} @c {{{ help @node help command @subsubsection help The help command displays a summary of the commands and their arguments. @c }}} @c {{{ local @node local command @subsubsection local The @code{local} command allows @code{chronyd} to be told that it is to appear as a reference source, even if it is not itself properly synchronised to an external source. (This can be used on isolated networks, to allow one computer to be a master time server with the other computers slaving to it.) The @code{local} command is somewhat equivalent to the @code{local} directive in the configuration file, see @ref{local directive}. The syntax is as shown in the following examples. @example local stratum 10 local off @end example The first example enables the local reference mode on the host, and sets the stratum at which it should claim to be synchronised. The second example disables the local reference mode. @c }}} @c {{{ makestep @node makestep command @subsubsection makestep Normally chronyd will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, the system clock may be so far adrift that this slewing process would take a very long time to correct the system clock. The @code{makestep} command can be used in this situation. There are two forms of the command. The first form has no parameters. It tells @code{chronyd} to cancel any remaining correction that was being slewed and jump the system clock by the equivalent amount, making it correct immediately. The second form configures the automatic stepping, similarly to the @code{makestep} directive (@pxref{makestep directive}). It has two parameters, stepping threshold (in seconds) and number of future clock updates for which will be the threshold active. This can be used with the @code{burst} command to quickly make a new measurement and correct the clock by stepping if needed, without waiting for @code{chronyd} to complete the measurement and update the clock. @example makestep 0.1 1 burst 1/2 @end example BE WARNED - certain software will be seriously affected by such jumps to the system time. (That is the reason why chronyd uses slewing normally.) @c }}} @c {{{ manual @node manual command @subsubsection manual The manual command enables and disables use of the @code{settime} command (@pxref{settime command}), and is used to modify the behaviour of the manual clock driver. Examples of the command are shown below. @example manual on manual off manual delete 1 manual list manual reset @end example The @code{on} form of the command enables use of the @code{settime} command. The @code{off} form of the command disables use of the @code{settime} command. The @code{list} form of the command lists all the samples currently stored in @code{chronyd}. The output is illustrated below. @example 210 n_samples = 1 # Date Time(UTC) Slewed Original Residual ==================================================== 0 27Jan99 22:09:20 0.00 0.97 0.00 @end example The columns as as follows : @enumerate 1 @item The sample index (used for the @code{manual delete} command) @item The date and time of the sample @item The system clock error when the timestamp was entered, adjusted to allow for changes made to the system clock since. @item The system clock error when the timestamp was entered, as it originally was (without allowing for changes to the system clock since). @item The regression residual at this point, in seconds. This allows 'outliers' to be easily spotted, so that they can be deleted using the @code{manual delete} command. @end enumerate The @code{delete} form of the command deletes a single sample. The parameter is the index of the sample, as shown in the first column of the output from @code{manual list}. Following deletion of the data point, the current error and drift rate are re-estimated from the remaining data points and the system clock trimmed if necessary. This option is intended to allow 'outliers' to be discarded, i.e. samples where the administrator realises he/she has entered a very poor timestamp. The @code{reset} form of the command deletes all samples at once. The system clock is left running as it was before the command was entered. @c }}} @c {{{ maxdelay @node maxdelay command @subsubsection maxdelay This allows the @code{maxdelay} option for one of the sources to be modified, in the same way as specifying the @code{maxdelay} option for the @code{server} directive in the configuration file (@pxref{server directive}). The following examples illustrate the syntax @example maxdelay foo.example.net 0.3 maxdelay 1.2.3.4 0.0015 maxdelay 2001:db8::1 0.0015 @end example The first example sets the maximum network delay allowed for a measurement to the host @code{foo.example.net} to 0.3 seconds. The second and third examples set the maximum network delay for a measurement to the host with IPv4 address @code{1.2.3.4} and the host with IPv6 address @code{2001:db8::1} to 1.5 milliseconds. (Any measurement whose network delay exceeds the specified value is discarded.) @c }}} @c {{{ maxdelaydevratio @node maxdelaydevratio command @subsubsection maxdelaydevratio This allows the @code{maxdelaydevratio} option for one of the sources to be modified, in the same way as specifying the @code{maxdelaydevratio} option for the @code{server} directive in the configuration file (@pxref{server directive}). The following examples illustrate the syntax @example maxdelaydevratio foo.example.net 0.1 maxdelaydevratio 1.2.3.4 1.0 maxdelaydevratio 2001:db8::1 100.0 @end example @c }}} @c {{{ maxdelayratio @node maxdelayratio command @subsubsection maxdelayratio This allows the @code{maxdelayratio} option for one of the sources to be modified, in the same way as specifying the @code{maxdelayratio} option for the @code{server} directive in the configuration file (@pxref{server directive}). The following examples illustrate the syntax @example maxdelayratio foo.example.net 1.5 maxdelayratio 1.2.3.4 2.0 maxdelayratio 2001:db8::1 2.0 @end example The first example sets the maximum network delay for a measurement to the host @code{foo.example.net} to be 1.5 times the minimum delay found amongst the previous measurements that have been retained. The second and third examples set the maximum network delay for a measurement to the host with IPv4 address @code{1.2.3.4} and the host with IPv6 address @code{2001:db8::1} to be double the retained minimum. As for @code{maxdelay}, any measurement whose network delay is too large will be discarded. @c }}} @c {{{ maxpoll @node maxpoll command @subsubsection maxpoll The @code{maxpoll} command is used to modify the minimum polling interval for one of the current set of sources. It is equivalent to the @code{maxpoll} option in the @code{server} directive in the configuration file (@pxref{server directive}). The syntax is as follows @example maxpoll @end example where the host can be specified as either a machine name or IP address. The new minimum poll is specified as a base-2 logarithm of the number of seconds between polls (e.g. specify 6 for 64 second sampling). An example is @example maxpoll foo.example.net 10 @end example which sets the maximum polling interval for the host @code{foo.example.net} to 1024 seconds. Note that the new maximum polling interval only takes effect after the next measurement has been made. @c }}} @c {{{ maxupdateskew @node maxupdateskew command @subsubsection maxupdateskew This command has the same effect as the @code{maxupdateskew} directive in the configuration file, see @ref{maxupdateskew directive}. @c }}} @c {{{ minpoll @node minpoll command @subsubsection minpoll The @code{minpoll} command is used to modify the minimum polling interval for one of the current set of sources. It is equivalent to the @code{minpoll} option in the @code{server} directive in the configuration file (@pxref{server directive}). The syntax is as follows @example minpoll @end example where the host can be specified as either a machine name or IP address. The new minimum poll is specified as a base-2 logarithm of the number of seconds between polls (e.g. specify 6 for 64 second sampling). An example is @example minpoll foo.example.net 5 @end example which sets the minimum polling interval for the host @code{foo.example.net} to 32 seconds. Note that the new minimum polling interval only takes effect after the next measurement has been made. @c }}} @c {{{ minstratum @node minstratum command @subsubsection minstratum The @code{minstratum} command is used to modify the minimum stratum for one of the current set of sources. It is equivalent to the @code{minstratum} option in the @code{server} directive in the configuration file (@pxref{server directive}). The syntax is as follows @example minstratum @end example where the host can be specified as either a machine name or IP address. An example is @example minpoll foo.example.net 5 @end example which sets the minimum stratum for the host @code{foo.example.net} to 5. Note that the new minimum stratum only takes effect after the next measurement has been made. @c }}} @c {{{ offline @node offline command @subsubsection offline The @code{offline} command is used to warn @code{chronyd} that the network connection to a particular host or hosts is about to be lost. It should be used on computers with a dial-up or similar connection to their time sources, to warn @code{chronyd} that the connection is about to be broken. An example of how to use @code{offline} in this case is shown in @ref{Advising chronyd of internet availability}. Another case where @code{offline} could be used is where a computer serves time to a local group of computers, and has a permanant connection to true time servers outside the organisation. However, the external connection is heavily loaded at certain times of the day and the measurements obtained are less reliable at those times. In this case, it is probably most useful to determine the gain/loss rate during the quiet periods and let the whole network coast through the loaded periods. The @code{offline} and @code{online} commands can be used to achieve this. The situation is shown in the figure below. @example @group +----------+ |Ext source| +----------+ | | |/| <-- Link with variable | reliability | +-------------------+ |Local master server| +-------------------+ | +---+---+-----+-----+----+----+ | | | | | | | Local clients @end group @end example If the source to which @code{chronyd} is currently synchronised is indicated offline in this way, @code{chronyd} will continue to treat it as the synchronisation source. If the network connection were broken without the @code{offline} command being used, @code{chronyd} would assume that the source had failed and would attempt to pick another synchronisation source. There are four forms of the @code{offline} command. The first form is a wildcard, meaning all sources. The second form allows an IP address mask and a masked address to be specified. The third form uses the CIDR notation. The fourth form uses an IP address or a hostname. These forms are illustrated below. @example offline offline 255.255.255.0/1.2.3.0 offline 2001:db8:789a::/48 offline foo.example.net @end example The second form means that the @code{offline} command is to be applied to any source whose IPv4 address is in the @code{1.2.3} subnet. (The host's address is logically and-ed with the mask, and if the result matches the masked-address the host is processed). The third form means that the command is to be applied to all sources whose IPv6 addresses have first 48 bits equal to @code{2001:db8:789a}. The fourth form means that the command is to be applied only to that one source. The wildcard form of the address is actually equivalent to @example offline 0.0.0.0/0.0.0.0 offline ::/0 @end example @c }}} @c {{{ online @node online command @subsubsection online The @code{online} command is opposite in function to the @code{offline} command. It is used to advise @code{chronyd} that network connectivity to a particular source or sources has been restored. The syntax is identical to that of the @code{offline} command, see @ref{offline command}. @c }}} @c {{{ password @node password command @subsubsection password The password command is used to allow chronyc to send privileged commands to @code{chronyd}. The password can either be entered on the command line, or can be entered without echoing. The syntax for entering the password on the command line is as follows @example password xyzzy password ASCII:xyzzy password HEX:78797a7a79 @end example To enter the password without it being echoed, enter @example password @end example The computer will respond with a @samp{Password:} prompt, at which you should enter the password and press return. The password can be encoded as a string of characters not containing a space with optional @code{ASCII:} prefix or as a hexadecimal number with @code{HEX:} prefix. It has to match @code{chronyd's} currently defined command key (@pxref{commandkey directive}). If the command key was specified with a different hash function than MD5, it's necessary to select the hash function with the @code{authhash} command (@pxref{authhash command}) before entering the password. The password command is run automatically on start if @code{chronyc} was started with the `-a' option. @c }}} @c {{{ polltarget @node polltarget command @subsubsection polltarget The @code{polltarget} command is used to modify the poll target for one of the current set of sources. It is equivalent to the @code{polltarget} option in the @code{server} directive in the configuration file (@pxref{server directive}). The syntax is as follows @example polltarget @end example where the host can be specified as either a machine name or IP address. An example is @example polltarget foo.example.net 12 @end example which sets the poll target for the host @code{foo.example.net} to 12. @c }}} @c {{{ quit @node quit command @subsubsection quit The quit command exits from chronyc and returns the user to the shell (same as the exit command). @c }}} @c {{{ reselect command @node reselect command @subsubsection reselect To avoid excessive switching between sources, @code{chronyd} may stay synchronised to a source even when it is not currently the best one among the available sources. The @code{reselect} command can be used to force @code{chronyd} to reselect the best synchronisation source. @c }}} @c {{{ reselectdist command @node reselectdist command @subsubsection reselectdist The @code{reselectdist} command sets the reselect distance. It is equivalent to the @code{reselectdist} directive in the configuration file (@pxref{reselectdist directive}). @c }}} @c {{{ retries @node retries command @subsubsection retries The @code{retries} command sets the maximum number of retries for @code{chronyc} requests before giving up. The response timeout is controlled by @code{timeout} command (@pxref{timeout command}). The default is 2. @c }}} @c {{{ rtcdata @node rtcdata command @subsubsection rtcdata The @code{rtcdata} command displays the current real time clock RTC parameters. An example output is shown below. @example RTC ref time (GMT) : Sat May 30 07:25:56 1998 Number of samples : 10 Number of runs : 5 Sample span period : 549 RTC is fast by : -1.632736 seconds RTC gains time at : -107.623 ppm @end example The fields have the following meaning @table @code @item RTC ref time (GMT) This is the RTC reading the last time its error was measured. @item Number of samples This is the number of previous measurements being used to determine the RTC gain/loss rate. @item Number of runs This is the number of runs of residuals of the same sign following the regression fit for (RTC error) versus (RTC time). A value which is small indicates that the measurements are not well approximated by a linear model, and that the algorithm will tend to delete the older measurements to improve the fit. @item Sample span period This is the period that the measurements span (from the oldest to the newest). Without a unit the value is in seconds; suffixes `m' for minutes, `h' for hours, `d' for days or `y' for years may be used. @item RTC is fast by This is the estimate of how many seconds fast the RTC when it thought the time was at the reference time (above). If this value is large, you may (or may not) want to use the @code{trimrtc} command to bring the RTC into line with the system clock. (Note, a large error will not affect @code{chronyd's} operation, unless it becomes so big as to start causing rounding errors. @item RTC gains time at This is the amount of time gained (positive) or lost (negative) by the real time clock for each second that it ticks. It is measured in parts per million. So if the value shown was +1, suppose the RTC was exactly right when it crosses a particular second boundary. Then it would be 1 microsecond fast when it crosses its next second boundary. @end table @c }}} @c {{{ settime @node settime command @subsubsection settime The @code{settime} command allows the current time to be entered manually, if this option has been configured into @code{chronyd}. (It may be configured either with the @code{manual} directive in the configuration file (@pxref{manual directive}), or with the @code{manual} command of chronyc (@pxref{manual command}). It should be noted that the computer's sense of time will only be as accurate as the reference you use for providing this input (e.g. your watch), as well as how well you can time the press of the return key. Providing your computer's time zone is set up properly, you will be able to enter a local time (rather than UTC). The response to a successful @code{settime} command indicates the amount that the computer's clock was wrong. It should be apparent from this if you have entered the time wrongly, e.g. with the wrong time zone. The rate of drift of the system clock is estimated by a regression process using the entered measurement and all previous measurements entered during the present run of @code{chronyd}. However, the entered measurement is used for adjusting the current clock offset (rather than the estimated intercept from the regression, which is ignored). Contrast what happens with the @code{manual delete} command, where the intercept is used to set the current offset (since there is no measurement that has just been typed in in that case). The time is parsed by the public domain @file{getdate} algorithm. Consequently, you can only specify time to the nearest second. Examples of inputs that are valid are shown below. @example settime 16:30 settime 16:30:05 settime Nov 21, 1997 16:30:05 @end example For a full description of @code{getdate}, get hold of the getdate documentation (bundled, for example, with the source for GNU tar). @c }}} @c {{{ smoothing @node smoothing command @subsubsection smoothing The @code{smoothing} command displays the current state of the NTP server time smoothing. An example of the output is shown below. @example Active : Yes Offset : +1.000268817 seconds Frequency : -0.142859 ppm Wander : -0.010000 ppm per second Last update : 17.8 seconds ago Remaining time : 19988.4 seconds @end example The fields are explained as follows. @table @code @item Active This shows if the server time smoothing is currently active. Possible values are @code{Yes} and @code{No}. If the @code{leaponly} option is included in the @code{smoothtime} directive, @code{(leap second only)} will be shown on the line. @item Offset This is the current offset applied to the time sent to NTP clients. Positive value means the clients are getting time that's ahead of true time. @item Frequency The current frequency offset of the served time. Negative value means the time observed by clients is running slower than true time. @item Wander The current frequency wander of the served time. Negative value means the time observed by clients is slowing down. @item Last update This field shows how long ago was the time smoothing process updated, e.g. @code{chronyd} accumulated a new measurement. @item Remaining time The time it would take for the smoothing process to get to zero offset and frequency if there were no more updates. @end table @c }}} @c {{{ smoothtime @node smoothtime command @subsubsection smoothtime The @code{smoothtime} command can be used to reset or activate the server time smoothing process if it is configured with the @code{smoothtime} directive (@pxref{smoothtime directive}). The syntax is as follows @example smoothtime reset smoothtime activate @end example @c }}} @c {{{ sources @node sources command @subsubsection sources This command displays information about the current time sources that @code{chronyd} is accessing. The optional argument @code{-v} can be specified, meaning @emph{verbose}. In this case, extra caption lines are shown as a reminder of the meanings of the columns. @example @group 210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns ^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms ^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms @end group @end example The columns are as follows: @table @code @item M This indicates the mode of the source. @code{^} means a server, @code{=} means a peer and @code{#} indicates a locally connected reference clock. @item S This column indicates the state of the sources. @code{*} indicates the source to which @code{chronyd} is currently synchronised. @code{+} indicates acceptable sources which are combined with the selected source. @code{-} indicates acceptable sources which are excluded by the combining algorithm. @code{?} indicates sources to which connectivity has been lost or whose packets don't pass all tests. @code{x} indicates a clock which @code{chronyd} thinks is is a falseticker (i.e. its time is inconsistent with a majority of other sources). @code{~} indicates a source whose time appears to have too much variability. The @code{?} condition is also shown at start-up, until at least 3 samples have been gathered from it. @item Name/IP address This shows the name or the IP address of the source, or refid for reference clocks. @item Stratum This shows the stratum of the source, as reported in its most recently received sample. Stratum 1 indicates a computer with a locally attached reference clock. A computer that is synchronised to a stratum 1 computer is at stratum 2. A computer that is synchronised to a stratum 2 computer is at stratum 3, and so on. @item Poll This shows the rate at which the source is being polled, as a base-2 logarithm of the interval in seconds. Thus, a value of 6 would indicate that a measurement is being made every 64 seconds. @code{chronyd} automatically varies the polling rate in response to prevailing conditions. @item Reach This shows the source's reachability register printed as octal number. The register has 8 bits and is updated on every received or missed packet from the source. A value of 377 indicates that a valid reply was received for all from the last eight transmissions. @item LastRx This column shows how long ago the last sample was received from the source. This is normally in seconds. The letters @code{m}, @code{h}, @code{d} or @code{y} indicate minutes, hours, days or years. A value of 10 years indicates there were no samples received from this source yet. @item Last sample This column shows the offset between the local clock and the source at the last measurement. The number in the square brackets shows the actual measured offset. This may be suffixed by @code{ns} (indicating nanoseconds), @code{us} (indicating microseconds), @code{ms} (indicating milliseconds), or @code{s} (indicating seconds). The number to the left of the square brackets shows the original measurement, adjusted to allow for any slews applied to the local clock since. The number following the @code{+/-} indicator shows the margin of error in the measurement. Positive offsets indicate that the local clock is fast of the source. @end table @c }}} @c {{{ sourcestats @node sourcestats command @subsubsection sourcestats The @code{sourcestats} command displays information about the drift rate and offset estimatation process for each of the sources currently being examined by @code{chronyd}. The optional argument @code{-v} can be specified, meaning @emph{verbose}. In this case, extra caption lines are shown as a reminder of the meanings of the columns. An example report is @example @group 210 Number of sources = 1 Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev =============================================================================== abc.def.ghi 11 5 46m -0.001 0.045 1us 25us @end group @end example The columns are as follows @table @code @item Name/IP Address This is the name or IP address of the NTP server (or peer) or refid of the refclock to which the rest of the line relates. @item NP This is the number of sample points currently being retained for the server. The drift rate and current offset are estimated by performing a linear regression through these points. @item NR This is the number of runs of residuals having the same sign following the last regression. If this number starts to become too small relative to the number of samples, it indicates that a straight line is no longer a good fit to the data. If the number of runs is too low, @code{chronyd} discards older samples and re-runs the regression until the number of runs becomes acceptable. @item Span This is the interval between the oldest and newest samples. If no unit is shown the value is in seconds. In the example, the interval is 46 minutes. @item Frequency This is the estimated residual frequency for the server, in parts per million. In this case, the computer's clock is estimated to be running 1 part in 10**9 slow relative to the server. @item Freq Skew This is the estimated error bounds on @code{Freq} (again in parts per million). @item Offset This is the estimated offset of the source. @item Std Dev This is the estimated sample standard deviation. @end table @c }}} @c {{{ timeout @node timeout command @subsubsection timeout The @code{timeout} command sets the initial timeout for @code{chronyc} requests in milliseconds. If no response is received from @code{chronyd}, the timeout is doubled and the request is resent. The maximum number of retries is configured with the @code{retries} command (@pxref{retries command}). By default, the timeout is 1000 milliseconds or 100 milliseconds if @code{chronyc} is contacting localhost (i.e. the `-h' option wasn't specified) and @code{chronyd} was compiled with asynchronous name resolving. @c }}} @c {{{ tracking @node tracking command @subsubsection tracking The @code{tracking} command displays parameters about the system's clock performance. An example of the output is shown below. @example Reference ID : 1.2.3.4 (foo.example.net) Stratum : 3 Ref time (UTC) : Fri Feb 3 15:00:29 2012 System time : 0.000001501 seconds slow of NTP time Last offset : -0.000001632 seconds RMS offset : 0.000002360 seconds Frequency : 331.898 ppm fast Residual freq : 0.004 ppm Skew : 0.154 ppm Root delay : 0.373169 seconds Root dispersion : 0.024780 seconds Update interval : 64.2 seconds Leap status : Normal @end example The fields are explained as follows. @table @code @item Reference ID This is the refid and name (or IP address) if available, of the server to which the computer is currently synchronised. If this is @code{127.127.1.1} it means the computer is not synchronised to any external source and that you have the `local' mode operating (via the @code{local} command in @code{chronyc} (@pxref{local command}), or the @code{local} directive in the @file{@SYSCONFDIR@/chrony.conf} file (@pxref{local directive})). @item Stratum The stratum indicates how many hops away from a computer with an attached reference clock we are. Such a computer is a stratum-1 computer, so the computer in the example is two hops away (i.e. @code{foo.example.net} is a stratum-2 and is synchronised from a stratum-1). @item Ref time This is the time (UTC) at which the last measurement from the reference source was processed. @item System time In normal operation, @code{chronyd} @emph{never} steps the system clock, because any jump in the timescale can have adverse consequences for certain application programs. Instead, any error in the system clock is corrected by slightly speeding up or slowing down the system clock until the error has been removed, and then returning to the system clock's normal speed. A consequence of this is that there will be a period when the system clock (as read by other programs using the @code{gettimeofday()} system call, or by the @code{date} command in the shell) will be different from @code{chronyd's} estimate of the current true time (which it reports to NTP clients when it is operating in server mode). The value reported on this line is the difference due to this effect. On systems other than Linux, @code{chronyd} doesn't adjust the fundamental rate of the system clock, so keeps the system time correct by periodically making offsets to it as though an error had been measured. The build up of these offsets will be observed in this report. @item Last offset This is the estimated local offset on the last clock update. @item RMS offset This is a long-term average of the offset value. @item Frequency The `frequency' is the rate by which the system's clock would be would be wrong if @code{chronyd} was not correcting it. It is expressed in ppm (parts per million). For example, a value of 1ppm would mean that when the system's clock thinks it has advanced 1 second, it has actually advanced by 1.000001 seconds relative to true time. As you can see in the example, the clock in the computer is not a very good one - it gains about 30 seconds per day! @item Residual freq This shows the `residual frequency' for the currently selected reference source. This reflects any difference between what the measurements from the reference source indicate the frequency should be and the frequency currently being used. The reason this is not always zero is that a smoothing procedure is applied to the frequency. Each time a measurement from the reference source is obtained and a new residual frequency computed, the estimated accuracy of this residual is compared with the estimated accuracy (see `skew' next) of the existing frequency value. A weighted average is computed for the new frequency, with weights depending on these accuracies. If the measurements from the reference source follow a consistent trend, the residual will be driven to zero over time. @item Skew This is the estimated error bound on the the frequency. @item Root delay This is the total of the network path delays to the stratum-1 computer from which the computer is ultimately synchronised. @item Root dispersion This is the total dispersion accumulated through all the computers back to the stratum-1 computer from which the computer is ultimately synchronised. Dispersion is due to system clock resolution, statistical measurement variations etc. An absolute bound on the computer's clock accuracy (assuming the stratum-1 computer is correct) is given by @example clock_error <= root_dispersion + (0.5 * |root_delay|) @end example @item Update interval This is the interval between the last two clock updates. @item Leap status This is the leap status, which can be @code{Normal}, @code{Insert second}, @code{Delete second} or @code{Not synchronised}. @end table @c }}} @c {{{ trimrtc @node trimrtc command @subsubsection trimrtc The @code{trimrtc} command is used to correct the system's real time clock (RTC) to the main system clock. It has no effect if the error between the two clocks is currently estimated at less than a second (the resolution of the RTC is only 1 second). The command takes no arguments. It performs the following steps (if the RTC is more than 1 second away from the system clock): @enumerate 1 @item Remember the currently estimated gain/loss rate of the RTC and flush the previous measurements. @item Step the real time clock to bring it within a second of the system clock. @item Make several measurements to accurately determine the new offset between the RTC and the system clock (i.e. the remaining fraction of a second error) @item Save the RTC parameters to the RTC file (specified with the @code{rtcfile} directive in the configuration file (@pxref{rtcfile directive}). @end enumerate The last step is done as a precaution against the computer suffering a power failure before either the daemon exits or the @code{writertc} command is issued. @code{chronyd} will still work perfectly well both whilst operating and across machine reboots even if the @code{trimrtc} command is never used (and the RTC is allowed to drift away from true time). The @code{trimrtc} command is provided as a method by which it can be corrected, in a manner compatible with @code{chronyd} using it to maintain accurate time across machine reboots. The @code{trimrtc} command can be executed automatically by @code{chronyd} with the @code{rtcautotrim} directive (@pxref{rtcautotrim directive}). @c }}} @c {{{ waitsync @node waitsync command @subsubsection waitsync The @code{waitsync} command waits for @code{chronyd} to synchronise. Up to three optional arguments can be specified, the first is the maximum number of tries in 10 second intervals before giving up and returning a non-zero error code. When 0 is specified, or there are no arguments, the number of tries will not be limited. The second and third arguments are the maximum allowed remaining correction of the system clock and the maximum allowed skew (in ppm) as reported by the @code{tracking} command (@pxref{tracking command}) in the @code{System time} and @code{Skew} fields. If not specified or zero, the value will not be checked. An example is @example waitsync 60 0.01 @end example which will wait up to about 10 minutes for @code{chronyd} to synchronise to a source and the remaining correction to be less than 10 milliseconds. @c }}} @c {{{ writertc @node writertc command @subsubsection writertc The @code{writertc} command writes the currently estimated error and gain/loss rate parameters for the RTC to the RTC file (specified with the @code{rtcfile} directive (@pxref{rtcfile directive})). This information is also written automatically when @code{chronyd} is killed (with SIGHUP, SIGINT, SIGQUIT or SIGTERM) or when the @code{trimrtc} command is issued. @c }}} @c }}} @c }}} @c }}} @c {{{ apx:GNU General Public License @node GPL @appendix GNU General Public License @center GNU GENERAL PUBLIC LICENSE @center Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 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. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. , 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 Lesser General Public License instead of this License. @c }}} @contents @bye @c vim:cms=@c\ %s:fdm=marker:fdc=5:syntax=off chrony-2.1.1/chronyc.1.in0000644000000000000000000000405412542263372013660 0ustar rootroot.TH CHRONYC 1 "June 2015" "chrony 2.1.1" "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 parameters 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 (default 127.0.0.1) .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 \fB\-4\fR resolve hostnames only to IPv4 addresses .TP \fB\-6\fR resolve hostnames only to IPv6 addresses .TP \fB\-m\fR allow multiple commands to be specified on the command line. Each argument will be interpreted as a whole command. .TP \fB\-f\fR \fIconf-file\fR This option can be used to specify an alternate location for the configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR). The configuration file is needed for the \fB-a\fR option. .TP \fB\-a\fR With this option chronyc will try to authenticate automatically on start. It will read the configuration file, read the command key from the keyfile and run the authhash and password commands. .TP \fIcommand\fR specify command. If no command is given, chronyc will read commands interactively. .SH BUGS To report bugs, please visit \fIhttp://chrony.tuxfamily.org\fR .SH "SEE ALSO" .BR chronyd(8) .I http://chrony.tuxfamily.org/ .SH AUTHOR Richard Curnow This man-page was written by Jan Schaumann as part of "The Missing Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details. The complete chrony documentation is supplied in texinfo format. chrony-2.1.1/chronyd.8.in0000644000000000000000000001301012542263372013660 0ustar rootroot.TH CHRONYD 8 "June 2015" "chrony 2.1.1" "System Administration" .SH NAME chronyd \- chrony background daemon .SH SYNOPSIS .B chronyd [\fIOPTIONS\fR] [\fIconfiguration commands\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 privileges. If \fBchronyd\fR has been installed to its default location \fI@SBINDIR@/chronyd\fR, starting it is simply a matter of entering the command: \fI@SBINDIR@/chronyd\fR Information messages and warnings will be logged to syslog. If no configuration commands are specified on the command line, \fBchronyd\fR will read the commands from the configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR). .SH OPTIONS A summary of the options supported by \fBchronyd\fR is included below. .TP \fB\-P\fR \fIpriority\fR This option will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). This mode is supported only on Linux. .TP .B \-m This option will lock chronyd into RAM so that it will never be paged out. This mode is only supported on Linux. .TP .B \-n When run in this mode, the program will not detach itself from the terminal. .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. When \fBchronyd\fR was compiled with debugging support, this option can be used twice to print also debugging messages. .TP \fB\-f\fR \fIconf-file\fR This option can be used to specify an alternate location for the configuration file (default \fI@SYSCONFDIR@/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 other systems this option should not be used. .TP .B \-R When this option is used, the \fIinitstepslew\fR directive and the \fImakestep\fR directive used with a positive limit will be ignored. This option is useful when restarting \fBchronyd\fR and can be used in conjunction with the \fB-r\fR option. .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/hwclock\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 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 (RTC). 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 RTC and system clock last time the computer was on. If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC installed, the system clock will be set with this option forward to the time of the last modification of the drift file (specified by the \fIdriftfile\fR directive) to restore the system time at which \fBchronyd\fR was previously stopped. .TP \fB\-u\fR \fIuser\fR This option sets the name of the user to which will \fBchronyd\fR switch to drop root privileges if compiled with Linux capabilities support (default \fB@DEFAULT_USER@\fR). .TP .B \-q When run in this mode, chronyd will set the system clock once and exit. It will not detach from the terminal. .TP .B \-Q This option is similar to \fB\-q\fR, but it will only print the offset and not correct the clock. .TP .B \-v This option displays \fBchronyd\fR's version number to the terminal and exits .TP .B \-4 Resolve hostnames only to IPv4 addresses and create only IPv4 sockets. .TP .B \-6 Resolve hostnames only to IPv6 addresses and create only IPv6 sockets. .SH FILES \fI@SYSCONFDIR@/chrony.conf\fR .SH BUGS To report bugs, please visit \fIhttp://chrony.tuxfamily.org/\fR .SH "SEE ALSO" \fBchronyd\fR is documented in detail in the documentation supplied with the distribution (\fIchrony.txt\fR and \fIchrony.texi\fR). .BR chronyc(1), .BR chrony.conf(5), .BR hwclock(8), .BR ntpd(8) .I http://chrony.tuxfamily.org/ .SH AUTHOR Richard Curnow This man-page was written by Jan Schaumann as part of "The Missing Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details. The complete chrony documentation is supplied in texinfo format. chrony-2.1.1/client.c0000644000000000000000000024013712542263351013147 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Command line client for configuring the daemon and obtaining status from it whilst running. */ #include "config.h" #include "sysincl.h" #include "candm.h" #include "nameserv.h" #include "hash.h" #include "getdate.h" #include "cmdparse.h" #include "pktlength.h" #include "util.h" #ifdef FEAT_READLINE #ifdef USE_EDITLINE #include #else #include #include #endif #endif /* ================================================== */ union sockaddr_in46 { struct sockaddr_in in4; #ifdef FEAT_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; static int sock_fd; union sockaddr_in46 his_addr; static socklen_t his_addr_len; static int on_terminal = 0; static int no_dns = 0; static int recv_errqueue = 0; /* ================================================== */ /* Read a single line of commands from standard input. Eventually we might want to use the GNU readline library. */ static char * read_line(void) { static char line[2048]; static const char *prompt = "chronyc> "; if (on_terminal) { #ifdef FEAT_READLINE char *cmd; /* save line only if not empty */ cmd = readline(prompt); if( cmd == NULL ) return( NULL ); /* user pressed return */ if( *cmd != '\0' ) { strncpy(line, cmd, sizeof(line) - 1); line[sizeof(line) - 1] = '\0'; add_history(cmd); /* free the buffer allocated by readline */ free(cmd); } else { /* simulate the user has entered an empty line */ *line = '\0'; } return( line ); #else printf("%s", prompt); #endif } if (fgets(line, sizeof(line), stdin)) { return line; } else { return NULL; } } /* ================================================== */ /* Initialise the socket used to talk to the daemon */ static void open_io(const char *hostname, int port) { IPAddr ip; int on_off = 1; /* Note, this call could block for a while */ if (DNS_Name2IPAddress(hostname, &ip, 1) != DNS_Success) { fprintf(stderr, "Could not get IP address for %s\n", hostname); exit(1); } switch (ip.family) { case IPADDR_INET4: sock_fd = socket(AF_INET, SOCK_DGRAM, 0); break; #ifdef FEAT_IPV6 case IPADDR_INET6: sock_fd = socket(AF_INET6, SOCK_DGRAM, 0); break; #endif default: assert(0); } his_addr_len = UTI_IPAndPortToSockaddr(&ip, port, &his_addr.u); if (sock_fd < 0) { perror("Can't create socket"); exit(1); } /* Enable extended error reporting (e.g. ECONNREFUSED on ICMP unreachable) */ #ifdef IP_RECVERR if (ip.family == IPADDR_INET4 && !setsockopt(sock_fd, IPPROTO_IP, IP_RECVERR, &on_off, sizeof(on_off))) { recv_errqueue = 1; } #endif #ifdef FEAT_IPV6 #ifdef IPV6_RECVERR if (ip.family == IPADDR_INET6 && !setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVERR, &on_off, sizeof(on_off))) { recv_errqueue = 1; } #endif #endif } /* ================================================== */ static void close_io(void) { close(sock_fd); } /* ================================================== */ static void bits_to_mask(int bits, int family, IPAddr *mask) { int i; mask->family = family; switch (family) { case IPADDR_INET4: if (bits < 0) bits = 32; if (bits > 0) { mask->addr.in4 = -1; mask->addr.in4 <<= 32 - bits; } else { mask->addr.in4 = 0; } break; case IPADDR_INET6: if (bits > 128 || bits < 0) bits = 128; for (i = 0; i < bits / 8; i++) mask->addr.in6[i] = 0xff; if (i < 16) mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff; for (; i < 16; i++) mask->addr.in6[i] = 0x0; break; default: assert(0); } } /* ================================================== */ static int read_mask_address(char *line, IPAddr *mask, IPAddr *address) { unsigned int bits; char *p, *q; p = line; if (!*p) { mask->family = address->family = IPADDR_UNSPEC; return 1; } else { q = strchr(p, '/'); if (q) { *q++ = 0; if (UTI_StringToIP(p, mask)) { p = q; if (UTI_StringToIP(p, address)) { if (address->family == mask->family) return 1; } else if (sscanf(p, "%u", &bits) == 1) { *address = *mask; bits_to_mask(bits, address->family, mask); return 1; } } } else { if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) { bits_to_mask(-1, address->family, mask); return 1; } else { fprintf(stderr, "Could not get address for hostname\n"); return 0; } } } fprintf(stderr, "Invalid syntax for mask/address\n"); return 0; } /* ================================================== */ static int process_cmd_offline(CMD_Request *msg, char *line) { IPAddr mask, address; int ok; if (read_mask_address(line, &mask, &address)) { UTI_IPHostToNetwork(&mask, &msg->data.offline.mask); UTI_IPHostToNetwork(&address, &msg->data.offline.address); msg->command = htons(REQ_OFFLINE); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_online(CMD_Request *msg, char *line) { IPAddr mask, address; int ok; if (read_mask_address(line, &mask, &address)) { UTI_IPHostToNetwork(&mask, &msg->data.online.mask); UTI_IPHostToNetwork(&address, &msg->data.online.address); msg->command = htons(REQ_ONLINE); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int read_address_integer(char *line, IPAddr *address, int *value) { char *hostname; int ok = 0; hostname = line; line = CPS_SplitWord(line); if (sscanf(line, "%d", value) != 1) { fprintf(stderr, "Invalid syntax for address value\n"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { fprintf(stderr, "Could not get address for hostname\n"); ok = 0; } else { ok = 1; } } return ok; } /* ================================================== */ static int read_address_double(char *line, IPAddr *address, double *value) { char *hostname; int ok = 0; hostname = line; line = CPS_SplitWord(line); if (sscanf(line, "%lf", value) != 1) { fprintf(stderr, "Invalid syntax for address value\n"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { fprintf(stderr, "Could not get address for hostname\n"); ok = 0; } else { ok = 1; } } return ok; } /* ================================================== */ static int process_cmd_minpoll(CMD_Request *msg, char *line) { IPAddr address; int minpoll; int ok; if (read_address_integer(line, &address, &minpoll)) { UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address); msg->data.modify_minpoll.new_minpoll = htonl(minpoll); msg->command = htons(REQ_MODIFY_MINPOLL); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxpoll(CMD_Request *msg, char *line) { IPAddr address; int maxpoll; int ok; if (read_address_integer(line, &address, &maxpoll)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address); msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll); msg->command = htons(REQ_MODIFY_MAXPOLL); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelay(CMD_Request *msg, char *line) { IPAddr address; double max_delay; int ok; if (read_address_double(line, &address, &max_delay)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address); msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay); msg->command = htons(REQ_MODIFY_MAXDELAY); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelaydevratio(CMD_Request *msg, char *line) { IPAddr address; double max_delay_dev_ratio; int ok; if (read_address_double(line, &address, &max_delay_dev_ratio)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address); msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio); msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelayratio(CMD_Request *msg, char *line) { IPAddr address; double max_delay_ratio; int ok; if (read_address_double(line, &address, &max_delay_ratio)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address); msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio); msg->command = htons(REQ_MODIFY_MAXDELAYRATIO); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_minstratum(CMD_Request *msg, char *line) { IPAddr address; int min_stratum; int ok; if (read_address_integer(line, &address, &min_stratum)) { UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address); msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum); msg->command = htons(REQ_MODIFY_MINSTRATUM); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_polltarget(CMD_Request *msg, char *line) { IPAddr address; int poll_target; int ok; if (read_address_integer(line, &address, &poll_target)) { UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address); msg->data.modify_polltarget.new_poll_target = htonl(poll_target); msg->command = htons(REQ_MODIFY_POLLTARGET); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxupdateskew(CMD_Request *msg, char *line) { int ok; double new_max_update_skew; if (sscanf(line, "%lf", &new_max_update_skew) == 1) { msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew); msg->command = htons(REQ_MODIFY_MAXUPDATESKEW); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static void process_cmd_dump(CMD_Request *msg, char *line) { msg->command = htons(REQ_DUMP); msg->data.dump.pad = htonl(0); } /* ================================================== */ static void process_cmd_writertc(CMD_Request *msg, char *line) { msg->command = htons(REQ_WRITERTC); } /* ================================================== */ static void process_cmd_trimrtc(CMD_Request *msg, char *line) { msg->command = htons(REQ_TRIMRTC); } /* ================================================== */ static void process_cmd_cyclelogs(CMD_Request *msg, char *line) { msg->command = htons(REQ_CYCLELOGS); } /* ================================================== */ static int process_cmd_burst(CMD_Request *msg, char *line) { int n_good_samples, n_total_samples; char *s1, *s2; IPAddr address, mask; s1 = line; s2 = CPS_SplitWord(s1); CPS_SplitWord(s2); if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) { fprintf(stderr, "Invalid syntax for burst command\n"); return 0; } mask.family = address.family = IPADDR_UNSPEC; if (*s2 && !read_mask_address(s2, &mask, &address)) { return 0; } msg->command = htons(REQ_BURST); msg->data.burst.n_good_samples = ntohl(n_good_samples); msg->data.burst.n_total_samples = ntohl(n_total_samples); UTI_IPHostToNetwork(&mask, &msg->data.burst.mask); UTI_IPHostToNetwork(&address, &msg->data.burst.address); return 1; } /* ================================================== */ static int process_cmd_local(CMD_Request *msg, const char *line) { const char *p; int stratum; p = line; if (!strcmp(p, "off")) { msg->data.local.on_off = htonl(0); msg->data.local.stratum = htonl(0); } else if (sscanf(p, "stratum%d", &stratum) == 1) { msg->data.local.on_off = htonl(1); msg->data.local.stratum = htonl(stratum); } else { fprintf(stderr, "Invalid syntax for local command\n"); return 0; } msg->command = htons(REQ_LOCAL); return 1; } /* ================================================== */ static int process_cmd_manual(CMD_Request *msg, const char *line) { const char *p; p = line; if (!strcmp(p, "off")) { msg->data.manual.option = htonl(0); } else if (!strcmp(p, "on")) { msg->data.manual.option = htonl(1); } else if (!strcmp(p, "reset")) { msg->data.manual.option = htonl(2); } else { fprintf(stderr, "Invalid syntax for manual command\n"); return 0; } msg->command = htons(REQ_MANUAL); return 1; } /* ================================================== */ static int parse_allow_deny(CMD_Request *msg, char *line) { unsigned long a, b, c, d; int n, specified_subnet_bits; IPAddr ip; char *p; p = line; if (!*p) { /* blank line - applies to all addresses */ ip.family = IPADDR_UNSPEC; UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); msg->data.allow_deny.subnet_bits = htonl(0); } else { char *slashpos; slashpos = strchr(p, '/'); if (slashpos) *slashpos = 0; n = 0; if (!UTI_StringToIP(p, &ip) && (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) { /* Try to parse as the name of a machine */ if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { fprintf(stderr, "Could not read address\n"); return 0; } else { UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); if (ip.family == IPADDR_INET6) msg->data.allow_deny.subnet_bits = htonl(128); else msg->data.allow_deny.subnet_bits = htonl(32); } } else { if (n == 0) { if (ip.family == IPADDR_INET6) msg->data.allow_deny.subnet_bits = htonl(128); else msg->data.allow_deny.subnet_bits = htonl(32); } else { ip.family = IPADDR_INET4; a &= 0xff; b &= 0xff; c &= 0xff; d &= 0xff; switch (n) { case 1: ip.addr.in4 = htonl((a<<24)); msg->data.allow_deny.subnet_bits = htonl(8); break; case 2: ip.addr.in4 = htonl((a<<24) | (b<<16)); msg->data.allow_deny.subnet_bits = htonl(16); break; case 3: ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8)); msg->data.allow_deny.subnet_bits = htonl(24); break; case 4: ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8) | d); msg->data.allow_deny.subnet_bits = htonl(32); break; default: assert(0); } } UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); if (slashpos) { n = sscanf(slashpos+1, "%d", &specified_subnet_bits); if (n == 1) { msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits); } else { fprintf(stderr, "Warning: badly formatted subnet size, using %ld\n", (long) ntohl(msg->data.allow_deny.subnet_bits)); } } } } return 1; } /* ================================================== */ static int process_cmd_allow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_allowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_deny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_denyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddeny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddenyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int accheck_getaddr(char *line, IPAddr *addr) { unsigned long a, b, c, d; IPAddr ip; char *p; p = line; if (!*p) { return 0; } else { if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) { addr->family = IPADDR_INET4; addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; return 1; } else { if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { return 0; } else { *addr = ip; return 1; } } } } /* ================================================== */ static int process_cmd_accheck(CMD_Request *msg, char *line) { IPAddr ip; msg->command = htons(REQ_ACCHECK); if (accheck_getaddr(line, &ip)) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { fprintf(stderr, "Could not read address\n"); return 0; } } /* ================================================== */ static int process_cmd_cmdaccheck(CMD_Request *msg, char *line) { IPAddr ip; msg->command = htons(REQ_CMDACCHECK); if (accheck_getaddr(line, &ip)) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { fprintf(stderr, "Could not read address\n"); return 0; } } /* ================================================== */ static void process_cmd_dfreq(CMD_Request *msg, char *line) { double dfreq; msg->command = htons(REQ_DFREQ); if (sscanf(line, "%lf", &dfreq) == 1) { msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); } else { msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0); } } /* ================================================== */ static void cvt_to_sec_usec(double x, long *sec, long *usec) { long s, us; s = (long) x; us = (long)(0.5 + 1.0e6 * (x - (double) s)); while (us >= 1000000) { us -= 1000000; s += 1; } while (us < 0) { us += 1000000; s -= 1; } *sec = s; *usec = us; } /* ================================================== */ static void process_cmd_doffset(CMD_Request *msg, char *line) { double doffset; long sec, usec; msg->command = htons(REQ_DOFFSET); if (sscanf(line, "%lf", &doffset) == 1) { cvt_to_sec_usec(doffset, &sec, &usec); msg->data.doffset.sec = htonl(sec); msg->data.doffset.usec = htonl(usec); } else { msg->data.doffset.sec = htonl(0); msg->data.doffset.usec = htonl(0); } } /* ================================================== */ static int process_cmd_add_server_or_peer(CMD_Request *msg, char *line) { CPS_NTP_Source data; CPS_Status status; IPAddr ip_addr; char str[64]; int result = 0; status = CPS_ParseNTPSourceAdd(line, &data); switch (status) { case CPS_Success: if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { fprintf(stderr, "Invalid host/IP address\n"); break; } if (data.params.min_stratum != SRC_DEFAULT_MINSTRATUM) { fprintf(stderr, "Option minstratum not supported\n"); break; } if (data.params.poll_target != SRC_DEFAULT_POLLTARGET) { fprintf(stderr, "Option polltarget not supported\n"); break; } if (data.params.max_delay_dev_ratio != SRC_DEFAULT_MAXDELAYDEVRATIO) { fprintf(stderr, "Option maxdelaydevratio not supported\n"); break; } if (data.params.version != NTP_VERSION) { fprintf(stderr, "Option version not supported\n"); break; } if (data.params.max_sources != SRC_DEFAULT_MAXSOURCES) { fprintf(stderr, "Option maxsources not supported\n"); break; } if (data.params.min_samples != SRC_DEFAULT_MINSAMPLES) { fprintf(stderr, "Option minsamples not supported\n"); break; } if (data.params.max_samples != SRC_DEFAULT_MAXSAMPLES) { fprintf(stderr, "Option maxsamples not supported\n"); break; } msg->data.ntp_source.port = htonl((unsigned long) data.port); UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr); msg->data.ntp_source.minpoll = htonl(data.params.minpoll); msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); msg->data.ntp_source.authkey = htonl(data.params.authkey); msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay); msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio); msg->data.ntp_source.flags = htonl( (data.params.online ? REQ_ADDSRC_ONLINE : 0) | (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) | (data.params.iburst ? REQ_ADDSRC_IBURST : 0) | (data.params.sel_option == SRC_SelectPrefer ? REQ_ADDSRC_PREFER : 0) | (data.params.sel_option == SRC_SelectNoselect ? REQ_ADDSRC_NOSELECT : 0)); result = 1; break; default: CPS_StatusToString(status, str, sizeof (str)); fprintf(stderr, "%s\n", str); break; } return result; } /* ================================================== */ static int process_cmd_add_server(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_SERVER); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_add_peer(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_PEER); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_delete(CMD_Request *msg, char *line) { char *hostname; int ok = 0; IPAddr address; msg->command = htons(REQ_DEL_SOURCE); hostname = line; CPS_SplitWord(line); if (!*hostname) { fprintf(stderr, "Invalid syntax for address\n"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { fprintf(stderr, "Could not get address for hostname\n"); ok = 0; } else { UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr); ok = 1; } } return ok; } /* ================================================== */ static char *password = NULL; static int password_length; static int auth_hash_id; /* ================================================== */ static int process_cmd_password(CMD_Request *msg, char *line) { char *p; struct timeval now; int i, len; /* Blank and free the old password */ if (password) { for (i = 0; i < password_length; i++) password[i] = 0; free(password); password = NULL; } p = line; if (!*p) { /* blank line, prompt for password */ p = getpass("Password: "); } if (!*p) return 0; len = strlen(p); password_length = UTI_DecodePasswordFromText(p); if (password_length > 0) { password = malloc(password_length); memcpy(password, p, password_length); } /* Erase the password from the input or getpass buffer */ for (i = 0; i < len; i++) p[i] = 0; if (password_length <= 0) { fprintf(stderr, "Could not decode password\n"); return 0; } if (gettimeofday(&now, NULL) < 0) { printf("500 - Could not read time of day\n"); return 0; } else { msg->command = htons(REQ_LOGON); /* Just force a round trip so that we get tokens etc */ UTI_TimevalHostToNetwork(&now, &msg->data.logon.ts); return 1; } } /* ================================================== */ static int generate_auth(CMD_Request *msg) { int data_len; data_len = PKL_CommandLength(msg); assert(auth_hash_id >= 0); return UTI_GenerateNTPAuth(auth_hash_id, (unsigned char *)password, password_length, (unsigned char *)msg, data_len, ((unsigned char *)msg) + data_len, sizeof (msg->auth)); } /* ================================================== */ static int check_reply_auth(CMD_Reply *msg, int len) { int data_len; data_len = PKL_ReplyLength(msg); assert(auth_hash_id >= 0); return UTI_CheckNTPAuth(auth_hash_id, (unsigned char *)password, password_length, (unsigned char *)msg, data_len, ((unsigned char *)msg) + data_len, len - data_len); } /* ================================================== */ static void give_help(void) { printf("Commands:\n"); printf("accheck
: Check whether NTP access is allowed to
\n"); printf("activity : Check how many NTP sources are online/offline\n"); printf("add peer
... : Add a new NTP peer\n"); printf("add server
... : Add a new NTP server\n"); printf("allow [] : Allow NTP access to that subnet as a default\n"); printf("allow all [] : Allow NTP access to that subnet and all children\n"); printf("burst / [/] : Start a rapid set of measurements\n"); printf("clients : Report on clients that have accessed the server\n"); printf("cmdaccheck
: Check whether command access is allowed to
\n"); printf("cmdallow [] : Allow command access to that subnet as a default\n"); printf("cmdallow all [] : Allow command access to that subnet and all children\n"); printf("cmddeny [] : Deny command access to that subnet as a default\n"); printf("cmddeny all [] : Deny command access to that subnet and all children\n"); printf("cyclelogs : Close and re-open logs files\n"); printf("delete
: Remove an NTP server or peer\n"); printf("deny [] : Deny NTP access to that subnet as a default\n"); printf("deny all [] : Deny NTP access to that subnet and all children\n"); printf("dump : Dump all measurements to save files\n"); printf("local off : Disable server capability for unsynchronised clock\n"); printf("local stratum : Enable server capability for unsynchronised clock\n"); printf("makestep [ ] : Correct clock by stepping\n"); printf("manual off|on|reset : Disable/enable/reset settime command and statistics\n"); printf("manual list : Show previous settime entries\n"); printf("maxdelay
: Modify maximum round-trip valid sample delay for source\n"); printf("maxdelayratio
: Modify max round-trip delay ratio for source\n"); printf("maxdelaydevratio
: Modify max round-trip delay dev ratio for source\n"); printf("maxpoll
: Modify maximum polling interval of source\n"); printf("maxupdateskew : Modify maximum skew for a clock frequency update to be made\n"); printf("minpoll
: Modify minimum polling interval of source\n"); printf("minstratum
: Modify minimum stratum of source\n"); printf("offline [/] : Set sources in subnet to offline status\n"); printf("online [/] : Set sources in subnet to online status\n"); printf("password [] : Set command authentication password\n"); printf("polltarget
: Modify poll target of source\n"); printf("reselect : Reselect synchronisation source\n"); printf("rtcdata : Print current RTC performance parameters\n"); printf("settime : Manually set the daemon time\n"); printf("smoothing : Display current time smoothing state\n"); printf("smoothtime reset|activate : Reset/activate time smoothing\n"); printf("sources [-v] : Display information about current sources\n"); printf("sourcestats [-v] : Display estimation information about current sources\n"); printf("tracking : Display system time information\n"); printf("trimrtc : Correct RTC relative to system clock\n"); printf("waitsync [max-tries [max-correction [max-skew]]] : Wait until synchronised\n"); printf("writertc : Save RTC parameters to file\n"); printf("\n"); printf("authhash : Set command authentication hash function\n"); printf("dns -n|+n : Disable/enable resolving IP addresses to hostnames\n"); printf("dns -4|-6|-46 : Resolve hostnames only to IPv4/IPv6/both addresses\n"); printf("timeout : Set initial response timeout\n"); printf("retries : Set maximum number of retries\n"); printf("exit|quit : Leave the program\n"); printf("help : Generate this help\n"); printf("\n"); } /* ================================================== */ static unsigned long sequence = 0; static unsigned long utoken = 0; static unsigned long token = 0; static int max_retries = 2; static int initial_timeout = 1000; static int proto_version = PROTO_VERSION_NUMBER; /* This is the core protocol module. Complete particular fields in the outgoing packet, send it, wait for a response, handle retries, etc. Returns a Boolean indicating whether the protocol was successful or not.*/ static int submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok) { unsigned long tx_sequence; socklen_t where_from_len; union sockaddr_in46 where_from; int bad_length, bad_sender, bad_sequence, bad_header; int select_status; int recvfrom_status; int read_length; int expected_length; int command_length; int padding_length; int auth_length; struct timeval tv; int timeout; int n_attempts; fd_set rdfd, wrfd, exfd; request->pkt_type = PKT_TYPE_CMD_REQUEST; request->res1 = 0; request->res2 = 0; tx_sequence = sequence++; request->sequence = htonl(tx_sequence); request->attempt = 0; request->utoken = htonl(utoken); request->token = htonl(token); timeout = initial_timeout; n_attempts = 0; do { request->version = proto_version; command_length = PKL_CommandLength(request); padding_length = PKL_CommandPaddingLength(request); assert(command_length > 0 && command_length > padding_length); /* Zero the padding to avoid sending uninitialized data. This needs to be done before generating auth data as it includes the padding. */ memset(((char *)request) + command_length - padding_length, 0, padding_length); /* Decide whether to authenticate */ if (password) { if (!utoken || (request->command == htons(REQ_LOGON))) { /* Otherwise, the daemon won't bother authenticating our packet and we won't get a token back */ request->utoken = htonl(SPECIAL_UTOKEN); } auth_length = generate_auth(request); } else { auth_length = 0; } /* add empty MD5 auth so older servers will not drop the request due to bad length */ if (!auth_length) { memset(((char *)request) + command_length, 0, 16); auth_length = 16; } #if 0 printf("Sent command length=%d bytes auth length=%d bytes\n", command_length, auth_length); #endif if (sendto(sock_fd, (void *) request, command_length + auth_length, 0, &his_addr.u, his_addr_len) < 0) { #if 0 perror("Could not send packet"); #endif return 0; } /* Increment this for next time */ ++ request->attempt; tv.tv_sec = timeout / 1000; tv.tv_usec = timeout % 1000 * 1000; timeout *= 2; FD_ZERO(&rdfd); FD_ZERO(&wrfd); FD_ZERO(&exfd); FD_SET(sock_fd, &rdfd); select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv); if (select_status < 0) { #if 0 perror("Select returned negative status"); #endif } else if (select_status == 0) { /* Timeout must have elapsed, try a resend? */ n_attempts ++; if (n_attempts > max_retries) { return 0; } /* Back to top of loop to do resend */ continue; } else { where_from_len = sizeof(where_from); recvfrom_status = recvfrom(sock_fd, (void *) reply, sizeof(CMD_Reply), 0, &where_from.u, &where_from_len); #if 0 printf("Received packet, status=%d\n", recvfrom_status); #endif if (recvfrom_status < 0) { /* If we get connrefused here, it suggests the sendto is going to a dead port - but only if the daemon machine is running Linux (Solaris doesn't return anything) */ #ifdef IP_RECVERR /* Fetch the message from the error queue */ if (recv_errqueue && recvfrom(sock_fd, (void *)reply, sizeof(CMD_Reply), MSG_ERRQUEUE, &where_from.u, &where_from_len) < 0) ; #endif n_attempts++; if (n_attempts > max_retries) { return 0; } } else { read_length = recvfrom_status; if (read_length >= offsetof(CMD_Reply, data)) { expected_length = PKL_ReplyLength(reply); } else { expected_length = 0; } bad_length = (read_length < expected_length || expected_length < offsetof(CMD_Reply, data)); bad_sender = (where_from.u.sa_family != his_addr.u.sa_family || (where_from.u.sa_family == AF_INET && (where_from.in4.sin_addr.s_addr != his_addr.in4.sin_addr.s_addr || where_from.in4.sin_port != his_addr.in4.sin_port)) || #ifdef FEAT_IPV6 (where_from.u.sa_family == AF_INET6 && (memcmp(where_from.in6.sin6_addr.s6_addr, his_addr.in6.sin6_addr.s6_addr, sizeof (where_from.in6.sin6_addr.s6_addr)) != 0 || where_from.in6.sin6_port != his_addr.in6.sin6_port)) || #endif 0); if (!bad_length) { bad_sequence = (ntohl(reply->sequence) != tx_sequence); } else { bad_sequence = 0; } if (bad_length || bad_sender || bad_sequence) { n_attempts++; if (n_attempts > max_retries) { return 0; } continue; } bad_header = ((reply->version != proto_version && !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT && ntohs(reply->status) == STT_BADPKTVERSION)) || (reply->pkt_type != PKT_TYPE_CMD_REPLY) || (reply->res1 != 0) || (reply->res2 != 0) || (reply->command != request->command)); if (bad_header) { n_attempts++; if (n_attempts > max_retries) { return 0; } continue; } #if PROTO_VERSION_NUMBER == 6 /* Protocol version 5 is similar to 6 except there is no padding. If a version 5 reply with STT_BADPKTVERSION is received, switch our version and try again. */ if (proto_version == PROTO_VERSION_NUMBER && reply->version == PROTO_VERSION_NUMBER - 1) { proto_version = PROTO_VERSION_NUMBER - 1; continue; } #else #error unknown compatibility with PROTO_VERSION - 1 #endif /* Good packet received, print out results */ #if 0 printf("Reply cmd=%d reply=%d stat=%d seq=%d utok=%08lx tok=%d\n", ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status), ntohl(reply->sequence), ntohl(reply->utoken), ntohl(reply->token)); #endif if (password) { *reply_auth_ok = check_reply_auth(reply, read_length); } else { /* Assume in this case that the reply is always considered to be authentic */ *reply_auth_ok = 1; } utoken = ntohl(reply->utoken); if (*reply_auth_ok) { /* If we're in authenticated mode, only acquire the utoken and new token values if the reply authenticated properly. This protects against forged packets with bogus tokens in. We won't accept a repeat of an old message with a stale token in it, due to bad_sequence processing earlier. */ utoken = ntohl(reply->utoken); token = ntohl(reply->token); } break; } } } while (1); return 1; } /* ================================================== */ static int request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int verbose) { int reply_auth_ok; int status; if (!submit_request(request, reply, &reply_auth_ok)) { printf("506 Cannot talk to daemon\n"); return 0; } status = ntohs(reply->status); if (verbose || status != STT_SUCCESS) { switch (status) { case STT_SUCCESS: printf("200 OK"); break; case STT_ACCESSALLOWED: printf("208 Access allowed"); break; case STT_ACCESSDENIED: printf("209 Access denied"); break; case STT_FAILED: printf("500 Failure"); break; case STT_UNAUTH: printf("501 Not authorised"); break; case STT_INVALID: printf("502 Invalid command"); break; case STT_NOSUCHSOURCE: printf("503 No such source"); break; case STT_INVALIDTS: printf("504 Duplicate or stale logon detected"); break; case STT_NOTENABLED: printf("505 Facility not enabled in daemon"); break; case STT_BADSUBNET: printf("507 Bad subnet"); break; case STT_NOHOSTACCESS: printf("510 No command access from this host"); break; case STT_SOURCEALREADYKNOWN: printf("511 Source already present"); break; case STT_TOOMANYSOURCES: printf("512 Too many sources present"); break; case STT_NORTC: printf("513 RTC driver not running"); break; case STT_BADRTCFILE: printf("514 Can't write RTC parameters"); break; case STT_INVALIDAF: printf("515 Invalid address family"); break; case STT_BADSAMPLE: printf("516 Sample index out of range"); break; case STT_BADPKTVERSION: printf("517 Protocol version mismatch"); break; case STT_BADPKTLENGTH: printf("518 Packet length mismatch"); break; case STT_INACTIVE: printf("519 Client logging is not active in the daemon"); break; default: printf("520 Got unexpected error from daemon"); } if (reply_auth_ok) { printf("\n"); } else { printf(" --- Reply not authenticated\n"); } } if (status != STT_SUCCESS && status != STT_ACCESSALLOWED && status != STT_ACCESSDENIED) { return 0; } if (ntohs(reply->reply) != requested_reply) { printf("508 Bad reply from daemon\n"); return 0; } return 1; } /* ================================================== */ static void print_seconds(unsigned long s) { unsigned long d; if (s <= 1024) { printf("%4ld", s); } else if (s < 36000) { printf("%3ldm", s / 60); } else if (s < 345600) { printf("%3ldh", s / 3600); } else { d = s / 86400; if (d > 999) { printf("%3ldy", d / 365); } else { printf("%3ldd", d); } } } /* ================================================== */ static void print_nanoseconds(double s) { s = fabs(s); if (s < 9999.5e-9) { printf("%4.0fns", s * 1e9); } else if (s < 9999.5e-6) { printf("%4.0fus", s * 1e6); } else if (s < 9999.5e-3) { printf("%4.0fms", s * 1e3); } else if (s < 999.5) { printf("%5.1fs", s); } else if (s < 99999.5) { printf("%5.0fs", s); } else if (s < 99999.5 * 60) { printf("%5.0fm", s / 60); } else if (s < 99999.5 * 3600) { printf("%5.0fh", s / 3600); } else if (s < 99999.5 * 3600 * 24) { printf("%5.0fd", s / (3600 * 24)); } else { printf("%5.0fy", s / (3600 * 24 * 365)); } } /* ================================================== */ static void print_signed_nanoseconds(double s) { double x; x = fabs(s); if (x < 9999.5e-9) { printf("%+5.0fns", s * 1e9); } else if (x < 9999.5e-6) { printf("%+5.0fus", s * 1e6); } else if (x < 9999.5e-3) { printf("%+5.0fms", s * 1e3); } else if (x < 999.5) { printf("%+6.1fs", s); } else if (x < 99999.5) { printf("%+6.0fs", s); } else if (x < 99999.5 * 60) { printf("%+6.0fm", s / 60); } else if (x < 99999.5 * 3600) { printf("%+6.0fh", s / 3600); } else if (x < 99999.5 * 3600 * 24) { printf("%+6.0fd", s / (3600 * 24)); } else { printf("%+6.0fy", s / (3600 * 24 * 365)); } } /* ================================================== */ static void print_freq_ppm(double f) { if (fabs(f) < 99999.5) { printf("%10.3f", f); } else { printf("%10.0f", f); } } /* ================================================== */ static void print_signed_freq_ppm(double f) { if (fabs(f) < 99999.5) { printf("%+10.3f", f); } else { printf("%+10.0f", f); } } /* ================================================== */ static int check_for_verbose_flag(char *line) { char *p = line; if (!strcmp(p, "-v")) { return 1; } else { return 0; } } /* ================================================== */ static int process_cmd_sources(char *line) { CMD_Request request; CMD_Reply reply; int n_sources, i; int verbose = 0; double orig_latest_meas, latest_meas, latest_meas_err; IPAddr ip_addr; uint32_t latest_meas_ago; int16_t poll; uint16_t stratum, state, mode, flags, reachability; char hostname_buf[50]; /* Check whether to output verbose headers */ verbose = check_for_verbose_flag(line); request.command = htons(REQ_N_SOURCES); if (request_reply(&request, &reply, RPY_N_SOURCES, 0)) { n_sources = ntohl(reply.data.n_sources.n_sources); printf("210 Number of sources = %d\n", n_sources); if (verbose) { printf("\n"); printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n"); printf(" / .- Source state '*' = current synced, '+' = combined , '-' = not combined,\n"); printf("| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.\n"); printf("|| .- xxxx [ yyyy ] +/- zzzz\n"); printf("|| Reachability register (octal) -. | xxxx = adjusted offset,\n"); printf("|| Log2(Polling interval) --. | | yyyy = measured offset,\n"); printf("|| \\ | | zzzz = estimated error.\n"); printf("|| | | \\\n"); } printf("MS Name/IP address Stratum Poll Reach LastRx Last sample\n"); printf("===============================================================================\n"); /* "MS NNNNNNNNNNNNNNNNNNNNNNNNNNN SS PP RRR RRRR SSSSSSS[SSSSSSS] +/- SSSSSS" */ for (i=0; i> 24); b = (ref_id >> 16) & 0xff; c = (ref_id >> 8) & 0xff; d = (ref_id) & 0xff; UTI_IPNetworkToHost(&reply.data.tracking.ip_addr, &ip_addr); if (ip_addr.family == IPADDR_UNSPEC) { ref_ip = UTI_RefidToString(ref_id); } else if (no_dns) { ref_ip = UTI_IPToString(&ip_addr); } else { DNS_IPAddress2Name(&ip_addr, host, sizeof (host)); ref_ip = host; } switch (ntohs(reply.data.tracking.leap_status)) { case LEAP_Normal: leap_status = "Normal"; break; case LEAP_InsertSecond: leap_status = "Insert second"; break; case LEAP_DeleteSecond: leap_status = "Delete second"; break; case LEAP_Unsynchronised: leap_status = "Not synchronised"; break; default: leap_status = "Unknown"; break; } printf("Reference ID : %lu.%lu.%lu.%lu (%s)\n", a, b, c, d, ref_ip); printf("Stratum : %lu\n", (unsigned long) ntohs(reply.data.tracking.stratum)); UTI_TimevalNetworkToHost(&reply.data.tracking.ref_time, &ref_time); ref_time_tm = *gmtime((time_t *)&ref_time.tv_sec); printf("Ref time (UTC) : %s", asctime(&ref_time_tm)); correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction); last_offset = UTI_FloatNetworkToHost(reply.data.tracking.last_offset); rms_offset = UTI_FloatNetworkToHost(reply.data.tracking.rms_offset); printf("System time : %.9f seconds %s of NTP time\n", fabs(correction), (correction > 0.0) ? "slow" : "fast"); printf("Last offset : %+.9f seconds\n", last_offset); printf("RMS offset : %.9f seconds\n", rms_offset); freq_ppm = UTI_FloatNetworkToHost(reply.data.tracking.freq_ppm); resid_freq_ppm = UTI_FloatNetworkToHost(reply.data.tracking.resid_freq_ppm); skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm); root_delay = UTI_FloatNetworkToHost(reply.data.tracking.root_delay); root_dispersion = UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion); last_update_interval = UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval); printf("Frequency : %.3f ppm %s\n", fabs(freq_ppm), (freq_ppm < 0.0) ? "slow" : "fast"); printf("Residual freq : %+.3f ppm\n", resid_freq_ppm); printf("Skew : %.3f ppm\n", skew_ppm); printf("Root delay : %.6f seconds\n", root_delay); printf("Root dispersion : %.6f seconds\n", root_dispersion); printf("Update interval : %.1f seconds\n", last_update_interval); printf("Leap status : %s\n", leap_status); return 1; } return 0; } /* ================================================== */ static int process_cmd_smoothing(char *line) { CMD_Request request; CMD_Reply reply; uint32_t flags; double offset; double freq_ppm; double wander_ppm; double last_update_ago; double remaining_time; request.command = htons(REQ_SMOOTHING); if (request_reply(&request, &reply, RPY_SMOOTHING, 0)) { flags = ntohl(reply.data.smoothing.flags); offset = UTI_FloatNetworkToHost(reply.data.smoothing.offset); freq_ppm = UTI_FloatNetworkToHost(reply.data.smoothing.freq_ppm); wander_ppm = UTI_FloatNetworkToHost(reply.data.smoothing.wander_ppm); last_update_ago = UTI_FloatNetworkToHost(reply.data.smoothing.last_update_ago); remaining_time = UTI_FloatNetworkToHost(reply.data.smoothing.remaining_time); printf("Active : %s%s\n", flags & RPY_SMT_FLAG_ACTIVE ? "Yes" : "No", flags & RPY_SMT_FLAG_LEAPONLY ? " (leap second only)" : ""); printf("Offset : %+.9f seconds\n", offset); printf("Frequency : %+.6f ppm\n", freq_ppm); printf("Wander : %+.6f ppm per second\n", wander_ppm); printf("Last update : %.1f seconds ago\n", last_update_ago); printf("Remaining time : %.1f seconds\n", remaining_time); return 1; } return 0; } /* ================================================== */ static int process_cmd_smoothtime(CMD_Request *msg, const char *line) { if (!strcmp(line, "reset")) { msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_RESET); } else if (!strcmp(line, "activate")) { msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE); } else { fprintf(stderr, "Invalid syntax for smoothtime command\n"); return 0; } msg->command = htons(REQ_SMOOTHTIME); return 1; } /* ================================================== */ static int process_cmd_rtcreport(char *line) { CMD_Request request; CMD_Reply reply; struct timeval ref_time; struct tm ref_time_tm; unsigned short n_samples; unsigned short n_runs; unsigned long span_seconds; double coef_seconds_fast; double coef_gain_rate_ppm; request.command = htons(REQ_RTCREPORT); if (request_reply(&request, &reply, RPY_RTC, 0)) { UTI_TimevalNetworkToHost(&reply.data.rtc.ref_time, &ref_time); ref_time_tm = *gmtime(&ref_time.tv_sec); n_samples = ntohs(reply.data.rtc.n_samples); n_runs = ntohs(reply.data.rtc.n_runs); span_seconds = ntohl(reply.data.rtc.span_seconds); coef_seconds_fast = UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast); coef_gain_rate_ppm = UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm); printf("RTC ref time (UTC) : %s", asctime(&ref_time_tm)); printf("Number of samples : %d\n", n_samples); printf("Number of runs : %d\n", n_runs); printf("Sample span period : "); print_seconds(span_seconds); printf("\n"); printf("RTC is fast by : %12.6f seconds\n", coef_seconds_fast); printf("RTC gains time at : %9.3f ppm\n", coef_gain_rate_ppm); return 1; } return 0; } /* ================================================== */ static int process_cmd_clients(char *line) { CMD_Request request; CMD_Reply reply; unsigned long next_index; int j; IPAddr ip; unsigned long client_hits; unsigned long peer_hits; unsigned long cmd_hits_auth; unsigned long cmd_hits_normal; unsigned long cmd_hits_bad; unsigned long last_ntp_hit_ago; unsigned long last_cmd_hit_ago; char hostname_buf[50]; int n_replies; int n_indices_in_table; next_index = 0; printf("Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC\n" "========================= ====== ====== ====== ====== ====== ==== ====\n"); do { request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX); request.data.client_accesses_by_index.first_index = htonl(next_index); request.data.client_accesses_by_index.n_indices = htonl(MAX_CLIENT_ACCESSES); if (request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX, 0)) { n_replies = ntohl(reply.data.client_accesses_by_index.n_clients); n_indices_in_table = ntohl(reply.data.client_accesses_by_index.n_indices); if (n_replies == 0) { goto finished; } for (j=0; j= n_indices_in_table) { goto finished; } } else { return 0; } } while (1); /* keep going until all subnets have been expanded, down to single nodes */ finished: return 1; } /* ================================================== */ /* Process the manual list command */ static int process_cmd_manual_list(const char *line) { CMD_Request request; CMD_Reply reply; int n_samples; RPY_ManualListSample *sample; int i; struct timeval when; double slewed_offset, orig_offset, residual; request.command = htons(REQ_MANUAL_LIST); if (request_reply(&request, &reply, RPY_MANUAL_LIST, 0)) { n_samples = ntohl(reply.data.manual_list.n_samples); printf("210 n_samples = %d\n", n_samples); printf("# Date Time(UTC) Slewed Original Residual\n" "=======================================================\n"); for (i=0; iwhen, &when); slewed_offset = UTI_FloatNetworkToHost(sample->slewed_offset); orig_offset = UTI_FloatNetworkToHost(sample->orig_offset); residual = UTI_FloatNetworkToHost(sample->residual); printf("%2d %s %10.2f %10.2f %10.2f\n", i, UTI_TimeToLogForm(when.tv_sec), slewed_offset, orig_offset, residual); } return 1; } return 0; } /* ================================================== */ static int process_cmd_manual_delete(CMD_Request *msg, const char *line) { int index; if (sscanf(line, "%d", &index) != 1) { fprintf(stderr, "Bad syntax for manual delete command\n"); return 0; } msg->command = htons(REQ_MANUAL_DELETE); msg->data.manual_delete.index = htonl(index); return 1; } /* ================================================== */ static int process_cmd_settime(char *line) { struct timeval ts; time_t now, new_time; CMD_Request request; CMD_Reply reply; long offset_cs; double dfreq_ppm, new_afreq_ppm; double offset; now = time(NULL); new_time = get_date(line, &now); if (new_time == -1) { printf("510 - Could not parse date string\n"); } else { ts.tv_sec = new_time; ts.tv_usec = 0; UTI_TimevalHostToNetwork(&ts, &request.data.settime.ts); request.command = htons(REQ_SETTIME); if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) { offset_cs = ntohl(reply.data.manual_timestamp.centiseconds); offset = 0.01 * (double)(int32_t)offset_cs; dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm); new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm); printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n", offset, dfreq_ppm, new_afreq_ppm); return 1; } } return 0; } /* ================================================== */ static void process_cmd_rekey(CMD_Request *msg, char *line) { msg->command = htons(REQ_REKEY); } /* ================================================== */ static int process_cmd_makestep(CMD_Request *msg, char *line) { int limit; double threshold; if (*line) { if (sscanf(line, "%lf %d", &threshold, &limit) != 2) { fprintf(stderr, "Bad syntax for makestep command\n"); return 0; } msg->command = htons(REQ_MODIFY_MAKESTEP); msg->data.modify_makestep.limit = htonl(limit); msg->data.modify_makestep.threshold = UTI_FloatHostToNetwork(threshold); } else { msg->command = htons(REQ_MAKESTEP); } return 1; } /* ================================================== */ static int process_cmd_activity(const char *line) { CMD_Request request; CMD_Reply reply; request.command = htons(REQ_ACTIVITY); if (request_reply(&request, &reply, RPY_ACTIVITY, 1)) { printf( "%ld sources online\n" "%ld sources offline\n" "%ld sources doing burst (return to online)\n" "%ld sources doing burst (return to offline)\n" "%ld sources with unknown address\n", (long) ntohl(reply.data.activity.online), (long) ntohl(reply.data.activity.offline), (long) ntohl(reply.data.activity.burst_online), (long) ntohl(reply.data.activity.burst_offline), (long) ntohl(reply.data.activity.unresolved)); return 1; } return 0; } /* ================================================== */ static int process_cmd_reselectdist(CMD_Request *msg, char *line) { double dist; int ok; msg->command = htons(REQ_RESELECTDISTANCE); if (sscanf(line, "%lf", &dist) == 1) { msg->data.reselect_distance.distance = UTI_FloatHostToNetwork(dist); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static void process_cmd_reselect(CMD_Request *msg, char *line) { msg->command = htons(REQ_RESELECT); } /* ================================================== */ static int process_cmd_waitsync(char *line) { CMD_Request request; CMD_Reply reply; uint32_t ref_id, a, b, c, d; double correction, skew_ppm, max_correction, max_skew_ppm; int ret = 0, max_tries, i; max_tries = 0; max_correction = 0.0; max_skew_ppm = 0.0; sscanf(line, "%d %lf %lf", &max_tries, &max_correction, &max_skew_ppm); request.command = htons(REQ_TRACKING); for (i = 1; ; i++) { if (request_reply(&request, &reply, RPY_TRACKING, 0)) { ref_id = ntohl(reply.data.tracking.ref_id); a = (ref_id >> 24); b = (ref_id >> 16) & 0xff; c = (ref_id >> 8) & 0xff; d = (ref_id) & 0xff; correction = UTI_FloatNetworkToHost(reply.data.tracking.current_correction); correction = fabs(correction); skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm); printf("try: %d, refid: %d.%d.%d.%d, correction: %.9f, skew: %.3f\n", i, a, b, c, d, correction, skew_ppm); if (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */ && (max_correction == 0.0 || correction <= max_correction) && (max_skew_ppm == 0.0 || skew_ppm <= max_skew_ppm)) { ret = 1; } } if (!ret && (!max_tries || i < max_tries)) { sleep(10); } else { break; } } return ret; } /* ================================================== */ static int process_cmd_dns(const char *line) { if (!strcmp(line, "-46")) { DNS_SetAddressFamily(IPADDR_UNSPEC); } else if (!strcmp(line, "-4")) { DNS_SetAddressFamily(IPADDR_INET4); } else if (!strcmp(line, "-6")) { DNS_SetAddressFamily(IPADDR_INET6); } else if (!strcmp(line, "-n")) { no_dns = 1; } else if (!strcmp(line, "+n")) { no_dns = 0; } else { fprintf(stderr, "Unrecognized dns command\n"); return 0; } return 1; } /* ================================================== */ static int process_cmd_authhash(const char *line) { const char *hash_name; int new_hash_id; assert(auth_hash_id >= 0); hash_name = line; if (!*hash_name) { fprintf(stderr, "Could not parse hash name\n"); return 0; } new_hash_id = HSH_GetHashId(hash_name); if (new_hash_id < 0) { fprintf(stderr, "Unknown hash name: %s\n", hash_name); return 0; } auth_hash_id = new_hash_id; return 1; } /* ================================================== */ static int process_cmd_timeout(const char *line) { int timeout; timeout = atoi(line); if (timeout < 100) { fprintf(stderr, "Timeout %d is too short\n", timeout); return 0; } initial_timeout = timeout; return 1; } /* ================================================== */ static int process_cmd_retries(const char *line) { int retries; retries = atoi(line); if (retries < 0) { fprintf(stderr, "Invalid maximum number of retries\n"); return 0; } max_retries = retries; return 1; } /* ================================================== */ static int process_line(char *line, int *quit) { char *command; int do_normal_submit; int ret; CMD_Request tx_message; CMD_Reply rx_message; *quit = 0; ret = 0; do_normal_submit = 1; CPS_NormalizeLine(line); if (!*line) { fflush(stderr); fflush(stdout); return 1; }; command = line; line = CPS_SplitWord(line); if (!strcmp(command, "accheck")) { do_normal_submit = process_cmd_accheck(&tx_message, line); } else if (!strcmp(command, "activity")) { do_normal_submit = 0; ret = process_cmd_activity(line); } else if (!strcmp(command, "add") && !strncmp(line, "peer", 4)) { do_normal_submit = process_cmd_add_peer(&tx_message, CPS_SplitWord(line)); } else if (!strcmp(command, "add") && !strncmp(line, "server", 6)) { do_normal_submit = process_cmd_add_server(&tx_message, CPS_SplitWord(line)); } else if (!strcmp(command, "allow")) { if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line)); } else { do_normal_submit = process_cmd_allow(&tx_message, line); } } else if (!strcmp(command, "authhash")) { ret = process_cmd_authhash(line); do_normal_submit = 0; } else if (!strcmp(command, "burst")) { do_normal_submit = process_cmd_burst(&tx_message, line); } else if (!strcmp(command, "clients")) { ret = process_cmd_clients(line); do_normal_submit = 0; } else if (!strcmp(command, "cmdaccheck")) { do_normal_submit = process_cmd_cmdaccheck(&tx_message, line); } else if (!strcmp(command, "cmdallow")) { if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_cmdallowall(&tx_message, CPS_SplitWord(line)); } else { do_normal_submit = process_cmd_cmdallow(&tx_message, line); } } else if (!strcmp(command, "cmddeny")) { if (!strncmp(line, "all", 3)) { line = CPS_SplitWord(line); do_normal_submit = process_cmd_cmddenyall(&tx_message, line); } else { do_normal_submit = process_cmd_cmddeny(&tx_message, line); } } else if (!strcmp(command, "cyclelogs")) { process_cmd_cyclelogs(&tx_message, line); } else if (!strcmp(command, "delete")) { do_normal_submit = process_cmd_delete(&tx_message, line); } else if (!strcmp(command, "deny")) { if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_denyall(&tx_message, CPS_SplitWord(line)); } else { do_normal_submit = process_cmd_deny(&tx_message, line); } } else if (!strcmp(command, "dfreq")) { process_cmd_dfreq(&tx_message, line); } else if (!strcmp(command, "dns")) { ret = process_cmd_dns(line); do_normal_submit = 0; } else if (!strcmp(command, "doffset")) { process_cmd_doffset(&tx_message, line); } else if (!strcmp(command, "dump")) { process_cmd_dump(&tx_message, line); } else if (!strcmp(command, "exit")) { do_normal_submit = 0; *quit = 1; ret = 1; } else if (!strcmp(command, "help")) { do_normal_submit = 0; give_help(); ret = 1; } else if (!strcmp(command, "local")) { do_normal_submit = process_cmd_local(&tx_message, line); } else if (!strcmp(command, "makestep")) { do_normal_submit = process_cmd_makestep(&tx_message, line); } else if (!strcmp(command, "manual")) { if (!strncmp(line, "list", 4)) { do_normal_submit = 0; ret = process_cmd_manual_list(CPS_SplitWord(line)); } else if (!strncmp(line, "delete", 6)) { do_normal_submit = process_cmd_manual_delete(&tx_message, CPS_SplitWord(line)); } else { do_normal_submit = process_cmd_manual(&tx_message, line); } } else if (!strcmp(command, "maxdelay")) { do_normal_submit = process_cmd_maxdelay(&tx_message, line); } else if (!strcmp(command, "maxdelaydevratio")) { do_normal_submit = process_cmd_maxdelaydevratio(&tx_message, line); } else if (!strcmp(command, "maxdelayratio")) { do_normal_submit = process_cmd_maxdelayratio(&tx_message, line); } else if (!strcmp(command, "maxpoll")) { do_normal_submit = process_cmd_maxpoll(&tx_message, line); } else if (!strcmp(command, "maxupdateskew")) { do_normal_submit = process_cmd_maxupdateskew(&tx_message, line); } else if (!strcmp(command, "minpoll")) { do_normal_submit = process_cmd_minpoll(&tx_message, line); } else if (!strcmp(command, "minstratum")) { do_normal_submit = process_cmd_minstratum(&tx_message, line); } else if (!strcmp(command, "offline")) { do_normal_submit = process_cmd_offline(&tx_message, line); } else if (!strcmp(command, "online")) { do_normal_submit = process_cmd_online(&tx_message, line); } else if (!strcmp(command, "password")) { do_normal_submit = process_cmd_password(&tx_message, line); } else if (!strcmp(command, "polltarget")) { do_normal_submit = process_cmd_polltarget(&tx_message, line); } else if (!strcmp(command, "quit")) { do_normal_submit = 0; *quit = 1; ret = 1; } else if (!strcmp(command, "rekey")) { process_cmd_rekey(&tx_message, line); } else if (!strcmp(command, "reselect")) { process_cmd_reselect(&tx_message, line); } else if (!strcmp(command, "reselectdist")) { do_normal_submit = process_cmd_reselectdist(&tx_message, line); } else if (!strcmp(command, "retries")) { ret = process_cmd_retries(line); do_normal_submit = 0; } else if (!strcmp(command, "rtcdata")) { do_normal_submit = 0; ret = process_cmd_rtcreport(line); } else if (!strcmp(command, "settime")) { do_normal_submit = 0; ret = process_cmd_settime(line); } else if (!strcmp(command, "smoothing")) { do_normal_submit = 0; ret = process_cmd_smoothing(line); } else if (!strcmp(command, "smoothtime")) { do_normal_submit = process_cmd_smoothtime(&tx_message, line); } else if (!strcmp(command, "sources")) { do_normal_submit = 0; ret = process_cmd_sources(line); } else if (!strcmp(command, "sourcestats")) { do_normal_submit = 0; ret = process_cmd_sourcestats(line); } else if (!strcmp(command, "timeout")) { ret = process_cmd_timeout(line); do_normal_submit = 0; } else if (!strcmp(command, "tracking")) { ret = process_cmd_tracking(line); do_normal_submit = 0; } else if (!strcmp(command, "trimrtc")) { process_cmd_trimrtc(&tx_message, line); } else if (!strcmp(command, "waitsync")) { ret = process_cmd_waitsync(line); do_normal_submit = 0; } else if (!strcmp(command, "writertc")) { process_cmd_writertc(&tx_message, line); } else { fprintf(stderr, "Unrecognized command\n"); do_normal_submit = 0; } if (do_normal_submit) { ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1); } fflush(stderr); fflush(stdout); return ret; } /* ================================================== */ static int authenticate_from_config(const char *filename) { CMD_Request tx_message; CMD_Reply rx_message; char line[2048], keyfile[2048], *command, *arg, *password; const char *hashname; uint32_t key_id = 0, key_id2; int key_id_valid = 1, ret; FILE *in; in = fopen(filename, "r"); if (!in) { fprintf(stderr, "Could not open file %s : %s\n", filename, strerror(errno)); return 0; } *keyfile = '\0'; while (fgets(line, sizeof (line), in)) { CPS_NormalizeLine(line); command = line; arg = CPS_SplitWord(line); if (!strcasecmp(command, "keyfile")) { snprintf(keyfile, sizeof (keyfile), "%s", arg); } else if (!strcasecmp(command, "commandkey")) { key_id_valid = sscanf(arg, "%"SCNu32, &key_id) == 1; } } fclose(in); if (!*keyfile || !key_id_valid) { fprintf(stderr, "Could not read keyfile or commandkey in file %s\n", filename); return 0; } in = fopen(keyfile, "r"); if (!in) { fprintf(stderr, "Could not open keyfile %s : %s\n", keyfile, strerror(errno)); return 0; } key_id2 = key_id + 1; while (fgets(line, sizeof (line), in)) { CPS_NormalizeLine(line); if (!*line || !CPS_ParseKey(line, &key_id2, &hashname, &password)) continue; if (key_id == key_id2) break; } fclose(in); if (key_id == key_id2) { if (process_cmd_authhash(hashname) && process_cmd_password(&tx_message, password)) { ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1); } else { ret = 0; } } else { fprintf(stderr, "Could not find key %"PRIu32" in keyfile %s\n", key_id, keyfile); ret = 0; } /* Erase password from stack */ memset(line, 0, sizeof (line)); return ret; } /* ================================================== */ static int process_args(int argc, char **argv, int multi) { int total_length, i, ret, quit; char *line; total_length = 0; for(i=0; i 0) { ret = process_args(argc, argv, multi); } else { do { line = read_line(); if (line) { ret = process_line(line, &quit); }else { /* supply the final '\n' when user exits via ^D */ if( on_terminal ) printf("\n"); } } while (line && !quit); } close_io(); free(password); return !ret; } chrony-2.1.1/clientlog.c0000644000000000000000000002247212542263351013651 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "clientlog.h" #include "conf.h" #include "memory.h" #include "reports.h" #include "util.h" #include "logging.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<addr.in6[i * 4 + 0] << 24 | ip->addr.in6[i * 4 + 1] << 16 | ip->addr.in6[i * 4 + 2] << 8 | ip->addr.in6[i * 4 + 3]; } /* ================================================== */ inline static uint32_t get_subnet(uint32_t *addr, unsigned int where) { int off; off = where / 32; where %= 32; return (addr[off] >> (32 - NBITS - where)) & ((1UL << NBITS) - 1); } /* ================================================== */ static void clear_subnet(Subnet *subnet) { int i; for (i=0; ientry[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_subnet4); clear_subnet(&top_subnet6); if (CNF_GetNoClientLog()) { active = 0; } else { active = 1; } nodes = NULL; max_nodes = 0; n_nodes = 0; alloced = 0; alloc_limit = CNF_GetClientLogLimit(); alloc_limit_reached = 0; } /* ================================================== */ void CLG_Finalise(void) { int i; for (i = 0; i < n_nodes; i++) Free(nodes[i]); Free(nodes); } /* ================================================== */ static void check_alloc_limit() { if (alloc_limit_reached) return; if (alloced >= alloc_limit) { LOG(LOGS_WARN, LOGF_ClientLog, "Client log memory limit reached"); alloc_limit_reached = 1; } } /* ================================================== */ 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]); alloced += sizeof (Subnet); check_alloc_limit(); } /* ================================================== */ 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); alloced += sizeof (Node); if (n_nodes == max_nodes) { if (nodes) { assert(max_nodes > 0); max_nodes *= 2; nodes = ReallocArray(Node *, max_nodes, nodes); } else { assert(max_nodes == 0); max_nodes = 16; nodes = MallocArray(Node *, max_nodes); } alloced += sizeof (Node *) * (max_nodes - n_nodes); } nodes[n_nodes++] = (Node *) new_node; check_alloc_limit(); } /* ================================================== */ /* 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, uint32_t *addr, int addr_len, int bits_consumed) { uint32_t this_subnet; this_subnet = get_subnet(addr, bits_consumed); bits_consumed += NBITS; if (bits_consumed < 32 * addr_len) { if (!subnet->entry[this_subnet]) { if (alloc_limit_reached) return NULL; create_subnet(subnet, this_subnet); } return find_subnet((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed); } else { if (!subnet->entry[this_subnet]) { if (alloc_limit_reached) return NULL; create_node(subnet, this_subnet); } return subnet->entry[this_subnet]; } } /* ================================================== */ void CLG_LogNTPClientAccess (IPAddr *client, time_t now) { uint32_t ip6[4]; Node *node; if (active) { switch (client->family) { case IPADDR_INET4: node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0); break; case IPADDR_INET6: split_ip6(client, ip6); node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0); break; default: assert(0); } if (node == NULL) return; node->ip_addr = *client; ++node->client_hits; node->last_ntp_hit = now; } } /* ================================================== */ void CLG_LogNTPPeerAccess(IPAddr *client, time_t now) { uint32_t ip6[4]; Node *node; if (active) { switch (client->family) { case IPADDR_INET4: node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0); break; case IPADDR_INET6: split_ip6(client, ip6); node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0); break; default: assert(0); } if (node == NULL) return; node->ip_addr = *client; ++node->peer_hits; node->last_ntp_hit = now; } } /* ================================================== */ void CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now) { uint32_t ip6[4]; Node *node; if (active) { switch (client->family) { case IPADDR_INET4: node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0); break; case IPADDR_INET6: split_ip6(client, ip6); node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0); break; default: assert(0); } if (node == NULL) return; 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: assert(0); break; } } } /* ================================================== */ 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; } } chrony-2.1.1/clientlog.h0000644000000000000000000000446512542263351013660 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This module contains facilities for logging access by clients. */ #ifndef GOT_CLIENTLOG_H #define GOT_CLIENTLOG_H #include "sysincl.h" #include "reports.h" extern void CLG_Initialise(void); extern void CLG_Finalise(void); extern void CLG_LogNTPClientAccess(IPAddr *client, time_t now); extern void CLG_LogNTPPeerAccess(IPAddr *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(IPAddr *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; CLG_Status CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, time_t now, unsigned long *n_indices); #endif /* GOT_CLIENTLOG_H */ chrony-2.1.1/cmdmon.c0000644000000000000000000017033312542263351013146 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Command and monitoring module in the main program */ #include "config.h" #include "sysincl.h" #include "cmdmon.h" #include "candm.h" #include "sched.h" #include "util.h" #include "logging.h" #include "keys.h" #include "ntp_sources.h" #include "ntp_core.h" #include "smooth.h" #include "sources.h" #include "sourcestats.h" #include "reference.h" #include "manual.h" #include "memory.h" #include "local.h" #include "addrfilt.h" #include "conf.h" #include "rtc.h" #include "pktlength.h" #include "clientlog.h" #include "refclock.h" /* ================================================== */ union sockaddr_in46 { struct sockaddr_in in4; #ifdef FEAT_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; /* File descriptors for command and monitoring sockets */ static int sock_fd4; #ifdef FEAT_IPV6 static int sock_fd6; #endif /* Flag indicating whether this module has been initialised or not */ static int initialised = 0; /* Token which is unique every time the daemon is run */ static unsigned long utoken; /* The register of issued tokens */ static unsigned long issued_tokens; /* The register of received tokens */ static unsigned long returned_tokens; /* The token number corresponding to the base of the registers */ static unsigned long token_base; /* The position of the next free token to issue in the issue register */ static unsigned long issue_pointer; /* Type and linked list for buffering responses */ typedef struct _ResponseCell { struct _ResponseCell *next; unsigned long tok; /* The token that the client sent in the message to which this was the reply */ unsigned long next_tok; /* The next token issued to the same client. If we receive a request with this token, it implies the reply stored in this cell was successfully received */ unsigned long msg_seq; /* Client's sequence number used in request to which this is the response. */ unsigned long attempt; /* Attempt number that we saw in the last request with this sequence number (prevents attacker firing the same request at us to make us keep generating the same reply). */ struct timeval ts; /* Time we saved the reply - allows purging based on staleness. */ CMD_Reply rpy; } ResponseCell; static ResponseCell kept_replies; static ResponseCell *free_replies; /* ================================================== */ /* Array of permission levels for command types */ static const char permissions[] = { PERMIT_OPEN, /* NULL */ PERMIT_AUTH, /* ONLINE */ PERMIT_AUTH, /* OFFLINE */ PERMIT_AUTH, /* BURST */ PERMIT_AUTH, /* MODIFY_MINPOLL */ PERMIT_AUTH, /* MODIFY_MAXPOLL */ PERMIT_AUTH, /* DUMP */ PERMIT_AUTH, /* MODIFY_MAXDELAY */ PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */ PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */ PERMIT_OPEN, /* LOGON */ PERMIT_AUTH, /* SETTIME */ PERMIT_AUTH, /* LOCAL */ PERMIT_AUTH, /* MANUAL */ PERMIT_OPEN, /* N_SOURCES */ PERMIT_OPEN, /* SOURCE_DATA */ PERMIT_AUTH, /* REKEY */ PERMIT_AUTH, /* ALLOW */ PERMIT_AUTH, /* ALLOWALL */ PERMIT_AUTH, /* DENY */ PERMIT_AUTH, /* DENYALL */ PERMIT_AUTH, /* CMDALLOW */ PERMIT_AUTH, /* CMDALLOWALL */ PERMIT_AUTH, /* CMDDENY */ PERMIT_AUTH, /* CMDDENYALL */ PERMIT_AUTH, /* ACCHECK */ PERMIT_AUTH, /* CMDACCHECK */ PERMIT_AUTH, /* ADD_SERVER */ PERMIT_AUTH, /* ADD_PEER */ PERMIT_AUTH, /* DEL_SOURCE */ PERMIT_AUTH, /* WRITERTC */ PERMIT_AUTH, /* DFREQ */ PERMIT_AUTH, /* DOFFSET */ PERMIT_OPEN, /* TRACKING */ PERMIT_OPEN, /* SOURCESTATS */ PERMIT_OPEN, /* RTCREPORT */ PERMIT_AUTH, /* TRIMRTC */ PERMIT_AUTH, /* CYCLELOGS */ PERMIT_AUTH, /* SUBNETS_ACCESSED */ PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */ PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */ PERMIT_OPEN, /* MANUAL_LIST */ PERMIT_AUTH, /* MANUAL_DELETE */ PERMIT_AUTH, /* MAKESTEP */ PERMIT_OPEN, /* ACTIVITY */ PERMIT_AUTH, /* MODIFY_MINSTRATUM */ PERMIT_AUTH, /* MODIFY_POLLTARGET */ PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */ PERMIT_AUTH, /* RESELECT */ PERMIT_AUTH, /* RESELECTDISTANCE */ PERMIT_AUTH, /* MODIFY_MAKESTEP */ PERMIT_OPEN, /* SMOOTHING */ PERMIT_AUTH, /* SMOOTHTIME */ }; /* ================================================== */ /* This authorisation table is used for checking whether particular machines are allowed to make command and monitoring requests. */ static ADF_AuthTable access_auth_table; /* ================================================== */ /* Forward prototypes */ static void read_from_cmd_socket(void *anything); /* ================================================== */ static int prepare_socket(int family, int port_number) { int sock_fd; socklen_t my_addr_len; union sockaddr_in46 my_addr; IPAddr bind_address; int on_off = 1; sock_fd = socket(family, SOCK_DGRAM, 0); if (sock_fd < 0) { LOG(LOGS_ERR, LOGF_CmdMon, "Could not open %s command socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); return -1; } /* Close on exec */ UTI_FdSetCloexec(sock_fd); /* Allow reuse of port number */ if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options"); /* Don't quit - we might survive anyway */ } #ifdef IP_FREEBIND /* Allow binding to address that doesn't exist yet */ if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option"); } #endif #ifdef FEAT_IPV6 if (family == AF_INET6) { #ifdef IPV6_V6ONLY /* Receive IPv6 packets only */ if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option"); } #endif } #endif memset(&my_addr, 0, sizeof (my_addr)); switch (family) { case AF_INET: my_addr_len = sizeof (my_addr.in4); my_addr.in4.sin_family = family; my_addr.in4.sin_port = htons((unsigned short)port_number); CNF_GetBindCommandAddress(IPADDR_INET4, &bind_address); if (bind_address.family == IPADDR_INET4) my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4); else my_addr.in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); break; #ifdef FEAT_IPV6 case AF_INET6: my_addr_len = sizeof (my_addr.in6); my_addr.in6.sin6_family = family; my_addr.in6.sin6_port = htons((unsigned short)port_number); CNF_GetBindCommandAddress(IPADDR_INET6, &bind_address); if (bind_address.family == IPADDR_INET6) memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6, sizeof (my_addr.in6.sin6_addr.s6_addr)); else my_addr.in6.sin6_addr = in6addr_loopback; break; #endif default: assert(0); } if (bind(sock_fd, &my_addr.u, my_addr_len) < 0) { LOG(LOGS_ERR, LOGF_CmdMon, "Could not bind %s command socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); close(sock_fd); return -1; } /* Register handler for read events on the socket */ SCH_AddInputFileHandler(sock_fd, read_from_cmd_socket, (void *)(long)sock_fd); return sock_fd; } /* ================================================== */ void CAM_Initialise(int family) { int i, port_number; assert(!initialised); initialised = 1; assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES); for (i = 0; i < N_REQUEST_TYPES; i++) { CMD_Request r; int command_length, padding_length; r.version = PROTO_VERSION_NUMBER; r.command = htons(i); command_length = PKL_CommandLength(&r); padding_length = PKL_CommandPaddingLength(&r); assert(padding_length <= MAX_PADDING_LENGTH && padding_length <= command_length); assert(command_length == 0 || command_length >= offsetof(CMD_Reply, data)); } utoken = (unsigned long) time(NULL); issued_tokens = returned_tokens = issue_pointer = 0; token_base = 1; /* zero is the value used when the previous command was unauthenticated */ free_replies = NULL; kept_replies.next = NULL; port_number = CNF_GetCommandPort(); if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4)) sock_fd4 = prepare_socket(AF_INET, port_number); else sock_fd4 = -1; #ifdef FEAT_IPV6 if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET6)) sock_fd6 = prepare_socket(AF_INET6, port_number); else sock_fd6 = -1; #endif if (port_number && sock_fd4 < 0 #ifdef FEAT_IPV6 && sock_fd6 < 0 #endif ) { LOG_FATAL(LOGF_CmdMon, "Could not open any command socket"); } access_auth_table = ADF_CreateTable(); } /* ================================================== */ void CAM_Finalise(void) { if (sock_fd4 >= 0) { SCH_RemoveInputFileHandler(sock_fd4); close(sock_fd4); } sock_fd4 = -1; #ifdef FEAT_IPV6 if (sock_fd6 >= 0) { SCH_RemoveInputFileHandler(sock_fd6); close(sock_fd6); } sock_fd6 = -1; #endif ADF_DestroyTable(access_auth_table); initialised = 0; } /* ================================================== */ /* This function checks whether the authenticator field of the packet checks correctly against what we would compute locally given the rest of the packet */ static int check_rx_packet_auth(CMD_Request *packet, int packet_len) { int pkt_len, auth_len; pkt_len = PKL_CommandLength(packet); auth_len = packet_len - pkt_len; return KEY_CheckAuth(KEY_GetCommandKey(), (unsigned char *)packet, pkt_len, ((unsigned char *)packet) + pkt_len, auth_len); } /* ================================================== */ static int generate_tx_packet_auth(CMD_Reply *packet) { int pkt_len; pkt_len = PKL_ReplyLength(packet); return KEY_GenerateAuth(KEY_GetCommandKey(), (unsigned char *)packet, pkt_len, ((unsigned char *)packet) + pkt_len, sizeof (packet->auth)); } /* ================================================== */ static void shift_tokens(void) { do { issued_tokens >>= 1; returned_tokens >>= 1; token_base++; issue_pointer--; } while ((issued_tokens & 1) && (returned_tokens & 1)); } /* ================================================== */ static unsigned long get_token(void) { unsigned long result; if (issue_pointer == 32) { /* The lowest number open token has not been returned - bad luck to that command client */ shift_tokens(); } result = token_base + issue_pointer; issued_tokens |= (1UL << issue_pointer); issue_pointer++; return result; } /* ================================================== */ static int check_token(unsigned long token) { int result; unsigned long pos; if (token < token_base) { /* Token too old */ result = 0; } else { pos = token - token_base; if (pos >= issue_pointer) { /* Token hasn't been issued yet */ result = 0; } else { if (returned_tokens & (1UL << pos)) { /* Token has already been returned */ result = 0; } else { /* Token is OK */ result = 1; returned_tokens |= (1UL << pos); if (pos == 0) { shift_tokens(); } } } } return result; } /* ================================================== */ #define TS_MARGIN 20 /* ================================================== */ typedef struct _TimestampCell { struct _TimestampCell *next; struct timeval ts; } TimestampCell; static struct _TimestampCell seen_ts_list={NULL}; static struct _TimestampCell *free_ts_list=NULL; #define EXTEND_QUANTUM 32 /* ================================================== */ static TimestampCell * allocate_ts_cell(void) { TimestampCell *result; int i; if (free_ts_list == NULL) { free_ts_list = MallocArray(TimestampCell, EXTEND_QUANTUM); for (i=0; inext; return result; } /* ================================================== */ static void release_ts_cell(TimestampCell *node) { node->next = free_ts_list; free_ts_list = node; } /* ================================================== */ /* Return 1 if not found, 0 if found (i.e. not unique). Prune out any stale entries. */ static int check_unique_ts(struct timeval *ts, struct timeval *now) { TimestampCell *last_valid, *cell, *next; int ok; ok = 1; last_valid = &(seen_ts_list); cell = last_valid->next; while (cell) { next = cell->next; /* Check if stale */ if ((now->tv_sec - cell->ts.tv_sec) > TS_MARGIN) { release_ts_cell(cell); last_valid->next = next; } else { /* Timestamp in cell is still within window */ last_valid->next = cell; last_valid = cell; if ((cell->ts.tv_sec == ts->tv_sec) && (cell->ts.tv_usec == ts->tv_usec)) { ok = 0; } } cell = next; } if (ok) { /* Need to add this timestamp to the list */ cell = allocate_ts_cell(); last_valid->next = cell; cell->next = NULL; cell->ts = *ts; } return ok; } /* ================================================== */ static int ts_is_unique_and_not_stale(struct timeval *ts, struct timeval *now) { int within_margin=0; int is_unique=0; long diff; diff = now->tv_sec - ts->tv_sec; if ((diff < TS_MARGIN) && (diff > -TS_MARGIN)) { within_margin = 1; } else { within_margin = 0; } is_unique = check_unique_ts(ts, now); return within_margin && is_unique; } /* ================================================== */ #define REPLY_EXTEND_QUANTUM 8 static void get_more_replies(void) { ResponseCell *new_replies; int i; if (!free_replies) { new_replies = MallocArray(ResponseCell, REPLY_EXTEND_QUANTUM); for (i=1; inext; return result; } /* ================================================== */ static void free_reply_slot(ResponseCell *cell) { cell->next = free_replies; free_replies = cell; } /* ================================================== */ static void save_reply(CMD_Reply *msg, unsigned long tok_reply_to, unsigned long new_tok_issued, unsigned long client_msg_seq, unsigned short attempt, struct timeval *now) { ResponseCell *cell; cell = get_reply_slot(); cell->ts = *now; memcpy(&cell->rpy, msg, sizeof(CMD_Reply)); cell->tok = tok_reply_to; cell->next_tok = new_tok_issued; cell->msg_seq = client_msg_seq; cell->attempt = (unsigned long) attempt; cell->next = kept_replies.next; kept_replies.next = cell; } /* ================================================== */ static CMD_Reply * lookup_reply(unsigned long prev_msg_token, unsigned long client_msg_seq, unsigned short attempt) { ResponseCell *ptr; ptr = kept_replies.next; while (ptr) { if ((ptr->tok == prev_msg_token) && (ptr->msg_seq == client_msg_seq) && ((unsigned long) attempt > ptr->attempt)) { /* Set the attempt field to remember the highest number we have had so far */ ptr->attempt = (unsigned long) attempt; return &ptr->rpy; } ptr = ptr->next; } return NULL; } /* ================================================== */ #define REPLY_MAXAGE 300 static void token_acknowledged(unsigned long token, struct timeval *now) { ResponseCell *last_valid, *cell, *next; last_valid = &kept_replies; cell = kept_replies.next; while(cell) { next = cell->next; /* Discard if it's the one or if the reply is stale */ if ((cell->next_tok == token) || ((now->tv_sec - cell->ts.tv_sec) > REPLY_MAXAGE)) { free_reply_slot(cell); last_valid->next = next; } else { last_valid->next = cell; last_valid = cell; } cell = next; } } /* ================================================== */ static void transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len) { int status; int tx_message_length; int sock_fd; socklen_t addrlen; switch (where_to->u.sa_family) { case AF_INET: sock_fd = sock_fd4; addrlen = sizeof (where_to->in4); break; #ifdef FEAT_IPV6 case AF_INET6: sock_fd = sock_fd6; addrlen = sizeof (where_to->in6); break; #endif default: assert(0); } tx_message_length = PKL_ReplyLength(msg) + auth_len; status = sendto(sock_fd, (void *) msg, tx_message_length, 0, &where_to->u, addrlen); if (status < 0) { unsigned short port; IPAddr ip; UTI_SockaddrToIPAndPort(&where_to->u, &ip, &port); DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port); } } /* ================================================== */ static void handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message) { SRC_DumpSources(); } /* ================================================== */ static void handle_online(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.online.address, &address); if (!NSR_TakeSourcesOnline(&mask, &address)) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.offline.address, &address); if (!NSR_TakeSourcesOffline(&mask, &address)) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.burst.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.burst.address, &address); if (!NSR_InitiateSampleBurst(ntohl(rx_message->data.burst.n_good_samples), ntohl(rx_message->data.burst.n_total_samples), &mask, &address)) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_minpoll(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); if (!NSR_ModifyMinpoll(&address, ntohl(rx_message->data.modify_minpoll.new_minpoll))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_maxpoll(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); if (!NSR_ModifyMaxpoll(&address, ntohl(rx_message->data.modify_minpoll.new_minpoll))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_maxdelay(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelay.address, &address); if (!NSR_ModifyMaxdelay(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelay.new_max_delay))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_maxdelayratio(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelayratio.address, &address); if (!NSR_ModifyMaxdelayratio(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelayratio.new_max_delay_ratio))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_maxdelaydevratio(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelaydevratio.address, &address); if (!NSR_ModifyMaxdelaydevratio(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelaydevratio.new_max_delay_dev_ratio))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_minstratum(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); if (!NSR_ModifyMinstratum(&address, ntohl(rx_message->data.modify_minstratum.new_min_stratum))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_polltarget(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_polltarget.address, &address); if (!NSR_ModifyPolltarget(&address, ntohl(rx_message->data.modify_polltarget.new_poll_target))) tx_message->status = htons(STT_NOSUCHSOURCE); } /* ================================================== */ static void handle_modify_maxupdateskew(CMD_Request *rx_message, CMD_Reply *tx_message) { REF_ModifyMaxupdateskew(UTI_FloatNetworkToHost(rx_message->data.modify_maxupdateskew.new_max_update_skew)); } /* ================================================== */ static void handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message) { REF_ModifyMakestep(ntohl(rx_message->data.modify_makestep.limit), UTI_FloatNetworkToHost(rx_message->data.modify_makestep.threshold)); } /* ================================================== */ static void handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message) { struct timeval ts; long offset_cs; double dfreq_ppm, new_afreq_ppm; UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts); if (!MNL_IsEnabled()) { tx_message->status = htons(STT_NOTENABLED); } else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) { tx_message->reply = htons(RPY_MANUAL_TIMESTAMP); tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs); tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm); tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm); } else { tx_message->status = htons(STT_FAILED); } } /* ================================================== */ static void handle_local(CMD_Request *rx_message, CMD_Reply *tx_message) { int on_off, stratum; on_off = ntohl(rx_message->data.local.on_off); if (on_off) { stratum = ntohl(rx_message->data.local.stratum); REF_EnableLocal(stratum); } else { REF_DisableLocal(); } } /* ================================================== */ static void handle_manual(CMD_Request *rx_message, CMD_Reply *tx_message) { int option; option = ntohl(rx_message->data.manual.option); switch (option) { case 0: MNL_Disable(); break; case 1: MNL_Enable(); break; case 2: MNL_Reset(); break; default: tx_message->status = htons(STT_INVALID); break; } } /* ================================================== */ static void handle_n_sources(CMD_Request *rx_message, CMD_Reply *tx_message) { int n_sources; n_sources = SRC_ReadNumberOfSources(); tx_message->reply = htons(RPY_N_SOURCES); tx_message->data.n_sources.n_sources = htonl(n_sources); } /* ================================================== */ static void handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_SourceReport report; struct timeval now_corr; /* Get data */ SCH_GetLastEventTime(&now_corr, NULL, NULL); if (SRC_ReportSource(ntohl(rx_message->data.source_data.index), &report, &now_corr)) { switch (SRC_GetType(ntohl(rx_message->data.source_data.index))) { case SRC_NTP: NSR_ReportSource(&report, &now_corr); break; case SRC_REFCLOCK: RCL_ReportSource(&report, &now_corr); break; } tx_message->reply = htons(RPY_SOURCE_DATA); UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.source_data.ip_addr); tx_message->data.source_data.stratum = htons(report.stratum); tx_message->data.source_data.poll = htons(report.poll); switch (report.state) { case RPT_SYNC: tx_message->data.source_data.state = htons(RPY_SD_ST_SYNC); break; case RPT_UNREACH: tx_message->data.source_data.state = htons(RPY_SD_ST_UNREACH); break; case RPT_FALSETICKER: tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER); break; case RPT_JITTERY: tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY); break; case RPT_CANDIDATE: tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE); break; case RPT_OUTLIER: tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLIER); break; } switch (report.mode) { case RPT_NTP_CLIENT: tx_message->data.source_data.mode = htons(RPY_SD_MD_CLIENT); break; case RPT_NTP_PEER: tx_message->data.source_data.mode = htons(RPY_SD_MD_PEER); break; case RPT_LOCAL_REFERENCE: tx_message->data.source_data.mode = htons(RPY_SD_MD_REF); break; } switch (report.sel_option) { case RPT_NORMAL: tx_message->data.source_data.flags = htons(0); break; case RPT_PREFER: tx_message->data.source_data.flags = htons(RPY_SD_FLAG_PREFER); break; case RPT_NOSELECT: tx_message->data.source_data.flags = htons(RPY_SD_FLAG_NOSELECT); break; } tx_message->data.source_data.reachability = htons(report.reachability); tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago); tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas); tx_message->data.source_data.latest_meas = UTI_FloatHostToNetwork(report.latest_meas); tx_message->data.source_data.latest_meas_err = UTI_FloatHostToNetwork(report.latest_meas_err); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message) { KEY_Reload(); } /* ================================================== */ static void handle_allowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all) { IPAddr ip; int subnet_bits; UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip); subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits); if (!NCR_AddAccessRestriction(&ip, subnet_bits, allow, all)) tx_message->status = htons(STT_BADSUBNET); } /* ================================================== */ static void handle_cmdallowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all) { IPAddr ip; int subnet_bits; UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip); subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits); if (!CAM_AddAccessRestriction(&ip, subnet_bits, allow, all)) tx_message->status = htons(STT_BADSUBNET); } /* ================================================== */ static void handle_accheck(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr ip; UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip); if (NCR_CheckAccessRestriction(&ip)) { tx_message->status = htons(STT_ACCESSALLOWED); } else { tx_message->status = htons(STT_ACCESSDENIED); } } /* ================================================== */ static void handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message) { IPAddr ip; UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip); if (CAM_CheckAccessRestriction(&ip)) { tx_message->status = htons(STT_ACCESSALLOWED); } else { tx_message->status = htons(STT_ACCESSDENIED); } } /* ================================================== */ static void handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_message) { NTP_Remote_Address rem_addr; SourceParameters params; NSR_Status status; UTI_IPNetworkToHost(&rx_message->data.ntp_source.ip_addr, &rem_addr.ip_addr); rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port)); params.minpoll = ntohl(rx_message->data.ntp_source.minpoll); params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll); params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll); params.authkey = ntohl(rx_message->data.ntp_source.authkey); params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0; params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0; params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0; params.sel_option = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SelectPrefer : ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SelectNoselect : SRC_SelectNormal; params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay); params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio); /* not transmitted in cmdmon protocol yet */ params.min_stratum = SRC_DEFAULT_MINSTRATUM; params.poll_target = SRC_DEFAULT_POLLTARGET; params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; params.version = NTP_VERSION; params.max_sources = SRC_DEFAULT_MAXSOURCES; params.min_samples = SRC_DEFAULT_MINSAMPLES; params.max_samples = SRC_DEFAULT_MAXSAMPLES; status = NSR_AddSource(&rem_addr, type, ¶ms); switch (status) { case NSR_Success: break; case NSR_AlreadyInUse: tx_message->status = htons(STT_SOURCEALREADYKNOWN); break; case NSR_TooManySources: tx_message->status = htons(STT_TOOMANYSOURCES); break; case NSR_InvalidAF: tx_message->status = htons(STT_INVALIDAF); break; case NSR_NoSuchSource: assert(0); break; } } /* ================================================== */ static void handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message) { NTP_Remote_Address rem_addr; NSR_Status status; UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr); rem_addr.port = 0; status = NSR_RemoveSource(&rem_addr); switch (status) { case NSR_Success: break; case NSR_NoSuchSource: tx_message->status = htons(STT_NOSUCHSOURCE); break; case NSR_TooManySources: case NSR_AlreadyInUse: case NSR_InvalidAF: assert(0); break; } } /* ================================================== */ static void handle_writertc(CMD_Request *rx_message, CMD_Reply *tx_message) { switch (RTC_WriteParameters()) { case RTC_ST_OK: break; case RTC_ST_NODRV: tx_message->status = htons(STT_NORTC); break; case RTC_ST_BADFILE: tx_message->status = htons(STT_BADRTCFILE); break; } } /* ================================================== */ static void handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message) { double dfreq; dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq); LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6); LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq); } /* ================================================== */ static void handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message) { long sec, usec; double doffset; sec = (int32_t)ntohl(rx_message->data.doffset.sec); usec = (int32_t)ntohl(rx_message->data.doffset.usec); doffset = (double) sec + 1.0e-6 * (double) usec; LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset); LCL_AccumulateOffset(doffset, 0.0); } /* ================================================== */ static void handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_TrackingReport rpt; REF_GetTrackingReport(&rpt); tx_message->reply = htons(RPY_TRACKING); tx_message->data.tracking.ref_id = htonl(rpt.ref_id); UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr); tx_message->data.tracking.stratum = htons(rpt.stratum); tx_message->data.tracking.leap_status = htons(rpt.leap_status); UTI_TimevalHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time); tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction); tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset); tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset); tx_message->data.tracking.freq_ppm = UTI_FloatHostToNetwork(rpt.freq_ppm); tx_message->data.tracking.resid_freq_ppm = UTI_FloatHostToNetwork(rpt.resid_freq_ppm); tx_message->data.tracking.skew_ppm = UTI_FloatHostToNetwork(rpt.skew_ppm); tx_message->data.tracking.root_delay = UTI_FloatHostToNetwork(rpt.root_delay); tx_message->data.tracking.root_dispersion = UTI_FloatHostToNetwork(rpt.root_dispersion); tx_message->data.tracking.last_update_interval = UTI_FloatHostToNetwork(rpt.last_update_interval); } /* ================================================== */ static void handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_SmoothingReport report; struct timeval now; SCH_GetLastEventTime(&now, NULL, NULL); if (!SMT_GetSmoothingReport(&report, &now)) { tx_message->status = htons(STT_NOTENABLED); return; } tx_message->reply = htons(RPY_SMOOTHING); tx_message->data.smoothing.flags = htonl((report.active ? RPY_SMT_FLAG_ACTIVE : 0) | (report.leap_only ? RPY_SMT_FLAG_LEAPONLY : 0)); tx_message->data.smoothing.offset = UTI_FloatHostToNetwork(report.offset); tx_message->data.smoothing.freq_ppm = UTI_FloatHostToNetwork(report.freq_ppm); tx_message->data.smoothing.wander_ppm = UTI_FloatHostToNetwork(report.wander_ppm); tx_message->data.smoothing.last_update_ago = UTI_FloatHostToNetwork(report.last_update_ago); tx_message->data.smoothing.remaining_time = UTI_FloatHostToNetwork(report.remaining_time); } /* ================================================== */ static void handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message) { struct timeval now; int option; if (!SMT_IsEnabled()) { tx_message->status = htons(STT_NOTENABLED); return; } option = ntohl(rx_message->data.smoothtime.option); SCH_GetLastEventTime(&now, NULL, NULL); switch (option) { case REQ_SMOOTHTIME_RESET: SMT_Reset(&now); break; case REQ_SMOOTHTIME_ACTIVATE: SMT_Activate(&now); break; default: tx_message->status = htons(STT_INVALID); break; } } /* ================================================== */ static void handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; RPT_SourcestatsReport report; struct timeval now_corr; SCH_GetLastEventTime(&now_corr, NULL, NULL); status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index), &report, &now_corr); if (status) { tx_message->reply = htons(RPY_SOURCESTATS); tx_message->data.sourcestats.ref_id = htonl(report.ref_id); UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.sourcestats.ip_addr); tx_message->data.sourcestats.n_samples = htonl(report.n_samples); tx_message->data.sourcestats.n_runs = htonl(report.n_runs); tx_message->data.sourcestats.span_seconds = htonl(report.span_seconds); tx_message->data.sourcestats.resid_freq_ppm = UTI_FloatHostToNetwork(report.resid_freq_ppm); tx_message->data.sourcestats.skew_ppm = UTI_FloatHostToNetwork(report.skew_ppm); tx_message->data.sourcestats.sd = UTI_FloatHostToNetwork(report.sd); tx_message->data.sourcestats.est_offset = UTI_FloatHostToNetwork(report.est_offset); tx_message->data.sourcestats.est_offset_err = UTI_FloatHostToNetwork(report.est_offset_err); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; RPT_RTC_Report report; status = RTC_GetReport(&report); if (status) { tx_message->reply = htons(RPY_RTC); UTI_TimevalHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time); tx_message->data.rtc.n_samples = htons(report.n_samples); tx_message->data.rtc.n_runs = htons(report.n_runs); tx_message->data.rtc.span_seconds = htonl(report.span_seconds); tx_message->data.rtc.rtc_seconds_fast = UTI_FloatHostToNetwork(report.rtc_seconds_fast); tx_message->data.rtc.rtc_gain_rate_ppm = UTI_FloatHostToNetwork(report.rtc_gain_rate_ppm); } else { tx_message->status = htons(STT_NORTC); } } /* ================================================== */ static void handle_trimrtc(CMD_Request *rx_message, CMD_Reply *tx_message) { if (!RTC_Trim()) tx_message->status = htons(STT_NORTC); } /* ================================================== */ static void handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message) { LOG_CycleLogFiles(); } /* ================================================== */ static void handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message) { CLG_Status result; RPT_ClientAccessByIndex_Report report; unsigned long first_index, n_indices, n_indices_in_table; int i, j; struct timeval now; SCH_GetLastEventTime(&now, NULL, NULL); first_index = ntohl(rx_message->data.client_accesses_by_index.first_index); n_indices = ntohl(rx_message->data.client_accesses_by_index.n_indices); if (n_indices > MAX_CLIENT_ACCESSES) n_indices = MAX_CLIENT_ACCESSES; tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX); for (i = 0, j = 0; i < n_indices; i++) { result = CLG_GetClientAccessReportByIndex(first_index + i, &report, now.tv_sec, &n_indices_in_table); tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table); switch (result) { case CLG_SUCCESS: UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.client_accesses_by_index.clients[j].ip); tx_message->data.client_accesses_by_index.clients[j].client_hits = htonl(report.client_hits); tx_message->data.client_accesses_by_index.clients[j].peer_hits = htonl(report.peer_hits); tx_message->data.client_accesses_by_index.clients[j].cmd_hits_auth = htonl(report.cmd_hits_auth); tx_message->data.client_accesses_by_index.clients[j].cmd_hits_normal = htonl(report.cmd_hits_normal); tx_message->data.client_accesses_by_index.clients[j].cmd_hits_bad = htonl(report.cmd_hits_bad); tx_message->data.client_accesses_by_index.clients[j].last_ntp_hit_ago = htonl(report.last_ntp_hit_ago); tx_message->data.client_accesses_by_index.clients[j].last_cmd_hit_ago = htonl(report.last_cmd_hit_ago); j++; break; case CLG_INDEXTOOLARGE: break; /* ignore this index */ case CLG_INACTIVE: tx_message->status = htons(STT_INACTIVE); return; default: assert(0); break; } } tx_message->data.client_accesses_by_index.next_index = htonl(first_index + i); tx_message->data.client_accesses_by_index.n_clients = htonl(j); } /* ================================================== */ static void handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message) { int n_samples; int i; RPY_ManualListSample *sample; RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES]; tx_message->reply = htons(RPY_MANUAL_LIST); MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples); tx_message->data.manual_list.n_samples = htonl(n_samples); for (i=0; idata.manual_list.samples[i]; UTI_TimevalHostToNetwork(&report[i].when, &sample->when); sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset); sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset); sample->residual = UTI_FloatHostToNetwork(report[i].residual); } } /* ================================================== */ static void handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message) { int index; index = ntohl(rx_message->data.manual_delete.index); if (!MNL_DeleteSample(index)) tx_message->status = htons(STT_BADSAMPLE); } /* ================================================== */ static void handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message) { if (!LCL_MakeStep()) tx_message->status = htons(STT_FAILED); } /* ================================================== */ static void handle_activity(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_ActivityReport report; NSR_GetActivityReport(&report); tx_message->data.activity.online = htonl(report.online); tx_message->data.activity.offline = htonl(report.offline); tx_message->data.activity.burst_online = htonl(report.burst_online); tx_message->data.activity.burst_offline = htonl(report.burst_offline); tx_message->data.activity.unresolved = htonl(report.unresolved); tx_message->reply = htons(RPY_ACTIVITY); } /* ================================================== */ static void handle_reselect_distance(CMD_Request *rx_message, CMD_Reply *tx_message) { double dist; dist = UTI_FloatNetworkToHost(rx_message->data.reselect_distance.distance); SRC_SetReselectDistance(dist); } /* ================================================== */ static void handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message) { SRC_ReselectSource(); } /* ================================================== */ /* Read a packet and process it */ static void read_from_cmd_socket(void *anything) { int status; int read_length; /* Length of packet read */ int expected_length; /* Expected length of packet without auth data */ unsigned long flags; CMD_Request rx_message; CMD_Reply tx_message, *prev_tx_message; int rx_message_length, tx_message_length; int sock_fd; union sockaddr_in46 where_from; socklen_t from_length; IPAddr remote_ip; unsigned short remote_port; int auth_length; int auth_ok; int utoken_ok, token_ok; int issue_token; int valid_ts; int authenticated; int localhost; int allowed; unsigned short rx_command; unsigned long rx_message_token; unsigned long tx_message_token; unsigned long rx_message_seq; unsigned long rx_attempt; struct timeval now; struct timeval cooked_now; flags = 0; rx_message_length = sizeof(rx_message); from_length = sizeof(where_from); sock_fd = (long)anything; status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, flags, &where_from.u, &from_length); if (status < 0) { LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d", strerror(errno), sock_fd); return; } if (from_length > sizeof (where_from)) LOG_FATAL(LOGF_CmdMon, "Truncated source address"); read_length = status; /* Get current time cheaply */ SCH_GetLastEventTime(&cooked_now, NULL, &now); UTI_SockaddrToIPAndPort(&where_from.u, &remote_ip, &remote_port); /* Check if it's a loopback address (127.0.0.1 or ::1) */ switch (remote_ip.family) { case IPADDR_INET4: localhost = remote_ip.addr.in4 == INADDR_LOOPBACK; break; #ifdef FEAT_IPV6 case IPADDR_INET6: localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback, sizeof (in6addr_loopback)); break; #endif default: assert(0); } if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) { /* The client is not allowed access, so don't waste any more time on him. Note that localhost is always allowed access regardless of the defined access rules - otherwise, we could shut ourselves out completely! */ return; } /* Message size sanity check */ if (read_length >= offsetof(CMD_Request, data)) { expected_length = PKL_CommandLength(&rx_message); } else { expected_length = 0; } if (expected_length < offsetof(CMD_Request, data) || read_length < offsetof(CMD_Reply, data) || rx_message.pkt_type != PKT_TYPE_CMD_REQUEST || rx_message.res1 != 0 || rx_message.res2 != 0) { /* We don't know how to process anything like this */ CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec); return; } rx_command = ntohs(rx_message.command); tx_message.version = PROTO_VERSION_NUMBER; tx_message.pkt_type = PKT_TYPE_CMD_REPLY; tx_message.res1 = 0; tx_message.res2 = 0; tx_message.command = rx_message.command; tx_message.sequence = rx_message.sequence; tx_message.reply = htons(RPY_NULL); tx_message.status = htons(STT_SUCCESS); tx_message.pad1 = 0; tx_message.pad2 = 0; tx_message.pad3 = 0; tx_message.utoken = htonl(utoken); /* Set this to a default (invalid) value. This protects against the token field being set to an arbitrary value if we reject the message, e.g. due to the host failing the access check. */ tx_message.token = htonl(0xffffffffUL); memset(&tx_message.auth, 0, sizeof(tx_message.auth)); if (rx_message.version != PROTO_VERSION_NUMBER) { DEBUG_LOG(LOGF_CmdMon, "Read command packet with protocol version %d (expected %d) from %s:%hu", rx_message.version, PROTO_VERSION_NUMBER, UTI_IPToString(&remote_ip), remote_port); CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec); if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) { tx_message.status = htons(STT_BADPKTVERSION); transmit_reply(&tx_message, &where_from, 0); } return; } if (rx_command >= N_REQUEST_TYPES) { DEBUG_LOG(LOGF_CmdMon, "Read command packet with invalid command %d from %s:%hu", rx_command, UTI_IPToString(&remote_ip), remote_port); CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec); tx_message.status = htons(STT_INVALID); transmit_reply(&tx_message, &where_from, 0); return; } if (read_length < expected_length) { DEBUG_LOG(LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port); CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec); tx_message.status = htons(STT_BADPKTLENGTH); transmit_reply(&tx_message, &where_from, 0); return; } /* OK, we have a valid message. Now dispatch on message type and process it. */ /* Do authentication stuff and command tokens here. Well-behaved clients will set their utokens to 0 to save us wasting our time if the packet is unauthenticatable. */ if (rx_message.utoken != 0) { auth_ok = check_rx_packet_auth(&rx_message, read_length); } else { auth_ok = 0; } /* All this malarky is to protect the system against various forms of attack. Simple packet forgeries are blocked by requiring the packet to authenticate properly with MD5 or other crypto hash. (The assumption is that the command key is in a read-only keys file read by the daemon, and is known only to administrators.) Replay attacks are prevented by 2 fields in the packet. The 'token' field is where the client plays back to us a token that he was issued in an earlier reply. Each time we reply to a suitable packet, we issue a new token. The 'utoken' field is set to a new (hopefully increasing) value each time the daemon is run. This prevents packets from a previous incarnation being played back at us when the same point in the 'token' sequence comes up. (The token mechanism also prevents a non-idempotent command from being executed twice from the same client, if the client fails to receive our reply the first time and tries a resend.) The problem is how a client should get its first token. Our token handling only remembers a finite number of issued tokens (actually 32) - if a client replies with a (legitimate) token older than that, it will be treated as though a duplicate token has been supplied. If a simple token-request protocol were used, the whole thing would be vulnerable to a denial of service attack, where an attacker just replays valid token-request packets at us, causing us to keep issuing new tokens, invalidating all the ones we have given out to true clients already. To protect against this, the token-request (REQ_LOGON) packet includes a timestamp field. To issue a token, we require that this field is different from any we have processed before. To bound our storage, we require that the timestamp is within a certain period of our current time. For clients running on the same host this will be easily satisfied. */ utoken_ok = (ntohl(rx_message.utoken) == utoken); /* Avoid binning a valid user's token if we merely get a forged packet */ rx_message_token = ntohl(rx_message.token); rx_message_seq = ntohl(rx_message.sequence); rx_attempt = ntohs(rx_message.attempt); if (auth_ok && utoken_ok) { token_ok = check_token(rx_message_token); } else { token_ok = 0; } if (auth_ok && utoken_ok && !token_ok) { /* This might be a resent message, due to the client not getting our reply to the first attempt. See if we can find the message. */ prev_tx_message = lookup_reply(rx_message_token, rx_message_seq, rx_attempt); if (prev_tx_message) { /* Just send this message again */ tx_message_length = PKL_ReplyLength(prev_tx_message); status = sendto(sock_fd, (void *) prev_tx_message, tx_message_length, 0, &where_from.u, from_length); if (status < 0) { DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&remote_ip), remote_port); } return; } /* Otherwise, just fall through into normal processing */ } if (auth_ok && utoken_ok && token_ok) { /* See whether we can discard the previous reply from storage */ token_acknowledged(rx_message_token, &now); } valid_ts = 0; issue_token = 0; if (auth_ok) { if (utoken_ok && token_ok) { issue_token = 1; } else if (rx_command == REQ_LOGON && ntohl(rx_message.utoken) == SPECIAL_UTOKEN) { struct timeval ts; UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts); valid_ts = ts_is_unique_and_not_stale(&ts, &now); if (valid_ts) { issue_token = 1; } } } authenticated = auth_ok & utoken_ok & token_ok; if (authenticated) { CLG_LogCommandAccess(&remote_ip, CLG_CMD_AUTH, cooked_now.tv_sec); } else { CLG_LogCommandAccess(&remote_ip, CLG_CMD_NORMAL, cooked_now.tv_sec); } if (issue_token) { /* Only command clients where the user has apparently 'logged on' get a token to allow them to emit an authenticated command next time */ tx_message_token = get_token(); } else { tx_message_token = 0xffffffffUL; } tx_message.token = htonl(tx_message_token); if (rx_command >= N_REQUEST_TYPES) { /* This should be already handled */ assert(0); } else { /* Check level of authority required to issue the command */ switch(permissions[rx_command]) { case PERMIT_AUTH: if (authenticated) { allowed = 1; } else { allowed = 0; } break; case PERMIT_LOCAL: if (authenticated || localhost) { allowed = 1; } else { allowed = 0; } break; case PERMIT_OPEN: allowed = 1; break; default: assert(0); allowed = 0; } if (allowed) { switch(rx_command) { case REQ_NULL: /* Do nothing */ break; case REQ_DUMP: handle_dump(&rx_message, &tx_message); break; case REQ_ONLINE: handle_online(&rx_message, &tx_message); break; case REQ_OFFLINE: handle_offline(&rx_message, &tx_message); break; case REQ_BURST: handle_burst(&rx_message, &tx_message); break; case REQ_MODIFY_MINPOLL: handle_modify_minpoll(&rx_message, &tx_message); break; case REQ_MODIFY_MAXPOLL: handle_modify_maxpoll(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAY: handle_modify_maxdelay(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYRATIO: handle_modify_maxdelayratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYDEVRATIO: handle_modify_maxdelaydevratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXUPDATESKEW: handle_modify_maxupdateskew(&rx_message, &tx_message); break; case REQ_MODIFY_MAKESTEP: handle_modify_makestep(&rx_message, &tx_message); break; case REQ_LOGON: /* If the log-on fails, record the reason why */ if (!issue_token) { DEBUG_LOG(LOGF_CmdMon, "Bad command logon from %s port %d (auth_ok=%d valid_ts=%d)", UTI_IPToString(&remote_ip), remote_port, auth_ok, valid_ts); } if (issue_token == 1) { tx_message.status = htons(STT_SUCCESS); } else if (!auth_ok) { tx_message.status = htons(STT_UNAUTH); } else if (!valid_ts) { tx_message.status = htons(STT_INVALIDTS); } else { tx_message.status = htons(STT_FAILED); } break; case REQ_SETTIME: handle_settime(&rx_message, &tx_message); break; case REQ_LOCAL: handle_local(&rx_message, &tx_message); break; case REQ_MANUAL: handle_manual(&rx_message, &tx_message); break; case REQ_N_SOURCES: handle_n_sources(&rx_message, &tx_message); break; case REQ_SOURCE_DATA: handle_source_data(&rx_message, &tx_message); break; case REQ_REKEY: handle_rekey(&rx_message, &tx_message); break; case REQ_ALLOW: handle_allowdeny(&rx_message, &tx_message, 1, 0); break; case REQ_ALLOWALL: handle_allowdeny(&rx_message, &tx_message, 1, 1); break; case REQ_DENY: handle_allowdeny(&rx_message, &tx_message, 0, 0); break; case REQ_DENYALL: handle_allowdeny(&rx_message, &tx_message, 0, 1); break; case REQ_CMDALLOW: handle_cmdallowdeny(&rx_message, &tx_message, 1, 0); break; case REQ_CMDALLOWALL: handle_cmdallowdeny(&rx_message, &tx_message, 1, 1); break; case REQ_CMDDENY: handle_cmdallowdeny(&rx_message, &tx_message, 0, 0); break; case REQ_CMDDENYALL: handle_cmdallowdeny(&rx_message, &tx_message, 0, 1); break; case REQ_ACCHECK: handle_accheck(&rx_message, &tx_message); break; case REQ_CMDACCHECK: handle_cmdaccheck(&rx_message, &tx_message); break; case REQ_ADD_SERVER: handle_add_source(NTP_SERVER, &rx_message, &tx_message); break; case REQ_ADD_PEER: handle_add_source(NTP_PEER, &rx_message, &tx_message); break; case REQ_DEL_SOURCE: handle_del_source(&rx_message, &tx_message); break; case REQ_WRITERTC: handle_writertc(&rx_message, &tx_message); break; case REQ_DFREQ: handle_dfreq(&rx_message, &tx_message); break; case REQ_DOFFSET: handle_doffset(&rx_message, &tx_message); break; case REQ_TRACKING: handle_tracking(&rx_message, &tx_message); break; case REQ_SMOOTHING: handle_smoothing(&rx_message, &tx_message); break; case REQ_SMOOTHTIME: handle_smoothtime(&rx_message, &tx_message); break; case REQ_SOURCESTATS: handle_sourcestats(&rx_message, &tx_message); break; case REQ_RTCREPORT: handle_rtcreport(&rx_message, &tx_message); break; case REQ_TRIMRTC: handle_trimrtc(&rx_message, &tx_message); break; case REQ_CYCLELOGS: handle_cyclelogs(&rx_message, &tx_message); break; case REQ_CLIENT_ACCESSES_BY_INDEX: handle_client_accesses_by_index(&rx_message, &tx_message); break; case REQ_MANUAL_LIST: handle_manual_list(&rx_message, &tx_message); break; case REQ_MANUAL_DELETE: handle_manual_delete(&rx_message, &tx_message); break; case REQ_MAKESTEP: handle_make_step(&rx_message, &tx_message); break; case REQ_ACTIVITY: handle_activity(&rx_message, &tx_message); break; case REQ_RESELECTDISTANCE: handle_reselect_distance(&rx_message, &tx_message); break; case REQ_RESELECT: handle_reselect(&rx_message, &tx_message); break; case REQ_MODIFY_MINSTRATUM: handle_modify_minstratum(&rx_message, &tx_message); break; case REQ_MODIFY_POLLTARGET: handle_modify_polltarget(&rx_message, &tx_message); break; default: assert(0); break; } } else { tx_message.status = htons(STT_UNAUTH); } } if (auth_ok) { auth_length = generate_tx_packet_auth(&tx_message); } else { auth_length = 0; } if (token_ok) { save_reply(&tx_message, rx_message_token, tx_message_token, rx_message_seq, rx_attempt, &now); } /* Transmit the response */ { /* Include a simple way to lose one message in three to test resend */ static int do_it=1; if (do_it) { transmit_reply(&tx_message, &where_from, auth_length); } #if 0 do_it = ((do_it + 1) % 3); #endif } } /* ================================================== */ int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all) { ADF_Status status; if (allow) { if (all) { status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits); } else { status = ADF_Allow(access_auth_table, ip_addr, subnet_bits); } } else { if (all) { status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits); } else { status = ADF_Deny(access_auth_table, ip_addr, subnet_bits); } } if (status == ADF_BADSUBNET) { return 0; } else if (status == ADF_SUCCESS) { return 1; } else { return 0; } } /* ================================================== */ int CAM_CheckAccessRestriction(IPAddr *ip_addr) { return ADF_IsAllowed(access_auth_table, ip_addr); } /* ================================================== */ /* ================================================== */ chrony-2.1.1/cmdmon.h0000644000000000000000000000256212542263351013151 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for the control and monitoring module in the software */ #ifndef GOT_CMDMON_H #define GOT_CMDMON_H #include "addressing.h" extern void CAM_Initialise(int family); extern void CAM_Finalise(void); extern int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int CAM_CheckAccessRestriction(IPAddr *ip_addr); #endif /* GOT_CMDMON_H */ chrony-2.1.1/cmdparse.c0000644000000000000000000002247712542263351013474 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2013-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module for parsing various forms of directive and command lines that are common to the configuration file and to the command client. */ #include "config.h" #include "sysincl.h" #include "cmdparse.h" #include "memory.h" #include "nameserv.h" #include "ntp.h" #include "util.h" /* ================================================== */ CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) { char *hostname, *cmd; int n, done; CPS_Status result; src->port = SRC_DEFAULT_PORT; src->params.minpoll = SRC_DEFAULT_MINPOLL; src->params.maxpoll = SRC_DEFAULT_MAXPOLL; src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL; src->params.authkey = INACTIVE_AUTHKEY; src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.online = 1; src->params.auto_offline = 0; src->params.iburst = 0; src->params.min_stratum = SRC_DEFAULT_MINSTRATUM; src->params.poll_target = SRC_DEFAULT_POLLTARGET; src->params.version = NTP_VERSION; src->params.max_sources = SRC_DEFAULT_MAXSOURCES; src->params.min_samples = SRC_DEFAULT_MINSAMPLES; src->params.max_samples = SRC_DEFAULT_MAXSAMPLES; src->params.sel_option = SRC_SelectNormal; result = CPS_Success; hostname = line; line = CPS_SplitWord(line); if (!*hostname) { result = CPS_BadHost; } else { src->name = hostname; /* Parse subfields */ done = 0; do { cmd = line; line = CPS_SplitWord(line); if (*cmd) { if (!strcasecmp(cmd, "port")) { if (sscanf(line, "%hu%n", &src->port, &n) != 1) { result = CPS_BadPort; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "minpoll")) { if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) { result = CPS_BadMinpoll; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxpoll")) { if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) { result = CPS_BadMaxpoll; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "presend")) { if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) { result = CPS_BadPresend; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxdelaydevratio")) { if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) { result = CPS_BadMaxdelaydevratio; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxdelayratio")) { if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) { result = CPS_BadMaxdelayratio; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxdelay")) { if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) { result = CPS_BadMaxdelay; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "key")) { if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || src->params.authkey == INACTIVE_AUTHKEY) { result = CPS_BadKey; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "offline")) { src->params.online = 0; } else if (!strcasecmp(cmd, "auto_offline")) { src->params.auto_offline = 1; } else if (!strcasecmp(cmd, "iburst")) { src->params.iburst = 1; } else if (!strcasecmp(cmd, "minstratum")) { if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) { result = CPS_BadMinstratum; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "polltarget")) { if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) { result = CPS_BadPolltarget; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "noselect")) { src->params.sel_option = SRC_SelectNoselect; } else if (!strcasecmp(cmd, "prefer")) { src->params.sel_option = SRC_SelectPrefer; } else if (!strcasecmp(cmd, "version")) { if (sscanf(line, "%d%n", &src->params.version, &n) != 1) { result = CPS_BadVersion; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxsources")) { if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) { result = CPS_BadMaxsources; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "minsamples")) { if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) { result = CPS_BadMinsamples; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxsamples")) { if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) { result = CPS_BadMaxsamples; done = 1; } else { line += n; } } else { result = CPS_BadOption; done = 1; } } else { done = 1; } } while (!done); } return result; } /* ================================================== */ void CPS_StatusToString(CPS_Status status, char *dest, int len) { const char *s = NULL; if (len > 0) dest[0] = '\0'; switch (status) { case CPS_Success: return; case CPS_BadOption: s = "server/peer/pool option"; break; case CPS_BadHost: s = "address"; break; case CPS_BadPort: s = "port"; break; case CPS_BadMinpoll: s = "minpoll"; break; case CPS_BadMaxpoll: s = "maxpoll"; break; case CPS_BadPresend: s = "presend"; break; case CPS_BadMaxdelaydevratio: s = "maxdelaydevratio"; break; case CPS_BadMaxdelayratio: s = "maxdelayratio"; break; case CPS_BadMaxdelay: s = "maxdelay"; break; case CPS_BadKey: s = "key"; break; case CPS_BadMinstratum: s = "minstratum"; break; case CPS_BadPolltarget: s = "polltarget"; break; case CPS_BadVersion: s = "version"; break; case CPS_BadMaxsources: s = "maxsources"; break; case CPS_BadMinsamples: s = "minsamples"; break; case CPS_BadMaxsamples: s = "maxsamples"; break; } snprintf(dest, len, "Invalid %s", s); } /* ================================================== */ void CPS_NormalizeLine(char *line) { char *p, *q; int space = 1, first = 1; /* Remove white-space at beginning and replace white-spaces with space char */ for (p = q = line; *p; p++) { if (isspace((unsigned char)*p)) { if (!space) *q++ = ' '; space = 1; continue; } /* Discard comment lines */ if (first && strchr("!;#%", *p)) break; *q++ = *p; space = first = 0; } /* Strip trailing space */ if (q > line && q[-1] == ' ') q--; *q = '\0'; } /* ================================================== */ char * CPS_SplitWord(char *line) { char *p = line, *q = line; /* Skip white-space before the word */ while (*q && isspace((unsigned char)*q)) q++; /* Move the word to the beginning */ while (*q && !isspace((unsigned char)*q)) *p++ = *q++; /* Find the next word */ while (*q && isspace((unsigned char)*q)) q++; *p = '\0'; /* Return pointer to the next word or NUL */ return q; } /* ================================================== */ int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) { char *s1, *s2, *s3, *s4; s1 = line; s2 = CPS_SplitWord(s1); s3 = CPS_SplitWord(s2); s4 = CPS_SplitWord(s3); /* Require two or three words */ if (!*s2 || *s4) return 0; if (sscanf(s1, "%"SCNu32, id) != 1) return 0; if (*s3) { *hash = s2; *key = s3; } else { *hash = "MD5"; *key = s2; } return 1; } chrony-2.1.1/cmdparse.h0000644000000000000000000000417512542263351013474 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for the command parser */ #ifndef GOT_CMDPARSE_H #define GOT_CMDPARSE_H #include "srcparams.h" #include "addressing.h" typedef enum { CPS_Success, CPS_BadOption, CPS_BadHost, CPS_BadPort, CPS_BadMinpoll, CPS_BadMaxpoll, CPS_BadPresend, CPS_BadMaxdelaydevratio, CPS_BadMaxdelayratio, CPS_BadMaxdelay, CPS_BadKey, CPS_BadMinstratum, CPS_BadPolltarget, CPS_BadVersion, CPS_BadMaxsources, CPS_BadMinsamples, CPS_BadMaxsamples, } CPS_Status; typedef struct { char *name; unsigned short port; SourceParameters params; } CPS_NTP_Source; /* Parse a command to add an NTP server or peer */ extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); /* Get a string describing error status */ extern void CPS_StatusToString(CPS_Status status, char *dest, int len); /* Remove extra white-space and comments */ extern void CPS_NormalizeLine(char *line); /* Terminate first word and return pointer to the next word */ extern char *CPS_SplitWord(char *line); /* Parse a key from keyfile */ extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key); #endif /* GOT_CMDPARSE_H */ chrony-2.1.1/conf.c0000644000000000000000000012422012542263351012610 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module that reads and processes the configuration file. */ #include "config.h" #include "sysincl.h" #include "array.h" #include "conf.h" #include "ntp_sources.h" #include "ntp_core.h" #include "refclock.h" #include "cmdmon.h" #include "srcparams.h" #include "logging.h" #include "nameserv.h" #include "memory.h" #include "cmdparse.h" #include "util.h" /* ================================================== */ /* Forward prototypes */ static int parse_string(char *line, char **result); static int parse_int(char *line, int *result); static int parse_uint32(char *, uint32_t *result); static int parse_double(char *line, double *result); static int parse_null(char *line); static void parse_allow(char *); static void parse_bindacqaddress(char *); static void parse_bindaddress(char *); static void parse_bindcmdaddress(char *); static void parse_broadcast(char *); static void parse_clientloglimit(char *); static void parse_cmdallow(char *); static void parse_cmddeny(char *); static void parse_deny(char *); static void parse_fallbackdrift(char *); static void parse_include(char *); static void parse_initstepslew(char *); static void parse_leapsecmode(char *); static void parse_local(char *); static void parse_log(char *); static void parse_mailonchange(char *); static void parse_makestep(char *); static void parse_maxchange(char *); static void parse_peer(char *); static void parse_pool(char *); static void parse_refclock(char *); static void parse_server(char *); static void parse_smoothtime(char *); static void parse_tempcomp(char *); /* ================================================== */ /* Configuration variables */ static int restarted = 0; static int generate_command_key = 0; static char *rtc_device; static int acquisition_port = -1; static int ntp_port = 123; static char *keys_file = NULL; static char *drift_file = NULL; static char *rtc_file = NULL; static uint32_t command_key_id; static double max_update_skew = 1000.0; static double correction_time_ratio = 3.0; static double max_clock_error = 1.0; /* in ppm */ static double max_slew_rate = 1e6 / 12.0; /* in ppm */ static double reselect_distance = 1e-4; static double stratum_weight = 1e-3; static double combine_limit = 3.0; static int cmd_port = DEFAULT_CANDM_PORT; static int do_log_measurements = 0; static int do_log_statistics = 0; static int do_log_tracking = 0; static int do_log_rtc = 0; static int do_log_refclocks = 0; static int do_log_tempcomp = 0; static int do_dump_on_exit = 0; static int log_banner = 32; static char *logdir; static char *dumpdir; static int enable_local=0; static int local_stratum; /* Threshold (in seconds) - if absolute value of initial error is less than this, slew instead of stepping */ static double init_slew_threshold; /* Array of IPAddr */ static ARR_Instance init_sources; static int enable_manual=0; /* Flag set if the RTC runs UTC (default is it runs local time incl. daylight saving). */ static int rtc_on_utc = 0; /* Filename used to read the hwclock(8) LOCAL/UTC setting */ static char *hwclock_file = NULL; /* Flag set if the RTC should be automatically synchronised by kernel */ static int rtc_sync = 0; /* Limit and threshold for clock stepping */ static int make_step_limit = 0; static double make_step_threshold = 0.0; /* Threshold for automatic RTC trimming */ static double rtc_autotrim_threshold = 0.0; /* Minimum number of selectables sources required to update the clock */ static int min_sources = 1; /* Number of updates before offset checking, number of ignored updates before exiting and the maximum allowed offset */ static int max_offset_delay = -1; static int max_offset_ignore; static double max_offset; /* Maximum and minimum number of samples per source */ static int max_samples = 0; /* no limit */ static int min_samples = 0; /* Flag set if we should log to syslog when a time adjustment exceeding the threshold is initiated */ static int do_log_change = 0; static double log_change_threshold = 0.0; static char *mail_user_on_change = NULL; static double mail_change_threshold = 0.0; /* Flag indicating that we don't want to log clients, e.g. to save memory */ static int no_client_log = 0; /* Limit memory allocated for the clients log */ static unsigned long client_log_limit = 524288; /* Minimum and maximum fallback drift intervals */ static int fb_drift_min = 0; static int fb_drift_max = 0; /* IP addresses for binding the NTP server sockets to. UNSPEC family means INADDR_ANY will be used */ static IPAddr bind_address4, bind_address6; /* IP addresses for binding the NTP client sockets to. UNSPEC family means INADDR_ANY will be used */ static IPAddr bind_acq_address4, bind_acq_address6; /* IP addresses for binding the command socket to. UNSPEC family means the loopback address will be used */ static IPAddr bind_cmd_address4, bind_cmd_address6; /* Filename to use for storing pid of running chronyd, to prevent multiple * chronyds being started. */ static char *pidfile; /* Smoothing constants */ static double smooth_max_freq = 0.0; /* in ppm */ static double smooth_max_wander = 0.0; /* in ppm/s */ static int smooth_leap_only = 0; /* Temperature sensor, update interval and compensation coefficients */ static char *tempcomp_sensor_file = NULL; static char *tempcomp_point_file = NULL; static double tempcomp_interval; static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2; static int sched_priority = 0; static int lock_memory = 0; /* Leap second handling mode */ static REF_LeapMode leapsec_mode = REF_LeapModeSystem; /* Name of a system timezone containing leap seconds occuring at midnight */ static char *leapsec_tz = NULL; /* Name of the user to which will be dropped root privileges. */ static char *user; typedef struct { NTP_Source_Type type; int pool; CPS_NTP_Source params; } NTP_Source; /* Array of NTP_Source */ static ARR_Instance ntp_sources; /* Array of RefclockParameters */ static ARR_Instance refclock_sources; typedef struct _AllowDeny { IPAddr ip; int subnet_bits; int all; /* 1 to override existing more specific defns */ int allow; /* 0 for deny, 1 for allow */ } AllowDeny; /* Arrays of AllowDeny */ static ARR_Instance ntp_restrictions; static ARR_Instance cmd_restrictions; typedef struct { IPAddr addr; unsigned short port; int interval; } NTP_Broadcast_Destination; /* Array of NTP_Broadcast_Destination */ static ARR_Instance broadcasts; /* ================================================== */ /* The line number in the configuration file being processed */ static int line_number; static const char *processed_file; static const char *processed_command; /* ================================================== */ static void command_parse_error(void) { LOG_FATAL(LOGF_Configure, "Could not parse %s directive at line %d%s%s", processed_command, line_number, processed_file ? " in file " : "", processed_file ? processed_file : ""); } /* ================================================== */ static void other_parse_error(const char *message) { LOG_FATAL(LOGF_Configure, "%s at line %d%s%s", message, line_number, processed_file ? " in file " : "", processed_file ? processed_file : ""); } /* ================================================== */ static int get_number_of_args(char *line) { int num = 0; /* The line is normalized, between arguments is just one space */ if (*line == ' ') line++; if (*line) num++; for (; *line; line++) { if (*line == ' ') num++; } return num; } /* ================================================== */ static void check_number_of_args(char *line, int num) { num -= get_number_of_args(line); if (num) { LOG_FATAL(LOGF_Configure, "%s arguments for %s directive at line %d%s%s", num > 0 ? "Missing" : "Too many", processed_command, line_number, processed_file ? " in file " : "", processed_file ? processed_file : ""); } } /* ================================================== */ void CNF_Initialise(int r) { restarted = r; init_sources = ARR_CreateInstance(sizeof (IPAddr)); ntp_sources = ARR_CreateInstance(sizeof (NTP_Source)); refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters)); broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); dumpdir = Strdup("."); logdir = Strdup("."); pidfile = Strdup("/var/run/chronyd.pid"); rtc_device = Strdup("/dev/rtc"); user = Strdup(DEFAULT_USER); } /* ================================================== */ void CNF_Finalise(void) { unsigned int i; for (i = 0; i < ARR_GetSize(ntp_sources); i++) Free(((NTP_Source *)ARR_GetElement(ntp_sources, i))->params.name); ARR_DestroyInstance(init_sources); ARR_DestroyInstance(ntp_sources); ARR_DestroyInstance(refclock_sources); ARR_DestroyInstance(broadcasts); ARR_DestroyInstance(ntp_restrictions); ARR_DestroyInstance(cmd_restrictions); Free(drift_file); Free(dumpdir); Free(hwclock_file); Free(keys_file); Free(leapsec_tz); Free(logdir); Free(pidfile); Free(rtc_device); Free(rtc_file); Free(user); Free(mail_user_on_change); Free(tempcomp_sensor_file); Free(tempcomp_point_file); } /* ================================================== */ /* Read the configuration file */ void CNF_ReadFile(const char *filename) { FILE *in; char line[2048]; int i; in = fopen(filename, "r"); if (!in) { LOG_FATAL(LOGF_Configure, "Could not open configuration file %s", filename); return; } for (i = 1; fgets(line, sizeof(line), in); i++) { CNF_ParseLine(filename, i, line); } fclose(in); } /* ================================================== */ /* Parse one configuration line */ void CNF_ParseLine(const char *filename, int number, char *line) { char *p, *command; /* Set global variables used in error messages */ processed_file = filename; line_number = number; /* Remove extra white-space and comments */ CPS_NormalizeLine(line); /* Skip blank lines */ if (!*line) return; /* We have a real line, now try to match commands */ processed_command = command = line; p = CPS_SplitWord(line); if (!strcasecmp(command, "acquisitionport")) { parse_int(p, &acquisition_port); } else if (!strcasecmp(command, "allow")) { parse_allow(p); } else if (!strcasecmp(command, "bindacqaddress")) { parse_bindacqaddress(p); } else if (!strcasecmp(command, "bindaddress")) { parse_bindaddress(p); } else if (!strcasecmp(command, "bindcmdaddress")) { parse_bindcmdaddress(p); } else if (!strcasecmp(command, "broadcast")) { parse_broadcast(p); } else if (!strcasecmp(command, "clientloglimit")) { parse_clientloglimit(p); } else if (!strcasecmp(command, "cmdallow")) { parse_cmdallow(p); } else if (!strcasecmp(command, "cmddeny")) { parse_cmddeny(p); } else if (!strcasecmp(command, "cmdport")) { parse_int(p, &cmd_port); } else if (!strcasecmp(command, "combinelimit")) { parse_double(p, &combine_limit); } else if (!strcasecmp(command, "commandkey")) { parse_uint32(p, &command_key_id); } else if (!strcasecmp(command, "corrtimeratio")) { parse_double(p, &correction_time_ratio); } else if (!strcasecmp(command, "deny")) { parse_deny(p); } else if (!strcasecmp(command, "driftfile")) { parse_string(p, &drift_file); } else if (!strcasecmp(command, "dumpdir")) { parse_string(p, &dumpdir); } else if (!strcasecmp(command, "dumponexit")) { do_dump_on_exit = parse_null(p); } else if (!strcasecmp(command, "fallbackdrift")) { parse_fallbackdrift(p); } else if (!strcasecmp(command, "generatecommandkey")) { generate_command_key = parse_null(p); } else if (!strcasecmp(command, "hwclockfile")) { parse_string(p, &hwclock_file); } else if (!strcasecmp(command, "include")) { parse_include(p); } else if (!strcasecmp(command, "initstepslew")) { parse_initstepslew(p); } else if (!strcasecmp(command, "keyfile")) { parse_string(p, &keys_file); } else if (!strcasecmp(command, "leapsecmode")) { parse_leapsecmode(p); } else if (!strcasecmp(command, "leapsectz")) { parse_string(p, &leapsec_tz); } else if (!strcasecmp(command, "linux_freq_scale")) { LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command); } else if (!strcasecmp(command, "linux_hz")) { LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command); } else if (!strcasecmp(command, "local")) { parse_local(p); } else if (!strcasecmp(command, "lock_all")) { lock_memory = parse_null(p); } else if (!strcasecmp(command, "log")) { parse_log(p); } else if (!strcasecmp(command, "logbanner")) { parse_int(p, &log_banner); } else if (!strcasecmp(command, "logchange")) { do_log_change = parse_double(p, &log_change_threshold); } else if (!strcasecmp(command, "logdir")) { parse_string(p, &logdir); } else if (!strcasecmp(command, "mailonchange")) { parse_mailonchange(p); } else if (!strcasecmp(command, "makestep")) { parse_makestep(p); } else if (!strcasecmp(command, "manual")) { enable_manual = parse_null(p); } else if (!strcasecmp(command, "maxchange")) { parse_maxchange(p); } else if (!strcasecmp(command, "maxclockerror")) { parse_double(p, &max_clock_error); } else if (!strcasecmp(command, "maxsamples")) { parse_int(p, &max_samples); } else if (!strcasecmp(command, "maxslewrate")) { parse_double(p, &max_slew_rate); } else if (!strcasecmp(command, "maxupdateskew")) { parse_double(p, &max_update_skew); } else if (!strcasecmp(command, "minsamples")) { parse_int(p, &min_samples); } else if (!strcasecmp(command, "minsources")) { parse_int(p, &min_sources); } else if (!strcasecmp(command, "noclientlog")) { no_client_log = parse_null(p); } else if (!strcasecmp(command, "peer")) { parse_peer(p); } else if (!strcasecmp(command, "pidfile")) { parse_string(p, &pidfile); } else if (!strcasecmp(command, "pool")) { parse_pool(p); } else if (!strcasecmp(command, "port")) { parse_int(p, &ntp_port); } else if (!strcasecmp(command, "refclock")) { parse_refclock(p); } else if (!strcasecmp(command, "reselectdist")) { parse_double(p, &reselect_distance); } else if (!strcasecmp(command, "rtcautotrim")) { parse_double(p, &rtc_autotrim_threshold); } else if (!strcasecmp(command, "rtcdevice")) { parse_string(p, &rtc_device); } else if (!strcasecmp(command, "rtcfile")) { parse_string(p, &rtc_file); } else if (!strcasecmp(command, "rtconutc")) { rtc_on_utc = parse_null(p); } else if (!strcasecmp(command, "rtcsync")) { rtc_sync = parse_null(p); } else if (!strcasecmp(command, "sched_priority")) { parse_int(p, &sched_priority); } else if (!strcasecmp(command, "server")) { parse_server(p); } else if (!strcasecmp(command, "smoothtime")) { parse_smoothtime(p); } else if (!strcasecmp(command, "stratumweight")) { parse_double(p, &stratum_weight); } else if (!strcasecmp(command, "tempcomp")) { parse_tempcomp(p); } else if (!strcasecmp(command, "user")) { parse_string(p, &user); } else { other_parse_error("Invalid command"); } } /* ================================================== */ static int parse_string(char *line, char **result) { check_number_of_args(line, 1); Free(*result); *result = Strdup(line); return 1; } /* ================================================== */ static int parse_int(char *line, int *result) { check_number_of_args(line, 1); if (sscanf(line, "%d", result) != 1) { command_parse_error(); return 0; } return 1; } /* ================================================== */ static int parse_uint32(char *line, uint32_t *result) { check_number_of_args(line, 1); if (sscanf(line, "%"SCNu32, result) != 1) { command_parse_error(); return 0; } return 1; } /* ================================================== */ static int parse_double(char *line, double *result) { check_number_of_args(line, 1); if (sscanf(line, "%lf", result) != 1) { command_parse_error(); return 0; } return 1; } /* ================================================== */ static int parse_null(char *line) { check_number_of_args(line, 0); return 1; } /* ================================================== */ static void parse_source(char *line, NTP_Source_Type type, int pool) { CPS_Status status; NTP_Source source; char str[64]; source.type = type; source.pool = pool; status = CPS_ParseNTPSourceAdd(line, &source.params); if (status != CPS_Success) { CPS_StatusToString(status, str, sizeof (str)); other_parse_error(str); return; } source.params.name = Strdup(source.params.name); ARR_AppendElement(ntp_sources, &source); } /* ================================================== */ static void parse_server(char *line) { parse_source(line, NTP_SERVER, 0); } /* ================================================== */ static void parse_peer(char *line) { parse_source(line, NTP_PEER, 0); } /* ================================================== */ static void parse_pool(char *line) { parse_source(line, NTP_SERVER, 1); } /* ================================================== */ static void parse_refclock(char *line) { int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples; uint32_t ref_id, lock_ref_id; double offset, delay, precision, max_dispersion; char *p, *cmd, *name, *param; unsigned char ref[5]; SRC_SelectOption sel_option; RefclockParameters *refclock; poll = 4; dpoll = 0; filter_length = 64; pps_rate = 0; min_samples = SRC_DEFAULT_MINSAMPLES; max_samples = SRC_DEFAULT_MAXSAMPLES; offset = 0.0; delay = 1e-9; precision = 0.0; max_dispersion = 0.0; ref_id = 0; lock_ref_id = 0; sel_option = SRC_SelectNormal; if (!*line) { command_parse_error(); return; } p = line; line = CPS_SplitWord(line); if (!*line) { command_parse_error(); return; } name = Strdup(p); p = line; line = CPS_SplitWord(line); param = Strdup(p); while (*line) { cmd = line; line = CPS_SplitWord(line); if (!strcasecmp(cmd, "refid")) { if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) break; ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; } else if (!strcasecmp(cmd, "lock")) { if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) break; lock_ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; } else if (!strcasecmp(cmd, "poll")) { if (sscanf(line, "%d%n", &poll, &n) != 1) { break; } } else if (!strcasecmp(cmd, "dpoll")) { if (sscanf(line, "%d%n", &dpoll, &n) != 1) { break; } } else if (!strcasecmp(cmd, "filter")) { if (sscanf(line, "%d%n", &filter_length, &n) != 1) { break; } } else if (!strcasecmp(cmd, "rate")) { if (sscanf(line, "%d%n", &pps_rate, &n) != 1) break; } else if (!strcasecmp(cmd, "minsamples")) { if (sscanf(line, "%d%n", &min_samples, &n) != 1) break; } else if (!strcasecmp(cmd, "maxsamples")) { if (sscanf(line, "%d%n", &max_samples, &n) != 1) break; } else if (!strcasecmp(cmd, "offset")) { if (sscanf(line, "%lf%n", &offset, &n) != 1) break; } else if (!strcasecmp(cmd, "delay")) { if (sscanf(line, "%lf%n", &delay, &n) != 1) break; } else if (!strcasecmp(cmd, "precision")) { if (sscanf(line, "%lf%n", &precision, &n) != 1) break; } else if (!strcasecmp(cmd, "maxdispersion")) { if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1) break; } else if (!strcasecmp(cmd, "noselect")) { n = 0; sel_option = SRC_SelectNoselect; } else if (!strcasecmp(cmd, "prefer")) { n = 0; sel_option = SRC_SelectPrefer; } else { break; } line += n; } if (*line) { other_parse_error("Invalid/unreadable refclock parameter"); return; } refclock = (RefclockParameters *)ARR_GetNewElement(refclock_sources); refclock->driver_name = name; refclock->driver_parameter = param; refclock->driver_poll = dpoll; refclock->poll = poll; refclock->filter_length = filter_length; refclock->pps_rate = pps_rate; refclock->min_samples = min_samples; refclock->max_samples = max_samples; refclock->offset = offset; refclock->delay = delay; refclock->precision = precision; refclock->max_dispersion = max_dispersion; refclock->sel_option = sel_option; refclock->ref_id = ref_id; refclock->lock_ref_id = lock_ref_id; } /* ================================================== */ static void parse_log(char *line) { char *log_name; do { log_name = line; line = CPS_SplitWord(line); if (*log_name) { if (!strcmp(log_name, "measurements")) { do_log_measurements = 1; } else if (!strcmp(log_name, "statistics")) { do_log_statistics = 1; } else if (!strcmp(log_name, "tracking")) { do_log_tracking = 1; } else if (!strcmp(log_name, "rtc")) { do_log_rtc = 1; } else if (!strcmp(log_name, "refclocks")) { do_log_refclocks = 1; } else if (!strcmp(log_name, "tempcomp")) { do_log_tempcomp = 1; } else { other_parse_error("Invalid log parameter"); break; } } else { break; } } while (1); } /* ================================================== */ static void parse_local(char *line) { int stratum; if (sscanf(line, "stratum%d", &stratum) == 1) { local_stratum = stratum; enable_local = 1; } else { command_parse_error(); } } /* ================================================== */ static void parse_initstepslew(char *line) { char *p, *hostname; IPAddr ip_addr; /* Ignore the line if chronyd was started with -R. */ if (restarted) { return; } ARR_SetSize(init_sources, 0); p = CPS_SplitWord(line); if (sscanf(line, "%lf", &init_slew_threshold) != 1) { command_parse_error(); return; } while (*p) { hostname = p; p = CPS_SplitWord(p); if (*hostname) { if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) { ARR_AppendElement(init_sources, &ip_addr); } else { LOG(LOGS_WARN, LOGF_Configure, "Could not resolve address of initstepslew server %s", hostname); } } } } /* ================================================== */ static void parse_leapsecmode(char *line) { if (!strcasecmp(line, "system")) leapsec_mode = REF_LeapModeSystem; else if (!strcasecmp(line, "slew")) leapsec_mode = REF_LeapModeSlew; else if (!strcasecmp(line, "step")) leapsec_mode = REF_LeapModeStep; else if (!strcasecmp(line, "ignore")) leapsec_mode = REF_LeapModeIgnore; else command_parse_error(); } /* ================================================== */ static void parse_clientloglimit(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lu", &client_log_limit) != 1) { command_parse_error(); } if (client_log_limit == 0) { /* unlimited */ client_log_limit = (unsigned long)-1; } } /* ================================================== */ static void parse_fallbackdrift(char *line) { check_number_of_args(line, 2); if (sscanf(line, "%d %d", &fb_drift_min, &fb_drift_max) != 2) { command_parse_error(); } } /* ================================================== */ static void parse_makestep(char *line) { check_number_of_args(line, 2); if (sscanf(line, "%lf %d", &make_step_threshold, &make_step_limit) != 2) { make_step_limit = 0; command_parse_error(); } /* Disable limited makestep if chronyd was started with -R. */ if (restarted && make_step_limit > 0) { make_step_limit = 0; } } /* ================================================== */ static void parse_maxchange(char *line) { check_number_of_args(line, 3); if (sscanf(line, "%lf %d %d", &max_offset, &max_offset_delay, &max_offset_ignore) != 3) { max_offset_delay = -1; command_parse_error(); } } /* ================================================== */ static void parse_mailonchange(char *line) { char *address; check_number_of_args(line, 2); address = line; line = CPS_SplitWord(line); Free(mail_user_on_change); if (sscanf(line, "%lf", &mail_change_threshold) == 1) { mail_user_on_change = Strdup(address); } else { mail_user_on_change = NULL; command_parse_error(); } } /* ================================================== */ static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow) { char *p; unsigned long a, b, c, d, n; int all = 0; AllowDeny *new_node = NULL; IPAddr ip_addr; p = line; if (!strncmp(p, "all", 3)) { all = 1; p = CPS_SplitWord(line); } if (!*p) { /* Empty line applies to all addresses */ new_node = (AllowDeny *)ARR_GetNewElement(restrictions); new_node->allow = allow; new_node->all = all; new_node->ip.family = IPADDR_UNSPEC; new_node->subnet_bits = 0; } else { char *slashpos; slashpos = strchr(p, '/'); if (slashpos) *slashpos = 0; check_number_of_args(p, 1); n = 0; if (UTI_StringToIP(p, &ip_addr) || (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) >= 1) { new_node = (AllowDeny *)ARR_GetNewElement(restrictions); new_node->allow = allow; new_node->all = all; if (n == 0) { new_node->ip = ip_addr; if (ip_addr.family == IPADDR_INET6) new_node->subnet_bits = 128; else new_node->subnet_bits = 32; } else { new_node->ip.family = IPADDR_INET4; a &= 0xff; b &= 0xff; c &= 0xff; d &= 0xff; switch (n) { case 1: new_node->ip.addr.in4 = (a<<24); new_node->subnet_bits = 8; break; case 2: new_node->ip.addr.in4 = (a<<24) | (b<<16); new_node->subnet_bits = 16; break; case 3: new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8); new_node->subnet_bits = 24; break; case 4: new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; new_node->subnet_bits = 32; break; default: assert(0); } } if (slashpos) { int specified_subnet_bits, n; n = sscanf(slashpos+1, "%d", &specified_subnet_bits); if (n == 1) { new_node->subnet_bits = specified_subnet_bits; } else { command_parse_error(); } } } else { if (DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) { new_node = (AllowDeny *)ARR_GetNewElement(restrictions); new_node->allow = allow; new_node->all = all; new_node->ip = ip_addr; if (ip_addr.family == IPADDR_INET6) new_node->subnet_bits = 128; else new_node->subnet_bits = 32; } else { command_parse_error(); } } } } /* ================================================== */ static void parse_allow(char *line) { parse_allow_deny(line, ntp_restrictions, 1); } /* ================================================== */ static void parse_deny(char *line) { parse_allow_deny(line, ntp_restrictions, 0); } /* ================================================== */ static void parse_cmdallow(char *line) { parse_allow_deny(line, cmd_restrictions, 1); } /* ================================================== */ static void parse_cmddeny(char *line) { parse_allow_deny(line, cmd_restrictions, 0); } /* ================================================== */ static void parse_bindacqaddress(char *line) { IPAddr ip; check_number_of_args(line, 1); if (UTI_StringToIP(line, &ip)) { if (ip.family == IPADDR_INET4) bind_acq_address4 = ip; else if (ip.family == IPADDR_INET6) bind_acq_address6 = ip; } else { command_parse_error(); } } /* ================================================== */ static void parse_bindaddress(char *line) { IPAddr ip; check_number_of_args(line, 1); if (UTI_StringToIP(line, &ip)) { if (ip.family == IPADDR_INET4) bind_address4 = ip; else if (ip.family == IPADDR_INET6) bind_address6 = ip; } else { command_parse_error(); } } /* ================================================== */ static void parse_bindcmdaddress(char *line) { IPAddr ip; check_number_of_args(line, 1); if (UTI_StringToIP(line, &ip)) { if (ip.family == IPADDR_INET4) bind_cmd_address4 = ip; else if (ip.family == IPADDR_INET6) bind_cmd_address6 = ip; } else { command_parse_error(); } } /* ================================================== */ static void parse_broadcast(char *line) { /* Syntax : broadcast [] */ NTP_Broadcast_Destination *destination; int port; int interval; char *p; IPAddr ip; p = line; line = CPS_SplitWord(line); if (sscanf(p, "%d", &interval) != 1) { command_parse_error(); return; } p = line; line = CPS_SplitWord(line); if (!UTI_StringToIP(p, &ip)) { command_parse_error(); return; } p = line; line = CPS_SplitWord(line); if (*p) { if (sscanf(p, "%d", &port) != 1 || *line) { command_parse_error(); return; } } else { /* default port */ port = 123; } destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts); destination->addr = ip; destination->port = port; destination->interval = interval; } /* ================================================== */ static void parse_smoothtime(char *line) { if (get_number_of_args(line) != 3) check_number_of_args(line, 2); if (sscanf(line, "%lf %lf", &smooth_max_freq, &smooth_max_wander) != 2) { smooth_max_freq = 0.0; command_parse_error(); } line = CPS_SplitWord(CPS_SplitWord(line)); smooth_leap_only = 0; if (*line) { if (!strcasecmp(line, "leaponly")) smooth_leap_only = 1; else command_parse_error(); } } /* ================================================== */ static void parse_tempcomp(char *line) { char *p; int point_form; point_form = get_number_of_args(line) == 3; if (!point_form) check_number_of_args(line, 6); p = line; line = CPS_SplitWord(line); if (!*p) { command_parse_error(); return; } Free(tempcomp_point_file); if (point_form) { if (sscanf(line, "%lf", &tempcomp_interval) != 1) { command_parse_error(); return; } tempcomp_point_file = Strdup(CPS_SplitWord(line)); } else { if (sscanf(line, "%lf %lf %lf %lf %lf", &tempcomp_interval, &tempcomp_T0, &tempcomp_k0, &tempcomp_k1, &tempcomp_k2) != 5) { command_parse_error(); return; } tempcomp_point_file = NULL; } Free(tempcomp_sensor_file); tempcomp_sensor_file = Strdup(p); } /* ================================================== */ static void parse_include(char *line) { check_number_of_args(line, 1); CNF_ReadFile(line); } /* ================================================== */ void CNF_AddInitSources(void) { CPS_NTP_Source cps_source; NTP_Remote_Address ntp_addr; char dummy_hostname[2] = "H"; unsigned int i; for (i = 0; i < ARR_GetSize(init_sources); i++) { /* Get the default NTP params */ CPS_ParseNTPSourceAdd(dummy_hostname, &cps_source); /* Add the address as an offline iburst server */ ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i); ntp_addr.port = cps_source.port; cps_source.params.iburst = 1; cps_source.params.online = 0; NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params); } ARR_SetSize(init_sources, 0); } /* ================================================== */ void CNF_AddSources(void) { NTP_Source *source; unsigned int i; for (i = 0; i < ARR_GetSize(ntp_sources); i++) { source = (NTP_Source *)ARR_GetElement(ntp_sources, i); NSR_AddSourceByName(source->params.name, source->params.port, source->pool, source->type, &source->params.params); Free(source->params.name); } ARR_SetSize(ntp_sources, 0); } /* ================================================== */ void CNF_AddRefclocks(void) { unsigned int i; for (i = 0; i < ARR_GetSize(refclock_sources); i++) { RCL_AddRefclock((RefclockParameters *)ARR_GetElement(refclock_sources, i)); } ARR_SetSize(refclock_sources, 0); } /* ================================================== */ void CNF_AddBroadcasts(void) { unsigned int i; NTP_Broadcast_Destination *destination; for (i = 0; i < ARR_GetSize(broadcasts); i++) { destination = (NTP_Broadcast_Destination *)ARR_GetElement(broadcasts, i); NCR_AddBroadcastDestination(&destination->addr, destination->port, destination->interval); } ARR_SetSize(broadcasts, 0); } /* ================================================== */ int CNF_GetNTPPort(void) { return ntp_port; } /* ================================================== */ int CNF_GetAcquisitionPort(void) { return acquisition_port; } /* ================================================== */ char * CNF_GetDriftFile(void) { return drift_file; } /* ================================================== */ int CNF_GetLogBanner(void) { return log_banner; } /* ================================================== */ char * CNF_GetLogDir(void) { return logdir; } /* ================================================== */ char * CNF_GetDumpDir(void) { return dumpdir; } /* ================================================== */ int CNF_GetLogMeasurements(void) { return do_log_measurements; } /* ================================================== */ int CNF_GetLogStatistics(void) { return do_log_statistics; } /* ================================================== */ int CNF_GetLogTracking(void) { return do_log_tracking; } /* ================================================== */ int CNF_GetLogRtc(void) { return do_log_rtc; } /* ================================================== */ int CNF_GetLogRefclocks(void) { return do_log_refclocks; } /* ================================================== */ int CNF_GetLogTempComp(void) { return do_log_tempcomp; } /* ================================================== */ char * CNF_GetKeysFile(void) { return keys_file; } /* ================================================== */ double CNF_GetRtcAutotrim(void) { return rtc_autotrim_threshold; } /* ================================================== */ char * CNF_GetRtcFile(void) { return rtc_file; } /* ================================================== */ char * CNF_GetRtcDevice(void) { return rtc_device; } /* ================================================== */ uint32_t CNF_GetCommandKey(void) { return command_key_id; } /* ================================================== */ int CNF_GetGenerateCommandKey(void) { return generate_command_key; } /* ================================================== */ int CNF_GetDumpOnExit(void) { return do_dump_on_exit; } /* ================================================== */ double CNF_GetMaxUpdateSkew(void) { return max_update_skew; } /* ================================================== */ double CNF_GetMaxClockError(void) { return max_clock_error; } /* ================================================== */ double CNF_GetCorrectionTimeRatio(void) { return correction_time_ratio; } /* ================================================== */ double CNF_GetMaxSlewRate(void) { return max_slew_rate; } /* ================================================== */ double CNF_GetReselectDistance(void) { return reselect_distance; } /* ================================================== */ double CNF_GetStratumWeight(void) { return stratum_weight; } /* ================================================== */ double CNF_GetCombineLimit(void) { return combine_limit; } /* ================================================== */ int CNF_GetManualEnabled(void) { return enable_manual; } /* ================================================== */ int CNF_GetCommandPort(void) { return cmd_port; } /* ================================================== */ int CNF_AllowLocalReference(int *stratum) { if (enable_local) { *stratum = local_stratum; return 1; } else { return 0; } } /* ================================================== */ int CNF_GetRtcOnUtc(void) { return rtc_on_utc; } /* ================================================== */ int CNF_GetRtcSync(void) { return rtc_sync; } /* ================================================== */ void CNF_GetMakeStep(int *limit, double *threshold) { *limit = make_step_limit; *threshold = make_step_threshold; } /* ================================================== */ void CNF_GetMaxChange(int *delay, int *ignore, double *offset) { *delay = max_offset_delay; *ignore = max_offset_ignore; *offset = max_offset; } /* ================================================== */ void CNF_GetLogChange(int *enabled, double *threshold) { *enabled = do_log_change; *threshold = log_change_threshold; } /* ================================================== */ void CNF_GetMailOnChange(int *enabled, double *threshold, char **user) { if (mail_user_on_change) { *enabled = 1; *threshold = mail_change_threshold; *user = mail_user_on_change; } else { *enabled = 0; *threshold = 0.0; *user = NULL; } } /* ================================================== */ void CNF_SetupAccessRestrictions(void) { AllowDeny *node; int status; unsigned int i; for (i = 0; i < ARR_GetSize(ntp_restrictions); i++) { node = ARR_GetElement(ntp_restrictions, i); status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); if (!status) { LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits); } } for (i = 0; i < ARR_GetSize(cmd_restrictions); i++) { node = ARR_GetElement(cmd_restrictions, i); status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); if (!status) { LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits); } } ARR_SetSize(ntp_restrictions, 0); ARR_SetSize(cmd_restrictions, 0); } /* ================================================== */ int CNF_GetNoClientLog(void) { return no_client_log; } /* ================================================== */ unsigned long CNF_GetClientLogLimit(void) { return client_log_limit; } /* ================================================== */ void CNF_GetFallbackDrifts(int *min, int *max) { *min = fb_drift_min; *max = fb_drift_max; } /* ================================================== */ void CNF_GetBindAddress(int family, IPAddr *addr) { if (family == IPADDR_INET4) *addr = bind_address4; else if (family == IPADDR_INET6) *addr = bind_address6; else addr->family = IPADDR_UNSPEC; } /* ================================================== */ void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr) { if (family == IPADDR_INET4) *addr = bind_acq_address4; else if (family == IPADDR_INET6) *addr = bind_acq_address6; else addr->family = IPADDR_UNSPEC; } /* ================================================== */ void CNF_GetBindCommandAddress(int family, IPAddr *addr) { if (family == IPADDR_INET4) *addr = bind_cmd_address4; else if (family == IPADDR_INET6) *addr = bind_cmd_address6; else addr->family = IPADDR_UNSPEC; } /* ================================================== */ char * CNF_GetPidFile(void) { return pidfile; } /* ================================================== */ REF_LeapMode CNF_GetLeapSecMode(void) { return leapsec_mode; } /* ================================================== */ char * CNF_GetLeapSecTimezone(void) { return leapsec_tz; } /* ================================================== */ int CNF_GetSchedPriority(void) { return sched_priority; } /* ================================================== */ int CNF_GetLockMemory(void) { return lock_memory; } /* ================================================== */ void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only) { *max_freq = smooth_max_freq; *max_wander = smooth_max_wander; *leap_only = smooth_leap_only; } /* ================================================== */ void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2) { *file = tempcomp_sensor_file; *point_file = tempcomp_point_file; *interval = tempcomp_interval; *T0 = tempcomp_T0; *k0 = tempcomp_k0; *k1 = tempcomp_k1; *k2 = tempcomp_k2; } /* ================================================== */ char * CNF_GetUser(void) { return user; } /* ================================================== */ int CNF_GetMaxSamples(void) { return max_samples; } /* ================================================== */ int CNF_GetMinSamples(void) { return min_samples; } /* ================================================== */ int CNF_GetMinSources(void) { return min_sources; } /* ================================================== */ char * CNF_GetHwclockFile(void) { return hwclock_file; } /* ================================================== */ int CNF_GetInitSources(void) { return ARR_GetSize(init_sources); } /* ================================================== */ double CNF_GetInitStepThreshold(void) { return init_slew_threshold; } chrony-2.1.1/conf.h0000644000000000000000000001004512542263351012614 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2013-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for configuration module */ #ifndef GOT_CONF_H #define GOT_CONF_H #include "addressing.h" #include "reference.h" extern void CNF_Initialise(int restarted); extern void CNF_Finalise(void); extern char *CNF_GetRtcDevice(void); extern void CNF_ReadFile(const char *filename); extern void CNF_ParseLine(const char *filename, int number, char *line); extern void CNF_AddInitSources(void); extern void CNF_AddSources(void); extern void CNF_AddBroadcasts(void); extern void CNF_AddRefclocks(void); extern int CNF_GetAcquisitionPort(void); extern int CNF_GetNTPPort(void); extern char *CNF_GetDriftFile(void); extern char *CNF_GetLogDir(void); extern char *CNF_GetDumpDir(void); extern int CNF_GetLogBanner(void); extern int CNF_GetLogMeasurements(void); extern int CNF_GetLogStatistics(void); extern int CNF_GetLogTracking(void); extern int CNF_GetLogRtc(void); extern int CNF_GetLogRefclocks(void); extern int CNF_GetLogTempComp(void); extern char *CNF_GetKeysFile(void); extern char *CNF_GetRtcFile(void); extern uint32_t CNF_GetCommandKey(void); extern int CNF_GetGenerateCommandKey(void); extern int CNF_GetDumpOnExit(void); extern int CNF_GetManualEnabled(void); extern int CNF_GetCommandPort(void); extern int CNF_GetRtcOnUtc(void); extern int CNF_GetRtcSync(void); extern void CNF_GetMakeStep(int *limit, double *threshold); extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset); extern void CNF_GetLogChange(int *enabled, double *threshold); extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user); extern int CNF_GetNoClientLog(void); extern unsigned long CNF_GetClientLogLimit(void); extern void CNF_GetFallbackDrifts(int *min, int *max); extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern char *CNF_GetPidFile(void); extern REF_LeapMode CNF_GetLeapSecMode(void); extern char *CNF_GetLeapSecTimezone(void); /* Value returned in ppm, as read from file */ extern double CNF_GetMaxUpdateSkew(void); extern double CNF_GetMaxClockError(void); extern double CNF_GetCorrectionTimeRatio(void); extern double CNF_GetMaxSlewRate(void); extern double CNF_GetReselectDistance(void); extern double CNF_GetStratumWeight(void); extern double CNF_GetCombineLimit(void); extern int CNF_AllowLocalReference(int *stratum); extern void CNF_SetupAccessRestrictions(void); extern int CNF_GetSchedPriority(void); extern int CNF_GetLockMemory(void); extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only); extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2); extern char *CNF_GetUser(void); extern int CNF_GetMaxSamples(void); extern int CNF_GetMinSamples(void); extern int CNF_GetMinSources(void); extern double CNF_GetRtcAutotrim(void); extern char *CNF_GetHwclockFile(void); extern int CNF_GetInitSources(void); extern double CNF_GetInitStepThreshold(void); #endif /* GOT_CONF_H */ chrony-2.1.1/configure0000755000000000000000000005267012542263351013437 0ustar rootroot#!/bin/sh # ======================================================================= # # chronyd/chronyc - Programs for keeping computer clocks accurate. # # Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Miroslav Lichvar 2009, 2012-2014 # # ======================================================================= # 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 MYCPPFLAGS="${CPPFLAGS}" if [ "x${MYCC}" = "xgcc" ]; then MYCFLAGS="${MYCFLAGS} -Wmissing-prototypes -Wall" fi MYLDFLAGS="${LDFLAGS}" # ====================================================================== # FUNCTIONS #{{{ test_code test_code () { name=$1 headers=$2 cflags=$3 ldflags=$4 code=$5 echo -n "Checking for $name : " ( for h in $headers; do echo "#include <$h>" done echo "int main(int argc, char **argv) {" echo "$code" echo "return 0; }" ) > docheck.c echo "docheck.c:" >> config.log cat docheck.c >> config.log echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ $MYLDFLAGS >> config.log $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ $MYLDFLAGS >> config.log 2>&1 if [ $? -eq 0 ] then echo "Yes" result=0 else echo "No" result=1 fi rm -f docheck.c docheck echo >> config.log return $result } #}}} #{{{ usage usage () { cat < if you have headers in a nonstandard directory LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory Use these variables to override the choices made by \`configure' or to help it to find libraries and programs with nonstandard names/locations. EOF } #}}} #{{{ add_def () { if [ "x$2" = "x" ]; then echo "#define $1 1" >> config.h else echo "#define $1 $2" >> config.h fi } #}}} #{{{ pkg_config pkg_config () { type pkg-config > /dev/null 2> /dev/null || return 1 pkg-config $@ 2> /dev/null } #}}} #{{{ get_features get_features () { ff=1 for f; do if [ "$ff" = "0" ]; then echo -n " " fi if grep "define FEAT_$f" config.h > /dev/null; then echo -n "+$f" else echo -n "-$f" fi ff=0 done } #}}} # ====================================================================== OPERATINGSYSTEM=`uname -s` VERSION=`uname -r` MACHINE=`uname -m` EXTRA_LIBS="" EXTRA_CLI_LIBS="" EXTRA_OBJECTS="" EXTRA_DEFS="" SYSDEFS="" feat_debug=0 feat_cmdmon=1 feat_ntp=1 feat_refclock=1 feat_readline=1 try_readline=1 try_editline=1 feat_sechash=1 try_nss=1 try_tomcrypt=1 feat_rtc=1 try_rtc=0 feat_droproot=1 try_libcap=0 readline_lib="" readline_inc="" ncurses_lib="" feat_ipv6=1 feat_phc=1 try_phc=0 feat_pps=1 try_setsched=0 try_lockmem=0 feat_asyncdns=1 feat_forcednsretry=1 ntp_era_split="" default_user="root" mail_program="/usr/lib/sendmail" for option do case "$option" in --enable-debug ) feat_debug=1 ;; --disable-readline ) feat_readline=0 ;; --without-readline ) try_readline=0 ;; --without-editline ) try_editline=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/^.*=//;'` ;; --prefix=* | --install_prefix=* ) SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'` ;; --exec-prefix=* ) SETEPREFIX=`echo $option | sed -e 's/[^=]*=//;'` ;; --sysconfdir=* ) SETSYSCONFDIR=`echo $option | sed -e 's/^.*=//;'` ;; --bindir=* ) SETBINDIR=`echo $option | sed -e 's/^.*=//;'` ;; --sbindir=* ) SETSBINDIR=`echo $option | sed -e 's/^.*=//;'` ;; --datarootdir=* ) SETDATAROOTDIR=`echo $option | sed -e 's/^.*=//;'` ;; --infodir=* ) SETINFODIR=`echo $option | sed -e 's/^.*=//;'` ;; --mandir=* ) SETMANDIR=`echo $option | sed -e 's/^.*=//;'` ;; --docdir=* ) SETDOCDIR=`echo $option | sed -e 's/^.*=//;'` ;; --localstatedir=* ) SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'` ;; --chronyvardir=* ) SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'` ;; --disable-cmdmon) feat_cmdmon=0 ;; --disable-ntp) feat_ntp=0 ;; --disable-refclock) feat_refclock=0 ;; --disable-rtc) feat_rtc=0 ;; --disable-ipv6) feat_ipv6=0 ;; --disable-phc) feat_phc=0 ;; --disable-pps) feat_pps=0 ;; --disable-linuxcaps) feat_droproot=0 ;; --disable-asyncdns) feat_asyncdns=0 ;; --disable-forcednsretry) feat_forcednsretry=0 ;; --with-ntp-era=* ) ntp_era_split=`echo $option | sed -e 's/^.*=//;'` ;; --with-user=* ) default_user=`echo $option | sed -e 's/^.*=//;'` ;; --with-sendmail=* ) mail_program=`echo $option | sed -e 's/^.*=//;'` ;; --disable-sechash ) feat_sechash=0 ;; --without-nss ) try_nss=0 ;; --without-tomcrypt ) try_tomcrypt=0 ;; --host-system=* ) OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'` ;; --host-release=* ) VERSION=`echo $option | sed -e 's/^.*=//;'` ;; --host-machine=* ) MACHINE=`echo $option | sed -e 's/^.*=//;'` ;; --help | -h ) usage exit 0 ;; * ) echo "Unrecognized option : " $option esac done rm -f config.h config.log SYSTEM=${OPERATINGSYSTEM}-${MACHINE} case $SYSTEM in SunOS-sun4* ) case $VERSION in 4.* ) EXTRA_OBJECTS="sys_sunos.o strerror.o" EXTRA_LIBS="-lkvm" add_def SUNOS echo "Configuring for SunOS (" $SYSTEM "version" $VERSION ")" ;; 5.* ) EXTRA_OBJECTS="sys_solaris.o" EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf" EXTRA_CLI_LIBS="-lsocket -lnsl" add_def SOLARIS echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" ;; esac ;; Linux* ) EXTRA_OBJECTS="sys_generic.o sys_linux.o wrap_adjtimex.o" try_libcap=1 try_rtc=1 try_setsched=1 try_lockmem=1 try_phc=1 add_def LINUX echo "Configuring for " $SYSTEM if [ "${MACHINE}" = "alpha" ]; then echo "Enabling -mieee" # FIXME: Should really test for GCC MYCFLAGS="$MYCFLAGS -mieee" fi ;; BSD/386-i[3456]86|FreeBSD-i386|FreeBSD-amd64 ) # Antti Jrvinen reported that this system can # be supported with the SunOS 4.x driver files. EXTRA_OBJECTS="sys_sunos.o strerror.o" EXTRA_LIBS="-lkvm" add_def SUNOS echo "Configuring for $SYSTEM (using SunOS driver)" ;; NetBSD-* ) EXTRA_OBJECTS="sys_netbsd.o" EXTRA_LIBS="-lkvm" SYSDEFS="" echo "Configuring for $SYSTEM" ;; Darwin-* ) EXTRA_OBJECTS="sys_macosx.o" EXTRA_LIBS="-lresolv" EXTRA_CLI_LIBS="-lresolv" add_def MACOSX echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")" ;; SunOS-i86pc* ) # Doug Woodward 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" add_def SOLARIS echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" ;; CYGWIN32_NT-i[3456]86 ) EXTRA_OBJECTS="sys_winnt.o" EXTRA_LIBS="" add_def WINNT echo "Configuring for Windows NT (Cygwin32)" ;; * ) echo "Sorry, I don't know how to build this software on your system." exit 1 ;; esac if [ $feat_debug = "1" ]; then add_def FEAT_DEBUG fi add_def DEBUG $feat_debug if [ $feat_cmdmon = "1" ]; then add_def FEAT_CMDMON EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o" fi if [ $feat_ntp = "1" ]; then add_def FEAT_NTP EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o" else feat_asyncdns=0 fi if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o" else feat_ipv6=0 fi if [ $feat_refclock = "1" ]; then add_def FEAT_REFCLOCK EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o" fi if test_code '64-bit time_t' 'time.h' '' '' ' char x[sizeof(time_t) > 4 ? 1 : -1] = {0}; return x[0];' then add_def HAVE_LONG_TIME_T 1 if [ "x$ntp_era_split" != "x" ]; then split_seconds=$ntp_era_split split_days=0 else split_seconds=`date '+%s'` if [ "x$split_seconds" = "" ]; then echo "Could not get current time, --with-ntp-era option is needed" exit 1 fi split_days=$((50 * 365)) fi add_def NTP_ERA_SPLIT "(${split_seconds}LL - $split_days * 24 * 3600)" date_format='+%Y-%m-%dT%H:%M:%SZ' # Print the full NTP interval if a suitable date is found if [ "x`date -u -d '1970-01-01 UTC 9 days ago 5 seconds 3 seconds' \ $date_format 2> /dev/null`" = "x1969-12-23T00:00:08Z" ] then time1="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds" \ $date_format`" time2="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds 4294967296 seconds" \ $date_format`" echo "NTP time mapped to $time1/$time2" fi fi MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));' if test_code 'math' 'math.h' '' '' "$MATHCODE"; then LIBS="" else if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then LIBS="-lm" else echo "Can't compile/link a program which uses sqrt(), log(), pow(), bailing out" exit 1 fi fi if test_code '' 'stdint.h' '' '' ''; then add_def HAVE_STDINT_H fi if test_code '' 'inttypes.h' '' '' ''; then add_def HAVE_INTTYPES_H fi if [ $feat_ipv6 = "1" ] && \ test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' '' ' struct sockaddr_in6 n; char p[100]; n.sin6_addr = in6addr_any; return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' then add_def FEAT_IPV6 if test_code 'in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' ' return sizeof(struct in6_pktinfo);' then add_def HAVE_IN6_PKTINFO else if test_code 'in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \ '-D_GNU_SOURCE' '' 'return sizeof(struct in6_pktinfo);' then add_def _GNU_SOURCE add_def HAVE_IN6_PKTINFO fi fi fi if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \ 'return getaddrinfo(0, 0, 0, 0);' then add_def HAVE_GETADDRINFO fi if [ $feat_asyncdns = "1" ] && \ test_code 'pthread' 'pthread.h' '-pthread' '' \ 'return pthread_create((void *)1, NULL, (void *)1, NULL);' then add_def FEAT_ASYNCDNS add_def USE_PTHREAD_ASYNCDNS EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o" MYCFLAGS="$MYCFLAGS -pthread" fi timepps_h="" if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then if test_code '' 'sys/timepps.h' '' '' ''; then timepps_h="sys/timepps.h" add_def HAVE_SYS_TIMEPPS_H else if test_code '' 'timepps.h' '' '' ''; then timepps_h="timepps.h" add_def HAVE_TIMEPPS_H fi fi fi if [ "x$timepps_h" != "x" ] && \ test_code 'PPSAPI' "string.h $timepps_h" '' '' ' pps_handle_t h = 0; pps_info_t i; struct timespec ts; return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);' then add_def FEAT_PPS fi if [ $feat_droproot = "1" ] && [ $try_libcap = "1" ] && \ test_code \ libcap \ 'sys/types.h pwd.h sys/prctl.h sys/capability.h grp.h' \ '' '-lcap' \ 'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));' then add_def FEAT_PRIVDROP EXTRA_LIBS="$EXTRA_LIBS -lcap" fi if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \ test_code '' 'sys/ioctl.h linux/rtc.h' '' '' \ 'ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);' then EXTRA_OBJECTS="$EXTRA_OBJECTS rtc_linux.o" add_def FEAT_RTC fi if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \ test_code '' 'sys/ioctl.h linux/ptp_clock.h' '' '' \ 'ioctl(1, PTP_CLOCK_GETCAPS, 0);' then if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then add_def FEAT_PHC else if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \ 'clock_gettime(0, NULL);' then EXTRA_LIBS="$EXTRA_LIBS -lrt" add_def FEAT_PHC fi fi fi if [ $try_setsched = "1" ] && \ test_code \ 'sched_setscheduler()' \ 'sched.h' '' '' ' struct sched_param sched; sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &sched);' then add_def HAVE_SCHED_SETSCHEDULER fi if [ $try_lockmem = "1" ] && \ test_code \ 'mlockall()' \ 'sys/mman.h sys/resource.h' '' '' ' struct rlimit rlim; setrlimit(RLIMIT_MEMLOCK, &rlim); mlockall(MCL_CURRENT|MCL_FUTURE);' then add_def HAVE_MLOCKALL fi if [ $feat_forcednsretry = "1" ] then add_def FORCE_DNSRETRY fi READLINE_COMPILE="" READLINE_LINK="" if [ $feat_readline = "1" ]; then if [ $try_editline = "1" ]; then if test_code editline 'stdio.h editline/readline.h' \ "$readline_inc" "$readline_lib -ledit" \ 'add_history(readline("prompt"));' then add_def FEAT_READLINE add_def USE_EDITLINE READLINE_COMPILE="$readline_inc" READLINE_LINK="$readline_lib -ledit" fi fi if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then if test_code readline 'stdio.h readline/readline.h readline/history.h' \ "$readline_inc" "$readline_lib -lreadline" \ 'add_history(readline("prompt"));' then add_def FEAT_READLINE READLINE_COMPILE="$readline_inc" READLINE_LINK="$readline_lib -lreadline" fi fi if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then if test_code 'readline with -lncurses' \ 'stdio.h readline/readline.h readline/history.h' \ "$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \ 'add_history(readline("prompt"));' then add_def FEAT_READLINE READLINE_COMPILE="$readline_inc" READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses" fi fi EXTRA_CLI_LIBS="$EXTRA_CLI_LIBS $READLINE_LINK" fi HASH_OBJ="hash_intmd5.o" HASH_COMPILE="" HASH_LINK="" if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then test_cflags="`pkg_config --cflags nss`" test_link="`pkg_config --libs-only-L nss` -lfreebl3" if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ "$test_cflags" "$test_link" \ 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' then HASH_OBJ="hash_nss.o" HASH_COMPILE="$test_cflags" HASH_LINK="$test_link" LIBS="$LIBS $HASH_LINK" add_def FEAT_SECHASH fi fi if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \ 'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);' then HASH_OBJ="hash_tomcrypt.o" HASH_COMPILE="-I/usr/include/tomcrypt" HASH_LINK="-ltomcrypt" LIBS="$LIBS $HASH_LINK" add_def FEAT_SECHASH fi fi SYSCONFDIR=/etc if [ "x$SETSYSCONFDIR" != "x" ]; then SYSCONFDIR=$SETSYSCONFDIR fi PREFIX=/usr/local if [ "x$SETPREFIX" != "x" ]; then PREFIX=$SETPREFIX fi EPREFIX=${PREFIX} if [ "x$SETEPREFIX" != "x" ]; then EPREFIX=$SETEPREFIX fi BINDIR=${EPREFIX}/bin if [ "x$SETBINDIR" != "x" ]; then BINDIR=$SETBINDIR fi SBINDIR=${EPREFIX}/sbin if [ "x$SETSBINDIR" != "x" ]; then SBINDIR=$SETSBINDIR fi DATAROOTDIR=${PREFIX}/share if [ "x$SETDATAROOTDIR" != "x" ]; then DATAROOTDIR=$SETDATAROOTDIR fi INFODIR=${DATAROOTDIR}/info if [ "x$SETINFODIR" != "x" ]; then INFODIR=$SETINFODIR fi MANDIR=${DATAROOTDIR}/man if [ "x$SETMANDIR" != "x" ]; then MANDIR=$SETMANDIR fi DOCDIR=${DATAROOTDIR}/doc/chrony if [ "x$SETDOCDIR" != "x" ]; then DOCDIR=$SETDOCDIR fi LOCALSTATEDIR=/var if [ "x$SETLOCALSTATEDIR" != "x" ]; then LOCALSTATEDIR=$SETLOCALSTATEDIR fi CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony if [ "x$SETCHRONYVARDIR" != "x" ]; then CHRONYVARDIR=$SETCHRONYVARDIR fi add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\"" add_def DEFAULT_USER "\"$default_user\"" add_def MAIL_PROGRAM "\"$mail_program\"" common_features="`get_features ASYNCDNS IPV6 SECHASH`" chronyc_features="`get_features READLINE`" chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP DEBUG`" add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\"" add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\"" echo "Features : $chronyd_features $chronyc_features $common_features" if [ -f version.txt ]; then add_def CHRONY_VERSION "\"`cat version.txt`\"" else add_def CHRONY_VERSION "\"DEVELOPMENT\"" fi for f in Makefile chrony.conf.5 chrony.texi chronyc.1 chronyd.8 do echo Creating $f sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\ s%@CC@%${MYCC}%;\ s%@CFLAGS@%${MYCFLAGS}%;\ s%@CPPFLAGS@%${MYCPPFLAGS}%;\ s%@LIBS@%${LIBS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\ s%@HASH_OBJ@%${HASH_OBJ}%;\ s%@HASH_COMPILE@%${HASH_COMPILE}%;\ s%@SYSCONFDIR@%${SYSCONFDIR}%;\ s%@BINDIR@%${BINDIR}%;\ s%@SBINDIR@%${SBINDIR}%;\ s%@DOCDIR@%${DOCDIR}%;\ s%@MANDIR@%${MANDIR}%;\ s%@INFODIR@%${INFODIR}%;\ s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\ s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\ s%@DEFAULT_USER@%${default_user}%;"\ < ${f}.in > $f done # ======================================================================= # vim:et:sw=2:ht=2:sts=2:fdm=marker:cms=#%s chrony-2.1.1/contrib/0000755000000000000000000000000012542263351013156 5ustar rootrootchrony-2.1.1/contrib/andrew_bishop_10000644000000000000000000001333012542263351016145 0ustar rootrootFrom 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 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=_+4F9R 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<9X5-C:IL9J:LYR^V2BP) M3>J*HJS`@Z],IG2Q*NJ*M0(SJ!1[TK-4FA7RL(&-DN;H MC5&%*>'/#&:K"XC'EH%".O>6"42,GBJ=G>\@F_*%B>S"1DJ7RSIKK0*-25Q< M0.*RN""P(@1 M:(;4:ASM37EK(Z.V;JU6.Y`TVH',VQRKF<-9FY.:FG0:DT+P!MA6^J-(J`O0 M%*4E[<54HO@[T,/E69&:_: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=BXAOL34[#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@J M)`]Q!A.\<[!':>,8``27$HH-\L)HN=HQ5;0#9XK_=ICY=`B.;Q8Z]::5Y.+J MXFQVG*]SS+QUT&H%D+I6V?C34T'7$+;X8&!%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=U2IN(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\#^->&'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%`8J1 M,(`R]TJ,'W107WT5L,G$JQ=T7/WQATKN5C\C7Q"DX`D/U2;3L#_MEDQ%I2%S')8QJX&",HVX+B<"F:0K]!%% M#ZFTB*`9W\G&=S+,HL=UX.+Y=BFH,M)G]%REC$(_6=9OB9JYAT?O@Q#,'M$RRYJ%I-BP6S*-*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-/7Q8\UM$)7OR)B"3DA>$*A ME/K-+N4-C6_6#H8>3#@,?655:YI6*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,`PQ[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\*!.B<=/5A>V1-ODQT9^ MRD?F?6#?+!`9287V2!]E%-8(C&:F'C7XG[`O,4-@*_4%2.R3V;>9K@:K0[N` MJ2^WQNG9TX>EHC=?!^_^1H1MSAXLNKBT=/TDW23^@N'L*06S;,KB.^G+ZA M9+VS]`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/ chrony-2.1.1/contrib/andrew_bishop_20000644000000000000000000000447712542263351016162 0ustar rootrootFrom 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 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" 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/ chrony-2.1.1/contrib/erik_bryer_10000644000000000000000000000275712542263351015471 0ustar rootroot#!/bin/sh # # chrony Start time synchronization. This script # starts chronyd. # # Hacked by: Erik Bryer 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 chrony-2.1.1/contrib/ken_gillett_10000644000000000000000000000363312542263351015627 0ustar rootroot#!/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 chrony-2.1.1/contrib/stephan_boettcher_10000644000000000000000000000720312542263351017024 0ustar rootrootFrom stephan@nevis1.nevis.columbia.edu Mon Jun 7 20:51:57 1999 Date: 04 Jun 1999 00:17:25 -0400 From: Stephan I. Boettcher 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 # # 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 chrony-2.1.1/contrib/wolfgang_weisselberg10000644000000000000000000000764312542263351017373 0ustar rootroot > 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 = ; 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"; } ############################################################ chrony-2.1.1/examples/0000755000000000000000000000000012542263372013337 5ustar rootrootchrony-2.1.1/examples/chrony-wait.service0000644000000000000000000000064312542263351017165 0ustar rootroot[Unit] Description=Wait for chrony to synchronize system clock After=chronyd.service Requires=chronyd.service Before=time-sync.target Wants=time-sync.target [Service] Type=oneshot # Wait up to ~10 minutes for chronyd to synchronize and the remaining # clock correction to be less than 0.1 seconds ExecStart=/usr/bin/chronyc waitsync 60 0.1 RemainAfterExit=yes StandardOutput=null [Install] WantedBy=multi-user.target chrony-2.1.1/examples/chrony.conf.example10000644000000000000000000000056512542263351017226 0ustar rootroot# Use public NTP servers from the pool.ntp.org project. pool pool.ntp.org iburst # Record the rate at which the system clock gains/losses time. driftfile /var/lib/chrony/drift # In first three updates step the system clock instead of slew # if the adjustment is larger than 10 seconds. makestep 10 3 # Enable kernel synchronization of the real-time clock (RTC). rtcsync chrony-2.1.1/examples/chrony.conf.example20000644000000000000000000000207312542263351017223 0ustar rootroot# Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). pool pool.ntp.org iburst # Record the rate at which the system clock gains/losses time. driftfile /var/lib/chrony/drift # In first three updates step the system clock instead of slew # if the adjustment is larger than 10 seconds. makestep 10 3 # Enable kernel synchronization of the real-time clock (RTC). rtcsync # Allow NTP client access from local network. #allow 192.168/16 # Serve time even if not synchronized to any NTP server. #local stratum 10 # Specify file containing keys for NTP and command authentication. keyfile /etc/chrony.keys # Specify key number for command authentication. commandkey 1 # Generate new command key on start if missing. generatecommandkey # Disable logging of client accesses. noclientlog # Send message to syslog when clock adjustment is larger than 0.5 seconds. logchange 0.5 # Specify directory for log files. logdir /var/log/chrony # Select which information is logged. #log measurements statistics tracking chrony-2.1.1/examples/chrony.conf.example30000644000000000000000000003175412542263351017234 0ustar rootroot####################################################################### # # 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. The more obscure options are not included. 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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://support.ntp.org/bin/view/Servers/WebHome or # you can use servers from the pool.ntp.org project. ! server foo.example.net iburst ! server bar.example.net iburst ! server baz.example.net iburst ! pool pool.ntp.org iburst # 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 foo.example.net offline ! server bar.example.net offline ! server baz.example.net offline ! pool pool.ntp.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 foo.example.net # 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 /var/lib/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 # With this directive a random password will be generated automatically. generatecommandkey # 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/lib/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 useful to quickly correct the clock on start if it's # off by a large amount. 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. The value '1' means the step is allowed only on the first update # of the clock. Some software can get upset if the system clock jumps # (especially backwards), so be careful! ! makestep 10 1 ####################################################################### ### 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 # The clientlog size is limited to 512KB by default. If you have many # clients, especially in many different subnets, you might want to # increase the limit. ! clientloglimit 4194304 ####################################################################### ### 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@foo.example.net 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. # By default chronyd binds to the loopback interface. Uncomment the # following lines to allow receiving command packets from remote hosts. ! bindcmdaddress 0.0.0.0 ! bindcmdaddress :: # 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. ####################################################################### ### 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 /var/lib/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 ####################################################################### ### REAL TIME SCHEDULER # This directive tells chronyd to use the real-time FIFO scheduler with the # specified priority (which must be between 0 and 100). This should result # in reduced latency. You don't need it unless you really have a requirement # for extreme clock stability. Works only on Linux. Note that the "-P" # command-line switch will override this. ! sched_priority 1 ####################################################################### ### LOCKING CHRONYD INTO RAM # This directive tells chronyd to use the mlockall() syscall to lock itself # into RAM so that it will never be paged out. This should result in reduced # latency. You don't need it unless you really have a requirement # for extreme clock stability. Works only on Linux. Note that the "-m" # command-line switch will also enable this feature. ! lock_all chrony-2.1.1/examples/chrony.keys.example0000644000000000000000000000246412542263351017173 0ustar rootroot####################################################################### # # 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. It should be readable # only by root or the user chronyd drops the root privileges to. 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 # ###################################################################### # Examples of valid keys: #1 ALongAndRandomPassword #2 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC #3 SHA1 HEX:1DC764E0791B11FA67EFC7ECBC4B0D73F68A070C # The keys should be random for maximum security. If you wanted to use a key # with ID 1 as your commandkey (i.e. chronyc password) you would put # "commandkey 1" into chrony.conf. If no commandkey is present in the keys # file and the generatecommandkey directive is specified in chrony.conf, # a random commandkey will be generated and added to the keys file # automatically on chronyd start. # You might want to define more keys if you use the authentication facility # in the network time protocol to authenticate request/response packets between # trusted clients and servers. chrony-2.1.1/examples/chrony.logrotate0000644000000000000000000000024312542263351016557 0ustar rootroot/var/log/chrony/*.log { missingok nocreate sharedscripts postrotate /usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true endscript } chrony-2.1.1/examples/chrony.nm-dispatcher0000644000000000000000000000067212542263351017323 0ustar rootroot#!/bin/sh # This is a NetworkManager dispatcher script for chronyd to set its NTP sources # online/offline when a default route is configured/removed on the system. export LC_ALL=C if [ "$2" = "up" ]; then /sbin/ip route list dev "$1" | grep -q '^default' && /usr/bin/chronyc -a online > /dev/null 2>&1 fi if [ "$2" = "down" ]; then /sbin/ip route list | grep -q '^default' || /usr/bin/chronyc -a offline > /dev/null 2>&1 fi exit 0 chrony-2.1.1/examples/chrony.spec0000644000000000000000000000304512542263372015517 0ustar rootroot%global chrony_version 2.1.1 %if 0%(echo %{chrony_version} | grep -q pre && echo 1) %global prerelease %(echo %{chrony_version} | sed 's/.*-//') %endif Summary: An NTP client/server Name: chrony Version: %(echo %{chrony_version} | sed 's/-.*//') Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}} Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz License: GPLv2 Group: Applications/Utilities BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n) Requires: info %description chrony is a client and server for the Network Time Protocol (NTP). This program keeps your computer's clock accurate. It was specially designed to support systems with intermittent Internet connections, but it also works well in permanently connected environments. It can also use hardware reference clocks, the system real-time clock, or manual input as time references. %prep %setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}} %build ./configure \ --prefix=%{_prefix} \ --bindir=%{_bindir} \ --sbindir=%{_sbindir} \ --infodir=%{_infodir} \ --mandir=%{_mandir} make make chrony.txt make chrony.info %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT%{_docdir} mkdir -p $RPM_BUILD_ROOT%{_infodir} cp chrony.info* $RPM_BUILD_ROOT%{_infodir} %files %{_sbindir}/chronyd %{_bindir}/chronyc %{_infodir}/chrony.info* %{_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 chrony-2.1.1/examples/chronyd.service0000644000000000000000000000045612542263351016371 0ustar rootroot[Unit] Description=NTP client/server After=ntpdate.service sntp.service ntpd.service Conflicts=ntpd.service systemd-timesyncd.service [Service] Type=forking PIDFile=/var/run/chronyd.pid EnvironmentFile=-/etc/sysconfig/chronyd ExecStart=/usr/sbin/chronyd $OPTIONS [Install] WantedBy=multi-user.target chrony-2.1.1/getdate.c0000644000000000000000000023234312542263351013306 0ustar rootroot/* A Bison parser, made by GNU Bison 2.7. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2012 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 3 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, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.7" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Copy the first part of user declarations. */ /* Line 371 of yacc.c */ #line 1 "getdate.y" /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990. ** ** This code is in the public domain and has no copyright. */ #ifdef HAVE_CONFIG_H # include # ifdef HAVE_ALLOCA_H # include # endif #endif /* Since the code of getdate.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if the code were included in the Emacs executable, it probably wouldn't do any harm to #undef it here; this will only cause problems if we try to write to a static variable, which I don't think this code needs to do. */ #ifdef emacs # undef static #endif #include #include #if HAVE_STDLIB_H # include /* for `free'; used by Bison 1.27 */ #endif #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) # define IN_CTYPE_DOMAIN(c) 1 #else # define IN_CTYPE_DOMAIN(c) isascii(c) #endif #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) /* ISDIGIT differs from ISDIGIT_LOCALE, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless it's important to use the locale's definition of `digit' even when the host does not conform to Posix. */ #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #if defined (STDC_HEADERS) || defined (USG) # include #endif #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) # define __attribute__(x) #endif #ifndef ATTRIBUTE_UNUSED # define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif /* Some old versions of bison generate parsers that use bcopy. That loses on systems that don't provide the function, so we have to redefine it here. */ #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) # define bcopy(from, to, len) memcpy ((to), (from), (len)) #endif /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), as well as gratuitiously global symbol names, so we can have multiple yacc generated parsers in the same program. Note that these are only the variables produced by yacc. If other parser generators (bison, byacc, etc) produce additional global names that conflict at link time, then those parser generators need to be fixed instead of adding those names to this list. */ #define yymaxdepth gd_maxdepth #define yyparse gd_parse #define yylex gd_lex #define yyerror gd_error #define yylval gd_lval #define yychar gd_char #define yydebug gd_debug #define yypact gd_pact #define yyr1 gd_r1 #define yyr2 gd_r2 #define yydef gd_def #define yychk gd_chk #define yypgo gd_pgo #define yyact gd_act #define yyexca gd_exca #define yyerrflag gd_errflag #define yynerrs gd_nerrs #define yyps gd_ps #define yypv gd_pv #define yys gd_s #define yy_yys gd_yys #define yystate gd_state #define yytmp gd_tmp #define yyv gd_v #define yy_yyv gd_yyv #define yyval gd_val #define yylloc gd_lloc #define yyreds gd_reds /* With YYDEBUG defined */ #define yytoks gd_toks /* With YYDEBUG defined */ #define yylhs gd_yylhs #define yylen gd_yylen #define yydefred gd_yydefred #define yydgoto gd_yydgoto #define yysindex gd_yysindex #define yyrindex gd_yyrindex #define yygindex gd_yygindex #define yytable gd_yytable #define yycheck gd_yycheck static int yylex (void); static int yyerror (char *s); #define EPOCH 1970 #define HOUR(x) ((x) * 60) #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { const char *name; int type; int value; } TABLE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of these by using a good ** union as the yacc stack. (This routine was originally written before ** yacc had the %union construct.) Maybe someday; right now we only use ** the %union very rarely. */ static const char *yyInput; static int yyDayOrdinal; static int yyDayNumber; static int yyHaveDate; static int yyHaveDay; static int yyHaveRel; static int yyHaveTime; static int yyHaveZone; static int yyTimezone; static int yyDay; static int yyHour; static int yyMinutes; static int yyMonth; static int yySeconds; static int yyYear; static MERIDIAN yyMeridian; static int yyRelDay; static int yyRelHour; static int yyRelMinutes; static int yyRelMonth; static int yyRelSeconds; static int yyRelYear; /* Line 371 of yacc.c */ #line 246 "getdate.c" # ifndef YY_NULL # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULL nullptr # else # define YY_NULL 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { tAGO = 258, tDAY = 259, tDAY_UNIT = 260, tDAYZONE = 261, tDST = 262, tHOUR_UNIT = 263, tID = 264, tMERIDIAN = 265, tMINUTE_UNIT = 266, tMONTH = 267, tMONTH_UNIT = 268, tSEC_UNIT = 269, tSNUMBER = 270, tUNUMBER = 271, tYEAR_UNIT = 272, tZONE = 273 }; #endif #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 387 of yacc.c */ #line 182 "getdate.y" int Number; enum _MERIDIAN Meridian; /* Line 387 of yacc.c */ #line 310 "getdate.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* Copy the second part of user declarations. */ /* Line 390 of yacc.c */ #line 338 "getdate.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(N) (N) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (YYID (0)) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 50 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 22 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 11 /* YYNRULES -- Number of rules. */ #define YYNRULES 51 /* YYNRULES -- Number of states. */ #define YYNSTATES 61 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 273 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 4, 7, 9, 11, 13, 15, 17, 19, 22, 27, 32, 39, 46, 48, 50, 53, 55, 58, 61, 65, 71, 75, 79, 82, 87, 90, 94, 97, 99, 102, 105, 107, 110, 113, 115, 118, 121, 123, 126, 129, 131, 134, 137, 139, 142, 145, 147, 149, 150 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 23, 0, -1, -1, 23, 24, -1, 25, -1, 26, -1, 28, -1, 27, -1, 29, -1, 31, -1, 16, 10, -1, 16, 19, 16, 32, -1, 16, 19, 16, 15, -1, 16, 19, 16, 19, 16, 32, -1, 16, 19, 16, 19, 16, 15, -1, 18, -1, 6, -1, 18, 7, -1, 4, -1, 4, 20, -1, 16, 4, -1, 16, 21, 16, -1, 16, 21, 16, 21, 16, -1, 16, 15, 15, -1, 16, 12, 15, -1, 12, 16, -1, 12, 16, 20, 16, -1, 16, 12, -1, 16, 12, 16, -1, 30, 3, -1, 30, -1, 16, 17, -1, 15, 17, -1, 17, -1, 16, 13, -1, 15, 13, -1, 13, -1, 16, 5, -1, 15, 5, -1, 5, -1, 16, 8, -1, 15, 8, -1, 8, -1, 16, 11, -1, 15, 11, -1, 11, -1, 16, 14, -1, 15, 14, -1, 14, -1, 16, -1, -1, 10, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 198, 198, 199, 202, 205, 208, 211, 214, 217, 220, 226, 232, 241, 247, 259, 262, 266, 271, 275, 279, 285, 289, 307, 313, 319, 323, 328, 332, 339, 347, 350, 353, 356, 359, 362, 365, 368, 371, 374, 377, 380, 383, 386, 389, 392, 395, 398, 401, 406, 440, 443 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "tAGO", "tDAY", "tDAY_UNIT", "tDAYZONE", "tDST", "tHOUR_UNIT", "tID", "tMERIDIAN", "tMINUTE_UNIT", "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tYEAR_UNIT", "tZONE", "':'", "','", "'/'", "$accept", "spec", "item", "time", "zone", "day", "date", "rel", "relunit", "number", "o_merid", YY_NULL }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 58, 44, 47 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 22, 23, 23, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 32, 32 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, 4, 4, 6, 6, 1, 1, 2, 1, 2, 2, 3, 5, 3, 3, 2, 4, 2, 3, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, 0, 1 }; /* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 2, 0, 1, 18, 39, 16, 42, 45, 0, 36, 48, 0, 49, 33, 15, 3, 4, 5, 7, 6, 8, 30, 9, 19, 25, 38, 41, 44, 35, 47, 32, 20, 37, 40, 10, 43, 27, 34, 46, 0, 31, 0, 0, 17, 29, 0, 24, 28, 23, 50, 21, 26, 51, 12, 0, 11, 0, 50, 22, 14, 13 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 1, 15, 16, 17, 18, 19, 20, 21, 22, 55 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -20 static const yytype_int8 yypact[] = { -20, 0, -20, -19, -20, -20, -20, -20, -13, -20, -20, 30, 15, -20, 14, -20, -20, -20, -20, -20, -20, 19, -20, -20, 4, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -6, -20, -20, 16, -20, 17, 23, -20, -20, 24, -20, -20, -20, 27, 28, -20, -20, -20, 29, -20, 32, -8, -20, -20, -20 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -7 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { 2, 23, 52, 24, 3, 4, 5, 59, 6, 46, 47, 7, 8, 9, 10, 11, 12, 13, 14, 31, 32, 43, 44, 33, 45, 34, 35, 36, 37, 38, 39, 48, 40, 49, 41, 25, 42, 52, 26, 50, 51, 27, 53, 28, 29, 57, 54, 30, 58, 56, 60 }; #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-20))) #define yytable_value_is_error(Yytable_value) \ YYID (0) static const yytype_uint8 yycheck[] = { 0, 20, 10, 16, 4, 5, 6, 15, 8, 15, 16, 11, 12, 13, 14, 15, 16, 17, 18, 4, 5, 7, 3, 8, 20, 10, 11, 12, 13, 14, 15, 15, 17, 16, 19, 5, 21, 10, 8, 16, 16, 11, 15, 13, 14, 16, 19, 17, 16, 21, 57 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 23, 0, 4, 5, 6, 8, 11, 12, 13, 14, 15, 16, 17, 18, 24, 25, 26, 27, 28, 29, 30, 31, 20, 16, 5, 8, 11, 13, 14, 17, 4, 5, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 7, 3, 20, 15, 16, 15, 16, 16, 16, 10, 15, 19, 32, 21, 16, 16, 15, 32 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. However, YYFAIL appears to be in use. Nevertheless, it is formally deprecated in Bison 2.4.2's NEWS entry, where a plan to phase it out is discussed. */ #define YYFAIL goto yyerrlab #if defined YYFAIL /* This is here to suppress warnings from the GCC cpp's -Wunused-macros. Normally we don't worry about that warning, but some users do, and we want to make it easy for users to remove YYFAIL uses, which will produce warnings from Bison 2.5. */ #endif #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { FILE *yyo = yyoutput; YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULL; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - Assume YYFAIL is not used. It's too flawed to consider. See for details. YYERROR is fine as it does not invoke this function. - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* The lookahead symbol. */ int yychar; #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif /* The semantic value of the lookahead symbol. */ YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 4: /* Line 1792 of yacc.c */ #line 202 "getdate.y" { yyHaveTime++; } break; case 5: /* Line 1792 of yacc.c */ #line 205 "getdate.y" { yyHaveZone++; } break; case 6: /* Line 1792 of yacc.c */ #line 208 "getdate.y" { yyHaveDate++; } break; case 7: /* Line 1792 of yacc.c */ #line 211 "getdate.y" { yyHaveDay++; } break; case 8: /* Line 1792 of yacc.c */ #line 214 "getdate.y" { yyHaveRel++; } break; case 10: /* Line 1792 of yacc.c */ #line 220 "getdate.y" { yyHour = (yyvsp[(1) - (2)].Number); yyMinutes = 0; yySeconds = 0; yyMeridian = (yyvsp[(2) - (2)].Meridian); } break; case 11: /* Line 1792 of yacc.c */ #line 226 "getdate.y" { yyHour = (yyvsp[(1) - (4)].Number); yyMinutes = (yyvsp[(3) - (4)].Number); yySeconds = 0; yyMeridian = (yyvsp[(4) - (4)].Meridian); } break; case 12: /* Line 1792 of yacc.c */ #line 232 "getdate.y" { yyHour = (yyvsp[(1) - (4)].Number); yyMinutes = (yyvsp[(3) - (4)].Number); yyMeridian = MER24; yyHaveZone++; yyTimezone = ((yyvsp[(4) - (4)].Number) < 0 ? -(yyvsp[(4) - (4)].Number) % 100 + (-(yyvsp[(4) - (4)].Number) / 100) * 60 : - ((yyvsp[(4) - (4)].Number) % 100 + ((yyvsp[(4) - (4)].Number) / 100) * 60)); } break; case 13: /* Line 1792 of yacc.c */ #line 241 "getdate.y" { yyHour = (yyvsp[(1) - (6)].Number); yyMinutes = (yyvsp[(3) - (6)].Number); yySeconds = (yyvsp[(5) - (6)].Number); yyMeridian = (yyvsp[(6) - (6)].Meridian); } break; case 14: /* Line 1792 of yacc.c */ #line 247 "getdate.y" { yyHour = (yyvsp[(1) - (6)].Number); yyMinutes = (yyvsp[(3) - (6)].Number); yySeconds = (yyvsp[(5) - (6)].Number); yyMeridian = MER24; yyHaveZone++; yyTimezone = ((yyvsp[(6) - (6)].Number) < 0 ? -(yyvsp[(6) - (6)].Number) % 100 + (-(yyvsp[(6) - (6)].Number) / 100) * 60 : - ((yyvsp[(6) - (6)].Number) % 100 + ((yyvsp[(6) - (6)].Number) / 100) * 60)); } break; case 15: /* Line 1792 of yacc.c */ #line 259 "getdate.y" { yyTimezone = (yyvsp[(1) - (1)].Number); } break; case 16: /* Line 1792 of yacc.c */ #line 262 "getdate.y" { yyTimezone = (yyvsp[(1) - (1)].Number) - 60; } break; case 17: /* Line 1792 of yacc.c */ #line 266 "getdate.y" { yyTimezone = (yyvsp[(1) - (2)].Number) - 60; } break; case 18: /* Line 1792 of yacc.c */ #line 271 "getdate.y" { yyDayOrdinal = 1; yyDayNumber = (yyvsp[(1) - (1)].Number); } break; case 19: /* Line 1792 of yacc.c */ #line 275 "getdate.y" { yyDayOrdinal = 1; yyDayNumber = (yyvsp[(1) - (2)].Number); } break; case 20: /* Line 1792 of yacc.c */ #line 279 "getdate.y" { yyDayOrdinal = (yyvsp[(1) - (2)].Number); yyDayNumber = (yyvsp[(2) - (2)].Number); } break; case 21: /* Line 1792 of yacc.c */ #line 285 "getdate.y" { yyMonth = (yyvsp[(1) - (3)].Number); yyDay = (yyvsp[(3) - (3)].Number); } break; case 22: /* Line 1792 of yacc.c */ #line 289 "getdate.y" { /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. The goal in recognizing YYYY/MM/DD is solely to support legacy machine-generated dates like those in an RCS log listing. If you want portability, use the ISO 8601 format. */ if ((yyvsp[(1) - (5)].Number) >= 1000) { yyYear = (yyvsp[(1) - (5)].Number); yyMonth = (yyvsp[(3) - (5)].Number); yyDay = (yyvsp[(5) - (5)].Number); } else { yyMonth = (yyvsp[(1) - (5)].Number); yyDay = (yyvsp[(3) - (5)].Number); yyYear = (yyvsp[(5) - (5)].Number); } } break; case 23: /* Line 1792 of yacc.c */ #line 307 "getdate.y" { /* ISO 8601 format. yyyy-mm-dd. */ yyYear = (yyvsp[(1) - (3)].Number); yyMonth = -(yyvsp[(2) - (3)].Number); yyDay = -(yyvsp[(3) - (3)].Number); } break; case 24: /* Line 1792 of yacc.c */ #line 313 "getdate.y" { /* e.g. 17-JUN-1992. */ yyDay = (yyvsp[(1) - (3)].Number); yyMonth = (yyvsp[(2) - (3)].Number); yyYear = -(yyvsp[(3) - (3)].Number); } break; case 25: /* Line 1792 of yacc.c */ #line 319 "getdate.y" { yyMonth = (yyvsp[(1) - (2)].Number); yyDay = (yyvsp[(2) - (2)].Number); } break; case 26: /* Line 1792 of yacc.c */ #line 323 "getdate.y" { yyMonth = (yyvsp[(1) - (4)].Number); yyDay = (yyvsp[(2) - (4)].Number); yyYear = (yyvsp[(4) - (4)].Number); } break; case 27: /* Line 1792 of yacc.c */ #line 328 "getdate.y" { yyMonth = (yyvsp[(2) - (2)].Number); yyDay = (yyvsp[(1) - (2)].Number); } break; case 28: /* Line 1792 of yacc.c */ #line 332 "getdate.y" { yyMonth = (yyvsp[(2) - (3)].Number); yyDay = (yyvsp[(1) - (3)].Number); yyYear = (yyvsp[(3) - (3)].Number); } break; case 29: /* Line 1792 of yacc.c */ #line 339 "getdate.y" { yyRelSeconds = -yyRelSeconds; yyRelMinutes = -yyRelMinutes; yyRelHour = -yyRelHour; yyRelDay = -yyRelDay; yyRelMonth = -yyRelMonth; yyRelYear = -yyRelYear; } break; case 31: /* Line 1792 of yacc.c */ #line 350 "getdate.y" { yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 32: /* Line 1792 of yacc.c */ #line 353 "getdate.y" { yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 33: /* Line 1792 of yacc.c */ #line 356 "getdate.y" { yyRelYear += (yyvsp[(1) - (1)].Number); } break; case 34: /* Line 1792 of yacc.c */ #line 359 "getdate.y" { yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 35: /* Line 1792 of yacc.c */ #line 362 "getdate.y" { yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 36: /* Line 1792 of yacc.c */ #line 365 "getdate.y" { yyRelMonth += (yyvsp[(1) - (1)].Number); } break; case 37: /* Line 1792 of yacc.c */ #line 368 "getdate.y" { yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 38: /* Line 1792 of yacc.c */ #line 371 "getdate.y" { yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 39: /* Line 1792 of yacc.c */ #line 374 "getdate.y" { yyRelDay += (yyvsp[(1) - (1)].Number); } break; case 40: /* Line 1792 of yacc.c */ #line 377 "getdate.y" { yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 41: /* Line 1792 of yacc.c */ #line 380 "getdate.y" { yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 42: /* Line 1792 of yacc.c */ #line 383 "getdate.y" { yyRelHour += (yyvsp[(1) - (1)].Number); } break; case 43: /* Line 1792 of yacc.c */ #line 386 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 44: /* Line 1792 of yacc.c */ #line 389 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 45: /* Line 1792 of yacc.c */ #line 392 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (1)].Number); } break; case 46: /* Line 1792 of yacc.c */ #line 395 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 47: /* Line 1792 of yacc.c */ #line 398 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); } break; case 48: /* Line 1792 of yacc.c */ #line 401 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (1)].Number); } break; case 49: /* Line 1792 of yacc.c */ #line 407 "getdate.y" { if (yyHaveTime && yyHaveDate && !yyHaveRel) yyYear = (yyvsp[(1) - (1)].Number); else { if ((yyvsp[(1) - (1)].Number)>10000) { yyHaveDate++; yyDay= ((yyvsp[(1) - (1)].Number))%100; yyMonth= ((yyvsp[(1) - (1)].Number)/100)%100; yyYear = (yyvsp[(1) - (1)].Number)/10000; } else { yyHaveTime++; if ((yyvsp[(1) - (1)].Number) < 100) { yyHour = (yyvsp[(1) - (1)].Number); yyMinutes = 0; } else { yyHour = (yyvsp[(1) - (1)].Number) / 100; yyMinutes = (yyvsp[(1) - (1)].Number) % 100; } yySeconds = 0; yyMeridian = MER24; } } } break; case 50: /* Line 1792 of yacc.c */ #line 440 "getdate.y" { (yyval.Meridian) = MER24; } break; case 51: /* Line 1792 of yacc.c */ #line 444 "getdate.y" { (yyval.Meridian) = (yyvsp[(1) - (1)].Meridian); } break; /* Line 1792 of yacc.c */ #line 2038 "getdate.c" default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } /* Line 2055 of yacc.c */ #line 449 "getdate.y" /* Include this file down here because bison inserts code above which may define-away `const'. We want the prototype for get_date to have the same signature as the function definition does. */ #include "getdate.h" extern struct tm *gmtime (); extern struct tm *localtime (); extern time_t mktime (); /* Month and day table. */ static TABLE const MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "sept", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, { "sunday", tDAY, 0 }, { "monday", tDAY, 1 }, { "tuesday", tDAY, 2 }, { "tues", tDAY, 2 }, { "wednesday", tDAY, 3 }, { "wednes", tDAY, 3 }, { "thursday", tDAY, 4 }, { "thur", tDAY, 4 }, { "thurs", tDAY, 4 }, { "friday", tDAY, 5 }, { "saturday", tDAY, 6 }, { NULL, 0, 0 } }; /* Time units table. */ static TABLE const UnitsTable[] = { { "year", tYEAR_UNIT, 1 }, { "month", tMONTH_UNIT, 1 }, { "fortnight", tDAY_UNIT, 14 }, { "week", tDAY_UNIT, 7 }, { "day", tDAY_UNIT, 1 }, { "hour", tHOUR_UNIT, 1 }, { "minute", tMINUTE_UNIT, 1 }, { "min", tMINUTE_UNIT, 1 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, { NULL, 0, 0 } }; /* Assorted relative-time words. */ static TABLE const OtherTable[] = { { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, { "today", tMINUTE_UNIT, 0 }, { "now", tMINUTE_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tMINUTE_UNIT, 0 }, { "next", tUNUMBER, 1 }, { "first", tUNUMBER, 1 }, /* { "second", tUNUMBER, 2 }, */ { "third", tUNUMBER, 3 }, { "fourth", tUNUMBER, 4 }, { "fifth", tUNUMBER, 5 }, { "sixth", tUNUMBER, 6 }, { "seventh", tUNUMBER, 7 }, { "eighth", tUNUMBER, 8 }, { "ninth", tUNUMBER, 9 }, { "tenth", tUNUMBER, 10 }, { "eleventh", tUNUMBER, 11 }, { "twelfth", tUNUMBER, 12 }, { "ago", tAGO, 1 }, { NULL, 0, 0 } }; /* The timezone table. */ static TABLE const TimezoneTable[] = { { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ { "utc", tZONE, HOUR ( 0) }, { "wet", tZONE, HOUR ( 0) }, /* Western European */ { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ { "wat", tZONE, HOUR ( 1) }, /* West Africa */ { "at", tZONE, HOUR ( 2) }, /* Azores */ #if 0 /* For completeness. BST is also British Summer, and GST is * also Guam Standard. */ { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ #endif #if 0 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ #endif { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ { "cat", tZONE, HOUR (10) }, /* Central Alaska */ { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ { "nt", tZONE, HOUR (11) }, /* Nome */ { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ { "cet", tZONE, -HOUR (1) }, /* Central European */ { "met", tZONE, -HOUR (1) }, /* Middle European */ { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ { "fwt", tZONE, -HOUR (1) }, /* French Winter */ { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ #if 0 { "it", tZONE, -HOUR (3.5) },/* Iran */ #endif { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ #if 0 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ #endif { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ #if 0 /* For completeness. NST is also Newfoundland Standard, and SST is * also Swedish Summer. */ { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ #endif /* 0 */ { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ #if 0 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ #endif { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ #if 0 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ #endif { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ { NULL, 0, 0 } }; /* Military timezone table. */ static TABLE const MilitaryTable[] = { { "a", tZONE, HOUR ( 1) }, { "b", tZONE, HOUR ( 2) }, { "c", tZONE, HOUR ( 3) }, { "d", tZONE, HOUR ( 4) }, { "e", tZONE, HOUR ( 5) }, { "f", tZONE, HOUR ( 6) }, { "g", tZONE, HOUR ( 7) }, { "h", tZONE, HOUR ( 8) }, { "i", tZONE, HOUR ( 9) }, { "k", tZONE, HOUR ( 10) }, { "l", tZONE, HOUR ( 11) }, { "m", tZONE, HOUR ( 12) }, { "n", tZONE, HOUR (- 1) }, { "o", tZONE, HOUR (- 2) }, { "p", tZONE, HOUR (- 3) }, { "q", tZONE, HOUR (- 4) }, { "r", tZONE, HOUR (- 5) }, { "s", tZONE, HOUR (- 6) }, { "t", tZONE, HOUR (- 7) }, { "u", tZONE, HOUR (- 8) }, { "v", tZONE, HOUR (- 9) }, { "w", tZONE, HOUR (-10) }, { "x", tZONE, HOUR (-11) }, { "y", tZONE, HOUR (-12) }, { "z", tZONE, HOUR ( 0) }, { NULL, 0, 0 } }; /* ARGSUSED */ static int yyerror (s) char *s ATTRIBUTE_UNUSED; { return 0; } static int ToHour (Hours, Meridian) int Hours; MERIDIAN Meridian; { switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) return -1; return Hours; case MERam: if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; return Hours; case MERpm: if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; return Hours + 12; default: abort (); } /* NOTREACHED */ } static int ToYear (Year) int Year; { if (Year < 0) Year = -Year; /* XPG4 suggests that years 00-68 map to 2000-2068, and years 69-99 map to 1969-1999. */ if (Year < 69) Year += 2000; else if (Year < 100) Year += 1900; return Year; } static int LookupWord (buff) char *buff; { register char *p; register char *q; register const TABLE *tp; int i; int abbrev; /* Make it lowercase. */ for (p = buff; *p; p++) if (ISUPPER ((unsigned char) *p)) *p = tolower ((unsigned char) *p); if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) { yylval.Meridian = MERam; return tMERIDIAN; } if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) { yylval.Meridian = MERpm; return tMERIDIAN; } /* See if we have an abbreviation for a month. */ if (strlen (buff) == 3) abbrev = 1; else if (strlen (buff) == 4 && buff[3] == '.') { abbrev = 1; buff[3] = '\0'; } else abbrev = 0; for (tp = MonthDayTable; tp->name; tp++) { if (abbrev) { if (strncmp (buff, tp->name, 3) == 0) { yylval.Number = tp->value; return tp->type; } } else if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } for (tp = TimezoneTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } if (strcmp (buff, "dst") == 0) return tDST; for (tp = UnitsTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ i = strlen (buff) - 1; if (buff[i] == 's') { buff[i] = '\0'; for (tp = UnitsTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } buff[i] = 's'; /* Put back for "this" in OtherTable. */ } for (tp = OtherTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Military timezones. */ if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) { for (tp = MilitaryTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Drop out any periods and try the timezone table again. */ for (i = 0, p = q = buff; *q; q++) if (*q != '.') *p++ = *q; else i++; *p = '\0'; if (i) for (tp = TimezoneTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } return tID; } static int yylex () { register unsigned char c; register char *p; char buff[20]; int Count; int sign; for (;;) { while (ISSPACE ((unsigned char) *yyInput)) yyInput++; if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; if (!ISDIGIT (*++yyInput)) /* skip the '-' sign */ continue; } else sign = 0; for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) yylval.Number = 10 * yylval.Number + c - '0'; yyInput--; if (sign < 0) yylval.Number = -yylval.Number; return sign ? tSNUMBER : tUNUMBER; } if (ISALPHA (c)) { for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) if (p < &buff[sizeof buff - 1]) *p++ = c; *p = '\0'; yyInput--; return LookupWord (buff); } if (c != '(') return *yyInput++; Count = 0; do { c = *yyInput++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static long difftm (struct tm *a, struct tm *b) { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); long days = ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay / 100 - by / 100) + ((ay / 100 >> 2) - (by / 100 >> 2)) /* + difference in years * 365 */ + (long) (ay - by) * 365 ); return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + (a->tm_min - b->tm_min)) + (a->tm_sec - b->tm_sec)); } time_t get_date (const char *p, const time_t *now) { struct tm tm, tm0, *tmp; time_t Start; yyInput = p; Start = now ? *now : time ((time_t *) NULL); tmp = localtime (&Start); if (!tmp) return -1; yyYear = tmp->tm_year + TM_YEAR_ORIGIN; yyMonth = tmp->tm_mon + 1; yyDay = tmp->tm_mday; yyHour = tmp->tm_hour; yyMinutes = tmp->tm_min; yySeconds = tmp->tm_sec; tm.tm_isdst = tmp->tm_isdst; yyMeridian = MER24; yyRelSeconds = 0; yyRelMinutes = 0; yyRelHour = 0; yyRelDay = 0; yyRelMonth = 0; yyRelYear = 0; yyHaveDate = 0; yyHaveDay = 0; yyHaveRel = 0; yyHaveTime = 0; yyHaveZone = 0; if (yyparse () || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) return -1; tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; tm.tm_mon = yyMonth - 1 + yyRelMonth; tm.tm_mday = yyDay + yyRelDay; if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) { tm.tm_hour = ToHour (yyHour, yyMeridian); if (tm.tm_hour < 0) return -1; tm.tm_min = yyMinutes; tm.tm_sec = yySeconds; } else { tm.tm_hour = tm.tm_min = tm.tm_sec = 0; } tm.tm_hour += yyRelHour; tm.tm_min += yyRelMinutes; tm.tm_sec += yyRelSeconds; /* Let mktime deduce tm_isdst if we have an absolute timestamp, or if the relative timestamp mentions days, months, or years. */ if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) tm.tm_isdst = -1; tm0 = tm; Start = mktime (&tm); if (Start == (time_t) -1) { /* Guard against falsely reporting errors near the time_t boundaries when parsing times in other time zones. For example, if the min time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead of UTC, then the min localtime value is 1970-01-01 08:00:00; if we apply mktime to 1970-01-01 00:00:00 we will get an error, so we apply mktime to 1970-01-02 08:00:00 instead and adjust the time zone by 24 hours to compensate. This algorithm assumes that there is no DST transition within a day of the time_t boundaries. */ if (yyHaveZone) { tm = tm0; if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) { tm.tm_mday++; yyTimezone -= 24 * 60; } else { tm.tm_mday--; yyTimezone += 24 * 60; } Start = mktime (&tm); } if (Start == (time_t) -1) return Start; } if (yyHaveDay && !yyHaveDate) { tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); Start = mktime (&tm); if (Start == (time_t) -1) return Start; } if (yyHaveZone) { long delta; struct tm *gmt = gmtime (&Start); if (!gmt) return -1; delta = yyTimezone * 60L + difftm (&tm, gmt); if ((Start + delta < Start) != (delta < 0)) return -1; /* time_t overflow */ Start += delta; } return Start; } #if defined (TEST) /* ARGSUSED */ int main (ac, av) int ac; char *av[]; { char buff[MAX_BUFF_LEN + 1]; time_t d; (void) printf ("Enter date, or blank line to exit.\n\t> "); (void) fflush (stdout); buff[MAX_BUFF_LEN] = 0; while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) { d = get_date (buff, (time_t *) NULL); if (d == -1) (void) printf ("Bad format - couldn't convert.\n"); else (void) printf ("%s", ctime (&d)); (void) printf ("\t> "); (void) fflush (stdout); } exit (0); /* NOTREACHED */ } #endif /* defined (TEST) */ chrony-2.1.1/getdate.h0000644000000000000000000000175612542263351013315 0ustar rootroot/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Modified from the original to add stdlib.h and string.h */ #ifndef GOT_GETDATE_H #define GOT_GETDATE_H #include #include #include time_t get_date (const char *p, const time_t *now); #endif /* GOT_GETDATE_H */ chrony-2.1.1/getdate.y0000644000000000000000000006230412542263351013332 0ustar rootroot%{ /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990. ** ** This code is in the public domain and has no copyright. */ #ifdef HAVE_CONFIG_H # include # ifdef HAVE_ALLOCA_H # include # endif #endif /* Since the code of getdate.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if the code were included in the Emacs executable, it probably wouldn't do any harm to #undef it here; this will only cause problems if we try to write to a static variable, which I don't think this code needs to do. */ #ifdef emacs # undef static #endif #include #include #if HAVE_STDLIB_H # include /* for `free'; used by Bison 1.27 */ #endif #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) # define IN_CTYPE_DOMAIN(c) 1 #else # define IN_CTYPE_DOMAIN(c) isascii(c) #endif #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c)) #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) /* ISDIGIT differs from ISDIGIT_LOCALE, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless it's important to use the locale's definition of `digit' even when the host does not conform to Posix. */ #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) #if defined (STDC_HEADERS) || defined (USG) # include #endif #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) # define __attribute__(x) #endif #ifndef ATTRIBUTE_UNUSED # define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif /* Some old versions of bison generate parsers that use bcopy. That loses on systems that don't provide the function, so we have to redefine it here. */ #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) # define bcopy(from, to, len) memcpy ((to), (from), (len)) #endif /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), as well as gratuitiously global symbol names, so we can have multiple yacc generated parsers in the same program. Note that these are only the variables produced by yacc. If other parser generators (bison, byacc, etc) produce additional global names that conflict at link time, then those parser generators need to be fixed instead of adding those names to this list. */ #define yymaxdepth gd_maxdepth #define yyparse gd_parse #define yylex gd_lex #define yyerror gd_error #define yylval gd_lval #define yychar gd_char #define yydebug gd_debug #define yypact gd_pact #define yyr1 gd_r1 #define yyr2 gd_r2 #define yydef gd_def #define yychk gd_chk #define yypgo gd_pgo #define yyact gd_act #define yyexca gd_exca #define yyerrflag gd_errflag #define yynerrs gd_nerrs #define yyps gd_ps #define yypv gd_pv #define yys gd_s #define yy_yys gd_yys #define yystate gd_state #define yytmp gd_tmp #define yyv gd_v #define yy_yyv gd_yyv #define yyval gd_val #define yylloc gd_lloc #define yyreds gd_reds /* With YYDEBUG defined */ #define yytoks gd_toks /* With YYDEBUG defined */ #define yylhs gd_yylhs #define yylen gd_yylen #define yydefred gd_yydefred #define yydgoto gd_yydgoto #define yysindex gd_yysindex #define yyrindex gd_yyrindex #define yygindex gd_yygindex #define yytable gd_yytable #define yycheck gd_yycheck static int yylex (void); static int yyerror (char *s); #define EPOCH 1970 #define HOUR(x) ((x) * 60) #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */ /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { const char *name; int type; int value; } TABLE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of these by using a good ** union as the yacc stack. (This routine was originally written before ** yacc had the %union construct.) Maybe someday; right now we only use ** the %union very rarely. */ static const char *yyInput; static int yyDayOrdinal; static int yyDayNumber; static int yyHaveDate; static int yyHaveDay; static int yyHaveRel; static int yyHaveTime; static int yyHaveZone; static int yyTimezone; static int yyDay; static int yyHour; static int yyMinutes; static int yyMonth; static int yySeconds; static int yyYear; static MERIDIAN yyMeridian; static int yyRelDay; static int yyRelHour; static int yyRelMinutes; static int yyRelMonth; static int yyRelSeconds; static int yyRelYear; %} /* This grammar has 13 shift/reduce conflicts. */ %expect 13 %union { int Number; enum _MERIDIAN Meridian; } %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE %type tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT %type tMONTH tMONTH_UNIT %type tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item ; item : time { yyHaveTime++; } | zone { yyHaveZone++; } | date { yyHaveDate++; } | day { yyHaveDay++; } | rel { yyHaveRel++; } | number ; time : tUNUMBER tMERIDIAN { yyHour = $1; yyMinutes = 0; yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yyMeridian = MER24; yyHaveZone++; yyTimezone = ($4 < 0 ? -$4 % 100 + (-$4 / 100) * 60 : - ($4 % 100 + ($4 / 100) * 60)); } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = MER24; yyHaveZone++; yyTimezone = ($6 < 0 ? -$6 % 100 + (-$6 / 100) * 60 : - ($6 % 100 + ($6 / 100) * 60)); } ; zone : tZONE { yyTimezone = $1; } | tDAYZONE { yyTimezone = $1 - 60; } | tZONE tDST { yyTimezone = $1 - 60; } ; day : tDAY { yyDayOrdinal = 1; yyDayNumber = $1; } | tDAY ',' { yyDayOrdinal = 1; yyDayNumber = $1; } | tUNUMBER tDAY { yyDayOrdinal = $1; yyDayNumber = $2; } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY. The goal in recognizing YYYY/MM/DD is solely to support legacy machine-generated dates like those in an RCS log listing. If you want portability, use the ISO 8601 format. */ if ($1 >= 1000) { yyYear = $1; yyMonth = $3; yyDay = $5; } else { yyMonth = $1; yyDay = $3; yyYear = $5; } } | tUNUMBER tSNUMBER tSNUMBER { /* ISO 8601 format. yyyy-mm-dd. */ yyYear = $1; yyMonth = -$2; yyDay = -$3; } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ yyDay = $1; yyMonth = $2; yyYear = -$3; } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyMonth = $2; yyDay = $1; } | tUNUMBER tMONTH tUNUMBER { yyMonth = $2; yyDay = $1; yyYear = $3; } ; rel : relunit tAGO { yyRelSeconds = -yyRelSeconds; yyRelMinutes = -yyRelMinutes; yyRelHour = -yyRelHour; yyRelDay = -yyRelDay; yyRelMonth = -yyRelMonth; yyRelYear = -yyRelYear; } | relunit ; relunit : tUNUMBER tYEAR_UNIT { yyRelYear += $1 * $2; } | tSNUMBER tYEAR_UNIT { yyRelYear += $1 * $2; } | tYEAR_UNIT { yyRelYear += $1; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tMONTH_UNIT { yyRelMonth += $1; } | tUNUMBER tDAY_UNIT { yyRelDay += $1 * $2; } | tSNUMBER tDAY_UNIT { yyRelDay += $1 * $2; } | tDAY_UNIT { yyRelDay += $1; } | tUNUMBER tHOUR_UNIT { yyRelHour += $1 * $2; } | tSNUMBER tHOUR_UNIT { yyRelHour += $1 * $2; } | tHOUR_UNIT { yyRelHour += $1; } | tUNUMBER tMINUTE_UNIT { yyRelMinutes += $1 * $2; } | tSNUMBER tMINUTE_UNIT { yyRelMinutes += $1 * $2; } | tMINUTE_UNIT { yyRelMinutes += $1; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tSNUMBER tSEC_UNIT { yyRelSeconds += $1 * $2; } | tSEC_UNIT { yyRelSeconds += $1; } ; number : tUNUMBER { if (yyHaveTime && yyHaveDate && !yyHaveRel) yyYear = $1; else { if ($1>10000) { yyHaveDate++; yyDay= ($1)%100; yyMonth= ($1/100)%100; yyYear = $1/10000; } else { yyHaveTime++; if ($1 < 100) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } yySeconds = 0; yyMeridian = MER24; } } } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Include this file down here because bison inserts code above which may define-away `const'. We want the prototype for get_date to have the same signature as the function definition does. */ #include "getdate.h" extern struct tm *gmtime (); extern struct tm *localtime (); extern time_t mktime (); /* Month and day table. */ static TABLE const MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "sept", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, { "sunday", tDAY, 0 }, { "monday", tDAY, 1 }, { "tuesday", tDAY, 2 }, { "tues", tDAY, 2 }, { "wednesday", tDAY, 3 }, { "wednes", tDAY, 3 }, { "thursday", tDAY, 4 }, { "thur", tDAY, 4 }, { "thurs", tDAY, 4 }, { "friday", tDAY, 5 }, { "saturday", tDAY, 6 }, { NULL, 0, 0 } }; /* Time units table. */ static TABLE const UnitsTable[] = { { "year", tYEAR_UNIT, 1 }, { "month", tMONTH_UNIT, 1 }, { "fortnight", tDAY_UNIT, 14 }, { "week", tDAY_UNIT, 7 }, { "day", tDAY_UNIT, 1 }, { "hour", tHOUR_UNIT, 1 }, { "minute", tMINUTE_UNIT, 1 }, { "min", tMINUTE_UNIT, 1 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, { NULL, 0, 0 } }; /* Assorted relative-time words. */ static TABLE const OtherTable[] = { { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, { "today", tMINUTE_UNIT, 0 }, { "now", tMINUTE_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tMINUTE_UNIT, 0 }, { "next", tUNUMBER, 1 }, { "first", tUNUMBER, 1 }, /* { "second", tUNUMBER, 2 }, */ { "third", tUNUMBER, 3 }, { "fourth", tUNUMBER, 4 }, { "fifth", tUNUMBER, 5 }, { "sixth", tUNUMBER, 6 }, { "seventh", tUNUMBER, 7 }, { "eighth", tUNUMBER, 8 }, { "ninth", tUNUMBER, 9 }, { "tenth", tUNUMBER, 10 }, { "eleventh", tUNUMBER, 11 }, { "twelfth", tUNUMBER, 12 }, { "ago", tAGO, 1 }, { NULL, 0, 0 } }; /* The timezone table. */ static TABLE const TimezoneTable[] = { { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ { "utc", tZONE, HOUR ( 0) }, { "wet", tZONE, HOUR ( 0) }, /* Western European */ { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */ { "wat", tZONE, HOUR ( 1) }, /* West Africa */ { "at", tZONE, HOUR ( 2) }, /* Azores */ #if 0 /* For completeness. BST is also British Summer, and GST is * also Guam Standard. */ { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */ { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */ #endif #if 0 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */ { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */ #endif { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOUR ( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */ { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */ { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */ { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */ { "cat", tZONE, HOUR (10) }, /* Central Alaska */ { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */ { "nt", tZONE, HOUR (11) }, /* Nome */ { "idlw", tZONE, HOUR (12) }, /* International Date Line West */ { "cet", tZONE, -HOUR (1) }, /* Central European */ { "met", tZONE, -HOUR (1) }, /* Middle European */ { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */ { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */ { "fwt", tZONE, -HOUR (1) }, /* French Winter */ { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */ { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */ { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */ #if 0 { "it", tZONE, -HOUR (3.5) },/* Iran */ #endif { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */ #if 0 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */ #endif { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */ #if 0 /* For completeness. NST is also Newfoundland Standard, and SST is * also Swedish Summer. */ { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */ { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */ #endif /* 0 */ { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */ #if 0 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */ #endif { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */ { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */ #if 0 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */ { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */ #endif { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */ { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */ { "nzt", tZONE, -HOUR (12) }, /* New Zealand */ { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */ { "idle", tZONE, -HOUR (12) }, /* International Date Line East */ { NULL, 0, 0 } }; /* Military timezone table. */ static TABLE const MilitaryTable[] = { { "a", tZONE, HOUR ( 1) }, { "b", tZONE, HOUR ( 2) }, { "c", tZONE, HOUR ( 3) }, { "d", tZONE, HOUR ( 4) }, { "e", tZONE, HOUR ( 5) }, { "f", tZONE, HOUR ( 6) }, { "g", tZONE, HOUR ( 7) }, { "h", tZONE, HOUR ( 8) }, { "i", tZONE, HOUR ( 9) }, { "k", tZONE, HOUR ( 10) }, { "l", tZONE, HOUR ( 11) }, { "m", tZONE, HOUR ( 12) }, { "n", tZONE, HOUR (- 1) }, { "o", tZONE, HOUR (- 2) }, { "p", tZONE, HOUR (- 3) }, { "q", tZONE, HOUR (- 4) }, { "r", tZONE, HOUR (- 5) }, { "s", tZONE, HOUR (- 6) }, { "t", tZONE, HOUR (- 7) }, { "u", tZONE, HOUR (- 8) }, { "v", tZONE, HOUR (- 9) }, { "w", tZONE, HOUR (-10) }, { "x", tZONE, HOUR (-11) }, { "y", tZONE, HOUR (-12) }, { "z", tZONE, HOUR ( 0) }, { NULL, 0, 0 } }; /* ARGSUSED */ static int yyerror (s) char *s ATTRIBUTE_UNUSED; { return 0; } static int ToHour (Hours, Meridian) int Hours; MERIDIAN Meridian; { switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) return -1; return Hours; case MERam: if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; return Hours; case MERpm: if (Hours < 1 || Hours > 12) return -1; if (Hours == 12) Hours = 0; return Hours + 12; default: abort (); } /* NOTREACHED */ } static int ToYear (Year) int Year; { if (Year < 0) Year = -Year; /* XPG4 suggests that years 00-68 map to 2000-2068, and years 69-99 map to 1969-1999. */ if (Year < 69) Year += 2000; else if (Year < 100) Year += 1900; return Year; } static int LookupWord (buff) char *buff; { register char *p; register char *q; register const TABLE *tp; int i; int abbrev; /* Make it lowercase. */ for (p = buff; *p; p++) if (ISUPPER ((unsigned char) *p)) *p = tolower ((unsigned char) *p); if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0) { yylval.Meridian = MERam; return tMERIDIAN; } if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0) { yylval.Meridian = MERpm; return tMERIDIAN; } /* See if we have an abbreviation for a month. */ if (strlen (buff) == 3) abbrev = 1; else if (strlen (buff) == 4 && buff[3] == '.') { abbrev = 1; buff[3] = '\0'; } else abbrev = 0; for (tp = MonthDayTable; tp->name; tp++) { if (abbrev) { if (strncmp (buff, tp->name, 3) == 0) { yylval.Number = tp->value; return tp->type; } } else if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } for (tp = TimezoneTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } if (strcmp (buff, "dst") == 0) return tDST; for (tp = UnitsTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ i = strlen (buff) - 1; if (buff[i] == 's') { buff[i] = '\0'; for (tp = UnitsTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } buff[i] = 's'; /* Put back for "this" in OtherTable. */ } for (tp = OtherTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Military timezones. */ if (buff[1] == '\0' && ISALPHA ((unsigned char) *buff)) { for (tp = MilitaryTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Drop out any periods and try the timezone table again. */ for (i = 0, p = q = buff; *q; q++) if (*q != '.') *p++ = *q; else i++; *p = '\0'; if (i) for (tp = TimezoneTable; tp->name; tp++) if (strcmp (buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } return tID; } static int yylex () { register unsigned char c; register char *p; char buff[20]; int Count; int sign; for (;;) { while (ISSPACE ((unsigned char) *yyInput)) yyInput++; if (ISDIGIT (c = *yyInput) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; if (!ISDIGIT (*++yyInput)) /* skip the '-' sign */ continue; } else sign = 0; for (yylval.Number = 0; ISDIGIT (c = *yyInput++);) yylval.Number = 10 * yylval.Number + c - '0'; yyInput--; if (sign < 0) yylval.Number = -yylval.Number; return sign ? tSNUMBER : tUNUMBER; } if (ISALPHA (c)) { for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';) if (p < &buff[sizeof buff - 1]) *p++ = c; *p = '\0'; yyInput--; return LookupWord (buff); } if (c != '(') return *yyInput++; Count = 0; do { c = *yyInput++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static long difftm (struct tm *a, struct tm *b) { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); long days = ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay / 100 - by / 100) + ((ay / 100 >> 2) - (by / 100 >> 2)) /* + difference in years * 365 */ + (long) (ay - by) * 365 ); return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + (a->tm_min - b->tm_min)) + (a->tm_sec - b->tm_sec)); } time_t get_date (const char *p, const time_t *now) { struct tm tm, tm0, *tmp; time_t Start; yyInput = p; Start = now ? *now : time ((time_t *) NULL); tmp = localtime (&Start); if (!tmp) return -1; yyYear = tmp->tm_year + TM_YEAR_ORIGIN; yyMonth = tmp->tm_mon + 1; yyDay = tmp->tm_mday; yyHour = tmp->tm_hour; yyMinutes = tmp->tm_min; yySeconds = tmp->tm_sec; tm.tm_isdst = tmp->tm_isdst; yyMeridian = MER24; yyRelSeconds = 0; yyRelMinutes = 0; yyRelHour = 0; yyRelDay = 0; yyRelMonth = 0; yyRelYear = 0; yyHaveDate = 0; yyHaveDay = 0; yyHaveRel = 0; yyHaveTime = 0; yyHaveZone = 0; if (yyparse () || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) return -1; tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear; tm.tm_mon = yyMonth - 1 + yyRelMonth; tm.tm_mday = yyDay + yyRelDay; if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay)) { tm.tm_hour = ToHour (yyHour, yyMeridian); if (tm.tm_hour < 0) return -1; tm.tm_min = yyMinutes; tm.tm_sec = yySeconds; } else { tm.tm_hour = tm.tm_min = tm.tm_sec = 0; } tm.tm_hour += yyRelHour; tm.tm_min += yyRelMinutes; tm.tm_sec += yyRelSeconds; /* Let mktime deduce tm_isdst if we have an absolute timestamp, or if the relative timestamp mentions days, months, or years. */ if (yyHaveDate | yyHaveDay | yyHaveTime | yyRelDay | yyRelMonth | yyRelYear) tm.tm_isdst = -1; tm0 = tm; Start = mktime (&tm); if (Start == (time_t) -1) { /* Guard against falsely reporting errors near the time_t boundaries when parsing times in other time zones. For example, if the min time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead of UTC, then the min localtime value is 1970-01-01 08:00:00; if we apply mktime to 1970-01-01 00:00:00 we will get an error, so we apply mktime to 1970-01-02 08:00:00 instead and adjust the time zone by 24 hours to compensate. This algorithm assumes that there is no DST transition within a day of the time_t boundaries. */ if (yyHaveZone) { tm = tm0; if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN) { tm.tm_mday++; yyTimezone -= 24 * 60; } else { tm.tm_mday--; yyTimezone += 24 * 60; } Start = mktime (&tm); } if (Start == (time_t) -1) return Start; } if (yyHaveDay && !yyHaveDate) { tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal))); Start = mktime (&tm); if (Start == (time_t) -1) return Start; } if (yyHaveZone) { long delta; struct tm *gmt = gmtime (&Start); if (!gmt) return -1; delta = yyTimezone * 60L + difftm (&tm, gmt); if ((Start + delta < Start) != (delta < 0)) return -1; /* time_t overflow */ Start += delta; } return Start; } #if defined (TEST) /* ARGSUSED */ int main (ac, av) int ac; char *av[]; { char buff[MAX_BUFF_LEN + 1]; time_t d; (void) printf ("Enter date, or blank line to exit.\n\t> "); (void) fflush (stdout); buff[MAX_BUFF_LEN] = 0; while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) { d = get_date (buff, (time_t *) NULL); if (d == -1) (void) printf ("Bad format - couldn't convert.\n"); else (void) printf ("%s", ctime (&d)); (void) printf ("\t> "); (void) fflush (stdout); } exit (0); /* NOTREACHED */ } #endif /* defined (TEST) */ chrony-2.1.1/hash.h0000644000000000000000000000262312542263351012615 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2012 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for crypto hashing. */ #ifndef GOT_HASH_H #define GOT_HASH_H /* length of hash values produced by SHA512 */ #define MAX_HASH_LENGTH 64 extern int HSH_GetHashId(const char *name); extern unsigned int HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, const unsigned char *in2, unsigned int in2_len, unsigned char *out, unsigned int out_len); extern void HSH_Finalise(void); #endif chrony-2.1.1/hash_intmd5.c0000644000000000000000000000330012542263351014061 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2012 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing crypto hashing using internal MD5 implementation. */ #include "config.h" #include "sysincl.h" #include "hash.h" #include "memory.h" #include "md5.c" static MD5_CTX ctx; int HSH_GetHashId(const char *name) { /* only MD5 is supported */ if (strcmp(name, "MD5")) return -1; return 0; } unsigned int HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, const unsigned char *in2, unsigned int in2_len, unsigned char *out, unsigned int out_len) { if (out_len < 16) return 0; MD5Init(&ctx); MD5Update(&ctx, in1, in1_len); if (in2) MD5Update(&ctx, in2, in2_len); MD5Final(&ctx); memcpy(out, ctx.digest, 16); return 16; } void HSH_Finalise(void) { } chrony-2.1.1/hash_nss.c0000644000000000000000000000506612542263351013477 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2012 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing crypto hashing using NSSLOWHASH API of the NSS library. */ #include #include #include /* #include "config.h" */ #include "hash.h" static NSSLOWInitContext *ictx; struct hash { HASH_HashType type; const char *name; NSSLOWHASHContext *context; }; static struct hash hashes[] = { { HASH_AlgMD5, "MD5", NULL }, { HASH_AlgSHA1, "SHA1", NULL }, { HASH_AlgSHA256, "SHA256", NULL }, { HASH_AlgSHA384, "SHA384", NULL }, { HASH_AlgSHA512, "SHA512", NULL }, { 0, NULL, NULL } }; int HSH_GetHashId(const char *name) { int i; for (i = 0; hashes[i].name; i++) { if (!strcmp(name, hashes[i].name)) break; } if (!hashes[i].name) return -1; /* not found */ if (!ictx && !(ictx = NSSLOW_Init())) return -1; /* couldn't init NSS */ if (!hashes[i].context && !(hashes[i].context = NSSLOWHASH_NewContext(ictx, hashes[i].type))) return -1; /* couldn't init hash */ return i; } unsigned int HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, const unsigned char *in2, unsigned int in2_len, unsigned char *out, unsigned int out_len) { unsigned int ret; NSSLOWHASH_Begin(hashes[id].context); NSSLOWHASH_Update(hashes[id].context, in1, in1_len); if (in2) NSSLOWHASH_Update(hashes[id].context, in2, in2_len); NSSLOWHASH_End(hashes[id].context, out, &ret, out_len); return ret; } void HSH_Finalise(void) { int i; for (i = 0; hashes[i].name; i++) { if (hashes[i].context) NSSLOWHASH_Destroy(hashes[i].context); } if (ictx) NSSLOW_Shutdown(ictx); } chrony-2.1.1/hash_tomcrypt.c0000644000000000000000000000550612542263351014554 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2012 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing crypto hashing using tomcrypt library. */ #include #include "config.h" #include "hash.h" struct hash { const char *name; const char *int_name; const struct ltc_hash_descriptor *desc; }; static const struct hash hashes[] = { { "MD5", "md5", &md5_desc }, #ifdef LTC_RIPEMD128 { "RMD128", "rmd128", &rmd128_desc }, #endif #ifdef LTC_RIPEMD160 { "RMD160", "rmd160", &rmd160_desc }, #endif #ifdef LTC_RIPEMD256 { "RMD256", "rmd256", &rmd256_desc }, #endif #ifdef LTC_RIPEMD320 { "RMD320", "rmd320", &rmd320_desc }, #endif #ifdef LTC_SHA1 { "SHA1", "sha1", &sha1_desc }, #endif #ifdef LTC_SHA256 { "SHA256", "sha256", &sha256_desc }, #endif #ifdef LTC_SHA384 { "SHA384", "sha384", &sha384_desc }, #endif #ifdef LTC_SHA512 { "SHA512", "sha512", &sha512_desc }, #endif #ifdef LTC_TIGER { "TIGER", "tiger", &tiger_desc }, #endif #ifdef LTC_WHIRLPOOL { "WHIRLPOOL", "whirlpool", &whirlpool_desc }, #endif { NULL, NULL, NULL } }; int HSH_GetHashId(const char *name) { int i, h; for (i = 0; hashes[i].name; i++) { if (!strcmp(name, hashes[i].name)) break; } if (!hashes[i].name) return -1; /* not found */ h = find_hash(hashes[i].int_name); if (h >= 0) return h; /* already registered */ /* register and try again */ register_hash(hashes[i].desc); return find_hash(hashes[i].int_name); } unsigned int HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len, const unsigned char *in2, unsigned int in2_len, unsigned char *out, unsigned int out_len) { unsigned long len; int r; len = out_len; if (in2) r = hash_memory_multi(id, out, &len, in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0); else r = hash_memory(id, in1, in1_len, out, &len); if (r != CRYPT_OK) return 0; return len; } void HSH_Finalise(void) { } chrony-2.1.1/keys.c0000644000000000000000000002136312542263351012642 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2012-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module for managing keys used for authenticating NTP packets and commands */ #include "config.h" #include "sysincl.h" #include "array.h" #include "keys.h" #include "cmdparse.h" #include "conf.h" #include "memory.h" #include "util.h" #include "local.h" #include "logging.h" typedef struct { uint32_t id; char *val; int len; int hash_id; int auth_delay; } Key; static ARR_Instance keys; static int command_key_valid; static uint32_t command_key_id; static int cache_valid; static uint32_t cache_key_id; static int cache_key_pos; /* ================================================== */ static int generate_key(uint32_t key_id) { #ifdef FEAT_SECHASH unsigned char key[20]; const char *hashname = "SHA1"; #else unsigned char key[16]; const char *hashname = "MD5"; #endif const char *key_file, *rand_dev = "/dev/urandom"; FILE *f; struct stat st; int i; key_file = CNF_GetKeysFile(); if (!key_file) return 0; f = fopen(rand_dev, "r"); if (!f || fread(key, sizeof (key), 1, f) != 1) { if (f) fclose(f); LOG_FATAL(LOGF_Keys, "Could not read %s", rand_dev); return 0; } fclose(f); f = fopen(key_file, "a"); if (!f) { LOG_FATAL(LOGF_Keys, "Could not open keyfile %s for writing", key_file); return 0; } /* Make sure the keyfile is not world-readable */ if (stat(key_file, &st) || chmod(key_file, st.st_mode & 0770)) { fclose(f); LOG_FATAL(LOGF_Keys, "Could not change permissions of keyfile %s", key_file); return 0; } fprintf(f, "\n%"PRIu32" %s HEX:", key_id, hashname); for (i = 0; i < sizeof (key); i++) fprintf(f, "%02hhX", key[i]); fprintf(f, "\n"); fclose(f); /* Erase the key from stack */ memset(key, 0, sizeof (key)); LOG(LOGS_INFO, LOGF_Keys, "Generated key %"PRIu32, key_id); return 1; } /* ================================================== */ static void free_keys(void) { unsigned int i; for (i = 0; i < ARR_GetSize(keys); i++) Free(((Key *)ARR_GetElement(keys, i))->val); ARR_SetSize(keys, 0); command_key_valid = 0; cache_valid = 0; } /* ================================================== */ void KEY_Initialise(void) { keys = ARR_CreateInstance(sizeof (Key)); command_key_valid = 0; cache_valid = 0; KEY_Reload(); if (CNF_GetGenerateCommandKey() && !KEY_KeyKnown(KEY_GetCommandKey())) { if (generate_key(KEY_GetCommandKey())) KEY_Reload(); } } /* ================================================== */ void KEY_Finalise(void) { free_keys(); ARR_DestroyInstance(keys); } /* ================================================== */ static Key * get_key(unsigned int index) { return ((Key *)ARR_GetElements(keys)) + index; } /* ================================================== */ static int determine_hash_delay(uint32_t key_id) { NTP_Packet pkt; struct timeval before, after; unsigned long usecs, min_usecs=0; int i; for (i = 0; i < 10; i++) { LCL_ReadRawTime(&before); KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH, (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data)); LCL_ReadRawTime(&after); usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec); if (i == 0 || usecs < min_usecs) { min_usecs = usecs; } } /* Add on a bit extra to allow for copying, conversions etc */ min_usecs += min_usecs >> 4; DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs); return min_usecs; } /* ================================================== */ /* 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; } } /* ================================================== */ void KEY_Reload(void) { unsigned int i, line_number; FILE *in; uint32_t key_id; char line[2048], *keyval, *key_file; const char *hashname; Key key; free_keys(); key_file = CNF_GetKeysFile(); line_number = 0; if (!key_file) return; in = fopen(key_file, "r"); if (!in) { LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file); return; } while (fgets(line, sizeof (line), in)) { line_number++; CPS_NormalizeLine(line); if (!*line) continue; if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) { LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file); continue; } key.hash_id = HSH_GetHashId(hashname); if (key.hash_id < 0) { LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id); continue; } key.len = UTI_DecodePasswordFromText(keyval); if (!key.len) { LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id); continue; } key.id = key_id; key.val = MallocArray(char, key.len); memcpy(key.val, keyval, key.len); ARR_AppendElement(keys, &key); } 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(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id); /* Check for duplicates */ for (i = 1; i < ARR_GetSize(keys); i++) { if (get_key(i - 1)->id == get_key(i)->id) LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id); } /* Erase any passwords from stack */ memset(line, 0, sizeof (line)); for (i = 0; i < ARR_GetSize(keys); i++) get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id); } /* ================================================== */ static int lookup_key(uint32_t id) { Key specimen, *where, *keys_ptr; int pos; keys_ptr = ARR_GetElements(keys); specimen.id = id; where = (Key *)bsearch((void *)&specimen, keys_ptr, ARR_GetSize(keys), sizeof (Key), compare_keys_by_id); if (!where) { return -1; } else { pos = where - keys_ptr; return pos; } } /* ================================================== */ static Key * get_key_by_id(uint32_t key_id) { int position; if (cache_valid && key_id == cache_key_id) return get_key(cache_key_pos); position = lookup_key(key_id); if (position >= 0) { cache_valid = 1; cache_key_pos = position; cache_key_id = key_id; return get_key(position); } return NULL; } /* ================================================== */ uint32_t KEY_GetCommandKey(void) { if (!command_key_valid) { command_key_id = CNF_GetCommandKey(); } return command_key_id; } /* ================================================== */ int KEY_KeyKnown(uint32_t key_id) { return get_key_by_id(key_id) != NULL; } /* ================================================== */ int KEY_GetAuthDelay(uint32_t key_id) { Key *key; key = get_key_by_id(key_id); if (!key) return 0; return key->auth_delay; } /* ================================================== */ int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, unsigned char *auth, int auth_len) { Key *key; key = get_key_by_id(key_id); if (!key) return 0; return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val, key->len, data, data_len, auth, auth_len); } /* ================================================== */ int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len) { Key *key; key = get_key_by_id(key_id); if (!key) return 0; return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val, key->len, data, data_len, auth, auth_len); } chrony-2.1.1/keys.h0000644000000000000000000000321512542263351012643 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header for key management module */ #ifndef GOT_KEYS_H #define GOT_KEYS_H #include "sysincl.h" extern void KEY_Initialise(void); extern void KEY_Finalise(void); extern void KEY_Reload(void); extern uint32_t KEY_GetCommandKey(void); extern int KEY_GetKey(uint32_t key_id, char **key, int *len); extern int KEY_KeyKnown(uint32_t key_id); extern int KEY_GetAuthDelay(uint32_t key_id); extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, unsigned char *auth, int auth_len); extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len); #endif /* GOT_KEYS_H */ chrony-2.1.1/local.c0000644000000000000000000004541412542263351012764 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011, 2014-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "conf.h" #include "local.h" #include "localp.h" #include "memory.h" #include "smooth.h" #include "util.h" #include "logging.h" /* ================================================== */ /* Maximum allowed frequency offset in ppm, the time must not stop or run backwards */ #define MAX_FREQ 500000.0 /* Variable to store the current frequency, in ppm */ static double current_freq_ppm; /* Temperature compensation, in ppm */ static double temp_comp_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_SetLeapDriver drv_set_leap; static lcl_SetSyncStatusDriver drv_set_sync_status; /* ================================================== */ /* 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; static double max_clock_error; /* ================================================== */ /* 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; int dusec, best_dusec; int iters; gettimeofday(&old_tv, NULL); best_dusec = 1000000; /* Assume we must be better than a second */ iters = 0; do { gettimeofday(&tv, NULL); 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); assert(best_dusec > 0); precision_quantum = best_dusec * 1.0e-6; /* Get rounded log2 value of the measured precision */ precision_log = 0; while (best_dusec < 707107) { precision_log--; best_dusec *= 2; } DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log); } /* ================================================== */ 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; temp_comp_ppm = 0.0; calculate_sys_precision(); max_clock_error = CNF_GetMaxClockError() * 1e-6; } /* ================================================== */ void LCL_Finalise(void) { while (change_list.next != &change_list) LCL_RemoveParameterChangeHandler(change_list.next->handler, change_list.next->anything); while (dispersion_notify_list.next != &dispersion_notify_list) LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler, dispersion_notify_list.next->anything); } /* ================================================== */ /* 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; } /* ================================================== */ double LCL_GetMaxClockError(void) { return max_clock_error; } /* ================================================== */ 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)) { assert(0); } } 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; } /* ================================================== */ /* Remove a handler */ 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; } } assert(ok); /* Unlink entry from the list */ ptr->next->prev = ptr->prev; ptr->prev->next = ptr->next; Free(ptr); } /* ================================================== */ int LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler) { return change_list.next->handler == handler; } /* ================================================== */ static void invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type) { ChangeListEntry *ptr; for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything); } } /* ================================================== */ 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)) { assert(0); } } 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; } /* ================================================== */ /* 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; } } assert(ok); /* Unlink entry from the list */ ptr->next->prev = ptr->prev; ptr->prev->next = ptr->next; Free(ptr); } /* ================================================== */ /* 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) { if (gettimeofday(result, NULL) < 0) { LOG_FATAL(LOGF_Local, "gettimeofday() failed"); } } /* ================================================== */ void LCL_ReadCookedTime(struct timeval *result, double *err) { struct timeval raw; LCL_ReadRawTime(&raw); LCL_CookTime(&raw, result, err); } /* ================================================== */ void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err) { double correction; LCL_GetOffsetCorrection(raw, &correction, err); UTI_AddDoubleToTimeval(raw, correction, cooked); } /* ================================================== */ void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err) { /* Call system specific driver to get correction */ (*drv_offset_convert)(raw, correction, err); } /* ================================================== */ /* Return current frequency */ double LCL_ReadAbsoluteFrequency(void) { double freq; freq = current_freq_ppm; /* Undo temperature compensation */ if (temp_comp_ppm != 0.0) { freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm); } return freq; } /* ================================================== */ static double clamp_freq(double freq) { if (freq <= MAX_FREQ && freq >= -MAX_FREQ) return freq; LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq); return freq >= MAX_FREQ ? MAX_FREQ : -MAX_FREQ; } /* ================================================== */ static int check_offset(struct timeval *now, double offset) { /* Check if the time will be still sane with accumulated offset */ if (UTI_IsTimeOffsetSane(now, -offset)) return 1; LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset); return 0; } /* ================================================== */ /* 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) { struct timeval raw, cooked; double dfreq; afreq_ppm = clamp_freq(afreq_ppm); /* Apply temperature compensation */ if (temp_comp_ppm != 0.0) { afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm; } /* Call the system-specific driver for setting the frequency */ afreq_ppm = (*drv_set_freq)(afreq_ppm); dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm); LCL_ReadRawTime(&raw); LCL_CookTime(&raw, &cooked, NULL); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust); current_freq_ppm = afreq_ppm; } /* ================================================== */ void LCL_AccumulateDeltaFrequency(double dfreq) { struct timeval raw, cooked; double old_freq_ppm; 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 += dfreq * (1.0e6 - current_freq_ppm); current_freq_ppm = clamp_freq(current_freq_ppm); /* Call the system-specific driver for setting the frequency */ current_freq_ppm = (*drv_set_freq)(current_freq_ppm); dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm); LCL_ReadRawTime(&raw); LCL_CookTime(&raw, &cooked, NULL); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust); } /* ================================================== */ void LCL_AccumulateOffset(double offset, double corr_rate) { struct timeval raw, cooked; /* 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); LCL_CookTime(&raw, &cooked, NULL); if (!check_offset(&cooked, offset)) return; (*drv_accrue_offset)(offset, corr_rate); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust); } /* ================================================== */ int LCL_ApplyStepOffset(double offset) { struct timeval raw, cooked; /* 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); LCL_CookTime(&raw, &cooked, NULL); if (!check_offset(&raw, offset)) return 0; if (!(*drv_apply_step_offset)(offset)) { LOG(LOGS_ERR, LOGF_Local, "Could not step clock"); return 0; } /* Reset smoothing on all clock steps */ SMT_Reset(&cooked); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep); return 1; } /* ================================================== */ void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, double offset, double dispersion) { /* Dispatch to all handlers */ invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep); lcl_InvokeDispersionNotifyHandlers(dispersion); } /* ================================================== */ void LCL_NotifyLeap(int leap) { struct timeval raw, cooked; LCL_ReadRawTime(&raw); LCL_CookTime(&raw, &cooked, NULL); /* Smooth the leap second out */ SMT_Leap(&cooked, leap); /* Dispatch to all handlers as if the clock was stepped */ invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep); } /* ================================================== */ void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) { struct timeval raw, cooked; double old_freq_ppm; LCL_ReadRawTime(&raw); /* Due to modifying the offset, this has to be the cooked time prior to the change we are about to make */ LCL_CookTime(&raw, &cooked, NULL); if (!check_offset(&cooked, doffset)) return; 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 += dfreq * (1.0e6 - current_freq_ppm); current_freq_ppm = clamp_freq(current_freq_ppm); DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec", old_freq_ppm, current_freq_ppm, doffset); /* Call the system-specific driver for setting the frequency */ current_freq_ppm = (*drv_set_freq)(current_freq_ppm); dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm); (*drv_accrue_offset)(doffset, corr_rate); /* Dispatch to all handlers */ invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust); } /* ================================================== */ 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_SetLeapDriver set_leap, lcl_SetSyncStatusDriver set_sync_status) { 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_set_leap = set_leap; drv_set_sync_status = set_sync_status; current_freq_ppm = (*drv_read_freq)(); DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm); } /* ================================================== */ /* Look at the current difference between the system time and the NTP time, and make a step to cancel it. */ int LCL_MakeStep(void) { struct timeval raw; double correction; LCL_ReadRawTime(&raw); LCL_GetOffsetCorrection(&raw, &correction, NULL); if (!check_offset(&raw, -correction)) return 0; /* Cancel remaining slew and make the step */ LCL_AccumulateOffset(correction, 0.0); if (!LCL_ApplyStepOffset(-correction)) return 0; LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction); return 1; } /* ================================================== */ int LCL_CanSystemLeap(void) { return drv_set_leap ? 1 : 0; } /* ================================================== */ void LCL_SetSystemLeap(int leap) { if (drv_set_leap) { (drv_set_leap)(leap); } } /* ================================================== */ double LCL_SetTempComp(double comp) { double uncomp_freq_ppm; if (temp_comp_ppm == comp) return comp; /* Undo previous compensation */ current_freq_ppm = (current_freq_ppm + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm); uncomp_freq_ppm = current_freq_ppm; /* Apply new compensation */ current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp; /* Call the system-specific driver for setting the frequency */ current_freq_ppm = (*drv_set_freq)(current_freq_ppm); temp_comp_ppm = (uncomp_freq_ppm - current_freq_ppm) / (1.0e-6 * uncomp_freq_ppm + 1.0); return temp_comp_ppm; } /* ================================================== */ void LCL_SetSyncStatus(int synchronised, double est_error, double max_error) { if (drv_set_sync_status) { (drv_set_sync_status)(synchronised, est_error, max_error); } } /* ================================================== */ chrony-2.1.1/local.h0000644000000000000000000002140012542263351012756 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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); /* Convert raw time to cooked. */ extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err); /* Read the current offset between the system clock and true time (i.e. 'cooked' - 'raw') (in seconds). */ extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err); /* 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) doffset : delta offset applied (positive => make local system fast by that amount, negative => make it slow by that amount) change_type : what type of change is being applied anything : Passthrough argument from call to registration routine */ typedef enum { LCL_ChangeAdjust, LCL_ChangeStep, LCL_ChangeUnknownStep } LCL_ChangeType; typedef void (*LCL_ParameterChangeHandler) (struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, 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); /* Check if a handler is invoked first when dispatching */ extern int LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler); /* 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). Provided is also a suggested correction rate (correction time * offset). */ extern void LCL_AccumulateOffset(double offset, double corr_rate); /* 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 int LCL_ApplyStepOffset(double offset); /* Routine to invoke notify handlers on an unexpected time jump in system clock */ extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, double offset, double dispersion); /* Routine to invoke notify handlers on leap second when the system clock doesn't correct itself */ extern void LCL_NotifyLeap(int leap); /* Perform the combination of modifying the frequency and applying a slew, in one easy step */ extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); /* 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 read the maximum frequency error of the local clock. This is a frequency stability, not an absolute error. */ extern double LCL_GetMaxClockError(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); /* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap does something */ extern int LCL_CanSystemLeap(void); /* Routine to set the system clock to correct itself for a leap second if supported. Leap second will be inserted at the end of the day if the argument is positive, deleted if negative, and zero resets the setting. */ extern void LCL_SetSystemLeap(int leap); /* Routine to set a frequency correction (in ppm) that should be applied to local clock to compensate for temperature changes. A positive argument means that the clock frequency should be increased. Return the actual compensation (may be different from the requested compensation due to clamping or rounding). */ extern double LCL_SetTempComp(double comp); /* Routine to update the synchronisation status in the kernel to allow other applications to know if the system clock is synchronised and error bounds */ extern void LCL_SetSyncStatus(int synchronised, double est_error, double max_error); #endif /* GOT_LOCAL_H */ chrony-2.1.1/localp.h0000644000000000000000000000613412542263351013145 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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. Return actual frequency (may be different from the requested frequency due to clamping or rounding). */ typedef double (*lcl_SetFrequencyDriver)(double freq_ppm); /* System driver to accrue an offset. A positive argument means slew the clock forwards. The suggested correction rate of time to correct the offset is given in 'corr_rate'. */ typedef void (*lcl_AccrueOffsetDriver)(double offset, double corr_rate); /* System driver to apply a step offset. A positive argument means step the clock forwards. */ typedef int (*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, double *err); /* System driver to schedule leap second */ typedef void (*lcl_SetLeapDriver)(int leap); /* System driver to set the synchronisation status */ typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error); 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_SetLeapDriver set_leap, lcl_SetSyncStatusDriver set_sync_status); #endif /* GOT_LOCALP_H */ chrony-2.1.1/logging.c0000644000000000000000000001646012542263351013317 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module to handle logging of diagnostic information */ #include "config.h" #include "sysincl.h" #include "conf.h" #include "logging.h" #include "mkdirpp.h" #include "util.h" /* This is used by DEBUG_LOG macro */ int log_debug_enabled = 0; /* ================================================== */ /* Flag indicating we have initialised */ static int initialised = 0; static int system_log = 0; static int parent_fd = 0; #define DEBUG_LEVEL_PRINT_FUNCTION 2 #define DEBUG_LEVEL_PRINT_DEBUG 2 static int debug_level = 0; #ifdef WINNT static FILE *logfile; #endif struct LogFile { const char *name; const char *banner; FILE *file; unsigned long writes; }; static int n_filelogs = 0; /* Increase this when adding a new logfile */ #define MAX_FILELOGS 6 static struct LogFile logfiles[MAX_FILELOGS]; /* ================================================== */ /* Init function */ void LOG_Initialise(void) { initialised = 1; #ifdef WINNT logfile = fopen("./chronyd.err", "a"); #endif } /* ================================================== */ /* Fini function */ void LOG_Finalise(void) { #ifdef WINNT if (logfile) { fclose(logfile); } #else if (system_log) { closelog(); } #endif LOG_CycleLogFiles(); initialised = 0; } /* ================================================== */ static void log_message(int fatal, LOG_Severity severity, const char *message) { #ifdef WINNT if (logfile) { fprintf(logfile, fatal ? "Fatal error : %s\n" : "%s\n", message); } #else if (system_log) { int priority; switch (severity) { case LOGS_DEBUG: priority = LOG_DEBUG; break; case LOGS_INFO: priority = LOG_INFO; break; case LOGS_WARN: priority = LOG_WARNING; break; case LOGS_ERR: priority = LOG_ERR; break; case LOGS_FATAL: priority = LOG_CRIT; break; default: assert(0); } syslog(priority, fatal ? "Fatal error : %s" : "%s", message); } else { fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message); } #endif } /* ================================================== */ void LOG_Message(LOG_Severity severity, LOG_Facility facility, int line_number, const char *filename, const char *function_name, const char *format, ...) { char buf[2048]; va_list other_args; time_t t; struct tm stm; #ifdef WINNT #else if (!system_log) { /* Don't clutter up syslog with timestamps and internal debugging info */ time(&t); stm = *gmtime(&t); strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm); fprintf(stderr, "%s ", buf); if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION) fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name); } #endif va_start(other_args, format); vsnprintf(buf, sizeof(buf), format, other_args); va_end(other_args); switch (severity) { case LOGS_DEBUG: case LOGS_INFO: case LOGS_WARN: case LOGS_ERR: log_message(0, severity, buf); break; case LOGS_FATAL: log_message(1, severity, buf); /* With syslog, send the message also to the grandparent process or write it to stderr if not detached */ if (system_log) { if (parent_fd > 0) { if (write(parent_fd, buf, strlen(buf) + 1) < 0) ; /* Not much we can do here */ } else if (parent_fd == 0) { system_log = 0; log_message(1, severity, buf); } } break; default: assert(0); } } /* ================================================== */ void LOG_OpenSystemLog(void) { #ifdef WINNT #else system_log = 1; openlog("chronyd", LOG_PID, LOG_DAEMON); #endif } /* ================================================== */ void LOG_SetDebugLevel(int level) { debug_level = level; if (level >= DEBUG_LEVEL_PRINT_DEBUG) { log_debug_enabled = 1; } } /* ================================================== */ void LOG_SetParentFd(int fd) { parent_fd = fd; } /* ================================================== */ void LOG_CloseParentFd() { if (parent_fd > 0) close(parent_fd); parent_fd = -1; } /* ================================================== */ LOG_FileID LOG_FileOpen(const char *name, const char *banner) { assert(n_filelogs < MAX_FILELOGS); logfiles[n_filelogs].name = name; logfiles[n_filelogs].banner = banner; logfiles[n_filelogs].file = NULL; logfiles[n_filelogs].writes = 0; return n_filelogs++; } /* ================================================== */ void LOG_FileWrite(LOG_FileID id, const char *format, ...) { va_list other_args; int banner; if (id < 0 || id >= n_filelogs || !logfiles[id].name) return; if (!logfiles[id].file) { char filename[512]; if (snprintf(filename, sizeof(filename), "%s/%s.log", CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) || !(logfiles[id].file = fopen(filename, "a"))) { LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename); logfiles[id].name = NULL; return; } /* Close on exec */ UTI_FdSetCloexec(fileno(logfiles[id].file)); } banner = CNF_GetLogBanner(); if (banner && logfiles[id].writes++ % banner == 0) { char bannerline[256]; int i, bannerlen; bannerlen = strlen(logfiles[id].banner); for (i = 0; i < bannerlen; i++) bannerline[i] = '='; bannerline[i] = '\0'; fprintf(logfiles[id].file, "%s\n", bannerline); fprintf(logfiles[id].file, "%s\n", logfiles[id].banner); fprintf(logfiles[id].file, "%s\n", bannerline); } va_start(other_args, format); vfprintf(logfiles[id].file, format, other_args); va_end(other_args); fprintf(logfiles[id].file, "\n"); fflush(logfiles[id].file); } /* ================================================== */ void LOG_CreateLogFileDir(void) { const char *logdir; logdir = CNF_GetLogDir(); if (!mkdir_and_parents(logdir)) { LOG(LOGS_ERR, LOGF_Logging, "Could not create directory %s", logdir); } } /* ================================================== */ void LOG_CycleLogFiles(void) { LOG_FileID i; for (i = 0; i < n_filelogs; i++) { if (logfiles[i].file) fclose(logfiles[i].file); logfiles[i].file = NULL; logfiles[i].writes = 0; } } /* ================================================== */ chrony-2.1.1/logging.h0000644000000000000000000001006612542263351013320 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2013-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for diagnostic logging module */ #ifndef GOT_LOGGING_H #define GOT_LOGGING_H #include "sysincl.h" /* Flag indicating whether debug messages are logged */ extern int log_debug_enabled; /* Line logging macros. If the compiler is GNU C, we take advantage of being able to get the function name also. */ #ifdef __GNUC__ #define FUNCTION_NAME __FUNCTION__ #define FORMAT_ATTRIBUTE_PRINTF(str, first) __attribute__ ((format (printf, str, first))) #else #define FUNCTION_NAME "" #define FORMAT_ATTRIBUTE_PRINTF(str, first) #endif #define DEBUG_LOG(facility, ...) \ do { \ if (DEBUG && log_debug_enabled) \ LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \ } while (0) #define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__) #define LOG_FATAL(facility, ...) \ do { \ LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \ exit(1); \ } while (0) /* Definition of severity */ typedef enum { LOGS_INFO, LOGS_WARN, LOGS_ERR, LOGS_FATAL, LOGS_DEBUG } 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_Memory, LOGF_ClientLog, LOGF_Configure, LOGF_CmdMon, LOGF_Acquire, LOGF_Manual, LOGF_Keys, LOGF_Logging, LOGF_Nameserv, LOGF_Rtc, LOGF_Regress, LOGF_Sys, LOGF_SysGeneric, LOGF_SysLinux, LOGF_SysMacOSX, LOGF_SysNetBSD, LOGF_SysSolaris, LOGF_SysSunOS, LOGF_SysWinnt, LOGF_TempComp, LOGF_RtcLinux, LOGF_Refclock, LOGF_Smooth, } LOG_Facility; /* Init function */ extern void LOG_Initialise(void); /* Fini function */ extern void LOG_Finalise(void); /* Line logging function */ FORMAT_ATTRIBUTE_PRINTF(6, 7) extern void LOG_Message(LOG_Severity severity, LOG_Facility facility, int line_number, const char *filename, const char *function_name, const char *format, ...); /* Set debug level: 0, 1 - only non-debug messages are logged 2 - debug messages are logged too, all messages are prefixed with filename, line, and function name */ extern void LOG_SetDebugLevel(int level); /* Log messages to syslog instead of stderr */ extern void LOG_OpenSystemLog(void); /* Send fatal message also to the foreground process */ extern void LOG_SetParentFd(int fd); /* Close the pipe to the foreground process so it can exit */ extern void LOG_CloseParentFd(void); /* File logging functions */ typedef int LOG_FileID; extern LOG_FileID LOG_FileOpen(const char *name, const char *banner); FORMAT_ATTRIBUTE_PRINTF(2, 3) extern void LOG_FileWrite(LOG_FileID id, const char *format, ...); extern void LOG_CreateLogFileDir(void); extern void LOG_CycleLogFiles(void); #endif /* GOT_LOGGING_H */ chrony-2.1.1/main.c0000644000000000000000000003153612542263351012616 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) John G. Hasler 2009 * Copyright (C) Miroslav Lichvar 2012-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= The main program */ #include "config.h" #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 "manual.h" #include "rtc.h" #include "refclock.h" #include "clientlog.h" #include "nameserv.h" #include "smooth.h" #include "tempcomp.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 exit_status = 0; static int reload = 0; static REF_Mode ref_mode = REF_ModeNormal; /* ================================================== */ 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); } /* ================================================== */ void MAI_CleanupAndExit(void) { if (!initialised) exit(exit_status); if (CNF_GetDumpOnExit()) { SRC_DumpSources(); } /* Don't update clock when removing sources */ REF_SetMode(REF_ModeIgnore); SMT_Finalise(); TMC_Finalise(); MNL_Finalise(); CLG_Finalise(); NSR_Finalise(); NCR_Finalise(); CAM_Finalise(); NIO_Finalise(); SST_Finalise(); KEY_Finalise(); RCL_Finalise(); SRC_Finalise(); REF_Finalise(); RTC_Finalise(); SYS_Finalise(); SCH_Finalise(); LCL_Finalise(); delete_pidfile(); CNF_Finalise(); LOG_Finalise(); HSH_Finalise(); exit(exit_status); } /* ================================================== */ static void signal_cleanup(int x) { if (!initialised) exit(0); SCH_QuitProgram(); } /* ================================================== */ static void ntp_source_resolving_end(void) { NSR_SetSourceResolvingEndHandler(NULL); 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(); } RTC_StartMeasurements(); RCL_StartRefclocks(); NSR_StartSources(); NSR_AutoStartSources(); /* Special modes can end only when sources update their reachability. Give up immediatelly if there are no active sources. */ if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) { REF_SetUnsynchronised(); } } /* ================================================== */ static void post_init_ntp_hook(void *anything) { if (ref_mode == REF_ModeInitStepSlew) { /* Remove the initstepslew sources and set normal mode */ NSR_RemoveAllSources(); ref_mode = REF_ModeNormal; REF_SetMode(ref_mode); } /* Close the pipe to the foreground process so it can exit */ LOG_CloseParentFd(); CNF_AddSources(); CNF_AddBroadcasts(); NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end); NSR_ResolveSources(); } /* ================================================== */ static void reference_mode_end(int result) { switch (ref_mode) { case REF_ModeNormal: case REF_ModeUpdateOnce: case REF_ModePrintOnce: exit_status = !result; SCH_QuitProgram(); break; case REF_ModeInitStepSlew: /* Switch to the normal mode, the delay is used to prevent polling interval shorter than the burst interval if some configured servers were used also for initstepslew */ SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL); break; default: assert(0); } } /* ================================================== */ static void post_init_rtc_hook(void *anything) { if (CNF_GetInitSources() > 0) { CNF_AddInitSources(); NSR_StartSources(); assert(REF_GetMode() != REF_ModeNormal); /* Wait for mode end notification */ } else { (post_init_ntp_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_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile); } else { fprintf(out, "%d\n", getpid()); fclose(out); } } /* ================================================== */ static void go_daemon(void) { #ifdef WINNT #else int pid, fd, pipefd[2]; /* Create pipe which will the daemon use to notify the grandparent when it's initialised or send an error message */ if (pipe(pipefd)) { LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno)); } /* Does this preserve existing signal handlers? */ pid = fork(); if (pid < 0) { LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); } else if (pid > 0) { /* In the 'grandparent' */ char message[1024]; int r; close(pipefd[1]); r = read(pipefd[0], message, sizeof (message)); if (r) { if (r > 0) { /* Print the error message from the child */ fprintf(stderr, "%.1024s\n", message); } exit(1); } else exit(0); } else { close(pipefd[0]); setsid(); /* Do 2nd fork, as-per recommended practice for launching daemons. */ pid = fork(); if (pid < 0) { LOG_FATAL(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 */ /* Change current directory to / */ if (chdir("/") < 0) { LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno)); } /* Don't keep stdin/out/err from before. But don't close the parent pipe yet. */ for (fd=0; fd<1024; fd++) { if (fd != pipefd[1]) close(fd); } LOG_SetParentFd(pipefd[1]); } } #endif } /* ================================================== */ int main (int argc, char **argv) { const char *conf_file = DEFAULT_CONF_FILE; const char *progname = argv[0]; char *user = NULL; int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; int do_init_rtc = 0, restarted = 0; int other_pid; int lock_memory = 0, sched_priority = 0; int system_log = 1; int config_args = 0; LOG_Initialise(); /* Parse command line options */ while (++argv, (--argc)>0) { if (!strcmp("-f", *argv)) { ++argv, --argc; conf_file = *argv; } else if (!strcmp("-P", *argv)) { ++argv, --argc; if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) { LOG_FATAL(LOGF_Main, "Bad scheduler priority"); } } else if (!strcmp("-m", *argv)) { lock_memory = 1; } else if (!strcmp("-r", *argv)) { reload = 1; } else if (!strcmp("-R", *argv)) { restarted = 1; } else if (!strcmp("-u", *argv)) { ++argv, --argc; if (argc == 0) { LOG_FATAL(LOGF_Main, "Missing user name"); } else { user = *argv; } } 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 (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES); return 0; } else if (!strcmp("-n", *argv)) { nofork = 1; } else if (!strcmp("-d", *argv)) { debug++; nofork = 1; system_log = 0; } else if (!strcmp("-q", *argv)) { ref_mode = REF_ModeUpdateOnce; nofork = 1; system_log = 0; } else if (!strcmp("-Q", *argv)) { ref_mode = REF_ModePrintOnce; nofork = 1; system_log = 0; } else if (!strcmp("-4", *argv)) { address_family = IPADDR_INET4; } else if (!strcmp("-6", *argv)) { address_family = IPADDR_INET6; } else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) { printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n", progname); return 0; } else if (*argv[0] == '-') { LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv); } else { /* Process remaining arguments and configuration lines */ config_args = argc; break; } } if (getuid() != 0) { /* This write to the terminal is OK, it comes before we turn into a daemon */ fprintf(stderr,"Not superuser\n"); return 1; } /* Turn into a daemon */ if (!nofork) { go_daemon(); } if (system_log) { LOG_OpenSystemLog(); } LOG_SetDebugLevel(debug); LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES); DNS_SetAddressFamily(address_family); CNF_Initialise(restarted); /* Parse the config file or the remaining command line arguments */ if (!config_args) { CNF_ReadFile(conf_file); } else { do { CNF_ParseLine(NULL, config_args - argc + 1, *argv); } while (++argv, --argc); } /* 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()); } /* Write our lockfile to prevent other chronyds running. This has *GOT* to * be done *AFTER* the daemon-creation fork() */ write_lockfile(); LCL_Initialise(); SCH_Initialise(); SYS_Initialise(); RTC_Initialise(do_init_rtc); SRC_Initialise(); RCL_Initialise(); KEY_Initialise(); /* Command-line switch must have priority */ if (!sched_priority) { sched_priority = CNF_GetSchedPriority(); } if (sched_priority) { SYS_SetScheduler(sched_priority); } if (lock_memory || CNF_GetLockMemory()) { SYS_LockMemory(); } if (!user) { user = CNF_GetUser(); } if (user && strcmp(user, "root")) { SYS_DropRoot(user); } LOG_CreateLogFileDir(); REF_Initialise(); SST_Initialise(); NIO_Initialise(address_family); CAM_Initialise(address_family); NCR_Initialise(); NSR_Initialise(); CLG_Initialise(); MNL_Initialise(); TMC_Initialise(); SMT_Initialise(); /* From now on, it is safe to do finalisation on exit */ initialised = 1; CNF_SetupAccessRestrictions(); if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) { ref_mode = REF_ModeInitStepSlew; } REF_SetModeEndHandler(reference_mode_end); REF_SetMode(ref_mode); 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(); LOG(LOGS_INFO, LOGF_Main, "chronyd exiting"); MAI_CleanupAndExit(); return 0; } /* ================================================== */ chrony-2.1.1/main.h0000644000000000000000000000224212542263351012613 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for main routine */ #ifndef GOT_MAIN_H #define GOT_MAIN_H /* Function to clean up at end of run */ extern void MAI_CleanupAndExit(void); #endif /* GOT_MAIN_H */ chrony-2.1.1/manual.c0000644000000000000000000001777712542263351013162 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.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 MIN_SAMPLE_SEPARATION 1.0 #define MAX_SAMPLES 16 static Sample samples[16]; static int n_samples; /* ================================================== */ static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *not_used); /* ================================================== */ void MNL_Initialise(void) { if (CNF_GetManualEnabled()) { enabled = 1; } else { enabled = 0; } n_samples = 0; LCL_AddParameterChangeHandler(slew_samples, NULL); } /* ================================================== */ void MNL_Finalise(void) { } /* ================================================== */ 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 max) { *n = max; } else { *n = n_samples; } for (i=0; i= 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, NULL); estimate_and_set_system(&now, 0, 0.0, NULL, NULL, NULL); return 1; } /* ================================================== */ chrony-2.1.1/manual.h0000644000000000000000000000306612542263351013151 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 int MNL_IsEnabled(void); extern void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n); extern int MNL_DeleteSample(int index); #endif /* GOT_MANUAL_H */ chrony-2.1.1/md5.c0000644000000000000000000002727712542263351012366 0ustar rootroot/* *********************************************************************** ** 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) ******************************** */ chrony-2.1.1/md5.h0000644000000000000000000000546012542263351012361 0ustar rootroot/* *********************************************************************** ** 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. ** *********************************************************************** */ #include "sysincl.h" /* 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) ******************************** */ chrony-2.1.1/memory.c0000644000000000000000000000304712542263351013176 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Utility functions for memory allocation. */ #include "config.h" #include "logging.h" #include "memory.h" void * Malloc(size_t size) { void *r; r = malloc(size); if (!r && size) LOG_FATAL(LOGF_Memory, "Could not allocate memory"); return r; } void * Realloc(void *ptr, size_t size) { void *r; r = realloc(ptr, size); if (!r && size) LOG_FATAL(LOGF_Memory, "Could not allocate memory"); return r; } char * Strdup(const char *s) { void *r; r = strdup(s); if (!r) LOG_FATAL(LOGF_Memory, "Could not allocate memory"); return r; } chrony-2.1.1/memory.h0000644000000000000000000000272412542263351013204 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for memory functions */ #ifndef GOT_MEMORY_H #define GOT_MEMORY_H /* Wrappers checking for errors */ extern void *Malloc(size_t size); extern void *Realloc(void *ptr, size_t size); extern char *Strdup(const char *s); /* Convenient macros */ #define MallocNew(T) ((T *) Malloc(sizeof(T))) #define MallocArray(T, n) ((T *) Malloc((n) * sizeof(T))) #define ReallocArray(T,n,x) ((T *) Realloc((void *)(x), (n)*sizeof(T))) #define Free(x) free(x) #endif /* GOT_MEMORY_H */ chrony-2.1.1/mkdirpp.c0000644000000000000000000000544512542263351013340 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= A function for creating a directory and any parent directories that don't exist. */ #include "config.h" #include "sysincl.h" #include "memory.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) { Free(p); 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 chrony-2.1.1/mkdirpp.h0000644000000000000000000000213112542263351013332 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= */ #ifndef GOT_MKDIRPP_H #define GOT_MKDIRPP_H extern int mkdir_and_parents(const char *path); #endif chrony-2.1.1/nameserv.c0000644000000000000000000001105712542263351013506 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2011 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Functions to do name to IP address conversion */ #include "config.h" #include "sysincl.h" #include "nameserv.h" #include "util.h" /* ================================================== */ static int address_family = IPADDR_UNSPEC; void DNS_SetAddressFamily(int family) { address_family = family; } DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) { #ifdef HAVE_GETADDRINFO struct addrinfo hints, *res, *ai; int i, result; memset(&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; result = getaddrinfo(name, NULL, &hints, &res); if (result) { #ifdef FORCE_DNSRETRY return DNS_TryAgain; #else return result == EAI_AGAIN ? DNS_TryAgain : DNS_Failure; #endif } for (ai = res, i = 0; i < max_addrs && ai != NULL; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4) continue; ip_addrs[i].family = IPADDR_INET4; ip_addrs[i].addr.in4 = ntohl(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr); i++; break; #ifdef FEAT_IPV6 case AF_INET6: if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6) continue; ip_addrs[i].family = IPADDR_INET6; memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, sizeof (ip_addrs->addr.in6)); i++; break; #endif } } for (; i < max_addrs; i++) ip_addrs[i].family = IPADDR_UNSPEC; freeaddrinfo(res); return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure; #else struct hostent *host; int i; if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4) return DNS_Failure; host = gethostbyname(name); if (host == NULL) { if (h_errno == TRY_AGAIN) return DNS_TryAgain; } else { if (host->h_addrtype != AF_INET || !host->h_addr_list[0]) return DNS_Failure; for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) { ip_addrs[i].family = IPADDR_INET4; ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]); } for (; i < max_addrs; i++) ip_addrs[i].family = IPADDR_UNSPEC; return DNS_Success; } #ifdef FORCE_DNSRETRY return DNS_TryAgain; #else return DNS_Failure; #endif #endif } /* ================================================== */ int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len) { char *result = NULL; #ifdef FEAT_IPV6 struct sockaddr_in6 in6; socklen_t slen; char hbuf[NI_MAXHOST]; slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6); if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) result = hbuf; #else struct hostent *host; uint32_t addr; switch (ip_addr->family) { case IPADDR_INET4: addr = htonl(ip_addr->addr.in4); host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET); break; #ifdef FEAT_IPV6 case IPADDR_INET6: host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6); break; #endif default: host = NULL; } if (host) result = host->h_name; #endif if (result == NULL) result = UTI_IPToString(ip_addr); if (snprintf(name, len, "%s", result) >= len) return 0; return 1; } /* ================================================== */ void DNS_Reload(void) { res_init(); } /* ================================================== */ chrony-2.1.1/nameserv.h0000644000000000000000000000275612542263351013521 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Module header for nameserver functions */ #ifndef GOT_NAMESERV_H #define GOT_NAMESERV_H #include "addressing.h" typedef enum { DNS_Success, DNS_TryAgain, DNS_Failure } DNS_Status; /* Resolve names only to selected address family */ extern void DNS_SetAddressFamily(int family); extern DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs); extern int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len); extern void DNS_Reload(void); #endif /* GOT_NAMESERV_H */ chrony-2.1.1/nameserv_async.c0000644000000000000000000000640312542263351014702 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Functions to asynchronously convert name to IP address */ #include "config.h" #include "sysincl.h" #include "nameserv_async.h" #include "logging.h" #include "memory.h" #include "sched.h" #include "util.h" #ifdef USE_PTHREAD_ASYNCDNS #include #define MAX_ADDRESSES 16 /* ================================================== */ struct DNS_Async_Instance { const char *name; DNS_Status status; IPAddr addresses[MAX_ADDRESSES]; DNS_NameResolveHandler handler; void *arg; pthread_t thread; int pipe[2]; }; static int resolving_threads = 0; /* ================================================== */ static void * start_resolving(void *anything) { struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; inst->status = DNS_Name2IPAddress(inst->name, inst->addresses, MAX_ADDRESSES); /* Notify the main thread that the result is ready */ if (write(inst->pipe[1], "", 1) < 0) ; return NULL; } /* ================================================== */ static void end_resolving(void *anything) { struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; int i; if (pthread_join(inst->thread, NULL)) { LOG_FATAL(LOGF_Nameserv, "pthread_join() failed"); } resolving_threads--; SCH_RemoveInputFileHandler(inst->pipe[0]); close(inst->pipe[0]); close(inst->pipe[1]); for (i = 0; inst->status == DNS_Success && i < MAX_ADDRESSES && inst->addresses[i].family != IPADDR_UNSPEC; i++) ; (inst->handler)(inst->status, i, inst->addresses, inst->arg); Free(inst); } /* ================================================== */ void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything) { struct DNS_Async_Instance *inst; inst = MallocNew(struct DNS_Async_Instance); inst->name = name; inst->handler = handler; inst->arg = anything; inst->status = DNS_Failure; if (pipe(inst->pipe)) { LOG_FATAL(LOGF_Nameserv, "pipe() failed"); } resolving_threads++; assert(resolving_threads <= 1); if (pthread_create(&inst->thread, NULL, start_resolving, inst)) { LOG_FATAL(LOGF_Nameserv, "pthread_create() failed"); } SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst); } /* ================================================== */ #else #error #endif chrony-2.1.1/nameserv_async.h0000644000000000000000000000305312542263351014705 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header for asynchronous nameserver functions */ #ifndef GOT_NAMESERV_ASYNC_H #define GOT_NAMESERV_ASYNC_H #include "nameserv.h" /* Function type for callback to process the result */ typedef void (*DNS_NameResolveHandler)(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything); /* Request resolving of a name to IP address. The handler will be called when the result is available, but it may be also called directly from this function call. */ extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything); #endif chrony-2.1.1/ntp.h0000644000000000000000000000642512542263351012477 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file containing common NTP bits and pieces */ #ifndef GOT_NTP_H #define GOT_NTP_H #include "sysincl.h" #include "hash.h" typedef struct { uint32_t hi; uint32_t lo; } NTP_int64; typedef uint32_t NTP_int32; /* The NTP protocol version that we support */ #define NTP_VERSION 4 /* Maximum stratum number (infinity) */ #define NTP_MAX_STRATUM 16 /* The minimum valid length of an extension field */ #define NTP_MIN_EXTENSION_LENGTH 16 /* The maximum assumed length of all extension fields in received packets (RFC 5905 doesn't specify a limit on length or number of extension fields in one packet) */ #define NTP_MAX_EXTENSIONS_LENGTH 1024 /* The minimum and maximum supported length of MAC */ #define NTP_MIN_MAC_LENGTH 16 #define NTP_MAX_MAC_LENGTH MAX_HASH_LENGTH /* 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; /* Optional extension fields, we don't send packets with them yet */ /* uint8_t extensions[] */ /* Optional message authentication code (MAC) */ NTP_int32 auth_keyid; uint8_t auth_data[NTP_MAX_MAC_LENGTH]; } NTP_Packet; #define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid) /* The buffer used to hold a datagram read from the network */ typedef struct { NTP_Packet ntp_pkt; uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH]; } NTP_Receive_Buffer; /* Macros to work with the lvm field */ #define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3) #define NTP_LVM_TO_VERSION(lvm) (((lvm) >> 3) & 0x7) #define NTP_LVM_TO_MODE(lvm) ((lvm) & 0x7) #define NTP_LVM(leap, version, mode) \ ((((leap) << 6) & 0xc0) | (((version) << 3) & 0x38) | ((mode) & 0x07)) /* Special NTP reference IDs */ #define NTP_REFID_UNSYNC 0x0UL #define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */ #define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */ #endif /* GOT_NTP_H */ chrony-2.1.1/ntp_core.c0000644000000000000000000020356312542263351013504 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Core NTP protocol engine */ #include "config.h" #include "sysincl.h" #include "array.h" #include "ntp_core.h" #include "ntp_io.h" #include "memory.h" #include "sched.h" #include "reference.h" #include "local.h" #include "smooth.h" #include "sources.h" #include "util.h" #include "conf.h" #include "logging.h" #include "keys.h" #include "addrfilt.h" #include "clientlog.h" /* ================================================== */ static LOG_FileID logfileid; /* ================================================== */ /* Enumeration used for remembering the operating mode of one of the sources */ typedef enum { MD_OFFLINE, /* No sampling at all */ MD_ONLINE, /* Normal sampling based on sampling interval */ MD_BURST_WAS_OFFLINE, /* Burst sampling, return to offline afterwards */ MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */ } OperatingMode; /* ================================================== */ /* Structure used for holding a single peer/server's protocol machine */ struct NCR_Instance_Record { NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */ NTP_Local_Address local_addr; /* Local address/socket used to send packets */ NTP_Mode mode; /* The source's NTP mode (client/server or symmetric active peer) */ OperatingMode opmode; /* Whether we are sampling this source or not and in what way */ int timer_running; /* Boolean indicating whether we have a timeout pending to transmit to the source */ SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are running on a timer. */ int tx_suspended; /* Boolean indicating we can't transmit yet */ int auto_offline; /* If 1, automatically go offline if server/peer isn't responding */ int local_poll; /* Log2 of polling interval at our end */ int remote_poll; /* Log2 of server/peer's polling interval (recovered from received packets) */ int remote_stratum; /* Stratum of the server/peer (recovered from received packets) */ int presend_minpoll; /* If the current polling interval is at least this, an extra client packet will be send some time before normal transmit. This ensures that both us and the server/peer have an ARP entry for each other ready, which means our measurement is not botched by an ARP round-trip on one side or the other. */ int presend_done; /* The presend packet has been sent */ int minpoll; /* Log2 of minimum defined polling interval */ int maxpoll; /* Log2 of maximum defined polling interval */ int min_stratum; /* Increase stratum in received packets to the minimum */ int poll_target; /* Target number of sourcestats samples */ int version; /* Version set in packets for server/peer */ double poll_score; /* Score of current local poll */ double max_delay; /* Maximum round-trip delay to the peer that we can tolerate and still use the sample for generating statistics from */ double max_delay_ratio; /* Largest ratio of delay / min_delay_in_register that we can tolerate. */ double max_delay_dev_ratio; /* Maximum ratio of increase in delay / stddev */ int do_auth; /* Flag indicating whether we authenticate packets we send to this machine (if it's serving us or the association is symmetric). Note : we don't authenticate if we can't find the key in our database. */ uint32_t auth_key_id; /* The ID of the authentication key to use. */ /* Count of how many packets we have transmitted since last successful receive from this peer */ int tx_count; /* Timestamp in tx field of last received packet. We have to reproduce this exactly as the orig field or our outgoing packet. */ NTP_int64 remote_orig; /* Local timestamp when the last packet was received from the source. We have to be prepared to tinker with this if the local clock has its frequency adjusted before we repond. The value we store here is what our own local time was when the same arrived. Before replying, we have to correct this to fit with the parameters for the current reference. (It must be stored relative to local time to permit frequency and offset adjustments to be made when we trim the local clock). */ struct timeval local_rx; /* Local timestamp when we last transmitted a packet to the source. We store two versions. The first is in NTP format, and is used to validate the next received packet from the source. Additionally, this is corrected to bring it into line with the current reference. The second is in timeval format, and is kept relative to the local clock. We modify this in accordance with local clock frequency/offset changes, and use this for computing statistics about the source when a return packet arrives. */ NTP_int64 local_ntp_tx; struct timeval local_tx; /* The instance record in the main source management module. This performs the statistical analysis on the samples we generate */ SRC_Instance source; int burst_good_samples_to_go; int burst_total_samples_to_go; }; typedef struct { NTP_Remote_Address addr; NTP_Local_Address local_addr; int interval; } BroadcastDestination; /* Array of BroadcastDestination */ static ARR_Instance broadcasts; /* ================================================== */ /* Initial delay period before first packet is transmitted (in seconds) */ #define INITIAL_DELAY 0.2 /* Spacing required between samples for any two servers/peers (to minimise risk of network collisions) (in seconds) */ #define SAMPLING_SEPARATION 0.2 /* Randomness added to spacing between samples for one server/peer */ #define SAMPLING_RANDOMNESS 0.02 /* Adjustment of the peer polling interval */ #define PEER_SAMPLING_ADJ 1.1 /* Spacing between samples in burst mode for one server/peer */ #define BURST_INTERVAL 2.0 /* Time to wait before retransmitting in burst mode, if we did not get a reply to the previous probe */ #define BURST_TIMEOUT 2.0 /* Number of samples in initial burst */ #define IBURST_GOOD_SAMPLES 4 #define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS /* Time to wait after sending packet to 'warm up' link */ #define WARM_UP_DELAY 4.0 /* Compatible NTP protocol versions */ #define NTP_MAX_COMPAT_VERSION NTP_VERSION #define NTP_MIN_COMPAT_VERSION 1 /* Maximum allowed dispersion - as defined in RFC 5905 (16 seconds) */ #define NTP_MAX_DISPERSION 16.0 /* Invalid stratum number */ #define NTP_INVALID_STRATUM 0 /* Minimum and maximum allowed poll interval */ #define MIN_POLL 0 #define MAX_POLL 24 /* Kiss-o'-Death codes */ #define KOD_RATE 0x52415445UL /* RATE */ /* Maximum poll interval set by KoD RATE */ #define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL /* Invalid socket, different from the one in ntp_io.c */ #define INVALID_SOCK_FD -2 /* ================================================== */ /* Server IPv4/IPv6 sockets */ static int server_sock_fd4; static int server_sock_fd6; static ADF_AuthTable access_auth_table; /* ================================================== */ /* Forward prototypes */ static void transmit_timeout(void *arg); static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx); /* ================================================== */ 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); } /* ================================================== */ static void do_time_checks(void) { struct timeval now; time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */ #ifdef HAVE_LONG_TIME_T /* Check that time before NTP_ERA_SPLIT underflows correctly */ struct timeval tv1 = {NTP_ERA_SPLIT, 1}, tv2 = {NTP_ERA_SPLIT - 1, 1}; NTP_int64 ntv1, ntv2; int r; UTI_TimevalToInt64(&tv1, &ntv1, 0); UTI_TimevalToInt64(&tv2, &ntv2, 0); UTI_Int64ToTimeval(&ntv1, &tv1); UTI_Int64ToTimeval(&ntv2, &tv2); r = tv1.tv_sec == NTP_ERA_SPLIT && tv1.tv_sec + (1ULL << 32) - 1 == tv2.tv_sec; assert(r); LCL_ReadRawTime(&now); if (tv2.tv_sec - now.tv_sec < warning_advance) LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!", UTI_TimeToLogForm(tv2.tv_sec)); #else LCL_ReadRawTime(&now); if (now.tv_sec > 0x7fffffff - warning_advance) LOG(LOGS_WARN, LOGF_NtpCore, "System time ends at %s!", UTI_TimeToLogForm(0x7fffffff)); #endif } /* ================================================== */ void NCR_Initialise(void) { do_size_checks(); do_time_checks(); logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements", " Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp.") : -1; access_auth_table = ADF_CreateTable(); broadcasts = ARR_CreateInstance(sizeof (BroadcastDestination)); /* Server socket will be opened when access is allowed */ server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD; } /* ================================================== */ void NCR_Finalise(void) { unsigned int i; if (server_sock_fd4 != INVALID_SOCK_FD) NIO_CloseServerSocket(server_sock_fd4); if (server_sock_fd6 != INVALID_SOCK_FD) NIO_CloseServerSocket(server_sock_fd6); for (i = 0; i < ARR_GetSize(broadcasts); i++) NIO_CloseServerSocket(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->local_addr.sock_fd); ARR_DestroyInstance(broadcasts); ADF_DestroyTable(access_auth_table); } /* ================================================== */ static void restart_timeout(NCR_Instance inst, double delay) { /* Check if we can transmit */ if (inst->tx_suspended) { assert(!inst->timer_running); return; } /* Stop old timer if running */ if (inst->timer_running) SCH_RemoveTimeout(inst->timeout_id); /* Start new timer for transmission */ inst->timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *)inst); inst->timer_running = 1; } /* ================================================== */ static void start_initial_timeout(NCR_Instance inst) { if (!inst->timer_running) { /* This will be the first transmission after mode change */ /* Mark source active */ SRC_SetActive(inst->source); } restart_timeout(inst, INITIAL_DELAY); } /* ================================================== */ static void close_client_socket(NCR_Instance inst) { if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) { NIO_CloseClientSocket(inst->local_addr.sock_fd); inst->local_addr.sock_fd = INVALID_SOCK_FD; } } /* ================================================== */ static void take_offline(NCR_Instance inst) { inst->opmode = MD_OFFLINE; if (inst->timer_running) { SCH_RemoveTimeout(inst->timeout_id); inst->timer_running = 0; } /* Mark source unreachable */ SRC_ResetReachability(inst->source); /* And inactive */ SRC_UnsetActive(inst->source); close_client_socket(inst); NCR_ResetInstance(inst); } /* ================================================== */ NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) { NCR_Instance result; result = MallocNew(struct NCR_Instance_Record); result->remote_addr = *remote_addr; result->local_addr.ip_addr.family = IPADDR_UNSPEC; switch (type) { case NTP_SERVER: /* Client socket will be obtained when sending request */ result->local_addr.sock_fd = INVALID_SOCK_FD; result->mode = MODE_CLIENT; break; case NTP_PEER: result->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr); result->mode = MODE_ACTIVE; break; default: assert(0); } result->minpoll = params->minpoll; if (result->minpoll < MIN_POLL) result->minpoll = SRC_DEFAULT_MINPOLL; else if (result->minpoll > MAX_POLL) result->minpoll = MAX_POLL; result->maxpoll = params->maxpoll; if (result->maxpoll < MIN_POLL) result->maxpoll = SRC_DEFAULT_MAXPOLL; else if (result->maxpoll > MAX_POLL) result->maxpoll = MAX_POLL; if (result->maxpoll < result->minpoll) result->maxpoll = result->minpoll; result->min_stratum = params->min_stratum; if (result->min_stratum >= NTP_MAX_STRATUM) result->min_stratum = NTP_MAX_STRATUM - 1; result->presend_minpoll = params->presend_minpoll; result->max_delay = params->max_delay; result->max_delay_ratio = params->max_delay_ratio; result->max_delay_dev_ratio = params->max_delay_dev_ratio; result->auto_offline = params->auto_offline; result->poll_target = params->poll_target; result->version = params->version; if (result->version < NTP_MIN_COMPAT_VERSION) result->version = NTP_MIN_COMPAT_VERSION; else if (result->version > NTP_VERSION) result->version = NTP_VERSION; if (params->authkey == INACTIVE_AUTHKEY) { result->do_auth = 0; result->auth_key_id = 0; } else { result->do_auth = 1; result->auth_key_id = params->authkey; if (!KEY_KeyKnown(result->auth_key_id)) { LOG(LOGS_WARN, LOGF_NtpCore, "Source %s added with unknown key %"PRIu32, UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id); } } /* Create a source instance for this NTP source */ result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr), SRC_NTP, params->sel_option, &result->remote_addr.ip_addr, params->min_samples, params->max_samples); result->timer_running = 0; result->timeout_id = 0; result->tx_suspended = 1; result->opmode = params->online ? MD_ONLINE : MD_OFFLINE; result->local_poll = result->minpoll; NCR_ResetInstance(result); if (params->iburst) { NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES); } return result; } /* ================================================== */ /* Destroy an instance */ void NCR_DestroyInstance(NCR_Instance instance) { if (instance->opmode != MD_OFFLINE) take_offline(instance); if (instance->mode == MODE_ACTIVE) NIO_CloseServerSocket(instance->local_addr.sock_fd); /* This will destroy the source instance inside the structure, which will cause reselection if this was the synchronising source etc. */ SRC_DestroyInstance(instance->source); /* Free the data structure */ Free(instance); } /* ================================================== */ void NCR_StartInstance(NCR_Instance instance) { instance->tx_suspended = 0; if (instance->opmode != MD_OFFLINE) start_initial_timeout(instance); } /* ================================================== */ void NCR_ResetInstance(NCR_Instance instance) { instance->tx_count = 0; instance->presend_done = 0; instance->poll_score = 0.0; instance->remote_poll = 0; instance->remote_stratum = 0; instance->remote_orig.hi = 0; instance->remote_orig.lo = 0; instance->local_rx.tv_sec = 0; instance->local_rx.tv_usec = 0; instance->local_tx.tv_sec = 0; instance->local_tx.tv_usec = 0; instance->local_ntp_tx.hi = 0; instance->local_ntp_tx.lo = 0; if (instance->local_poll != instance->minpoll) { instance->local_poll = instance->minpoll; /* The timer was set with a longer poll interval, restart it */ if (instance->timer_running) restart_timeout(instance, get_transmit_delay(instance, 0, 0.0)); } } /* ================================================== */ void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr) { inst->remote_addr = *remote_addr; inst->tx_count = 0; inst->presend_done = 0; if (inst->mode == MODE_CLIENT) close_client_socket(inst); else { NIO_CloseServerSocket(inst->local_addr.sock_fd); inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr); } /* Update the reference ID and reset the source/sourcestats instances */ SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr), &inst->remote_addr.ip_addr); SRC_ResetInstance(inst->source); } /* ================================================== */ static void adjust_poll(NCR_Instance inst, double adj) { inst->poll_score += adj; if (inst->poll_score >= 1.0) { inst->local_poll += (int)inst->poll_score; inst->poll_score -= (int)inst->poll_score; } if (inst->poll_score < 0.0) { inst->local_poll += (int)(inst->poll_score - 1.0); inst->poll_score -= (int)(inst->poll_score - 1.0); } /* Clamp polling interval to defined range */ if (inst->local_poll < inst->minpoll) { inst->local_poll = inst->minpoll; inst->poll_score = 0; } else if (inst->local_poll > inst->maxpoll) { inst->local_poll = inst->maxpoll; inst->poll_score = 1.0; } } /* ================================================== */ static double get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance) { double poll_adj; if (error_in_estimate > peer_distance) { int shift = 0; unsigned long temp = (int)(error_in_estimate / peer_distance); do { shift++; temp>>=1; } while (temp); poll_adj = -shift - inst->poll_score + 0.5; } else { int samples = SRC_Samples(inst->source); /* Adjust polling interval so that the number of sourcestats samples remains close to the target value */ poll_adj = ((double)samples / inst->poll_target - 1.0) / inst->poll_target; /* Make interval shortening quicker */ if (samples < inst->poll_target) { poll_adj *= 2.0; } } return poll_adj; } /* ================================================== */ static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx) { int poll_to_use, stratum_diff; double delay_time; /* If we're in burst mode, queue for immediate dispatch. If we're operating in client/server mode, queue the timeout for the poll interval hence. The fact that a timeout has been queued in the transmit handler is immaterial - that is only done so that we at least send something, if no reply is heard. If we're in symmetric mode, we have to take account of the peer's wishes, otherwise his sampling regime will fall to pieces. If we're in client/server mode, we don't care what poll interval the server responded with last time. */ switch (inst->opmode) { case MD_OFFLINE: assert(0); break; case MD_ONLINE: /* Normal processing, depending on whether we're in client/server or symmetric mode */ switch(inst->mode) { case MODE_CLIENT: /* Client/server association - aim at some randomised time approx the poll interval away */ poll_to_use = inst->local_poll; delay_time = (double) (1UL<local_poll; if (poll_to_use > inst->remote_poll) poll_to_use = inst->remote_poll; if (poll_to_use < inst->minpoll) poll_to_use = inst->minpoll; delay_time = (double) (1UL<remote_stratum - REF_GetOurStratum(); if ((stratum_diff > 0 && last_tx * PEER_SAMPLING_ADJ < delay_time) || (!on_tx && !stratum_diff && last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5)) delay_time *= PEER_SAMPLING_ADJ; /* Substract the already spend time */ if (last_tx > 0.0) delay_time -= last_tx; if (delay_time < 0.0) delay_time = 0.0; break; default: assert(0); break; } break; case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_OFFLINE: /* Burst modes */ delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL; break; default: assert(0); break; } return delay_time; } /* ================================================== */ static int transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */ int my_poll, /* The log2 of the local poll interval */ int version, /* The NTP version to be set in the packet */ int do_auth, /* Boolean indicating whether to authenticate the packet or not */ uint32_t key_id, /* The authentication key ID */ NTP_int64 *orig_ts, /* Originate timestamp (from received packet) */ struct timeval *local_rx, /* Local time request packet was received */ struct timeval *local_tx, /* RESULT : Time this reply is sent as local time, or NULL if don't want to know */ NTP_int64 *local_ntp_tx, /* RESULT : Time reply sent as NTP timestamp (including adjustment to reference), ignored if NULL */ NTP_Remote_Address *where_to, /* Where to address the reponse to */ NTP_Local_Address *from /* From what address to send it */ ) { NTP_Packet message; int leap, auth_len, length, ret; struct timeval local_receive, local_transmit; /* Parameters read from reference module */ int are_we_synchronised, our_stratum, smooth_time; NTP_Leap leap_status; uint32_t our_ref_id, ts_fuzz; struct timeval our_ref_time; double our_root_delay, our_root_dispersion, smooth_offset; /* Don't reply with version higher than ours */ if (version > NTP_VERSION) { version = NTP_VERSION; } /* This is accurate enough and cheaper than calling LCL_ReadCookedTime. A more accurate time stamp will be taken later in this function. */ SCH_GetLastEventTime(&local_transmit, NULL, NULL); REF_GetReferenceParams(&local_transmit, &are_we_synchronised, &leap_status, &our_stratum, &our_ref_id, &our_ref_time, &our_root_delay, &our_root_dispersion); /* Get current smoothing offset when sending packet to a client */ if (SMT_IsEnabled() && (my_mode == MODE_SERVER || my_mode == MODE_BROADCAST)) { smooth_offset = SMT_GetOffset(&local_transmit); smooth_time = fabs(smooth_offset) > LCL_GetSysPrecisionAsQuantum(); /* Suppress leap second when smoothing and slew mode are enabled */ if (REF_GetLeapMode() == REF_LeapModeSlew && (leap_status == LEAP_InsertSecond || leap_status == LEAP_DeleteSecond)) leap_status = LEAP_Normal; } else { smooth_time = 0; smooth_offset = 0.0; } if (smooth_time) { our_ref_id = NTP_REFID_SMOOTH; UTI_AddDoubleToTimeval(&our_ref_time, smooth_offset, &our_ref_time); UTI_AddDoubleToTimeval(local_rx, smooth_offset, &local_receive); } else { local_receive = *local_rx; } if (are_we_synchronised) { leap = (int) leap_status; } else { leap = LEAP_Unsynchronised; } /* Generate transmit packet */ message.lvm = NTP_LVM(leap, version, my_mode); /* Stratum 16 and larger are invalid */ if (our_stratum < NTP_MAX_STRATUM) { message.stratum = our_stratum; } else { message.stratum = NTP_INVALID_STRATUM; } message.poll = my_poll; message.precision = LCL_GetSysPrecisionAsLog(); /* If we're sending a client mode packet and we aren't synchronized yet, we might have to set up artificial values for some of these parameters */ message.root_delay = UTI_DoubleToInt32(our_root_delay); message.root_dispersion = UTI_DoubleToInt32(our_root_dispersion); message.reference_id = htonl(our_ref_id); /* Now fill in timestamps */ UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, 0); /* Originate - this comes from the last packet the source sent us */ message.originate_ts = *orig_ts; /* Receive - this is when we received the last packet from the source. This timestamp will have been adjusted so that it will now look to the source like we have been running on our latest estimate of frequency all along */ UTI_TimevalToInt64(&local_receive, &message.receive_ts, 0); /* Prepare random bits which will be added to the transmit timestamp. */ ts_fuzz = UTI_GetNTPTsFuzz(message.precision); /* Transmit - this our local time right now! Also, we might need to store this for our own use later, next time we receive a message from the source we're sending to now. */ LCL_ReadCookedTime(&local_transmit, NULL); if (smooth_time) UTI_AddDoubleToTimeval(&local_transmit, smooth_offset, &local_transmit); length = NTP_NORMAL_PACKET_LENGTH; /* Authenticate */ if (do_auth && key_id) { /* Pre-compensate the transmit time by approx. how long it will take to generate the authentication data. */ local_transmit.tv_usec += KEY_GetAuthDelay(key_id); UTI_NormaliseTimeval(&local_transmit); UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz); auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message, offsetof(NTP_Packet, auth_keyid), (unsigned char *)&message.auth_data, sizeof (message.auth_data)); if (auth_len > 0) { message.auth_keyid = htonl(key_id); length += sizeof (message.auth_keyid) + auth_len; } else { DEBUG_LOG(LOGF_NtpCore, "Could not generate auth data with key %"PRIu32" to send packet", key_id); return 0; } } else { if (do_auth) { /* Zero key ID means crypto-NAK, append only the ID without any data */ message.auth_keyid = 0; length += sizeof (message.auth_keyid); } UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz); } ret = NIO_SendPacket(&message, where_to, from, length); if (local_tx) { *local_tx = local_transmit; } if (local_ntp_tx) { *local_ntp_tx = message.transmit_ts; } return ret; } /* ================================================== */ /* Timeout handler for transmitting to a source. */ static void transmit_timeout(void *arg) { NCR_Instance inst = (NCR_Instance) arg; int sent; inst->timer_running = 0; switch (inst->opmode) { case MD_BURST_WAS_ONLINE: /* With online burst switch to online before last packet */ if (inst->burst_total_samples_to_go <= 1) inst->opmode = MD_ONLINE; case MD_BURST_WAS_OFFLINE: if (inst->burst_total_samples_to_go <= 0) take_offline(inst); break; default: break; } /* With auto_offline take the source offline on 2nd missed reply */ if (inst->auto_offline && inst->tx_count >= 2) NCR_TakeSourceOffline(inst); if (inst->opmode == MD_OFFLINE) { return; } DEBUG_LOG(LOGF_NtpCore, "Transmit timeout for [%s:%d]", UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); /* Open new client socket */ if (inst->mode == MODE_CLIENT) { close_client_socket(inst); assert(inst->local_addr.sock_fd == INVALID_SOCK_FD); inst->local_addr.sock_fd = NIO_OpenClientSocket(&inst->remote_addr); } /* Check whether we need to 'warm up' the link to the other end by sending an NTP exchange to ensure both ends' ARP caches are primed. On loaded systems this might also help ensure that bits of the program are paged in properly before we start. */ if ((inst->presend_minpoll > 0) && (inst->presend_minpoll <= inst->local_poll) && !inst->presend_done) { /* Send a client packet, don't store the local tx values as the reply will be ignored */ transmit_packet(MODE_CLIENT, inst->local_poll, inst->version, 0, 0, &inst->remote_orig, &inst->local_rx, NULL, NULL, &inst->remote_addr, &inst->local_addr); inst->presend_done = 1; /* Requeue timeout */ restart_timeout(inst, WARM_UP_DELAY); return; } inst->presend_done = 0; /* Reset for next time */ sent = transmit_packet(inst->mode, inst->local_poll, inst->version, inst->do_auth, inst->auth_key_id, &inst->remote_orig, &inst->local_rx, &inst->local_tx, &inst->local_ntp_tx, &inst->remote_addr, &inst->local_addr); ++inst->tx_count; /* If the source loses connectivity and our packets are still being sent, back off the sampling rate to reduce the network traffic. If it's the source to which we are currently locked, back off slowly. */ if (inst->tx_count >= 2) { /* Implies we have missed at least one transmission */ if (sent) { adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25); } SRC_UpdateReachability(inst->source, 0); } switch (inst->opmode) { case MD_BURST_WAS_ONLINE: /* When not reachable, don't stop online burst until sending succeeds */ if (!sent && !SRC_IsReachable(inst->source)) break; /* Fall through */ case MD_BURST_WAS_OFFLINE: --inst->burst_total_samples_to_go; break; default: break; } /* Restart timer for this message */ restart_timeout(inst, get_transmit_delay(inst, 1, 0.0)); } /* ================================================== */ static int check_packet_format(NTP_Packet *message, int length) { int version; /* Check version and length */ version = NTP_LVM_TO_VERSION(message->lvm); if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) { DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid version %d", version); return 0; } if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) { DEBUG_LOG(LOGF_NtpCore, "NTP packet has invalid length %d", length); return 0; } /* We can't reliably check the packet for invalid extension fields as we support MACs longer than the shortest valid extension field */ return 1; } /* ================================================== */ static int check_packet_auth(NTP_Packet *pkt, int length, int *has_auth, uint32_t *key_id) { int i, remainder, ext_length; unsigned char *data; uint32_t id; /* Go through extension fields and see if there is a valid MAC */ i = NTP_NORMAL_PACKET_LENGTH; data = (void *)pkt; while (1) { remainder = length - i; /* Check if the remaining data is a valid MAC. This needs to be done before trying to parse it as an extension field, because we support MACs longer than the shortest valid extension field. */ if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_MAC_LENGTH) { id = ntohl(*(uint32_t *)(data + i)); if (KEY_CheckAuth(id, (void *)pkt, i, (void *)(data + i + 4), remainder - 4)) { if (key_id) *key_id = id; if (has_auth) *has_auth = 1; return 1; } } /* Check if this is a valid field extension. They consist of 16-bit type, 16-bit length of the whole field aligned to 32 bits and data. */ if (remainder >= NTP_MIN_EXTENSION_LENGTH) { ext_length = ntohs(*(uint16_t *)(data + i + 2)); if (ext_length >= NTP_MIN_EXTENSION_LENGTH && ext_length <= remainder && ext_length % 4 == 0) { i += ext_length; continue; } } /* Invalid or missing MAC, or format error */ break; } /* This is not 100% reliable as a MAC could fail to authenticate and could pass as an extension field, leaving reminder smaller than the minimum MAC length. Not a big problem, at worst we won't reply with a crypto-NAK. */ if (has_auth) *has_auth = remainder >= NTP_MIN_MAC_LENGTH; return 0; } /* ================================================== */ static int receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, NTP_Local_Address *local_addr, int length) { int pkt_leap; uint32_t pkt_refid; double pkt_root_delay; double pkt_root_dispersion; /* The local time to which the (offset, delay, dispersion) triple will be taken to relate. For client/server operation this is practically the same as either the transmit or receive time. The difference comes in symmetric active mode, when the receive may come minutes after the transmit, and this time will be midway between the two */ struct timeval sample_time; /* The estimated offset in seconds, a positive value indicates that the local clock is SLOW of the remote source and a negative value indicates that the local clock is FAST of the remote source */ double offset; /* The estimated peer delay, dispersion and distance */ double delay, dispersion, distance; /* The total root delay and dispersion */ double root_delay, root_dispersion; /* The skew and estimated frequency offset relative to the remote source */ double skew, source_freq_lo, source_freq_hi; /* These are the timeval equivalents of the remote epochs */ struct timeval remote_receive_tv, remote_transmit_tv; struct timeval remote_reference_tv; struct timeval local_average, remote_average; double local_interval, remote_interval; /* RFC 5905 packet tests */ int test1, test2, test3, test5, test6, test7; int valid_packet; /* Additional tests */ int testA, testB, testC, testD; int good_packet; /* Kiss-o'-Death codes */ int kod_rate; /* Characters used to print synchronisation status */ static const char sync_stats[4] = {'N', '+', '-', '?'}; /* The estimated offset predicted from previous samples. The convention here is that positive means local clock FAST of reference, i.e. backwards to the way that 'offset' is defined. */ double estimated_offset; /* The absolute difference between the offset estimate and measurement in seconds */ double error_in_estimate; double delay_time, precision; int requeue_transmit; /* ==================== */ pkt_leap = NTP_LVM_TO_LEAP(message->lvm); pkt_refid = ntohl(message->reference_id); pkt_root_delay = UTI_Int32ToDouble(message->root_delay); pkt_root_dispersion = UTI_Int32ToDouble(message->root_dispersion); UTI_Int64ToTimeval(&message->receive_ts, &remote_receive_tv); UTI_Int64ToTimeval(&message->transmit_ts, &remote_transmit_tv); UTI_Int64ToTimeval(&message->reference_ts, &remote_reference_tv); /* Check if the packet is valid per RFC 5905, section 8. The test values are 1 when passed and 0 when failed. */ /* Test 1 checks for duplicate packet */ test1 = message->transmit_ts.hi != inst->remote_orig.hi || message->transmit_ts.lo != inst->remote_orig.lo; /* Test 2 checks for bogus packet. This ensures the source is responding to the latest packet we sent to it. */ test2 = message->originate_ts.hi == inst->local_ntp_tx.hi && message->originate_ts.lo == inst->local_ntp_tx.lo; /* Test 3 checks for invalid timestamps. This can happen when the association if not properly 'up'. */ test3 = (message->originate_ts.hi || message->originate_ts.lo) && (message->receive_ts.hi || message->receive_ts.lo) && (message->reference_ts.hi || message->reference_ts.lo) && (message->transmit_ts.hi || message->transmit_ts.lo); /* Test 4 would check for denied access. It would always pass as this function is called only for known sources. */ /* Test 5 checks for authentication failure. If we expect authenticated info from this peer/server and the packet doesn't have it or the authentication is bad, it's got to fail. If the peer or server sends us an authenticated frame, but we're not bothered about whether he authenticates or not, just ignore the test. */ test5 = inst->do_auth ? check_packet_auth(message, length, NULL, NULL) : 1; /* Test 6 checks for unsynchronised server */ test6 = pkt_leap != LEAP_Unsynchronised && message->stratum < NTP_MAX_STRATUM && message->stratum != NTP_INVALID_STRATUM; /* Test 7 checks for bad data. The root distance must be smaller than a defined maximum and the transmit time must not be before the time of the last synchronisation update. */ test7 = pkt_root_delay / 2.0 + pkt_root_dispersion < NTP_MAX_DISPERSION && UTI_CompareTimevals(&remote_reference_tv, &remote_transmit_tv) < 1; /* The packet is considered valid if the tests above passed */ valid_packet = test1 && test2 && test3 && test5 && test6 && test7; /* Check for Kiss-o'-Death codes */ kod_rate = 0; if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised && message->stratum == NTP_INVALID_STRATUM) { if (pkt_refid == KOD_RATE) kod_rate = 1; } /* The transmit timestamp and local receive timestamp must not be saved when the authentication test failed to prevent denial-of-service attacks on symmetric associations using authentication */ if (test5) { inst->remote_orig = message->transmit_ts; inst->local_rx = *now; } /* This protects against replay of the last packet we sent */ if (test2) inst->local_ntp_tx.hi = inst->local_ntp_tx.lo = 0; if (valid_packet) { precision = LCL_GetSysPrecisionAsQuantum() + UTI_Log2ToDouble(message->precision); SRC_GetFrequencyRange(inst->source, &source_freq_lo, &source_freq_hi); UTI_AverageDiffTimevals(&remote_receive_tv, &remote_transmit_tv, &remote_average, &remote_interval); UTI_AverageDiffTimevals(&inst->local_tx, now, &local_average, &local_interval); /* In our case, we work out 'delay' as the worst case delay, assuming worst case frequency error between us and the other source */ delay = local_interval - remote_interval * (1.0 + source_freq_lo); /* Clamp delay to avoid misleading results later */ delay = fabs(delay); if (delay < precision) delay = precision; /* Calculate offset. Following the NTP definition, this is negative if we are fast of the remote source. */ UTI_DiffTimevalsToDouble(&offset, &remote_average, &local_average); /* We treat the time of the sample as being midway through the local measurement period. An analysis assuming constant relative frequency and zero network delay shows this is the only possible choice to estimate the frequency difference correctly for every sample pair. */ sample_time = local_average; /* Calculate skew */ skew = (source_freq_hi - source_freq_lo) / 2.0; /* and then calculate peer dispersion */ dispersion = precision + now_err + skew * fabs(local_interval); /* Additional tests required to pass before accumulating the sample */ /* Test A requires that the round trip delay is less than an administrator-defined value */ testA = delay <= inst->max_delay; /* Test B requires that the ratio of the round trip delay to the minimum one currently in the stats data register is less than an administrator-defined value */ testB = inst->max_delay_ratio <= 1.0 || delay / SRC_MinRoundTripDelay(inst->source) <= inst->max_delay_ratio; /* Test C requires that the ratio of the increase in delay from the minimum one in the stats data register to the standard deviation of the offsets in the register is less than an administrator-defined value or the difference between measured offset and predicted offset is larger than the increase in delay */ testC = SRC_IsGoodSample(inst->source, -offset, delay, inst->max_delay_dev_ratio, LCL_GetMaxClockError(), &sample_time); /* Test D requires that the remote peer is not synchronised to us to prevent a synchronisation loop */ testD = message->stratum <= 1 || pkt_refid != UTI_IPToRefid(&local_addr->ip_addr); } else { offset = delay = dispersion = 0.0; sample_time = *now; testA = testB = testC = testD = 0; } /* The packet is considered good for synchronisation if the additional tests passed */ good_packet = testA && testB && testC && testD; root_delay = pkt_root_delay + delay; root_dispersion = pkt_root_dispersion + dispersion; distance = dispersion + 0.5 * delay; DEBUG_LOG(LOGF_NtpCore, "NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]", message->lvm, message->stratum, message->poll, message->precision, pkt_root_delay, pkt_root_dispersion, pkt_refid, message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : ""); DEBUG_LOG(LOGF_NtpCore, "reference=%s origin=%s receive=%s transmit=%s", UTI_TimestampToString(&message->reference_ts), UTI_TimestampToString(&message->originate_ts), UTI_TimestampToString(&message->receive_ts), UTI_TimestampToString(&message->transmit_ts)); DEBUG_LOG(LOGF_NtpCore, "offset=%f delay=%f dispersion=%f root_delay=%f root_dispersion=%f", offset, delay, dispersion, root_delay, root_dispersion); DEBUG_LOG(LOGF_NtpCore, "test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d valid=%d good=%d", test1, test2, test3, test5, test6, test7, testA, testB, testC, testD, kod_rate, valid_packet, good_packet); requeue_transmit = 0; /* Reduce polling rate if KoD RATE was received */ if (kod_rate) { if (message->poll > inst->minpoll) { /* Set our minpoll to message poll, but use a reasonable maximum */ if (message->poll <= MAX_KOD_RATE_POLL) inst->minpoll = message->poll; else if (inst->minpoll < MAX_KOD_RATE_POLL) inst->minpoll = MAX_KOD_RATE_POLL; if (inst->minpoll > inst->maxpoll) inst->maxpoll = inst->minpoll; if (inst->minpoll > inst->local_poll) inst->local_poll = inst->minpoll; LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE with poll %d from %s, minpoll set to %d", message->poll, UTI_IPToString(&inst->remote_addr.ip_addr), inst->minpoll); } /* Stop ongoing burst */ if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) { inst->burst_good_samples_to_go = 0; LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped", UTI_IPToString(&inst->remote_addr.ip_addr)); } requeue_transmit = 1; } if (valid_packet) { inst->remote_poll = message->poll; inst->remote_stratum = message->stratum; inst->tx_count = 0; SRC_UpdateReachability(inst->source, 1); if (good_packet) { /* Do this before we accumulate a new sample into the stats registers, obviously */ estimated_offset = SRC_PredictOffset(inst->source, &sample_time); SRC_AccumulateSample(inst->source, &sample_time, offset, delay, dispersion, root_delay, root_dispersion, message->stratum > inst->min_stratum ? message->stratum : inst->min_stratum, (NTP_Leap) pkt_leap); SRC_SelectSource(inst->source); /* Now examine the registers. First though, if the prediction is not even within +/- the peer distance of the peer, we are clearly not tracking the peer at all well, so we back off the sampling rate depending on just how bad the situation is. */ error_in_estimate = fabs(-offset - estimated_offset); /* Now update the polling interval */ adjust_poll(inst, get_poll_adj(inst, error_in_estimate, distance)); /* If we're in burst mode, check whether the burst is completed and revert to the previous mode */ switch (inst->opmode) { case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_OFFLINE: --inst->burst_good_samples_to_go; if (inst->burst_good_samples_to_go <= 0) { if (inst->opmode == MD_BURST_WAS_ONLINE) inst->opmode = MD_ONLINE; else take_offline(inst); } break; default: break; } } else { /* Slowly increase the polling interval if we can't get good packet */ adjust_poll(inst, 0.1); } /* If in client mode, no more packets are expected to be coming from the server and the socket can be closed */ close_client_socket(inst); requeue_transmit = 1; } /* And now, requeue the timer. */ if (requeue_transmit && inst->opmode != MD_OFFLINE) { delay_time = get_transmit_delay(inst, 0, local_interval); if (kod_rate) { /* Back off for a while */ delay_time += (double) (4 * (1UL << inst->minpoll)); } /* Get rid of old timeout and start a new one */ assert(inst->timer_running); restart_timeout(inst, delay_time); } /* Do measurement logging */ if (logfileid != -1) { LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e", UTI_TimeToLogForm(sample_time.tv_sec), UTI_IPToString(&inst->remote_addr.ip_addr), sync_stats[pkt_leap], message->stratum, test1, test2, test3, test5, test6, test7, testA, testB, testC, testD, inst->local_poll, inst->remote_poll, inst->poll_score, offset, delay, dispersion, pkt_root_delay, pkt_root_dispersion); } return valid_packet; } /* ================================================== */ /* From RFC 5905, the standard handling of received packets, depending on the mode of the packet and of the source, is : +------------------+---------------------------------------+ | | Packet Mode | +------------------+-------+-------+-------+-------+-------+ | Association Mode | 1 | 2 | 3 | 4 | 5 | +------------------+-------+-------+-------+-------+-------+ | No Association 0 | NEWPS | DSCRD | FXMIT | MANY | NEWBC | | Symm. Active 1 | PROC | PROC | DSCRD | DSCRD | DSCRD | | Symm. Passive 2 | PROC | ERR | DSCRD | DSCRD | DSCRD | | Client 3 | DSCRD | DSCRD | DSCRD | PROC | DSCRD | | Server 4 | DSCRD | DSCRD | DSCRD | DSCRD | DSCRD | | Broadcast 5 | DSCRD | DSCRD | DSCRD | DSCRD | DSCRD | | Bcast Client 6 | DSCRD | DSCRD | DSCRD | DSCRD | PROC | +------------------+-------+-------+-------+-------+-------+ Association mode 0 is implemented in NCR_ProcessUnknown(), other modes in NCR_ProcessKnown(). Broadcast, manycast and ephemeral symmetric passive associations are not supported yet. */ /* ================================================== */ /* 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 */ int NCR_ProcessKnown (NTP_Packet *message, /* the received message */ struct timeval *now, /* timestamp at time of receipt */ double now_err, NCR_Instance inst, /* the instance record for this peer/server */ NTP_Local_Address *local_addr, /* the receiving address */ int length /* the length of the received packet */ ) { int pkt_mode, proc_packet, proc_as_unknown, log_peer_access; if (!check_packet_format(message, length)) return 0; pkt_mode = NTP_LVM_TO_MODE(message->lvm); proc_packet = 0; proc_as_unknown = 0; log_peer_access = 0; /* Now, depending on the mode we decide what to do */ switch (pkt_mode) { case MODE_ACTIVE: switch (inst->mode) { case MODE_ACTIVE: /* Ordinary symmetric peering */ log_peer_access = 1; proc_packet = 1; break; case MODE_PASSIVE: /* In this software this case should not arise, we don't support unconfigured peers */ break; case MODE_CLIENT: /* This is where we have the remote configured as a server and he has us configured as a peer, process as from an unknown source */ proc_as_unknown = 1; break; default: /* Discard */ break; } break; case MODE_PASSIVE: switch (inst->mode) { case MODE_ACTIVE: /* This would arise if we have the remote configured as a peer and he does not have us configured */ log_peer_access = 1; proc_packet = 1; break; case MODE_PASSIVE: /* Error condition in RFC 5905 */ break; default: /* Discard */ break; } break; case MODE_CLIENT: /* If message is client mode, we just respond with a server mode packet, regardless of what we think the remote machine is supposed to be. However, even though this is a configured peer or server, we still implement access restrictions on client mode operation. This copes with the case for an isolated network where one machine is set by eye and is used as the master, with the other machines pointed at it. If the master goes down, we want to be able to reset its time at startup by relying on one of the secondaries to flywheel it. The behaviour coded here is required in the secondaries to make this possible. */ proc_as_unknown = 1; break; case MODE_SERVER: /* Ignore presend reply */ if (inst->presend_done) break; switch (inst->mode) { case MODE_CLIENT: /* Standard case where he's a server and we're the client */ proc_packet = 1; break; default: /* Discard */ break; } break; case MODE_BROADCAST: /* Just ignore these */ break; default: /* Obviously ignore */ break; } if (log_peer_access) CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, now->tv_sec); if (proc_packet) { /* Check if the reply was received by the socket that sent the request */ if (local_addr->sock_fd != inst->local_addr.sock_fd) { DEBUG_LOG(LOGF_NtpCore, "Packet received by wrong socket %d (expected %d)", local_addr->sock_fd, inst->local_addr.sock_fd); return 0; } /* Ignore packets from offline sources */ if (inst->opmode == MD_OFFLINE || inst->tx_suspended) { DEBUG_LOG(LOGF_NtpCore, "Packet from offline source"); return 0; } return receive_packet(message, now, now_err, inst, local_addr, length); } else if (proc_as_unknown) { NCR_ProcessUnknown(message, now, now_err, &inst->remote_addr, local_addr, length); /* It's not a reply to our request, don't return success */ return 0; } else { DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded pkt_mode=%d our_mode=%d", pkt_mode, inst->mode); return 0; } } /* ================================================== */ /* This routine is called when a new packet arrives off the network, and it relates to a source we don't know (not our server or peer) */ void NCR_ProcessUnknown (NTP_Packet *message, /* the received message */ struct timeval *now, /* timestamp at time of receipt */ double now_err, /* assumed error in the timestamp */ NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length /* the length of the received packet */ ) { NTP_Mode pkt_mode, my_mode; int has_auth, valid_auth; uint32_t key_id; /* Ignore the packet if it wasn't received by server socket */ if (!NIO_IsServerSocket(local_addr->sock_fd)) { DEBUG_LOG(LOGF_NtpCore, "NTP request packet received by client socket %d", local_addr->sock_fd); return; } if (!check_packet_format(message, length)) return; if (!ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) { DEBUG_LOG(LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); return; } pkt_mode = NTP_LVM_TO_MODE(message->lvm); switch (pkt_mode) { case MODE_ACTIVE: /* We are symmetric passive, even though we don't ever lock to him */ my_mode = MODE_PASSIVE; CLG_LogNTPPeerAccess(&remote_addr->ip_addr, now->tv_sec); break; case MODE_CLIENT: /* Reply with server packet */ my_mode = MODE_SERVER; CLG_LogNTPClientAccess(&remote_addr->ip_addr, now->tv_sec); break; default: /* Discard */ DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded pkt_mode=%d", pkt_mode); return; } /* Check if the packet includes MAC that authenticates properly */ valid_auth = check_packet_auth(message, length, &has_auth, &key_id); /* If authentication failed, reply with crypto-NAK */ if (!valid_auth) key_id = 0; /* Send a reply. - copy the poll value as the client may use it to control its polling interval - authenticate the packet if the request was authenticated - originate timestamp is the client's transmit time - don't save our transmit timestamp as we aren't maintaining state about this client */ transmit_packet(my_mode, message->poll, NTP_LVM_TO_VERSION(message->lvm), has_auth, key_id, &message->transmit_ts, now, NULL, NULL, remote_addr, local_addr); } /* ================================================== */ void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset) { double delta; if (inst->local_rx.tv_sec || inst->local_rx.tv_usec) UTI_AdjustTimeval(&inst->local_rx, when, &inst->local_rx, &delta, dfreq, doffset); if (inst->local_tx.tv_sec || inst->local_tx.tv_usec) UTI_AdjustTimeval(&inst->local_tx, when, &inst->local_tx, &delta, dfreq, doffset); } /* ================================================== */ void NCR_TakeSourceOnline(NCR_Instance inst) { switch (inst->opmode) { case MD_ONLINE: /* Nothing to do */ break; case MD_OFFLINE: LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr)); inst->opmode = MD_ONLINE; NCR_ResetInstance(inst); start_initial_timeout(inst); break; case MD_BURST_WAS_ONLINE: /* Will revert */ break; case MD_BURST_WAS_OFFLINE: inst->opmode = MD_BURST_WAS_ONLINE; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr)); break; } } /* ================================================== */ void NCR_TakeSourceOffline(NCR_Instance inst) { switch (inst->opmode) { case MD_ONLINE: LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr)); take_offline(inst); break; case MD_OFFLINE: break; case MD_BURST_WAS_ONLINE: inst->opmode = MD_BURST_WAS_OFFLINE; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr)); break; case MD_BURST_WAS_OFFLINE: break; } } /* ================================================== */ void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll) { if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL) return; inst->minpoll = new_minpoll; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll); if (inst->maxpoll < inst->minpoll) NCR_ModifyMaxpoll(inst, inst->minpoll); } /* ================================================== */ void NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll) { if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL) return; inst->maxpoll = new_maxpoll; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll); if (inst->minpoll > inst->maxpoll) NCR_ModifyMinpoll(inst, inst->maxpoll); } /* ================================================== */ void NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay) { inst->max_delay = new_max_delay; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f", UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay); } /* ================================================== */ void NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio) { inst->max_delay_ratio = new_max_delay_ratio; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay ratio %f", UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_ratio); } /* ================================================== */ void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio) { inst->max_delay_dev_ratio = new_max_delay_dev_ratio; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay dev ratio %f", UTI_IPToString(&inst->remote_addr.ip_addr), new_max_delay_dev_ratio); } /* ================================================== */ void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum) { inst->min_stratum = new_min_stratum; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minstratum %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_min_stratum); } /* ================================================== */ void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target) { inst->poll_target = new_poll_target; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new polltarget %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target); } /* ================================================== */ void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples) { if (inst->mode == MODE_CLIENT) { /* We want to prevent burst mode being used on symmetric active associations - it will play havoc with the peer's sampling strategy. (This obviously relies on us having the peer configured that way if he has us configured symmetric active - but there's not much else we can do.) */ switch (inst->opmode) { case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_ONLINE: /* If already burst sampling, don't start again */ break; case MD_ONLINE: case MD_OFFLINE: inst->opmode = inst->opmode == MD_ONLINE ? MD_BURST_WAS_ONLINE : MD_BURST_WAS_OFFLINE; inst->burst_good_samples_to_go = n_good_samples; inst->burst_total_samples_to_go = n_total_samples; start_initial_timeout(inst); break; default: assert(0); break; } } } /* ================================================== */ void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now) { report->poll = inst->local_poll; switch (inst->mode) { case MODE_CLIENT: report->mode = RPT_NTP_CLIENT; break; case MODE_ACTIVE: report->mode = RPT_NTP_PEER; break; default: assert(0); } } /* ================================================== */ int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all) { ADF_Status status; if (allow) { if (all) { status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits); } else { status = ADF_Allow(access_auth_table, ip_addr, subnet_bits); } } else { if (all) { status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits); } else { status = ADF_Deny(access_auth_table, ip_addr, subnet_bits); } } if (status != ADF_SUCCESS) return 0; /* Keep server sockets open only when an address allowed */ if (allow) { NTP_Remote_Address remote_addr; if (server_sock_fd4 == INVALID_SOCK_FD && ADF_IsAnyAllowed(access_auth_table, IPADDR_INET4)) { remote_addr.ip_addr.family = IPADDR_INET4; server_sock_fd4 = NIO_OpenServerSocket(&remote_addr); } if (server_sock_fd6 == INVALID_SOCK_FD && ADF_IsAnyAllowed(access_auth_table, IPADDR_INET6)) { remote_addr.ip_addr.family = IPADDR_INET6; server_sock_fd6 = NIO_OpenServerSocket(&remote_addr); } } else { if (server_sock_fd4 != INVALID_SOCK_FD && !ADF_IsAnyAllowed(access_auth_table, IPADDR_INET4)) { NIO_CloseServerSocket(server_sock_fd4); server_sock_fd4 = INVALID_SOCK_FD; } if (server_sock_fd6 != INVALID_SOCK_FD && !ADF_IsAnyAllowed(access_auth_table, IPADDR_INET6)) { NIO_CloseServerSocket(server_sock_fd6); server_sock_fd6 = INVALID_SOCK_FD; } } return 1; } /* ================================================== */ int NCR_CheckAccessRestriction(IPAddr *ip_addr) { return ADF_IsAllowed(access_auth_table, ip_addr); } /* ================================================== */ void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *offline, int *burst_online, int *burst_offline) { switch (inst->opmode) { case MD_BURST_WAS_OFFLINE: ++*burst_offline; break; case MD_BURST_WAS_ONLINE: ++*burst_online; break; case MD_ONLINE: ++*online; break; case MD_OFFLINE: ++*offline; break; default: assert(0); break; } } /* ================================================== */ NTP_Remote_Address * NCR_GetRemoteAddress(NCR_Instance inst) { return &inst->remote_addr; } /* ================================================== */ int NCR_IsSyncPeer(NCR_Instance inst) { return SRC_IsSyncPeer(inst->source); } /* ================================================== */ static void broadcast_timeout(void *arg) { BroadcastDestination *destination; NTP_int64 orig_ts; struct timeval recv_ts; destination = ARR_GetElement(broadcasts, (long)arg); orig_ts.hi = 0; orig_ts.lo = 0; recv_ts.tv_sec = 0; recv_ts.tv_usec = 0; transmit_packet(MODE_BROADCAST, 6 /* FIXME: should this be log2(interval)? */, NTP_VERSION, 0, 0, &orig_ts, &recv_ts, NULL, NULL, &destination->addr, &destination->local_addr); /* Requeue timeout. We don't care if interval drifts gradually. */ SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpBroadcastClass, broadcast_timeout, arg); } /* ================================================== */ void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) { BroadcastDestination *destination; destination = (BroadcastDestination *)ARR_GetNewElement(broadcasts); destination->addr.ip_addr = *addr; destination->addr.port = port; destination->local_addr.ip_addr.family = IPADDR_UNSPEC; destination->local_addr.sock_fd = NIO_OpenServerSocket(&destination->addr); destination->interval = interval; SCH_AddTimeoutInClass(destination->interval, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpBroadcastClass, broadcast_timeout, (void *)(long)(ARR_GetSize(broadcasts) - 1)); } chrony-2.1.1/ntp_core.h0000644000000000000000000001045312542263351013503 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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" typedef enum { NTP_SERVER, NTP_PEER } NTP_Source_Type; /* 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 or peer */ extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); /* Destroy an instance */ extern void NCR_DestroyInstance(NCR_Instance instance); /* Start an instance */ extern void NCR_StartInstance(NCR_Instance instance); /* Reset an instance */ extern void NCR_ResetInstance(NCR_Instance inst); /* Change the remote address of an instance */ extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr); /* 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 int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length); /* This routine is called when a new packet arrives off the network, and we do not recognize its source */ extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); /* 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_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio); extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum); extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target); 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(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int NCR_CheckAccessRestriction(IPAddr *ip_addr); extern void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *offline, int *burst_online, int *burst_offline); extern NTP_Remote_Address *NCR_GetRemoteAddress(NCR_Instance instance); extern int NCR_IsSyncPeer(NCR_Instance instance); extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval); #endif /* GOT_NTP_CORE_H */ chrony-2.1.1/ntp_io.c0000644000000000000000000004677012542263351013170 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Timo Teras 2009 * Copyright (C) Miroslav Lichvar 2009, 2013-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This file deals with the IO aspects of reading and writing NTP packets */ #include "config.h" #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" #define INVALID_SOCK_FD -1 union sockaddr_in46 { struct sockaddr_in in4; #ifdef FEAT_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; /* The server/peer and client sockets for IPv4 and IPv6 */ static int server_sock_fd4; static int client_sock_fd4; #ifdef FEAT_IPV6 static int server_sock_fd6; static int client_sock_fd6; #endif /* Reference counters for server sockets to keep them open only when needed */ static int server_sock_ref4; #ifdef FEAT_IPV6 static int server_sock_ref6; #endif /* Flag indicating we create a new connected client socket for each server instead of sharing client_sock_fd4 and client_sock_fd6 */ static int separate_client_sockets; /* Flag indicating the server sockets are not created dynamically when needed, either to have a socket for client requests when separate client sockets are disabled and client port is equal to server port, or the server port is disabled */ static int permanent_server_sockets; /* Flag indicating that we have been initialised */ static int initialised=0; /* ================================================== */ /* Forward prototypes */ static void read_from_socket(void *anything); /* ================================================== */ static int prepare_socket(int family, int port_number, int client_only) { union sockaddr_in46 my_addr; socklen_t my_addr_len; int sock_fd; IPAddr bind_address; int on_off = 1; /* Open Internet domain UDP socket for NTP message transmissions */ sock_fd = socket(family, SOCK_DGRAM, 0); if (sock_fd < 0) { if (!client_only) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); } else { DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); } return INVALID_SOCK_FD; } /* Close on exec */ UTI_FdSetCloexec(sock_fd); /* Prepare local address */ memset(&my_addr, 0, sizeof (my_addr)); my_addr_len = 0; switch (family) { case AF_INET: if (!client_only) CNF_GetBindAddress(IPADDR_INET4, &bind_address); else CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address); if (bind_address.family == IPADDR_INET4) my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4); else if (port_number) my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); else break; my_addr.in4.sin_family = family; my_addr.in4.sin_port = htons(port_number); my_addr_len = sizeof (my_addr.in4); break; #ifdef FEAT_IPV6 case AF_INET6: if (!client_only) CNF_GetBindAddress(IPADDR_INET6, &bind_address); else CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address); if (bind_address.family == IPADDR_INET6) memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6, sizeof (my_addr.in6.sin6_addr.s6_addr)); else if (port_number) my_addr.in6.sin6_addr = in6addr_any; else break; my_addr.in6.sin6_family = family; my_addr.in6.sin6_port = htons(port_number); my_addr_len = sizeof (my_addr.in6); break; #endif default: assert(0); } /* Make the socket capable of re-using an old address if binding to a specific port */ if (port_number && 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 (!client_only && 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 */ } #ifdef SO_TIMESTAMP /* Enable receiving of timestamp control messages */ if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set timestamp socket options"); /* Don't quit - we might survive anyway */ } #endif #ifdef IP_FREEBIND /* Allow binding to address that doesn't exist yet */ if (my_addr_len > 0 && setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option"); } #endif if (family == AF_INET) { #ifdef IP_PKTINFO /* We want the local IP info on server sockets */ if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set packet info socket option"); /* Don't quit - we might survive anyway */ } #endif } #ifdef FEAT_IPV6 else if (family == AF_INET6) { #ifdef IPV6_V6ONLY /* Receive IPv6 packets only */ if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option"); } #endif #ifdef IPV6_RECVPKTINFO if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option"); } #elif defined(IPV6_PKTINFO) if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option"); } #endif } #endif /* Bind the socket if a port or address was specified */ if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); close(sock_fd); return INVALID_SOCK_FD; } /* Register handler for read events on the socket */ SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd); return sock_fd; } /* ================================================== */ static int prepare_separate_client_socket(int family) { switch (family) { case IPADDR_INET4: return prepare_socket(AF_INET, 0, 1); #ifdef FEAT_IPV6 case IPADDR_INET6: return prepare_socket(AF_INET6, 0, 1); #endif default: return INVALID_SOCK_FD; } } /* ================================================== */ static int connect_socket(int sock_fd, NTP_Remote_Address *remote_addr) { union sockaddr_in46 addr; socklen_t addr_len; addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u); assert(addr_len); if (connect(sock_fd, &addr.u, addr_len) < 0) { DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno)); return 0; } return 1; } /* ================================================== */ static void close_socket(int sock_fd) { if (sock_fd == INVALID_SOCK_FD) return; SCH_RemoveInputFileHandler(sock_fd); close(sock_fd); } /* ================================================== */ void NIO_Initialise(int family) { int server_port, client_port; assert(!initialised); initialised = 1; server_port = CNF_GetNTPPort(); client_port = CNF_GetAcquisitionPort(); /* Use separate connected sockets if client port is negative */ separate_client_sockets = client_port < 0; if (client_port < 0) client_port = 0; permanent_server_sockets = !server_port || (!separate_client_sockets && client_port == server_port); server_sock_fd4 = INVALID_SOCK_FD; client_sock_fd4 = INVALID_SOCK_FD; server_sock_ref4 = 0; #ifdef FEAT_IPV6 server_sock_fd6 = INVALID_SOCK_FD; client_sock_fd6 = INVALID_SOCK_FD; server_sock_ref6 = 0; #endif if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { if (permanent_server_sockets && server_port) server_sock_fd4 = prepare_socket(AF_INET, server_port, 0); if (!separate_client_sockets) { if (client_port != server_port || !server_port) client_sock_fd4 = prepare_socket(AF_INET, client_port, 1); else client_sock_fd4 = server_sock_fd4; } } #ifdef FEAT_IPV6 if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { if (permanent_server_sockets && server_port) server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0); if (!separate_client_sockets) { if (client_port != server_port || !server_port) client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1); else client_sock_fd6 = server_sock_fd6; } } #endif if ((server_port && server_sock_fd4 == INVALID_SOCK_FD && permanent_server_sockets #ifdef FEAT_IPV6 && server_sock_fd6 == INVALID_SOCK_FD #endif ) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD #ifdef FEAT_IPV6 && client_sock_fd6 == INVALID_SOCK_FD #endif )) { LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets"); } } /* ================================================== */ void NIO_Finalise(void) { if (server_sock_fd4 != client_sock_fd4) close_socket(client_sock_fd4); close_socket(server_sock_fd4); server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD; #ifdef FEAT_IPV6 if (server_sock_fd6 != client_sock_fd6) close_socket(client_sock_fd6); close_socket(server_sock_fd6); server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; #endif initialised = 0; } /* ================================================== */ int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) { if (separate_client_sockets) { int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); if (sock_fd == INVALID_SOCK_FD) return INVALID_SOCK_FD; if (!connect_socket(sock_fd, remote_addr)) { close_socket(sock_fd); return INVALID_SOCK_FD; } return sock_fd; } else { switch (remote_addr->ip_addr.family) { case IPADDR_INET4: return client_sock_fd4; #ifdef FEAT_IPV6 case IPADDR_INET6: return client_sock_fd6; #endif default: return INVALID_SOCK_FD; } } } /* ================================================== */ int NIO_OpenServerSocket(NTP_Remote_Address *remote_addr) { switch (remote_addr->ip_addr.family) { case IPADDR_INET4: if (permanent_server_sockets) return server_sock_fd4; if (server_sock_fd4 == INVALID_SOCK_FD) server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0); if (server_sock_fd4 != INVALID_SOCK_FD) server_sock_ref4++; return server_sock_fd4; #ifdef FEAT_IPV6 case IPADDR_INET6: if (permanent_server_sockets) return server_sock_fd6; if (server_sock_fd6 == INVALID_SOCK_FD) server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0); if (server_sock_fd6 != INVALID_SOCK_FD) server_sock_ref6++; return server_sock_fd6; #endif default: return INVALID_SOCK_FD; } } /* ================================================== */ void NIO_CloseClientSocket(int sock_fd) { if (separate_client_sockets) close_socket(sock_fd); } /* ================================================== */ void NIO_CloseServerSocket(int sock_fd) { if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD) return; if (sock_fd == server_sock_fd4) { if (--server_sock_ref4 <= 0) { close_socket(server_sock_fd4); server_sock_fd4 = INVALID_SOCK_FD; } } #ifdef FEAT_IPV6 else if (sock_fd == server_sock_fd6) { if (--server_sock_ref6 <= 0) { close_socket(server_sock_fd6); server_sock_fd6 = INVALID_SOCK_FD; } } #endif else { assert(0); } } /* ================================================== */ int NIO_IsServerSocket(int sock_fd) { return sock_fd != INVALID_SOCK_FD && (sock_fd == server_sock_fd4 #ifdef FEAT_IPV6 || sock_fd == server_sock_fd6 #endif ); } /* ================================================== */ static void read_from_socket(void *anything) { /* This should only be called when there is something to read, otherwise it will block. */ int status, sock_fd; NTP_Receive_Buffer message; union sockaddr_in46 where_from; unsigned int flags = 0; struct timeval now; double now_err; NTP_Remote_Address remote_addr; NTP_Local_Address local_addr; char cmsgbuf[256]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; assert(initialised); SCH_GetLastEventTime(&now, &now_err, NULL); iov.iov_base = &message.ntp_pkt; iov.iov_len = sizeof(message); msg.msg_name = &where_from; msg.msg_namelen = sizeof(where_from); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (void *) cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); msg.msg_flags = 0; sock_fd = (long)anything; status = recvmsg(sock_fd, &msg, flags); /* 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) { if (msg.msg_namelen > sizeof (where_from)) LOG_FATAL(LOGF_NtpIO, "Truncated source address"); UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port); local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.sock_fd = sock_fd; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { #ifdef IP_PKTINFO if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo ipi; memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr); local_addr.ip_addr.family = IPADDR_INET4; } #endif #if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO) if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo ipi; memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, sizeof (local_addr.ip_addr.addr.in6)); local_addr.ip_addr.family = IPADDR_INET6; } #endif #ifdef SO_TIMESTAMP if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) { struct timeval tv; memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); LCL_CookTime(&tv, &now, &now_err); } #endif } DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d", status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port, UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd); if (status >= NTP_NORMAL_PACKET_LENGTH) { NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr, &local_addr, status); } else { /* Just ignore the packet if it's not of a recognized length */ } } } /* ================================================== */ /* Send a packet to remote address from local address */ static int send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) { union sockaddr_in46 remote; struct msghdr msg; struct iovec iov; char cmsgbuf[256]; int cmsglen; socklen_t addrlen = 0; assert(initialised); if (local_addr->sock_fd == INVALID_SOCK_FD) { DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); return 0; } /* Don't set address with connected socket */ if (local_addr->sock_fd == server_sock_fd4 || #ifdef FEAT_IPV6 local_addr->sock_fd == server_sock_fd6 || #endif !separate_client_sockets) { addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &remote.u); if (!addrlen) return 0; } if (addrlen) { msg.msg_name = &remote.u; msg.msg_namelen = addrlen; } else { msg.msg_name = NULL; msg.msg_namelen = 0; } iov.iov_base = packet; iov.iov_len = packetlen; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); msg.msg_flags = 0; cmsglen = 0; #ifdef IP_PKTINFO if (local_addr->ip_addr.family == IPADDR_INET4) { struct cmsghdr *cmsg; struct in_pktinfo *ipi; cmsg = CMSG_FIRSTHDR(&msg); memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo)); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); ipi = (struct in_pktinfo *) CMSG_DATA(cmsg); ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4); } #endif #if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO) if (local_addr->ip_addr.family == IPADDR_INET6) { struct cmsghdr *cmsg; struct in6_pktinfo *ipi; cmsg = CMSG_FIRSTHDR(&msg); memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg); memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6, sizeof(ipi->ipi6_addr.s6_addr)); } #endif msg.msg_controllen = cmsglen; /* This is apparently required on some systems */ if (!cmsglen) msg.msg_control = NULL; if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) { DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd, strerror(errno)); return 0; } DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); return 1; } /* ================================================== */ /* Send a packet to a given address */ int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) { return send_packet((void *) packet, length, remote_addr, local_addr); } chrony-2.1.1/ntp_io.h0000644000000000000000000000411312542263351013156 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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(int family); /* Function to finalise the module */ extern void NIO_Finalise(void); /* Function to obtain a socket for sending client packets */ extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr); /* Function to obtain a socket for sending server/peer packets */ extern int NIO_OpenServerSocket(NTP_Remote_Address *remote_addr); /* Function to close a socket returned by NIO_OpenClientSocket() */ extern void NIO_CloseClientSocket(int sock_fd); /* Function to close a socket returned by NIO_OpenServerSocket() */ extern void NIO_CloseServerSocket(int sock_fd); /* Function to check if socket is a server socket */ extern int NIO_IsServerSocket(int sock_fd); /* Function to transmit a packet */ extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); #endif /* GOT_NTP_IO_H */ chrony-2.1.1/ntp_sources.c0000644000000000000000000006511312542263351014234 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-2012, 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Functions which manage the pool of NTP sources that we are currently a client of or peering with. */ #include "config.h" #include "sysincl.h" #include "array.h" #include "ntp_sources.h" #include "ntp_core.h" #include "util.h" #include "logging.h" #include "local.h" #include "memory.h" #include "nameserv_async.h" #include "sched.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, non-NULL means this slot in table is in use */ NCR_Instance data; /* Data for the protocol engine for this source */ char *name; /* Name of the source, may be NULL */ int pool; /* Number of the pool from which was this source added or INVALID_POOL */ int tentative; /* Flag indicating there was no valid response yet and the source may be removed if other sources from the pool respond first */ } SourceRecord; /* Hash table of SourceRecord, the size should be a power of two */ static ARR_Instance records; /* Number of sources in the hash table */ static int n_sources; /* Flag indicating new sources will be started automatically when added */ static int auto_start_sources = 0; /* Source with unknown address (which may be resolved later) */ struct UnresolvedSource { char *name; int port; int replacement; union { struct { NTP_Source_Type type; SourceParameters params; int pool; int max_new_sources; } new_source; NTP_Remote_Address replace_source; }; struct UnresolvedSource *next; }; #define RESOLVE_INTERVAL_UNIT 7 #define MIN_RESOLVE_INTERVAL 2 #define MAX_RESOLVE_INTERVAL 9 #define MIN_REPLACEMENT_INTERVAL 8 static struct UnresolvedSource *unresolved_sources = NULL; static int resolving_interval = 0; static SCH_TimeoutID resolving_id; static struct UnresolvedSource *resolving_source = NULL; static NSR_SourceResolvingEndHandler resolving_end_handler = NULL; #define MAX_POOL_SOURCES 16 #define INVALID_POOL (-1) /* Pool of sources with the same name */ struct SourcePool { /* Number of sources added from this pool (ignoring tentative sources) */ int sources; /* Maximum number of sources */ int max_sources; }; /* Array of SourcePool */ static ARR_Instance pools; /* ================================================== */ /* Forward prototypes */ static void resolve_sources(void *arg); static void rehash_records(void); static void clean_source_record(SourceRecord *record); static void slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything); /* ================================================== */ /* Flag indicating whether module is initialised */ static int initialised = 0; /* ================================================== */ static SourceRecord * get_record(unsigned index) { return (SourceRecord *)ARR_GetElement(records, index); } /* ================================================== */ void NSR_Initialise(void) { n_sources = 0; initialised = 1; records = ARR_CreateInstance(sizeof (SourceRecord)); rehash_records(); pools = ARR_CreateInstance(sizeof (struct SourcePool)); LCL_AddParameterChangeHandler(slew_sources, NULL); } /* ================================================== */ void NSR_Finalise(void) { SourceRecord *record; struct UnresolvedSource *us; unsigned int i; ARR_DestroyInstance(pools); for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) clean_source_record(record); } ARR_DestroyInstance(records); while (unresolved_sources) { us = unresolved_sources; unresolved_sources = us->next; Free(us->name); Free(us); } initialised = 0; } /* ================================================== */ /* 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) { SourceRecord *record; uint32_t hash; unsigned int i, size; unsigned short port; uint8_t *ip6; size = ARR_GetSize(records); switch (remote_addr->ip_addr.family) { case IPADDR_INET6: ip6 = remote_addr->ip_addr.addr.in6; hash = (ip6[0] ^ ip6[4] ^ ip6[8] ^ ip6[12]) | (ip6[1] ^ ip6[5] ^ ip6[9] ^ ip6[13]) << 8 | (ip6[2] ^ ip6[6] ^ ip6[10] ^ ip6[14]) << 16 | (ip6[3] ^ ip6[7] ^ ip6[11] ^ ip6[15]) << 24; break; case IPADDR_INET4: hash = remote_addr->ip_addr.addr.in4; break; default: *found = *slot = 0; return; } port = remote_addr->port; for (i = 0; i < size / 2; i++) { /* Use quadratic probing */ *slot = (hash + (i + i * i) / 2) % size; record = get_record(*slot); if (!record->remote_addr) break; if (!UTI_CompareIPs(&record->remote_addr->ip_addr, &remote_addr->ip_addr, NULL)) { *found = record->remote_addr->port == port ? 2 : 1; return; } } *found = 0; } /* ================================================== */ /* Check if hash table of given size is sufficient to contain sources */ static int check_hashtable_size(unsigned int sources, unsigned int size) { return sources * 2 + 1 < size; } /* ================================================== */ static void rehash_records(void) { SourceRecord *temp_records; unsigned int i, old_size, new_size; int slot, found; old_size = ARR_GetSize(records); temp_records = MallocArray(SourceRecord, old_size); memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord)); /* The size of the hash table is always a power of two */ for (new_size = 4; !check_hashtable_size(n_sources, new_size); new_size *= 2) ; ARR_SetSize(records, new_size); for (i = 0; i < new_size; i++) get_record(i)->remote_addr = NULL; for (i = 0; i < old_size; i++) { if (!temp_records[i].remote_addr) continue; find_slot(temp_records[i].remote_addr, &slot, &found); assert(!found); *get_record(slot) = temp_records[i]; } Free(temp_records); } /* ================================================== */ /* Procedure to add a new source */ static NSR_Status add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool) { SourceRecord *record; int slot, found; assert(initialised); /* 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 (remote_addr->ip_addr.family != IPADDR_INET4 && remote_addr->ip_addr.family != IPADDR_INET6) { return NSR_InvalidAF; } else { n_sources++; if (!check_hashtable_size(n_sources, ARR_GetSize(records))) { rehash_records(); find_slot(remote_addr, &slot, &found); } assert(!found); record = get_record(slot); record->data = NCR_GetInstance(remote_addr, type, params); record->remote_addr = NCR_GetRemoteAddress(record->data); record->name = name ? Strdup(name) : NULL; record->pool = pool; record->tentative = pool != INVALID_POOL ? 1 : 0; if (auto_start_sources) NCR_StartInstance(record->data); return NSR_Success; } } } /* ================================================== */ static NSR_Status replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) { int slot1, slot2, found; SourceRecord *record; find_slot(old_addr, &slot1, &found); if (!found) return NSR_NoSuchSource; find_slot(new_addr, &slot2, &found); if (found) return NSR_AlreadyInUse; record = get_record(slot1); NCR_ChangeRemoteAddress(record->data, new_addr); record->remote_addr = NCR_GetRemoteAddress(record->data); /* The hash table must be rebuilt for the new address */ rehash_records(); LOG(LOGS_INFO, LOGF_NtpSources, "Source %s replaced with %s", UTI_IPToString(&old_addr->ip_addr), UTI_IPToString(&new_addr->ip_addr)); return NSR_Success; } /* ================================================== */ static void process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs) { NTP_Remote_Address address; int i, added; for (i = added = 0; i < n_addrs; i++) { DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&ip_addrs[i])); address.ip_addr = ip_addrs[i]; address.port = us->port; if (us->replacement) { if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse) break; } else { if (add_source(&address, us->name, us->new_source.type, &us->new_source.params, us->new_source.pool) == NSR_Success) added++; if (added >= us->new_source.max_new_sources) break; } } } /* ================================================== */ static void name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything) { struct UnresolvedSource *us, **i, *next; us = (struct UnresolvedSource *)anything; assert(us == resolving_source); DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs); switch (status) { case DNS_TryAgain: break; case DNS_Success: process_resolved_name(us, ip_addrs, n_addrs); break; case DNS_Failure: LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name); break; default: assert(0); } next = us->next; /* Remove the source from the list on success or failure, replacements are removed on any status */ if (us->replacement || status != DNS_TryAgain) { for (i = &unresolved_sources; *i; i = &(*i)->next) { if (*i == us) { *i = us->next; Free(us->name); Free(us); break; } } } resolving_source = next; if (next) { /* Continue with the next source in the list */ DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name); DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next); } else { /* This was the last source in the list. If some sources couldn't be resolved, try again in exponentially increasing interval. */ if (unresolved_sources) { if (resolving_interval < MIN_RESOLVE_INTERVAL) resolving_interval = MIN_RESOLVE_INTERVAL; else if (resolving_interval < MAX_RESOLVE_INTERVAL) resolving_interval++; resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT * (1 << resolving_interval), resolve_sources, NULL); } else { resolving_interval = 0; } /* This round of resolving is done */ if (resolving_end_handler) (resolving_end_handler)(); } } /* ================================================== */ static void resolve_sources(void *arg) { struct UnresolvedSource *us; assert(!resolving_source); DNS_Reload(); /* Start with the first source in the list, name_resolve_handler will iterate over the rest */ us = unresolved_sources; resolving_source = us; DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name); DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us); } /* ================================================== */ static void append_unresolved_source(struct UnresolvedSource *us) { struct UnresolvedSource **i; for (i = &unresolved_sources; *i; i = &(*i)->next) ; *i = us; us->next = NULL; } /* ================================================== */ NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) { return add_source(remote_addr, NULL, type, params, INVALID_POOL); } /* ================================================== */ void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) { struct UnresolvedSource *us; struct SourcePool *sp; NTP_Remote_Address remote_addr; /* If the name is an IP address, don't bother with full resolving now or later when trying to replace the source */ if (UTI_StringToIP(name, &remote_addr.ip_addr)) { remote_addr.port = port; NSR_AddSource(&remote_addr, type, params); return; } us = MallocNew(struct UnresolvedSource); us->name = Strdup(name); us->port = port; us->replacement = 0; us->new_source.type = type; us->new_source.params = *params; if (!pool) { us->new_source.pool = INVALID_POOL; us->new_source.max_new_sources = 1; } else { sp = (struct SourcePool *)ARR_GetNewElement(pools); sp->sources = 0; sp->max_sources = params->max_sources; us->new_source.pool = ARR_GetSize(pools) - 1; us->new_source.max_new_sources = MAX_POOL_SOURCES; } append_unresolved_source(us); } /* ================================================== */ void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler) { resolving_end_handler = handler; } /* ================================================== */ void NSR_ResolveSources(void) { /* Try to resolve unresolved sources now */ if (unresolved_sources) { /* Make sure no resolving is currently running */ if (!resolving_source) { if (resolving_interval) { SCH_RemoveTimeout(resolving_id); resolving_interval--; } resolve_sources(NULL); } } else { /* No unresolved sources, we are done */ if (resolving_end_handler) (resolving_end_handler)(); } } /* ================================================== */ void NSR_StartSources(void) { unsigned int i; for (i = 0; i < ARR_GetSize(records); i++) { if (!get_record(i)->remote_addr) continue; NCR_StartInstance(get_record(i)->data); } } /* ================================================== */ void NSR_AutoStartSources(void) { auto_start_sources = 1; } /* ================================================== */ static void clean_source_record(SourceRecord *record) { assert(record->remote_addr); record->remote_addr = NULL; NCR_DestroyInstance(record->data); if (record->name) Free(record->name); n_sources--; } /* ================================================== */ /* 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; } clean_source_record(get_record(slot)); /* Rehash the table to make sure there are no broken probe sequences. This is costly, but it's not expected to happen frequently. */ rehash_records(); return NSR_Success; } /* ================================================== */ void NSR_RemoveAllSources(void) { SourceRecord *record; unsigned int i; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (!record->remote_addr) continue; clean_source_record(record); } rehash_records(); } /* ================================================== */ static void resolve_source_replacement(SourceRecord *record) { struct UnresolvedSource *us; DEBUG_LOG(LOGF_NtpSources, "trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr)); us = MallocNew(struct UnresolvedSource); us->name = Strdup(record->name); us->port = record->remote_addr->port; us->replacement = 1; us->replace_source = *record->remote_addr; append_unresolved_source(us); NSR_ResolveSources(); } /* ================================================== */ void NSR_HandleBadSource(IPAddr *address) { static struct timeval last_replacement; struct timeval now; NTP_Remote_Address remote_addr; SourceRecord *record; int slot, found; double diff; remote_addr.ip_addr = *address; remote_addr.port = 0; find_slot(&remote_addr, &slot, &found); if (!found) return; record = get_record(slot); /* Only sources with a name can be replaced */ if (!record->name) return; /* Don't resolve names too frequently */ SCH_GetLastEventTime(NULL, NULL, &now); UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement); if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) { DEBUG_LOG(LOGF_NtpSources, "replacement postponed"); return; } last_replacement = now; resolve_source_replacement(record); } /* ================================================== */ static void remove_tentative_pool_sources(int pool) { SourceRecord *record; unsigned int i, removed; for (i = removed = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (!record->remote_addr || record->pool != pool || !record->tentative) continue; DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s", UTI_IPToString(&record->remote_addr->ip_addr)); clean_source_record(record); removed++; } if (removed) rehash_records(); } /* This routine is called by ntp_io when a new packet arrives off the network, possibly with an authentication tail */ void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) { SourceRecord *record; struct SourcePool *pool; int slot, found; assert(initialised); find_slot(remote_addr, &slot, &found); if (found == 2) { /* Must match IP address AND port number */ record = get_record(slot); if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length)) return; if (record->tentative) { /* First reply from a pool source */ record->tentative = 0; assert(record->pool != INVALID_POOL); pool = (struct SourcePool *)ARR_GetElement(pools, record->pool); pool->sources++; DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources", record->name, pool->sources); /* If the number of sources reached the configured maximum, remove the tentative sources added from this pool */ if (pool->sources >= pool->max_sources) remove_tentative_pool_sources(record->pool); } } else { NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length); } } /* ================================================== */ static void slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { SourceRecord *record; unsigned int i; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { if (change_type == LCL_ChangeUnknownStep) { NCR_ResetInstance(record->data); } else { NCR_SlewTimes(record->data, cooked, dfreq, doffset); } } } } /* ================================================== */ int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address) { SourceRecord *record; unsigned int i; int any; NSR_ResolveSources(); any = 0; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { if (address->family == IPADDR_UNSPEC || !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { any = 1; NCR_TakeSourceOnline(record->data); } } } if (address->family == IPADDR_UNSPEC) { struct UnresolvedSource *us; for (us = unresolved_sources; us; us = us->next) { if (us->replacement) continue; any = 1; us->new_source.params.online = 1; } } return any; } /* ================================================== */ int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address) { SourceRecord *record, *syncpeer; unsigned int i, any; any = 0; syncpeer = NULL; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { if (address->family == IPADDR_UNSPEC || !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { any = 1; if (NCR_IsSyncPeer(record->data)) { syncpeer = record; continue; } NCR_TakeSourceOffline(record->data); } } } /* Take sync peer offline as last to avoid reference switching */ if (syncpeer) { NCR_TakeSourceOffline(syncpeer->data); } if (address->family == IPADDR_UNSPEC) { struct UnresolvedSource *us; for (us = unresolved_sources; us; us = us->next) { if (us->replacement) continue; any = 1; us->new_source.params.online = 0; } } return any; } /* ================================================== */ int NSR_ModifyMinpoll(IPAddr *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(get_record(slot)->data, new_minpoll); return 1; } } /* ================================================== */ int NSR_ModifyMaxpoll(IPAddr *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(get_record(slot)->data, new_maxpoll); return 1; } } /* ================================================== */ int NSR_ModifyMaxdelay(IPAddr *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(get_record(slot)->data, new_max_delay); return 1; } } /* ================================================== */ int NSR_ModifyMaxdelayratio(IPAddr *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(get_record(slot)->data, new_max_delay_ratio); return 1; } } /* ================================================== */ int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_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_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio); return 1; } } /* ================================================== */ int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum) { 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_ModifyMinstratum(get_record(slot)->data, new_min_stratum); return 1; } } /* ================================================== */ int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target) { 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_ModifyPolltarget(get_record(slot)->data, new_poll_target); return 1; } } /* ================================================== */ int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address) { SourceRecord *record; unsigned int i; int any; any = 0; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { if (address->family == IPADDR_UNSPEC || !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { any = 1; NCR_InitiateSampleBurst(record->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(get_record(slot)->data, report, now); } else { report->poll = 0; report->latest_meas_ago = 0; } } /* ================================================== */ void NSR_GetActivityReport(RPT_ActivityReport *report) { SourceRecord *record; unsigned int i; struct UnresolvedSource *us; report->online = 0; report->offline = 0; report->burst_online = 0; report->burst_offline = 0; for (i = 0; i < ARR_GetSize(records); i++) { record = get_record(i); if (record->remote_addr) { NCR_IncrementActivityCounters(record->data, &report->online, &report->offline, &report->burst_online, &report->burst_offline); } } report->unresolved = 0; for (us = unresolved_sources; us; us = us->next) { report->unresolved++; } } /* ================================================== */ chrony-2.1.1/ntp_sources.h0000644000000000000000000001151312542263351014234 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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, /* AddSource - attempt to add a source that is already known */ NSR_TooManySources, /* AddSource - too many sources already present */ NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */ } NSR_Status; /* Procedure to add a new server or peer source. */ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); /* Procedure to add a new server, peer source, or pool of servers specified by name instead of address. The name is resolved in exponentially increasing intervals until it succeeds or fails with a non-temporary error. */ extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params); /* Function type for handlers to be called back when an attempt * (possibly unsuccessful) to resolve unresolved sources ends */ typedef void (*NSR_SourceResolvingEndHandler)(void); /* Set the handler, or NULL to disable the notification */ extern void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler); /* Procedure to start resolving unresolved sources */ extern void NSR_ResolveSources(void); /* Procedure to start all sources */ extern void NSR_StartSources(void); /* Start new sources automatically */ extern void NSR_AutoStartSources(void); /* Procedure to remove a source */ extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr); /* Procedure to remove all sources */ extern void NSR_RemoveAllSources(void); /* Procedure to try to find a replacement for a bad source */ extern void NSR_HandleBadSource(IPAddr *address); /* 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, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); /* 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(IPAddr *mask, IPAddr *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(IPAddr *mask, IPAddr *address); extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll); extern int NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll); extern int NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay); extern int NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio); extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_ratio); extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum); extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target); extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address); extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now); extern void NSR_GetActivityReport(RPT_ActivityReport *report); #endif /* GOT_NTP_SOURCES_H */ chrony-2.1.1/pktlength.c0000644000000000000000000003334612542263351013673 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "util.h" #include "pktlength.h" /* ================================================== */ static int command_unpadded_length(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.null.EOR); 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_MAXDELAYDEVRATIO: return offsetof(CMD_Request, data.modify_maxdelaydevratio.EOR); case REQ_MODIFY_MAXUPDATESKEW: return offsetof(CMD_Request, data.modify_maxupdateskew.EOR); case REQ_MODIFY_MAKESTEP: return offsetof(CMD_Request, data.modify_makestep.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.null.EOR); case REQ_SOURCE_DATA : return offsetof(CMD_Request, data.source_data.EOR); case REQ_REKEY : return offsetof(CMD_Request, data.null.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.null.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.null.EOR); case REQ_SOURCESTATS : return offsetof(CMD_Request, data.sourcestats.EOR); case REQ_RTCREPORT : return offsetof(CMD_Request, data.null.EOR); case REQ_TRIMRTC : return offsetof(CMD_Request, data.null.EOR); case REQ_CYCLELOGS : return offsetof(CMD_Request, data.null.EOR); case REQ_SUBNETS_ACCESSED : case REQ_CLIENT_ACCESSES: /* No longer supported */ return 0; 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.null.EOR); case REQ_MANUAL_DELETE: return offsetof(CMD_Request, data.manual_delete.EOR); case REQ_MAKESTEP: return offsetof(CMD_Request, data.null.EOR); case REQ_ACTIVITY: return offsetof(CMD_Request, data.null.EOR); case REQ_RESELECT: return offsetof(CMD_Request, data.null.EOR); case REQ_RESELECTDISTANCE: return offsetof(CMD_Request, data.reselect_distance.EOR); case REQ_MODIFY_MINSTRATUM: return offsetof(CMD_Request, data.modify_minstratum.EOR); case REQ_MODIFY_POLLTARGET: return offsetof(CMD_Request, data.modify_polltarget.EOR); case REQ_SMOOTHING: return offsetof(CMD_Request, data.null.EOR); case REQ_SMOOTHTIME: return offsetof(CMD_Request, data.smoothtime.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_CommandLength(CMD_Request *r) { int command_length; command_length = command_unpadded_length(r); if (!command_length) return 0; return command_length + PKL_CommandPaddingLength(r); } /* ================================================== */ #define PADDING_LENGTH_(request_length, reply_length) \ ((request_length) < (reply_length) ? (reply_length) - (request_length) : 0) #define PADDING_LENGTH(request_data, reply_data) \ PADDING_LENGTH_(offsetof(CMD_Request, request_data), offsetof(CMD_Reply, reply_data)) int PKL_CommandPaddingLength(CMD_Request *r) { int type; if (r->version < PROTO_VERSION_PADDING) return 0; type = ntohs(r->command); if (type < 0 || type >= N_REQUEST_TYPES) return 0; switch (type) { case REQ_NULL: return PADDING_LENGTH(data, data.null.EOR); case REQ_ONLINE: return PADDING_LENGTH(data.online.EOR, data.null.EOR); case REQ_OFFLINE: return PADDING_LENGTH(data.offline.EOR, data.null.EOR); case REQ_BURST: return PADDING_LENGTH(data.burst.EOR, data.null.EOR); case REQ_MODIFY_MINPOLL: return PADDING_LENGTH(data.modify_minpoll.EOR, data.null.EOR); case REQ_MODIFY_MAXPOLL: return PADDING_LENGTH(data.modify_maxpoll.EOR, data.null.EOR); case REQ_DUMP: return PADDING_LENGTH(data.dump.EOR, data.null.EOR); case REQ_MODIFY_MAXDELAY: return PADDING_LENGTH(data.modify_maxdelay.EOR, data.null.EOR); case REQ_MODIFY_MAXDELAYRATIO: return PADDING_LENGTH(data.modify_maxdelayratio.EOR, data.null.EOR); case REQ_MODIFY_MAXDELAYDEVRATIO: return PADDING_LENGTH(data.modify_maxdelaydevratio.EOR, data.null.EOR); case REQ_MODIFY_MAXUPDATESKEW: return PADDING_LENGTH(data.modify_maxupdateskew.EOR, data.null.EOR); case REQ_MODIFY_MAKESTEP: return PADDING_LENGTH(data.modify_makestep.EOR, data.null.EOR); case REQ_LOGON: return PADDING_LENGTH(data.logon.EOR, data.null.EOR); case REQ_SETTIME: return PADDING_LENGTH(data.settime.EOR, data.manual_timestamp.EOR); case REQ_LOCAL: return PADDING_LENGTH(data.local.EOR, data.null.EOR); case REQ_MANUAL: return PADDING_LENGTH(data.manual.EOR, data.null.EOR); case REQ_N_SOURCES: return PADDING_LENGTH(data.null.EOR, data.n_sources.EOR); case REQ_SOURCE_DATA: return PADDING_LENGTH(data.source_data.EOR, data.source_data.EOR); case REQ_REKEY: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_ALLOW: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_ALLOWALL: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_DENY: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_DENYALL: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_CMDALLOW: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_CMDALLOWALL: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_CMDDENY: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_CMDDENYALL: return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR); case REQ_ACCHECK: return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR); case REQ_CMDACCHECK: return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR); case REQ_ADD_SERVER: return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR); case REQ_ADD_PEER: return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR); case REQ_DEL_SOURCE: return PADDING_LENGTH(data.del_source.EOR, data.null.EOR); case REQ_WRITERTC: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_DFREQ: return PADDING_LENGTH(data.dfreq.EOR, data.null.EOR); case REQ_DOFFSET: return PADDING_LENGTH(data.doffset.EOR, data.null.EOR); case REQ_TRACKING: return PADDING_LENGTH(data.null.EOR, data.tracking.EOR); case REQ_SOURCESTATS: return PADDING_LENGTH(data.sourcestats.EOR, data.sourcestats.EOR); case REQ_RTCREPORT: return PADDING_LENGTH(data.null.EOR, data.rtc.EOR); case REQ_TRIMRTC: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_CYCLELOGS: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_SUBNETS_ACCESSED: case REQ_CLIENT_ACCESSES: /* No longer supported */ return 0; case REQ_CLIENT_ACCESSES_BY_INDEX: return PADDING_LENGTH(data.client_accesses_by_index.EOR, data.client_accesses_by_index.EOR); case REQ_MANUAL_LIST: return PADDING_LENGTH(data.null.EOR, data.manual_list.EOR); case REQ_MANUAL_DELETE: return PADDING_LENGTH(data.manual_delete.EOR, data.null.EOR); case REQ_MAKESTEP: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_ACTIVITY: return PADDING_LENGTH(data.null.EOR, data.activity.EOR); case REQ_RESELECT: return PADDING_LENGTH(data.null.EOR, data.null.EOR); case REQ_RESELECTDISTANCE: return PADDING_LENGTH(data.reselect_distance.EOR, data.null.EOR); case REQ_MODIFY_MINSTRATUM: return PADDING_LENGTH(data.modify_minstratum.EOR, data.null.EOR); case REQ_MODIFY_POLLTARGET: return PADDING_LENGTH(data.modify_polltarget.EOR, data.null.EOR); case REQ_SMOOTHING: return PADDING_LENGTH(data.null.EOR, data.smoothing.EOR); case REQ_SMOOTHTIME: return PADDING_LENGTH(data.smoothtime.EOR, data.null.EOR); default: /* If we fall through the switch, it most likely means we've forgotten to implement a new case */ assert(0); 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 : case RPY_CLIENT_ACCESSES: /* No longer supported */ return 0; case RPY_CLIENT_ACCESSES_BY_INDEX: { unsigned long nc = ntohl(r->data.client_accesses_by_index.n_clients); if (r->status == htons(STT_SUCCESS)) { if (nc > MAX_CLIENT_ACCESSES) return 0; 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 (ns > MAX_MANUAL_LIST_SAMPLES) return 0; 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); case RPY_SMOOTHING: return offsetof(CMD_Reply, data.smoothing.EOR); default: assert(0); } } return 0; } /* ================================================== */ chrony-2.1.1/pktlength.h0000644000000000000000000000252112542263351013667 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_CommandPaddingLength(CMD_Request *r); extern int PKL_ReplyLength(CMD_Reply *r); #endif /* GOT_PKTLENGTH_H */ chrony-2.1.1/refclock.c0000644000000000000000000006245412542263351013465 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing reference clocks. */ #include "config.h" #include "array.h" #include "refclock.h" #include "reference.h" #include "conf.h" #include "local.h" #include "memory.h" #include "util.h" #include "sources.h" #include "logging.h" #include "regress.h" #include "sched.h" /* list of refclock drivers */ extern RefclockDriver RCL_SHM_driver; extern RefclockDriver RCL_SOCK_driver; extern RefclockDriver RCL_PPS_driver; extern RefclockDriver RCL_PHC_driver; struct FilterSample { double offset; double dispersion; struct timeval sample_time; }; struct MedianFilter { int length; int index; int used; int last; int avg_var_n; double avg_var; double max_var; struct FilterSample *samples; int *selected; double *x_data; double *y_data; double *w_data; }; struct RCL_Instance_Record { RefclockDriver *driver; void *data; char *driver_parameter; int driver_parameter_length; int driver_poll; int driver_polled; int poll; int leap_status; int pps_rate; int pps_active; struct MedianFilter filter; uint32_t ref_id; uint32_t lock_ref; double offset; double delay; double precision; SCH_TimeoutID timeout_id; SRC_Instance source; }; /* Array of RCL_Instance_Record */ static ARR_Instance refclocks; static LOG_FileID logfileid; static int valid_sample_time(RCL_Instance instance, struct timeval *tv); static int pps_stratum(RCL_Instance instance, struct timeval *tv); static void poll_timeout(void *arg); static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything); static void add_dispersion(double dispersion, void *anything); static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion); static void filter_init(struct MedianFilter *filter, int length, double max_dispersion); static void filter_fini(struct MedianFilter *filter); static void filter_reset(struct MedianFilter *filter); static double filter_get_avg_sample_dispersion(struct MedianFilter *filter); static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion); static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion); static int filter_select_samples(struct MedianFilter *filter); static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion); static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset); static void filter_add_dispersion(struct MedianFilter *filter, double dispersion); static RCL_Instance get_refclock(unsigned int index) { return (RCL_Instance)ARR_GetElement(refclocks, index); } void RCL_Initialise(void) { refclocks = ARR_CreateInstance(sizeof (struct RCL_Instance_Record)); CNF_AddRefclocks(); if (ARR_GetSize(refclocks) > 0) { LCL_AddParameterChangeHandler(slew_samples, NULL); LCL_AddDispersionNotifyHandler(add_dispersion, NULL); } logfileid = CNF_GetLogRefclocks() ? LOG_FileOpen("refclocks", " Date (UTC) Time Refid DP L P Raw offset Cooked offset Disp.") : -1; } void RCL_Finalise(void) { unsigned int i; for (i = 0; i < ARR_GetSize(refclocks); i++) { RCL_Instance inst = get_refclock(i); if (inst->driver->fini) inst->driver->fini(inst); filter_fini(&inst->filter); Free(inst->driver_parameter); SRC_DestroyInstance(inst->source); } if (ARR_GetSize(refclocks) > 0) { LCL_RemoveParameterChangeHandler(slew_samples, NULL); LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL); } ARR_DestroyInstance(refclocks); } int RCL_AddRefclock(RefclockParameters *params) { int pps_source = 0; RCL_Instance inst = ARR_GetNewElement(refclocks); if (strcmp(params->driver_name, "SHM") == 0) { inst->driver = &RCL_SHM_driver; inst->precision = 1e-6; } else if (strcmp(params->driver_name, "SOCK") == 0) { inst->driver = &RCL_SOCK_driver; inst->precision = 1e-9; pps_source = 1; } else if (strcmp(params->driver_name, "PPS") == 0) { inst->driver = &RCL_PPS_driver; inst->precision = 1e-9; pps_source = 1; } else if (strcmp(params->driver_name, "PHC") == 0) { inst->driver = &RCL_PHC_driver; inst->precision = 1e-9; } else { LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name); return 0; } if (!inst->driver->init && !inst->driver->poll) { LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name); return 0; } inst->data = NULL; inst->driver_parameter = params->driver_parameter; inst->driver_parameter_length = 0; inst->driver_poll = params->driver_poll; inst->poll = params->poll; inst->driver_polled = 0; inst->leap_status = LEAP_Normal; inst->pps_rate = params->pps_rate; inst->pps_active = 0; inst->lock_ref = params->lock_ref_id; inst->offset = params->offset; inst->delay = params->delay; if (params->precision > 0.0) inst->precision = params->precision; inst->timeout_id = -1; inst->source = NULL; if (inst->driver_parameter) { int i; inst->driver_parameter_length = strlen(inst->driver_parameter); for (i = 0; i < inst->driver_parameter_length; i++) if (inst->driver_parameter[i] == ':') inst->driver_parameter[i] = '\0'; } if (pps_source) { if (inst->pps_rate < 1) inst->pps_rate = 1; } else { inst->pps_rate = 0; } if (params->ref_id) inst->ref_id = params->ref_id; else { unsigned char ref[5] = { 0, 0, 0, 0, 0 }; unsigned int index = ARR_GetSize(refclocks) - 1; snprintf((char *)ref, sizeof (ref), "%3.3s", params->driver_name); ref[3] = index % 10 + '0'; if (index >= 10) ref[2] = (index / 10) % 10 + '0'; inst->ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; } if (inst->driver->poll) { int max_samples; if (inst->driver_poll > inst->poll) inst->driver_poll = inst->poll; max_samples = 1 << (inst->poll - inst->driver_poll); if (max_samples < params->filter_length) { if (max_samples < 4) { LOG(LOGS_WARN, LOGF_Refclock, "Setting filter length for %s to %d", UTI_RefidToString(inst->ref_id), max_samples); } params->filter_length = max_samples; } } if (inst->driver->init) if (!inst->driver->init(inst)) { LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name); return 0; } filter_init(&inst->filter, params->filter_length, params->max_dispersion); inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL, params->min_samples, params->max_samples); DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d", params->driver_name, UTI_RefidToString(inst->ref_id), inst->poll, inst->driver_poll, params->filter_length); Free(params->driver_name); return 1; } void RCL_StartRefclocks(void) { unsigned int i, j, n; n = ARR_GetSize(refclocks); for (i = 0; i < n; i++) { RCL_Instance inst = get_refclock(i); SRC_SetActive(inst->source); inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst); if (inst->lock_ref) { /* Replace lock refid with index to refclocks */ for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++) ; inst->lock_ref = j < n ? j : -1; } else inst->lock_ref = -1; } } void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) { unsigned int i; uint32_t ref_id; assert(report->ip_addr.family == IPADDR_INET4); ref_id = report->ip_addr.addr.in4; for (i = 0; i < ARR_GetSize(refclocks); i++) { RCL_Instance inst = get_refclock(i); if (inst->ref_id == ref_id) { report->poll = inst->poll; report->mode = RPT_LOCAL_REFERENCE; break; } } } void RCL_SetDriverData(RCL_Instance instance, void *data) { instance->data = data; } void * RCL_GetDriverData(RCL_Instance instance) { return instance->data; } char * RCL_GetDriverParameter(RCL_Instance instance) { return instance->driver_parameter; } char * RCL_GetDriverOption(RCL_Instance instance, char *name) { char *s, *e; int n; s = instance->driver_parameter; e = s + instance->driver_parameter_length; n = strlen(name); while (1) { s += strlen(s) + 1; if (s >= e) break; if (!strncmp(name, s, n)) { if (s[n] == '=') return s + n + 1; if (s[n] == '\0') return s + n; } } return NULL; } int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap) { double correction, dispersion; struct timeval cooked_time; LCL_GetOffsetCorrection(sample_time, &correction, &dispersion); UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time); dispersion += instance->precision; /* Make sure the timestamp and offset provided by the driver are sane */ if (!UTI_IsTimeOffsetSane(sample_time, offset) || !valid_sample_time(instance, sample_time)) return 0; filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion); switch (leap) { case LEAP_Normal: case LEAP_InsertSecond: case LEAP_DeleteSecond: instance->leap_status = leap; break; default: instance->leap_status = LEAP_Unsynchronised; break; } instance->pps_active = 0; log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion); /* for logging purposes */ if (!instance->driver->poll) instance->driver_polled++; return 1; } int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second) { double correction, dispersion, offset; struct timeval cooked_time; int rate; NTP_Leap leap; leap = LEAP_Normal; LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion); UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time); dispersion += instance->precision; if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) || !valid_sample_time(instance, pulse_time)) return 0; rate = instance->pps_rate; assert(rate > 0); offset = -second - correction + instance->offset; /* Adjust the offset to [-0.5/rate, 0.5/rate) interval */ offset -= (long)(offset * rate) / (double)rate; if (offset < -0.5 / rate) offset += 1.0 / rate; else if (offset >= 0.5 / rate) offset -= 1.0 / rate; if (instance->lock_ref != -1) { RCL_Instance lock_refclock; struct timeval ref_sample_time; double sample_diff, ref_offset, ref_dispersion, shift; lock_refclock = get_refclock(instance->lock_ref); if (!filter_get_last_sample(&lock_refclock->filter, &ref_sample_time, &ref_offset, &ref_dispersion)) { DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample"); return 0; } ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter); UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time); if (fabs(sample_diff) >= 2.0 / rate) { DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f", sample_diff); return 0; } /* Align the offset to the reference sample */ if ((ref_offset - offset) >= 0.0) shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate; else shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate; offset += shift; if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) { DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f", ref_offset - offset, ref_dispersion, dispersion); return 0; } leap = lock_refclock->leap_status; DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f", second, offset, ref_offset - offset, sample_diff); } else { struct timeval ref_time; int is_synchronised, stratum; double root_delay, root_dispersion, distance; uint32_t ref_id; /* Ignore the pulse if we are not well synchronized */ REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum, &ref_id, &ref_time, &root_delay, &root_dispersion); distance = fabs(root_delay) / 2 + root_dispersion; if (!is_synchronised || distance >= 0.5 / rate) { DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f", second, is_synchronised, distance); /* Drop also all stored samples */ filter_reset(&instance->filter); return 0; } } filter_add_sample(&instance->filter, &cooked_time, offset, dispersion); instance->leap_status = leap; instance->pps_active = 1; log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion); /* for logging purposes */ if (!instance->driver->poll) instance->driver_polled++; return 1; } static int valid_sample_time(RCL_Instance instance, struct timeval *tv) { struct timeval raw_time; double diff; LCL_ReadRawTime(&raw_time); UTI_DiffTimevalsToDouble(&diff, &raw_time, tv); if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) { DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f tv=%s", UTI_RefidToString(instance->ref_id), diff, UTI_TimevalToString(tv)); return 0; } return 1; } static int pps_stratum(RCL_Instance instance, struct timeval *tv) { struct timeval ref_time; int is_synchronised, stratum; unsigned int i; double root_delay, root_dispersion; NTP_Leap leap; uint32_t ref_id; RCL_Instance refclock; REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum, &ref_id, &ref_time, &root_delay, &root_dispersion); /* Don't change our stratum if local stratum is active or this is the current source */ if (ref_id == instance->ref_id || REF_IsLocalActive()) return stratum - 1; /* Or the current source is another PPS refclock */ for (i = 0; i < ARR_GetSize(refclocks); i++) { refclock = get_refclock(i); if (refclock->ref_id == ref_id && refclock->pps_active && refclock->lock_ref == -1) return stratum - 1; } return 0; } static void poll_timeout(void *arg) { int poll; RCL_Instance inst = (RCL_Instance)arg; poll = inst->poll; if (inst->driver->poll) { poll = inst->driver_poll; inst->driver->poll(inst); inst->driver_polled++; } if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) { double offset, dispersion; struct timeval sample_time; int sample_ok, stratum; sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion); inst->driver_polled = 0; if (sample_ok) { if (inst->pps_active && inst->lock_ref == -1) /* Handle special case when PPS is used with local stratum */ stratum = pps_stratum(inst, &sample_time); else stratum = 0; SRC_UpdateReachability(inst->source, 1); SRC_AccumulateSample(inst->source, &sample_time, offset, inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status); SRC_SelectSource(inst->source); log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion); } else { SRC_UpdateReachability(inst->source, 0); } } inst->timeout_id = SCH_AddTimeoutByDelay(UTI_Log2ToDouble(poll), poll_timeout, arg); } static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { unsigned int i; for (i = 0; i < ARR_GetSize(refclocks); i++) { if (change_type == LCL_ChangeUnknownStep) filter_reset(&get_refclock(i)->filter); else filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset); } } static void add_dispersion(double dispersion, void *anything) { unsigned int i; for (i = 0; i < ARR_GetSize(refclocks); i++) filter_add_dispersion(&get_refclock(i)->filter, dispersion); } static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion) { char sync_stats[4] = {'N', '+', '-', '?'}; if (logfileid == -1) return; if (!filtered) { LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e", UTI_TimeToLogForm(sample_time->tv_sec), (int)sample_time->tv_usec, UTI_RefidToString(instance->ref_id), instance->driver_polled, sync_stats[instance->leap_status], pulse, raw_offset, cooked_offset, dispersion); } else { LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e", UTI_TimeToLogForm(sample_time->tv_sec), (int)sample_time->tv_usec, UTI_RefidToString(instance->ref_id), sync_stats[instance->leap_status], cooked_offset, dispersion); } } static void filter_init(struct MedianFilter *filter, int length, double max_dispersion) { if (length < 1) length = 1; filter->length = length; filter->index = -1; filter->used = 0; filter->last = -1; /* set first estimate to system precision */ filter->avg_var_n = 0; filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum(); filter->max_var = max_dispersion * max_dispersion; filter->samples = MallocArray(struct FilterSample, filter->length); filter->selected = MallocArray(int, filter->length); filter->x_data = MallocArray(double, filter->length); filter->y_data = MallocArray(double, filter->length); filter->w_data = MallocArray(double, filter->length); } static void filter_fini(struct MedianFilter *filter) { Free(filter->samples); Free(filter->selected); Free(filter->x_data); Free(filter->y_data); Free(filter->w_data); } static void filter_reset(struct MedianFilter *filter) { filter->index = -1; filter->used = 0; } static double filter_get_avg_sample_dispersion(struct MedianFilter *filter) { return sqrt(filter->avg_var); } static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion) { filter->index++; filter->index %= filter->length; filter->last = filter->index; if (filter->used < filter->length) filter->used++; filter->samples[filter->index].sample_time = *sample_time; filter->samples[filter->index].offset = offset; filter->samples[filter->index].dispersion = dispersion; DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f", filter->index, UTI_TimevalToString(sample_time), offset, dispersion); } static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion) { if (filter->last < 0) return 0; *sample_time = filter->samples[filter->last].sample_time; *offset = filter->samples[filter->last].offset; *dispersion = filter->samples[filter->last].dispersion; return 1; } static const struct FilterSample *tmp_sorted_array; static int sample_compare(const void *a, const void *b) { const struct FilterSample *s1, *s2; s1 = &tmp_sorted_array[*(int *)a]; s2 = &tmp_sorted_array[*(int *)b]; if (s1->offset < s2->offset) return -1; else if (s1->offset > s2->offset) return 1; return 0; } int filter_select_samples(struct MedianFilter *filter) { int i, j, k, o, from, to, *selected; double min_dispersion; if (filter->used < 1) return 0; /* for lengths below 4 require full filter, for 4 and above require at least 4 samples */ if ((filter->length < 4 && filter->used != filter->length) || (filter->length >= 4 && filter->used < 4)) return 0; selected = filter->selected; if (filter->used > 4) { /* select samples with dispersion better than 1.5 * minimum */ for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) { if (min_dispersion > filter->samples[i].dispersion) min_dispersion = filter->samples[i].dispersion; } for (i = j = 0; i < filter->used; i++) { if (filter->samples[i].dispersion <= 1.5 * min_dispersion) selected[j++] = i; } } else { j = 0; } if (j < 4) { /* select all samples */ for (j = 0; j < filter->used; j++) selected[j] = j; } /* and sort their indices by offset */ tmp_sorted_array = filter->samples; qsort(selected, j, sizeof (int), sample_compare); /* select 60 percent of the samples closest to the median */ if (j > 2) { from = j / 5; if (from < 1) from = 1; to = j - from; } else { from = 0; to = j; } /* mark unused samples and sort the rest from oldest to newest */ o = filter->used - filter->index - 1; for (i = 0; i < from; i++) selected[i] = -1; for (; i < to; i++) selected[i] = (selected[i] + o) % filter->used; for (; i < filter->used; i++) selected[i] = -1; for (i = from; i < to; i++) { j = selected[i]; selected[i] = -1; while (j != -1 && selected[j] != j) { k = selected[j]; selected[j] = j; j = k; } } for (i = j = 0, k = -1; i < filter->used; i++) { if (selected[i] != -1) selected[j++] = (selected[i] + filter->used - o) % filter->used; } return j; } static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion) { struct FilterSample *s, *ls; int i, n, dof; double x, y, d, e, var, prev_avg_var; n = filter_select_samples(filter); if (n < 1) return 0; ls = &filter->samples[filter->selected[n - 1]]; /* prepare data */ for (i = 0; i < n; i++) { s = &filter->samples[filter->selected[i]]; UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time); filter->y_data[i] = s->offset; filter->w_data[i] = s->dispersion; } /* mean offset, sample time and sample dispersion */ for (i = 0, x = y = e = 0.0; i < n; i++) { x += filter->x_data[i]; y += filter->y_data[i]; e += filter->w_data[i]; } x /= n; y /= n; e /= n; if (n >= 4) { double b0, b1, s2, sb0, sb1; /* set y axis to the mean sample time */ for (i = 0; i < n; i++) filter->x_data[i] -= x; /* make a linear fit and use the estimated standard deviation of intercept as dispersion */ RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n, &b0, &b1, &s2, &sb0, &sb1); var = s2; d = sb0; dof = n - 2; } else if (n >= 2) { for (i = 0, d = 0.0; i < n; i++) d += (filter->y_data[i] - y) * (filter->y_data[i] - y); var = d / (n - 1); d = sqrt(var); dof = n - 1; } else { var = filter->avg_var; d = sqrt(var); dof = 1; } /* avoid having zero dispersion */ if (var < 1e-20) { var = 1e-20; d = sqrt(var); } /* drop the sample if variance is larger than allowed maximum */ if (filter->max_var > 0.0 && var > filter->max_var) { DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f", sqrt(var), sqrt(filter->max_var)); return 0; } prev_avg_var = filter->avg_var; /* update exponential moving average of the variance */ if (filter->avg_var_n > 50) { filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var); } else { filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) / (dof + filter->avg_var_n); if (filter->avg_var_n == 0) prev_avg_var = filter->avg_var; filter->avg_var_n += dof; } /* reduce noise in sourcestats weights by using the long-term average instead of the estimated variance if it's not significantly lower */ if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var) d = sqrt(filter->avg_var) * d / sqrt(var); if (d < e) d = e; UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time); *offset = y; *dispersion = d; filter_reset(filter); return 1; } static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset) { int i; double delta_time; struct timeval *sample; for (i = 0; i < filter->used; i++) { sample = &filter->samples[i].sample_time; UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset); filter->samples[i].offset -= delta_time; } } static void filter_add_dispersion(struct MedianFilter *filter, double dispersion) { int i; for (i = 0; i < filter->used; i++) { filter->samples[i].dispersion += dispersion; } } chrony-2.1.1/refclock.h0000644000000000000000000000453312542263351013464 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2009 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for refclocks. */ #ifndef GOT_REFCLOCK_H #define GOT_REFCLOCK_H #include "srcparams.h" #include "sources.h" typedef struct { char *driver_name; char *driver_parameter; int driver_poll; int poll; int filter_length; int pps_rate; int min_samples; int max_samples; uint32_t ref_id; uint32_t lock_ref_id; double offset; double delay; double precision; double max_dispersion; SRC_SelectOption sel_option; } RefclockParameters; typedef struct RCL_Instance_Record *RCL_Instance; typedef struct { int (*init)(RCL_Instance instance); void (*fini)(RCL_Instance instance); int (*poll)(RCL_Instance instance); } RefclockDriver; extern void RCL_Initialise(void); extern void RCL_Finalise(void); extern int RCL_AddRefclock(RefclockParameters *params); extern void RCL_StartRefclocks(void); extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now); /* functions used by drivers */ extern void RCL_SetDriverData(RCL_Instance instance, void *data); extern void *RCL_GetDriverData(RCL_Instance instance); extern char *RCL_GetDriverParameter(RCL_Instance instance); extern char *RCL_GetDriverOption(RCL_Instance instance, char *name); extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap); extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second); #endif chrony-2.1.1/refclock_phc.c0000644000000000000000000001162012542263351014304 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2013 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= PTP hardware clock (PHC) refclock driver. */ #include "config.h" #include "refclock.h" #ifdef FEAT_PHC #include "sysincl.h" #include #include "refclock.h" #include "logging.h" #include "util.h" /* From linux/include/linux/posix-timers.h */ #define CPUCLOCK_MAX 3 #define CLOCKFD CPUCLOCK_MAX #define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK) #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) #define NUM_READINGS 10 static int no_sys_offset_ioctl = 0; struct phc_reading { struct timespec sys_ts1; struct timespec phc_ts;; struct timespec sys_ts2; }; static double diff_ts(struct timespec *ts1, struct timespec *ts2) { return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9; } static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n) { #if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES struct ptp_sys_offset sys_off; int i; /* Silence valgrind */ memset(&sys_off, 0, sizeof (sys_off)); sys_off.n_samples = n; if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) { LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno)); return 0; } for (i = 0; i < n; i++) { readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec; readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec; readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec; readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec; readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec; readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec; } return 1; #else /* Not available */ return 0; #endif } static int read_phc_user(struct phc_reading *readings, int phc_fd, int n) { clockid_t phc_id; int i; phc_id = FD_TO_CLOCKID(phc_fd); for (i = 0; i < n; i++) { if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) || clock_gettime(phc_id, &readings[i].phc_ts) || clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) { LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno)); return 0; } } return 1; } static int phc_initialise(RCL_Instance instance) { struct ptp_clock_caps caps; int phc_fd; char *path; path = RCL_GetDriverParameter(instance); phc_fd = open(path, O_RDONLY); if (phc_fd < 0) { LOG_FATAL(LOGF_Refclock, "open() failed on %s", path); return 0; } /* Make sure it is a PHC */ if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) { LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno)); return 0; } UTI_FdSetCloexec(phc_fd); RCL_SetDriverData(instance, (void *)(long)phc_fd); return 1; } static void phc_finalise(RCL_Instance instance) { close((long)RCL_GetDriverData(instance)); } static int phc_poll(RCL_Instance instance) { struct phc_reading readings[NUM_READINGS]; struct timeval tv; double offset = 0.0, delay, best_delay = 0.0; int i, phc_fd, best; phc_fd = (long)RCL_GetDriverData(instance); if (!no_sys_offset_ioctl) { if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) { no_sys_offset_ioctl = 1; return 0; } } else { if (!read_phc_user(readings, phc_fd, NUM_READINGS)) return 0; } /* Find the fastest reading */ for (i = 0; i < NUM_READINGS; i++) { delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1); if (!i || best_delay > delay) { best = i; best_delay = delay; } } offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0; tv.tv_sec = readings[best].sys_ts2.tv_sec; tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000; DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay); return RCL_AddSample(instance, &tv, offset, LEAP_Normal); } RefclockDriver RCL_PHC_driver = { phc_initialise, phc_finalise, phc_poll }; #else RefclockDriver RCL_PHC_driver = { NULL, NULL, NULL }; #endif chrony-2.1.1/refclock_pps.c0000644000000000000000000001022612542263351014335 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2009 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= PPSAPI refclock driver. */ #include "config.h" #include "refclock.h" #if FEAT_PPS #if defined(HAVE_SYS_TIMEPPS_H) #include #elif defined(HAVE_TIMEPPS_H) #include #endif #include "logging.h" #include "memory.h" #include "util.h" struct pps_instance { pps_handle_t handle; pps_seq_t last_seq; int edge_clear; }; static int pps_initialise(RCL_Instance instance) { pps_handle_t handle; pps_params_t params; struct pps_instance *pps; int fd, edge_clear, mode; char *path; path = RCL_GetDriverParameter(instance); edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0; fd = open(path, O_RDWR); if (fd < 0) { LOG_FATAL(LOGF_Refclock, "open() failed on %s", path); return 0; } UTI_FdSetCloexec(fd); if (time_pps_create(fd, &handle) < 0) { LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path); return 0; } if (time_pps_getcap(handle, &mode) < 0) { LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path); return 0; } if (time_pps_getparams(handle, ¶ms) < 0) { LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path); return 0; } if (!edge_clear) { if (!(mode & PPS_CAPTUREASSERT)) { LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path); return 0; } params.mode |= PPS_CAPTUREASSERT; params.mode &= ~PPS_CAPTURECLEAR; } else { if (!(mode & PPS_CAPTURECLEAR)) { LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path); return 0; } params.mode |= PPS_CAPTURECLEAR; params.mode &= ~PPS_CAPTUREASSERT; } if (time_pps_setparams(handle, ¶ms) < 0) { LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path); return 0; } pps = MallocNew(struct pps_instance); pps->handle = handle; pps->last_seq = 0; pps->edge_clear = edge_clear; RCL_SetDriverData(instance, pps); return 1; } static void pps_finalise(RCL_Instance instance) { struct pps_instance *pps; pps = (struct pps_instance *)RCL_GetDriverData(instance); time_pps_destroy(pps->handle); Free(pps); } static int pps_poll(RCL_Instance instance) { struct pps_instance *pps; struct timespec ts; struct timeval tv; pps_info_t pps_info; pps_seq_t seq; pps = (struct pps_instance *)RCL_GetDriverData(instance); ts.tv_sec = 0; ts.tv_nsec = 0; if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) { LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno)); return 0; } if (!pps->edge_clear) { seq = pps_info.assert_sequence; ts = pps_info.assert_timestamp; } else { seq = pps_info.clear_sequence; ts = pps_info.clear_timestamp; } if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) { DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu", seq, ts.tv_sec, ts.tv_nsec); return 0; } pps->last_seq = seq; tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9); } RefclockDriver RCL_PPS_driver = { pps_initialise, pps_finalise, pps_poll }; #else RefclockDriver RCL_PPS_driver = { NULL, NULL, NULL }; #endif chrony-2.1.1/refclock_shm.c0000644000000000000000000000657612542263351014337 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2009 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= SHM refclock driver. */ #include "config.h" #include "sysincl.h" #include "refclock.h" #include "logging.h" #include "util.h" #define SHMKEY 0x4e545030 struct shmTime { int mode; /* 0 - if valid set * use values, * clear valid * 1 - if valid set * if count before and after read of values is equal, * use values * clear valid */ volatile int count; time_t clockTimeStampSec; int clockTimeStampUSec; time_t receiveTimeStampSec; int receiveTimeStampUSec; int leap; int precision; int nsamples; volatile int valid; int clockTimeStampNSec; int receiveTimeStampNSec; int dummy[8]; }; static int shm_initialise(RCL_Instance instance) { int id, param, perm; char *s; struct shmTime *shm; param = atoi(RCL_GetDriverParameter(instance)); s = RCL_GetDriverOption(instance, "perm"); perm = s ? strtol(s, NULL, 8) & 0777 : 0600; id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm); if (id == -1) { LOG_FATAL(LOGF_Refclock, "shmget() failed"); return 0; } shm = (struct shmTime *)shmat(id, 0, 0); if ((long)shm == -1) { LOG_FATAL(LOGF_Refclock, "shmat() failed"); return 0; } RCL_SetDriverData(instance, shm); return 1; } static void shm_finalise(RCL_Instance instance) { shmdt(RCL_GetDriverData(instance)); } static int shm_poll(RCL_Instance instance) { struct timeval tv; struct shmTime t, *shm; double offset; shm = (struct shmTime *)RCL_GetDriverData(instance); t = *shm; if ((t.mode == 1 && t.count != shm->count) || !(t.mode == 0 || t.mode == 1) || !t.valid) { DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d", t.mode, t.count, t.valid); return 0; } shm->valid = 0; tv.tv_sec = t.receiveTimeStampSec; tv.tv_usec = t.receiveTimeStampUSec; offset = t.clockTimeStampSec - t.receiveTimeStampSec; if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec && t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) offset += (t.clockTimeStampNSec - t.receiveTimeStampNSec) * 1e-9; else offset += (t.clockTimeStampUSec - t.receiveTimeStampUSec) * 1e-6; return RCL_AddSample(instance, &tv, offset, t.leap); } RefclockDriver RCL_SHM_driver = { shm_initialise, shm_finalise, shm_poll }; chrony-2.1.1/refclock_sock.c0000644000000000000000000000621012542263351014470 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2009 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Unix domain socket refclock driver. */ #include "config.h" #include "sysincl.h" #include "refclock.h" #include "logging.h" #include "util.h" #include "sched.h" #define SOCK_MAGIC 0x534f434b struct sock_sample { struct timeval tv; double offset; int pulse; int leap; int _pad; int magic; }; static void read_sample(void *anything) { struct sock_sample sample; RCL_Instance instance; int sockfd, s; instance = (RCL_Instance)anything; sockfd = (long)RCL_GetDriverData(instance); s = recv(sockfd, &sample, sizeof (sample), 0); if (s < 0) { LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s", strerror(errno)); return; } if (s != sizeof (sample)) { LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld", s, (long)sizeof (sample)); return; } if (sample.magic != SOCK_MAGIC) { LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x", sample.magic, SOCK_MAGIC); return; } if (sample.pulse) { RCL_AddPulse(instance, &sample.tv, sample.offset); } else { RCL_AddSample(instance, &sample.tv, sample.offset, sample.leap); } } static int sock_initialise(RCL_Instance instance) { struct sockaddr_un s; int sockfd; char *path; path = RCL_GetDriverParameter(instance); s.sun_family = AF_UNIX; if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) { LOG_FATAL(LOGF_Refclock, "path %s is too long", path); return 0; } sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sockfd < 0) { LOG_FATAL(LOGF_Refclock, "socket() failed"); return 0; } UTI_FdSetCloexec(sockfd); unlink(path); if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { LOG_FATAL(LOGF_Refclock, "bind() failed"); return 0; } RCL_SetDriverData(instance, (void *)(long)sockfd); SCH_AddInputFileHandler(sockfd, read_sample, instance); return 1; } static void sock_finalise(RCL_Instance instance) { int sockfd; sockfd = (long)RCL_GetDriverData(instance); SCH_RemoveInputFileHandler(sockfd); close(sockfd); } RefclockDriver RCL_SOCK_driver = { sock_initialise, sock_finalise, NULL }; chrony-2.1.1/reference.c0000644000000000000000000011104612542263351013623 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "memory.h" #include "reference.h" #include "util.h" #include "conf.h" #include "logging.h" #include "local.h" #include "sched.h" /* ================================================== */ /* The minimum allowed skew */ #define MIN_SKEW 1.0e-12 static int are_we_synchronised; static int enable_local_stratum; static int local_stratum; static NTP_Leap our_leap_status; static int our_leap_sec; static int our_stratum; static uint32_t our_ref_id; static IPAddr our_ref_ip; struct timeval our_ref_time; static double our_skew; static double our_residual_freq; static double our_root_delay; static double our_root_dispersion; static double max_update_skew; static double last_offset; static double avg2_offset; static int avg2_moving; static double correction_time_ratio; /* Flag indicating that we are initialised */ static int initialised = 0; /* Current operating mode */ static REF_Mode mode; /* Threshold and update limit for stepping clock */ static int make_step_limit; static double make_step_threshold; /* Number of updates before offset checking, number of ignored updates before exiting and the maximum allowed offset */ static int max_offset_delay; static int max_offset_ignore; static double max_offset; /* 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; /* Handler for mode ending */ static REF_ModeEndHandler mode_end_handler = NULL; /* Filename of the drift file. */ static char *drift_file=NULL; static double drift_file_age; static void update_drift_file(double, double); /* Leap second handling mode */ static REF_LeapMode leap_mode; /* Flag indicating the clock was recently corrected for leap second and it may not have correct time yet (missing 23:59:60 in the UTC time scale) */ static int leap_in_progress; /* Timer for the leap second handler */ static int leap_timer_running; static SCH_TimeoutID leap_timeout_id; /* Name of a system timezone containing leap seconds occuring at midnight */ static char *leap_tzname; static time_t last_tz_leap_check; static NTP_Leap tz_leap; /* ================================================== */ static LOG_FileID logfileid; /* ================================================== */ /* Exponential moving averages of absolute clock frequencies used as a fallback when synchronisation is lost. */ struct fb_drift { double freq; double secs; }; static int fb_drift_min; static int fb_drift_max; static struct fb_drift *fb_drifts = NULL; static int next_fb_drift; static SCH_TimeoutID fb_drift_timeout_id; /* Timestamp of last reference update */ static struct timeval last_ref_update; static double last_ref_update_interval; /* ================================================== */ static NTP_Leap get_tz_leap(time_t when); static void update_leap_status(NTP_Leap leap, time_t now, int reset); /* ================================================== */ static void handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { double delta; struct timeval now; UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset); if (change_type == LCL_ChangeUnknownStep) { last_ref_update.tv_sec = 0; last_ref_update.tv_usec = 0; } else if (last_ref_update.tv_sec) { UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset); } /* When the clock was stepped, check if that doesn't change our leap status and also reset the leap timeout to undo the shift in the scheduler */ if (change_type != LCL_ChangeAdjust && our_leap_sec && !leap_in_progress) { LCL_ReadRawTime(&now); update_leap_status(our_leap_status, now.tv_sec, 1); } } /* ================================================== */ void REF_Initialise(void) { FILE *in; double file_freq_ppm, file_skew_ppm; double our_frequency_ppm; mode = REF_ModeNormal; are_we_synchronised = 0; our_leap_status = LEAP_Unsynchronised; our_leap_sec = 0; 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; drift_file_age = 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 (fscanf(in, "%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; if (our_skew < MIN_SKEW) our_skew = MIN_SKEW; LOG(LOGS_INFO, LOGF_Reference, "Frequency %.3f +/- %.3f ppm read from %s", file_freq_ppm, file_skew_ppm, drift_file); LCL_SetAbsoluteFrequency(our_frequency_ppm); } else { LOG(LOGS_WARN, LOGF_Reference, "Could not read valid frequency and skew from driftfile %s", drift_file); } fclose(in); } } if (our_frequency_ppm == 0.0) { our_frequency_ppm = LCL_ReadAbsoluteFrequency(); if (our_frequency_ppm != 0.0) { LOG(LOGS_INFO, LOGF_Reference, "Initial frequency %.3f ppm", our_frequency_ppm); } } logfileid = CNF_GetLogTracking() ? LOG_FileOpen("tracking", " Date (UTC) Time IP Address St Freq ppm Skew ppm Offset L Co Offset sd Rem. corr.") : -1; max_update_skew = fabs(CNF_GetMaxUpdateSkew()) * 1.0e-6; correction_time_ratio = CNF_GetCorrectionTimeRatio(); enable_local_stratum = CNF_AllowLocalReference(&local_stratum); leap_timer_running = 0; leap_in_progress = 0; leap_mode = CNF_GetLeapSecMode(); /* Switch to step mode if the system driver doesn't support leap */ if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap()) leap_mode = REF_LeapModeStep; leap_tzname = CNF_GetLeapSecTimezone(); if (leap_tzname) { /* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */ if (get_tz_leap(1341014400) == LEAP_InsertSecond && get_tz_leap(1356912000) == LEAP_Normal) { LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname); } else { LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname); leap_tzname = NULL; } } CNF_GetMakeStep(&make_step_limit, &make_step_threshold); CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset); CNF_GetLogChange(&do_log_change, &log_change_threshold); CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user); CNF_GetFallbackDrifts(&fb_drift_min, &fb_drift_max); if (fb_drift_max >= fb_drift_min && fb_drift_min > 0) { fb_drifts = MallocArray(struct fb_drift, fb_drift_max - fb_drift_min + 1); memset(fb_drifts, 0, sizeof (struct fb_drift) * (fb_drift_max - fb_drift_min + 1)); next_fb_drift = 0; fb_drift_timeout_id = -1; } last_ref_update.tv_sec = 0; last_ref_update.tv_usec = 0; last_ref_update_interval = 0.0; LCL_AddParameterChangeHandler(handle_slew, NULL); /* And just to prevent anything wierd ... */ if (do_log_change) { log_change_threshold = fabs(log_change_threshold); } /* Make first entry in tracking log */ REF_SetUnsynchronised(); } /* ================================================== */ void REF_Finalise(void) { update_leap_status(LEAP_Unsynchronised, 0, 0); if (drift_file) { update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); } Free(fb_drifts); initialised = 0; } /* ================================================== */ void REF_SetMode(REF_Mode new_mode) { mode = new_mode; } /* ================================================== */ REF_Mode REF_GetMode(void) { return mode; } /* ================================================== */ void REF_SetModeEndHandler(REF_ModeEndHandler handler) { mode_end_handler = handler; } /* ================================================== */ REF_LeapMode REF_GetLeapMode(void) { return leap_mode; } /* ================================================== */ 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; int r1, r2; /* 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 */ r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew); r2 = fclose(out); if (r1 < 0 || r2) { Free(temp_drift_file); LOG(LOGS_WARN, LOGF_Reference, "Could not write to temporary driftfile %s.tmp", drift_file); return; } /* Clone the file attributes from the existing file if there is one. */ if (!stat(drift_file,&buf)) { if (chown(temp_drift_file,buf.st_uid,buf.st_gid) || chmod(temp_drift_file,buf.st_mode & 0777)) { LOG(LOGS_WARN, LOGF_Reference, "Could not change ownership or permissions of temporary driftfile %s.tmp", drift_file); } } /* 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", drift_file,drift_file); return; } Free(temp_drift_file); } /* ================================================== */ static void update_fb_drifts(double freq_ppm, double update_interval) { int i, secs; assert(are_we_synchronised); if (next_fb_drift > 0) { #if 0 /* Reset drifts that were used when we were unsynchronised */ for (i = 0; i < next_fb_drift - fb_drift_min; i++) fb_drifts[i].secs = 0.0; #endif next_fb_drift = 0; } if (fb_drift_timeout_id != -1) { SCH_RemoveTimeout(fb_drift_timeout_id); fb_drift_timeout_id = -1; } if (update_interval < 1.0 || update_interval > last_ref_update_interval * 4.0) return; for (i = 0; i < fb_drift_max - fb_drift_min + 1; i++) { secs = 1 << (i + fb_drift_min); if (fb_drifts[i].secs < secs) { /* Calculate average over 2 * secs interval before switching to exponential updating */ fb_drifts[i].freq = (fb_drifts[i].freq * fb_drifts[i].secs + update_interval * 0.5 * freq_ppm) / (update_interval * 0.5 + fb_drifts[i].secs); fb_drifts[i].secs += update_interval * 0.5; } else { /* Update exponential moving average. The smoothing factor for update interval equal to secs is about 0.63, for half interval about 0.39, for double interval about 0.86. */ fb_drifts[i].freq += (1 - 1.0 / exp(update_interval / secs)) * (freq_ppm - fb_drifts[i].freq); } DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds", i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs); } } /* ================================================== */ static void fb_drift_timeout(void *arg) { assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max); fb_drift_timeout_id = -1; DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm", next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq); LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq); REF_SetUnsynchronised(); } /* ================================================== */ static void schedule_fb_drift(struct timeval *now) { int i, c, secs; double unsynchronised; struct timeval when; if (fb_drift_timeout_id != -1) return; /* already scheduled */ UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update); for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) { secs = 1 << i; if (fb_drifts[i - fb_drift_min].secs < secs) continue; if (unsynchronised < secs && i > next_fb_drift) break; c = i; } if (c > next_fb_drift) { LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq); next_fb_drift = c; DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c); } if (i <= fb_drift_max) { next_fb_drift = i; UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when); fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL); DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i); } } /* ================================================== */ static void end_ref_mode(int result) { mode = REF_ModeIgnore; /* Dispatch the handler */ if (mode_end_handler) (mode_end_handler)(result); } /* ================================================== */ #define BUFLEN 255 #define S_MAX_USER_LEN "128" static void maybe_log_offset(double offset, time_t now) { double abs_offset; FILE *p; char buffer[BUFLEN], host[BUFLEN]; 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)) { snprintf(buffer, sizeof(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, ""); } fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host); fputs("\n", p); 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); } } } /* ================================================== */ static int is_step_limit_reached(double offset, double offset_correction) { if (make_step_limit == 0) { return 0; } else if (make_step_limit > 0) { make_step_limit--; } return fabs(offset - offset_correction) > make_step_threshold; } /* ================================================== */ static int is_offset_ok(double offset) { if (max_offset_delay < 0) return 1; if (max_offset_delay > 0) { max_offset_delay--; return 1; } offset = fabs(offset); if (offset > max_offset) { LOG(LOGS_WARN, LOGF_Reference, "Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ", -offset, max_offset, !max_offset_ignore ? "exiting" : "ignored"); if (!max_offset_ignore) end_ref_mode(0); else if (max_offset_ignore > 0) max_offset_ignore--; return 0; } return 1; } /* ================================================== */ static int is_leap_second_day(struct tm *stm) { /* Allow leap second only on the last day of June and December */ return (stm->tm_mon == 5 && stm->tm_mday == 30) || (stm->tm_mon == 11 && stm->tm_mday == 31); } /* ================================================== */ static NTP_Leap get_tz_leap(time_t when) { struct tm stm; time_t t; char *tz_env, tz_orig[128]; /* Do this check at most twice a day */ when = when / (12 * 3600) * (12 * 3600); if (last_tz_leap_check == when) return tz_leap; last_tz_leap_check = when; tz_leap = LEAP_Normal; stm = *gmtime(&when); if (!is_leap_second_day(&stm)) return tz_leap; /* Temporarily switch to the timezone containing leap seconds */ tz_env = getenv("TZ"); if (tz_env) { if (strlen(tz_env) >= sizeof (tz_orig)) return tz_leap; strcpy(tz_orig, tz_env); } setenv("TZ", leap_tzname, 1); tzset(); /* Set the time to 23:59:60 and see how it overflows in mktime() */ stm.tm_sec = 60; stm.tm_min = 59; stm.tm_hour = 23; t = mktime(&stm); if (tz_env) setenv("TZ", tz_orig, 1); else unsetenv("TZ"); tzset(); if (t == -1) return tz_leap; if (stm.tm_sec == 60) tz_leap = LEAP_InsertSecond; else if (stm.tm_sec == 1) tz_leap = LEAP_DeleteSecond; return tz_leap; } /* ================================================== */ static void leap_end_timeout(void *arg) { leap_timer_running = 0; leap_in_progress = 0; our_leap_sec = 0; if (leap_mode == REF_LeapModeSystem) LCL_SetSystemLeap(0); if (our_leap_status == LEAP_InsertSecond || our_leap_status == LEAP_DeleteSecond) our_leap_status = LEAP_Normal; } /* ================================================== */ static void leap_start_timeout(void *arg) { leap_in_progress = 1; switch (leap_mode) { case REF_LeapModeSystem: DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction"); break; case REF_LeapModeSlew: LCL_NotifyLeap(our_leap_sec); LCL_AccumulateOffset(our_leap_sec, 0.0); LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second"); break; case REF_LeapModeStep: LCL_NotifyLeap(our_leap_sec); LCL_ApplyStepOffset(our_leap_sec); LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second"); break; case REF_LeapModeIgnore: LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second"); break; default: break; } /* Wait until the leap second is over with some extra room to be safe */ leap_timeout_id = SCH_AddTimeoutByDelay(2.0, leap_end_timeout, NULL); } /* ================================================== */ static void set_leap_timeout(time_t now) { struct timeval when; /* Stop old timer if there is one */ if (leap_timer_running) { SCH_RemoveTimeout(leap_timeout_id); leap_timer_running = 0; leap_in_progress = 0; } if (!our_leap_sec) return; /* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock will be corrected by the system, timeout slightly sooner to be sure it will happen before the system correction. */ when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600); when.tv_usec = 0; if (our_leap_sec < 0) when.tv_sec--; if (leap_mode == REF_LeapModeSystem) { when.tv_sec--; when.tv_usec = 500000; } leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL); leap_timer_running = 1; } /* ================================================== */ static void update_leap_status(NTP_Leap leap, time_t now, int reset) { int leap_sec; leap_sec = 0; if (leap_tzname && now && leap == LEAP_Normal) leap = get_tz_leap(now); if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) { /* Check that leap second is allowed today */ if (is_leap_second_day(gmtime(&now))) { if (leap == LEAP_InsertSecond) { leap_sec = 1; } else { leap_sec = -1; } } else { leap = LEAP_Normal; } } if (reset || (leap_sec != our_leap_sec && !REF_IsLeapSecondClose())) { our_leap_sec = leap_sec; switch (leap_mode) { case REF_LeapModeSystem: LCL_SetSystemLeap(our_leap_sec); /* Fall through */ case REF_LeapModeSlew: case REF_LeapModeStep: case REF_LeapModeIgnore: set_leap_timeout(now); break; default: assert(0); break; } } our_leap_status = leap; } /* ================================================== */ static void write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap, double freq, double skew, double offset, int combined_sources, double offset_sd, double uncorrected_offset) { const char leap_codes[4] = {'N', '+', '-', '?'}; if (logfileid != -1) { LOG_FileWrite(logfileid, "%s %-15s %2d %10.3f %10.3f %10.3e %1c %2d %10.3e %10.3e", UTI_TimeToLogForm(ref_time->tv_sec), ref, stratum, freq, skew, offset, leap_codes[leap], combined_sources, offset_sd, uncorrected_offset); } } /* ================================================== */ static void special_mode_sync(int valid, double offset) { int step; switch (mode) { case REF_ModeInitStepSlew: if (!valid) { LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew"); end_ref_mode(0); break; } step = fabs(offset) >= CNF_GetInitStepThreshold(); LOG(LOGS_INFO, LOGF_Reference, "System's initial offset : %.6f seconds %s of true (%s)", fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew"); if (step) LCL_ApplyStepOffset(offset); else LCL_AccumulateOffset(offset, 0.0); end_ref_mode(1); break; case REF_ModeUpdateOnce: case REF_ModePrintOnce: if (!valid) { LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation"); end_ref_mode(0); break; } step = mode == REF_ModeUpdateOnce; LOG(LOGS_INFO, LOGF_Reference, "System clock wrong by %.6f seconds (%s)", -offset, step ? "step" : "ignored"); if (step) LCL_ApplyStepOffset(offset); end_ref_mode(1); break; case REF_ModeIgnore: /* Do nothing until the mode is changed */ break; default: assert(0); } } /* ================================================== */ void REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, uint32_t ref_id, IPAddr *ref_ip, struct timeval *ref_time, double offset, double offset_sd, 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_offset; double our_frequency; double abs_freq_ppm; double update_interval; double elapsed; double correction_rate; double uncorrected_offset, accumulate_offset, step_offset; struct timeval now, raw_now; assert(initialised); /* Special modes are implemented elsewhere */ if (mode != REF_ModeNormal) { special_mode_sync(1, offset); return; } /* Guard against dividing by zero */ if (skew < MIN_SKEW) skew = MIN_SKEW; /* 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; } } LCL_ReadRawTime(&raw_now); LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL); UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now); UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time); our_offset = offset + elapsed * frequency; if (!is_offset_ok(our_offset)) return; are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0; our_stratum = stratum + 1; our_ref_id = ref_id; if (ref_ip) our_ref_ip = *ref_ip; else our_ref_ip.family = IPADDR_UNSPEC; our_ref_time = *ref_time; our_root_delay = root_delay; our_root_dispersion = root_dispersion; if (last_ref_update.tv_sec) { UTI_DiffTimevalsToDouble(&update_interval, &now, &last_ref_update); if (update_interval < 0.0) update_interval = 0.0; } else { update_interval = 0.0; } last_ref_update = now; /* We want to correct the offset quickly, but we also want to keep the frequency error caused by the correction itself low. Define correction rate as the area of the region bounded by the graph of offset corrected in time. Set the rate so that the time needed to correct an offset equal to the current sourcestats stddev will be equal to the update interval multiplied by the correction time ratio (assuming linear adjustment). The offset and the time needed to make the correction are inversely proportional. This is only a suggestion and it's up to the system driver how the adjustment will be executed. */ correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval; /* Check if the clock should be stepped */ if (is_step_limit_reached(our_offset, uncorrected_offset)) { /* Cancel the uncorrected offset and correct the total offset by step */ accumulate_offset = uncorrected_offset; step_offset = our_offset - uncorrected_offset; } else { accumulate_offset = our_offset; step_offset = 0.0; } /* Eliminate updates that are based on totally unreliable frequency information. Ignore this limit with manual reference. */ if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) { 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. With manual reference the old frequency has no weight. */ old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0; 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; LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate); } else { DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset); LCL_AccumulateOffset(accumulate_offset, correction_rate); our_residual_freq = frequency; } update_leap_status(leap, raw_now.tv_sec, 0); maybe_log_offset(our_offset, raw_now.tv_sec); if (step_offset != 0.0) { if (LCL_ApplyStepOffset(step_offset)) LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset); } LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion); abs_freq_ppm = LCL_ReadAbsoluteFrequency(); write_log(&now, our_ref_ip.family != IPADDR_UNSPEC ? UTI_IPToString(&our_ref_ip) : UTI_RefidToString(our_ref_id), our_stratum, our_leap_status, abs_freq_ppm, 1.0e6*our_skew, our_offset, combined_sources, offset_sd, uncorrected_offset); if (drift_file) { /* Update drift file at most once per hour */ drift_file_age += update_interval; if (drift_file_age < 0.0 || drift_file_age > 3600.0) { update_drift_file(abs_freq_ppm, our_skew); drift_file_age = 0.0; } } /* Update fallback drifts */ if (fb_drifts) { update_fb_drifts(abs_freq_ppm, update_interval); schedule_fb_drift(&now); } last_ref_update_interval = update_interval; last_offset = our_offset; /* Update the moving average of squares of offset, quickly on start */ if (avg2_moving) { avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset); } else { if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset) avg2_moving = 1; avg2_offset = our_offset * our_offset; } } /* ================================================== */ void REF_SetManualReference ( struct timeval *ref_time, double offset, double frequency, double skew ) { /* We are not synchronised to an external source, as such. This is only supposed to be used with the local source option, really. Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */ REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL, ref_time, offset, 0.0, frequency, skew, 0.0, 0.0); } /* ================================================== */ void REF_SetUnsynchronised(void) { /* Variables required for logging to statistics log */ struct timeval now, now_raw; double uncorrected_offset; assert(initialised); /* Special modes are implemented elsewhere */ if (mode != REF_ModeNormal) { special_mode_sync(0, 0.0); return; } LCL_ReadRawTime(&now_raw); LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL); UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now); if (fb_drifts) { schedule_fb_drift(&now); } update_leap_status(LEAP_Unsynchronised, 0, 0); are_we_synchronised = 0; LCL_SetSyncStatus(0, 0.0, 0.0); write_log(&now, "0.0.0.0", 0, our_leap_status, LCL_ReadAbsoluteFrequency(), 1.0e6*our_skew, 0.0, 0, 0.0, uncorrected_offset); } /* ================================================== */ void REF_GetReferenceParams ( struct timeval *local_time, int *is_synchronised, NTP_Leap *leap_status, int *stratum, uint32_t *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) + LCL_GetMaxClockError()) * elapsed; *leap_status = !leap_in_progress ? our_leap_status : LEAP_Unsynchronised; *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 = NTP_REFID_LOCAL; /* 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 7 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 = NTP_MAX_STRATUM; *ref_id = NTP_REFID_UNSYNC; 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 NTP_MAX_STRATUM; } } /* ================================================== */ double REF_GetSkew(void) { return our_skew; } /* ================================================== */ void REF_ModifyMaxupdateskew(double new_max_update_skew) { max_update_skew = new_max_update_skew * 1.0e-6; } /* ================================================== */ void REF_ModifyMakestep(int limit, double threshold) { make_step_limit = limit; make_step_threshold = threshold; } /* ================================================== */ void REF_EnableLocal(int stratum) { enable_local_stratum = 1; local_stratum = stratum; } /* ================================================== */ void REF_DisableLocal(void) { enable_local_stratum = 0; } /* ================================================== */ int REF_IsLocalActive(void) { return !are_we_synchronised && enable_local_stratum; } /* ================================================== */ #define LEAP_SECOND_CLOSE 5 int REF_IsLeapSecondClose(void) { struct timeval now, now_raw; time_t t; if (!our_leap_sec) return 0; SCH_GetLastEventTime(&now, NULL, &now_raw); t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec; if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) return 1; t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec; if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE) return 1; return 0; } /* ================================================== */ void REF_GetTrackingReport(RPT_TrackingReport *rep) { double elapsed; double extra_dispersion; struct timeval now_raw, now_cooked; double correction; LCL_ReadRawTime(&now_raw); LCL_GetOffsetCorrection(&now_raw, &correction, NULL); UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked); rep->ref_id = NTP_REFID_UNSYNC; rep->ip_addr.family = IPADDR_UNSPEC; rep->stratum = 0; rep->leap_status = our_leap_status; rep->ref_time.tv_sec = 0; rep->ref_time.tv_usec = 0; rep->current_correction = 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; rep->last_update_interval = last_ref_update_interval; rep->last_offset = last_offset; rep->rms_offset = sqrt(avg2_offset); if (are_we_synchronised) { UTI_DiffTimevalsToDouble(&elapsed, &now_cooked, &our_ref_time); extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed; rep->ref_id = our_ref_id; rep->ip_addr = our_ref_ip; rep->stratum = our_stratum; rep->ref_time = our_ref_time; 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 = NTP_REFID_LOCAL; rep->ip_addr.family = IPADDR_UNSPEC; rep->stratum = local_stratum; rep->ref_time = now_cooked; rep->root_dispersion = LCL_GetSysPrecisionAsQuantum(); } } chrony-2.1.1/reference.h0000644000000000000000000001165612542263351013636 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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" /* Leap second handling modes */ typedef enum { REF_LeapModeSystem, REF_LeapModeSlew, REF_LeapModeStep, REF_LeapModeIgnore, } REF_LeapMode; /* Init function */ extern void REF_Initialise(void); /* Fini function */ extern void REF_Finalise(void); typedef enum { REF_ModeNormal, REF_ModeInitStepSlew, REF_ModeUpdateOnce, REF_ModePrintOnce, REF_ModeIgnore, } REF_Mode; /* Set reference update mode */ extern void REF_SetMode(REF_Mode mode); /* Get reference update mode */ extern REF_Mode REF_GetMode(void); /* Function type for handlers to be called back when mode ends */ typedef void (*REF_ModeEndHandler)(int result); /* Set the handler for being notified of mode ending */ extern void REF_SetModeEndHandler(REF_ModeEndHandler handler); /* Get leap second handling mode */ extern REF_LeapMode REF_GetLeapMode(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, uint32_t *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, int combined_sources, uint32_t ref_id, IPAddr *ref_ip, struct timeval *ref_time, double offset, double offset_sd, 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 16 if the host is not synchronised */ extern int REF_GetOurStratum(void); /* Return the current skew */ extern double REF_GetSkew(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); /* Modify makestep settings */ extern void REF_ModifyMakestep(int limit, double threshold); extern void REF_EnableLocal(int stratum); extern void REF_DisableLocal(void); extern int REF_IsLocalActive(void); /* Check if current raw or cooked time is close to a leap second and is better to discard any measurements */ extern int REF_IsLeapSecondClose(void); extern void REF_GetTrackingReport(RPT_TrackingReport *rep); #endif /* GOT_REFERENCE_H */ chrony-2.1.1/regress.c0000644000000000000000000004254512542263351013346 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Regression algorithms. */ #include "config.h" #include "sysincl.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; assert(n >= 3); W = U = 0; for (i=0; i 0.0) && (resid[i] > 0.0))) { /* Nothing to do */ } else { nruns++; } } return nruns; } /* ================================================== */ /* Return a boolean indicating whether we had enough points for regression */ int RGR_FindBestRegression (double *x, /* independent variable */ double *y, /* measured data */ double *w, /* weightings (large => data less reliable) */ int n, /* number of data points */ int m, /* number of extra samples in x and y arrays (negative index) which can be used to extend runs test */ int min_samples, /* minimum number of samples to be kept after changing the starting index to pass the runs test */ /* 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[MAX_POINTS * REGRESS_RUNS_RATIO]; double ss; double a, b, u, ui, aa; int start, resid_start, nruns, npoints; int i; assert(n <= MAX_POINTS && m >= 0); assert(n * REGRESS_RUNS_RATIO < sizeof (critical_runs) / sizeof (critical_runs[0])); if (n < MIN_SAMPLES_FOR_REGRESS) { return 0; } start = 0; do { W = U = 0; for (i=start; i critical_runs[n - resid_start] || n - start <= MIN_SAMPLES_FOR_REGRESS || n - start <= min_samples) { if (start != resid_start) { /* Ignore extra samples in returned nruns */ nruns = n_runs_from_residuals(resid - resid_start + start, n - start); } 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= 0); /* 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 (l < v && 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; } } } 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]; memset(flags, 0, 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 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; assert(n < MAX_POINTS); 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 tol) { incr = 3.0 * sb; } else { incr = 3.0 * tol; } blo = b; bhi = b; do { /* Make sure incr is significant to blo and bhi */ while (bhi + incr == bhi || blo - incr == blo) { incr *= 2; } 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 or one is zero, i.e. root not in interval (rlo, rhi). */ /* 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 { assert(0); } } while ((bhi - blo) > tol && (bmid - blo) * (bhi - bmid) > 0.0); *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 critical_runs[n_points]) { break; } else { start++; } } while (1); *n_runs = nruns; *best_start = start; return 1; } /* ================================================== */ chrony-2.1.1/regress.h0000644000000000000000000001033512542263351013343 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 the value to apply to the variance to make an upper one-sided test assuming a chi-square distribution. */ extern double RGR_GetChi2Coef(int dof); /* Maximum ratio of number of points used for runs test to number of regression points */ #define REGRESS_RUNS_RATIO 2 /* Minimum number of samples for regression */ #define MIN_SAMPLES_FOR_REGRESS 3 /* 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 */ int m, /* number of extra samples in x and y arrays (negative index) which can be used to extend runs test */ int min_samples, /* minimum number of samples to be kept after changing the starting index to pass the runs test */ /* 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 */ chrony-2.1.1/reports.h0000644000000000000000000000636012542263351013372 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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" #include "addressing.h" #define REPORT_INVALID_OFFSET 0x80000000 typedef struct { IPAddr 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_CANDIDATE, RPT_OUTLIER} state; enum {RPT_NORMAL, RPT_PREFER, RPT_NOSELECT} sel_option; int reachability; unsigned long latest_meas_ago; /* seconds */ double orig_latest_meas; /* seconds */ double latest_meas; /* seconds */ double latest_meas_err; /* seconds */ } RPT_SourceReport ; typedef struct { uint32_t ref_id; IPAddr ip_addr; unsigned long stratum; unsigned long leap_status; struct timeval ref_time; double current_correction; double last_offset; double rms_offset; double freq_ppm; double resid_freq_ppm; double skew_ppm; double root_delay; double root_dispersion; double last_update_interval; } RPT_TrackingReport; typedef struct { uint32_t ref_id; IPAddr ip_addr; unsigned long n_samples; unsigned long n_runs; unsigned long span_seconds; double resid_freq_ppm; double skew_ppm; double sd; double est_offset; double est_offset_err; } RPT_SourcestatsReport; typedef struct { struct timeval 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 { IPAddr 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 { struct timeval when; double slewed_offset; double orig_offset; double residual; } RPT_ManualSamplesReport; typedef struct { int online; int offline; int burst_online; int burst_offline; int unresolved; } RPT_ActivityReport; typedef struct { int active; int leap_only; double offset; double freq_ppm; double wander_ppm; double last_update_ago; double remaining_time; } RPT_SmoothingReport; #endif /* GOT_REPORTS_H */ chrony-2.1.1/rtc.c0000644000000000000000000001244712542263351012462 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= */ #include "config.h" #include "sysincl.h" #include "rtc.h" #include "local.h" #include "logging.h" #include "conf.h" #if defined LINUX && defined FEAT_RTC #include "rtc_linux.h" #endif /* defined LINUX */ /* ================================================== */ static int driver_initialised = 0; static struct { int (*init)(void); void (*fini)(void); int (*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); } driver = { #if defined LINUX && defined FEAT_RTC RTC_Linux_Initialise, RTC_Linux_Finalise, RTC_Linux_TimePreInit, RTC_Linux_TimeInit, RTC_Linux_StartMeasurements, RTC_Linux_WriteParameters, RTC_Linux_GetReport, RTC_Linux_Trim #else NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL #endif }; /* ================================================== */ /* Set the system clock to the time of last modification of driftfile if it's in the future */ static void fallback_time_init(void) { struct timeval now; struct stat buf; char *drift_file; drift_file = CNF_GetDriftFile(); if (!drift_file) return; if (stat(drift_file, &buf)) return; LCL_ReadCookedTime(&now, NULL); if (now.tv_sec < buf.st_mtime) { if (LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime)) LOG(LOGS_INFO, LOGF_Rtc, "System clock set from driftfile %s", drift_file); } } /* ================================================== */ void RTC_Initialise(int initial_set) { char *file_name; /* Do an initial read of the RTC and set the system time to it. This is analogous to what /sbin/hwclock -s would do on Linux. If that fails or RTC is not supported, set the clock to the time of the last modification of driftfile, so we at least get closer to the truth. */ if (initial_set) { if (!driver.time_pre_init || !driver.time_pre_init()) { fallback_time_init(); } } driver_initialised = 0; /* 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 (CNF_GetRtcSync()) { LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync"); } if (driver.init) { if ((driver.init)()) { driver_initialised = 1; } } else { LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system"); } } } /* ================================================== */ 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 { (after_hook)(anything); } } /* ================================================== */ /* 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; } } /* ================================================== */ chrony-2.1.1/rtc.h0000644000000000000000000000267412542263351012470 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= */ #ifndef GOT_RTC_H #define GOT_RTC_H #include "reports.h" extern void RTC_Initialise(int initial_set); extern void RTC_Finalise(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); #endif /* GOT_RTC_H */ chrony-2.1.1/rtc_linux.c0000644000000000000000000007226312542263351013703 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2012-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Real-time clock driver for linux. This interfaces the program with the clock that keeps time when the machine is turned off. */ #include "config.h" #include "sysincl.h" #include #include "logging.h" #include "sched.h" #include "local.h" #include "util.h" #include "sys_linux.h" #include "reference.h" #include "regress.h" #include "rtc.h" #include "rtc_linux.h" #include "conf.h" #include "memory.h" /* ================================================== */ /* Forward prototypes */ static void measurement_timeout(void *any); static void read_from_device(void *any); /* ================================================== */ typedef enum { OM_NORMAL, OM_INITIAL, OM_AFTERTRIM } OperatingMode; static OperatingMode operating_mode = OM_NORMAL; /* ================================================== */ static int fd = -1; #define LOWEST_MEASUREMENT_PERIOD 15 #define HIGHEST_MEASUREMENT_PERIOD 480 #define N_SAMPLES_PER_REGRESSION 1 static int measurement_period = LOWEST_MEASUREMENT_PERIOD; static int timeout_running = 0; static SCH_TimeoutID timeout_id; static int skip_interrupts; /* ================================================== */ /* Maximum number of samples held */ #define MAX_SAMPLES 64 /* Real time clock samples. We store the seconds count as originally measured, together with a 'trim' that compensates these values for any steps made to the RTC to bring it back into line occasionally. The trim is in seconds. */ static time_t *rtc_sec = NULL; static double *rtc_trim = NULL; /* Reference time, against which delta times on the RTC scale are measured */ static time_t rtc_ref; /* System clock (gettimeofday) samples associated with the above samples. */ static struct timeval *system_times = NULL; /* Number of samples currently stored. */ static int n_samples; /* Number of new samples since last regression */ static int n_samples_since_regression; /* Number of runs of residuals in last regression (for logging) */ static int n_runs; /* Coefficients */ /* Whether they are valid */ static int coefs_valid; /* Reference time */ static time_t coef_ref_time; /* Number of seconds by which RTC was fast of the system time at coef_ref_time */ static double coef_seconds_fast; /* Estimated number of seconds that RTC gains relative to system time for each second of ITS OWN time */ static double coef_gain_rate; /* Gain rate saved just before we step the RTC to correct it to the nearest second, so that we can write a useful set of coefs to the RTC data file once we have reacquired its offset after the step */ static double saved_coef_gain_rate; /* Threshold for automatic RTC trimming in seconds, zero when disabled */ static double autotrim_threshold; /* Filename supplied by config file where RTC coefficients are stored. */ static char *coefs_file_name; /* ================================================== */ /* Coefficients read from file at start of run. */ /* Whether we have tried to load the coefficients */ static int tried_to_load_coefs = 0; /* Whether valid coefficients were read */ static int valid_coefs_from_file = 0; /* Coefs read in */ static time_t file_ref_time; static double file_ref_offset, file_rate_ppm; /* ================================================== */ /* Flag to remember whether to assume the RTC is running on UTC */ static int rtc_on_utc = 1; /* ================================================== */ static LOG_FileID logfileid; /* ================================================== */ static void (*after_init_hook)(void *) = NULL; static void *after_init_hook_arg = NULL; /* ================================================== */ static void discard_samples(int new_first) { int n_to_save; assert(new_first >= 0 && new_first < n_samples); n_to_save = n_samples - new_first; memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t)); memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double)); memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timeval)); n_samples = n_to_save; } /* ================================================== */ #define NEW_FIRST_WHEN_FULL 4 static void accumulate_sample(time_t rtc, struct timeval *sys) { if (n_samples == MAX_SAMPLES) { /* Discard oldest samples */ discard_samples(NEW_FIRST_WHEN_FULL); } /* Always use most recent sample as reference */ /* use sample only if n_sample is not negative*/ if(n_samples >=0) { rtc_ref = rtc; rtc_sec[n_samples] = rtc; rtc_trim[n_samples] = 0.0; system_times[n_samples] = *sys; ++n_samples_since_regression; } ++n_samples; } /* ================================================== */ /* The new_sample flag is to indicate whether to adjust the measurement period depending on the behaviour of the standard deviation. */ static void run_regression(int new_sample, int *valid, time_t *ref, double *fast, double *slope) { double rtc_rel[MAX_SAMPLES]; /* Relative times on RTC axis */ double offsets[MAX_SAMPLES]; /* How much the RTC is fast of the system clock */ int i; double est_intercept, est_slope; int best_new_start; if (n_samples > 0) { for (i=0; i 0) { discard_samples(best_new_start); } } else { /* Keep existing coefficients. */ } } else { /* Keep existing coefficients. */ } } /* ================================================== */ static void slew_samples (struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { int i; double delta_time; double old_seconds_fast, old_gain_rate; if (change_type == LCL_ChangeUnknownStep) { /* Drop all samples. */ n_samples = 0; } for (i=0; i= 0) { SCH_RemoveInputFileHandler(fd); close(fd); /* Save the RTC data */ (void) RTC_Linux_WriteParameters(); } Free(rtc_sec); Free(rtc_trim); Free(system_times); } /* ================================================== */ static void switch_interrupts(int onoff) { int status; if (onoff) { status = ioctl(fd, RTC_UIE_ON, 0); if (status < 0) { LOG(LOGS_ERR, LOGF_RtcLinux, "Could not start measurement : %s", strerror(errno)); return; } skip_interrupts = 1; } else { status = ioctl(fd, RTC_UIE_OFF, 0); if (status < 0) { LOG(LOGS_ERR, LOGF_RtcLinux, "Could not stop measurement : %s", strerror(errno)); return; } } } /* ================================================== */ static void measurement_timeout(void *any) { timeout_running = 0; switch_interrupts(1); } /* ================================================== */ static void set_rtc(time_t new_rtc_time) { struct tm rtc_tm; struct rtc_time rtc_raw; int status; rtc_tm = *rtc_from_t(&new_rtc_time); rtc_raw.tm_sec = rtc_tm.tm_sec; rtc_raw.tm_min = rtc_tm.tm_min; rtc_raw.tm_hour = rtc_tm.tm_hour; rtc_raw.tm_mday = rtc_tm.tm_mday; rtc_raw.tm_mon = rtc_tm.tm_mon; rtc_raw.tm_year = rtc_tm.tm_year; rtc_raw.tm_wday = rtc_tm.tm_wday; rtc_raw.tm_yday = rtc_tm.tm_yday; rtc_raw.tm_isdst = rtc_tm.tm_isdst; status = ioctl(fd, RTC_SET_TIME, &rtc_raw); if (status < 0) { LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time"); } } /* ================================================== */ static void handle_initial_trim(void) { double rate; long delta_time; double rtc_error_now, sys_error_now; /* The idea is to accumulate some number of samples at 1 second intervals, then do a robust regression fit to this. This should give a good fix on the intercept (=system clock error rel to RTC) at a particular time, removing risk of any particular sample being an outlier. We can then look at the elapsed interval since the epoch recorded in the RTC file, and correct the system time accordingly. */ run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); n_samples_since_regression = 0; /* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/ n_samples = -1; read_coefs_from_file(); if (valid_coefs_from_file) { /* Can process data */ delta_time = coef_ref_time - file_ref_time; rate = 1.0e-6 * file_rate_ppm; rtc_error_now = file_ref_offset + rate * (double) delta_time; /* sys_error_now is positive if the system clock is fast */ sys_error_now = rtc_error_now - coef_seconds_fast; LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now); LCL_AccumulateOffset(sys_error_now, 0.0); } else { LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time"); } coefs_valid = 0; (after_init_hook)(after_init_hook_arg); operating_mode = OM_NORMAL; } /* ================================================== */ static void handle_relock_after_trim(void) { int valid; time_t ref; double fast, slope; valid = 0; run_regression(1, &valid, &ref, &fast, &slope); if (valid) { write_coefs_to_file(1,ref,fast,saved_coef_gain_rate); } else { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not do regression after trim"); } coefs_valid = 0; n_samples = 0; n_samples_since_regression = 0; operating_mode = OM_NORMAL; measurement_period = LOWEST_MEASUREMENT_PERIOD; } /* ================================================== */ static void maybe_autotrim(void) { /* Trim only when in normal mode, the coefficients are fresh, the current offset is above the threshold and the system clock is synchronized */ if (operating_mode != OM_NORMAL || !coefs_valid || n_samples_since_regression) return; if (autotrim_threshold <= 0.0 || fabs(coef_seconds_fast) < autotrim_threshold) return; if (REF_GetOurStratum() >= 16) return; RTC_Linux_Trim(); } /* ================================================== */ static void process_reading(time_t rtc_time, struct timeval *system_time) { double rtc_fast; accumulate_sample(rtc_time, system_time); switch (operating_mode) { case OM_NORMAL: if (n_samples_since_regression >= N_SAMPLES_PER_REGRESSION) { run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); n_samples_since_regression = 0; maybe_autotrim(); } break; case OM_INITIAL: if (n_samples_since_regression >= 8) { handle_initial_trim(); } break; case OM_AFTERTRIM: if (n_samples_since_regression >= 8) { handle_relock_after_trim(); } break; default: assert(0); break; } if (logfileid != -1) { rtc_fast = (double)(rtc_time - system_time->tv_sec) - 1.0e-6 * (double) system_time->tv_usec; LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d", UTI_TimeToLogForm(system_time->tv_sec), rtc_fast, coefs_valid, coef_seconds_fast, coef_gain_rate * 1.0e6, n_samples, n_runs, measurement_period); } } /* ================================================== */ static void read_from_device(void *any) { int status; unsigned long data; struct timeval sys_time; struct rtc_time rtc_raw; struct tm rtc_tm; time_t rtc_t; int error = 0; status = read(fd, &data, sizeof(data)); if (status < 0) { /* This looks like a bad error : the file descriptor was indicating it was * ready to read but we couldn't read anything. Give up. */ LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno)); SCH_RemoveInputFileHandler(fd); switch_interrupts(0); /* Likely to raise error too, but just to be sure... */ close(fd); fd = -1; return; } if (skip_interrupts > 0) { /* Wait for the next interrupt, this one may be bogus */ skip_interrupts--; return; } if ((data & RTC_UF) == RTC_UF) { /* Update interrupt detected */ /* Read RTC time, sandwiched between two polls of the system clock so we can bound any error. */ SCH_GetLastEventTime(&sys_time, NULL, NULL); status = ioctl(fd, RTC_RD_TIME, &rtc_raw); if (status < 0) { LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno)); error = 1; goto turn_off_interrupt; } /* Convert RTC time into a struct timeval */ rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_mday = rtc_raw.tm_mday; rtc_tm.tm_mon = rtc_raw.tm_mon; rtc_tm.tm_year = rtc_raw.tm_year; rtc_t = t_from_rtc(&rtc_tm); if (rtc_t == (time_t)(-1)) { LOG(LOGS_ERR, LOGF_RtcLinux, "Could not convert RTC time to timeval"); error = 1; goto turn_off_interrupt; } process_reading(rtc_t, &sys_time); if (n_samples < 4) { measurement_period = LOWEST_MEASUREMENT_PERIOD; } else if (n_samples < 6) { measurement_period = LOWEST_MEASUREMENT_PERIOD << 1; } else if (n_samples < 10) { measurement_period = LOWEST_MEASUREMENT_PERIOD << 2; } else if (n_samples < 14) { measurement_period = LOWEST_MEASUREMENT_PERIOD << 3; } else { measurement_period = LOWEST_MEASUREMENT_PERIOD << 4; } } turn_off_interrupt: switch (operating_mode) { case OM_INITIAL: if (error) { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete initial step due to errors"); operating_mode = OM_NORMAL; (after_init_hook)(after_init_hook_arg); switch_interrupts(0); timeout_running = 1; timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); } break; case OM_AFTERTRIM: if (error) { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete after trim relock due to errors"); operating_mode = OM_NORMAL; switch_interrupts(0); timeout_running = 1; timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); } break; case OM_NORMAL: switch_interrupts(0); timeout_running = 1; timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); break; default: assert(0); break; } } /* ================================================== */ void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything) { after_init_hook = after_hook; after_init_hook_arg = anything; operating_mode = OM_INITIAL; timeout_running = 0; switch_interrupts(1); } /* ================================================== */ void RTC_Linux_StartMeasurements(void) { timeout_running = 0; measurement_timeout(NULL); } /* ================================================== */ int RTC_Linux_WriteParameters(void) { int retval; if (fd < 0) { return RTC_ST_NODRV; } if (coefs_valid) { retval = write_coefs_to_file(1,coef_ref_time, coef_seconds_fast, coef_gain_rate); } else { /* Don't change the existing file, it may not be 100% valid but is our current best guess. */ retval = RTC_ST_OK; /*write_coefs_to_file(0,0,0.0,0.0); */ } return(retval); } /* ================================================== */ /* Try to set the system clock from the RTC, in the same manner as /sbin/hwclock -s would do. We're not as picky about OS version etc in this case, since we have fewer requirements regarding the RTC behaviour than we do for the rest of the module. */ int RTC_Linux_TimePreInit(void) { int fd, status; struct rtc_time rtc_raw, rtc_raw_retry; struct tm rtc_tm; time_t rtc_t; double accumulated_error, sys_offset; struct timeval new_sys_time, old_sys_time; coefs_file_name = CNF_GetRtcFile(); setup_config(); read_coefs_from_file(); fd = open(CNF_GetRtcDevice(), O_RDONLY); if (fd < 0) { return 0; /* Can't open it, and won't be able to later */ } /* Retry reading the rtc until both read attempts give the same sec value. This way the race condition is prevented that the RTC has updated itself during the first read operation. */ do { status = ioctl(fd, RTC_RD_TIME, &rtc_raw); if (status >= 0) { status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry); } } while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec); /* Read system clock */ LCL_ReadCookedTime(&old_sys_time, NULL); close(fd); if (status >= 0) { /* Convert to seconds since 1970 */ rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_mday = rtc_raw.tm_mday; rtc_tm.tm_mon = rtc_raw.tm_mon; rtc_tm.tm_year = rtc_raw.tm_year; rtc_t = t_from_rtc(&rtc_tm); if (rtc_t != (time_t)(-1)) { /* Work out approximatation to correct time (to about the nearest second) */ if (valid_coefs_from_file) { accumulated_error = file_ref_offset + (rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm; } else { accumulated_error = 0.0; } /* Correct time */ new_sys_time.tv_sec = rtc_t; /* Average error in the RTC reading */ new_sys_time.tv_usec = 500000; UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time); UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time); /* Set system time only if the step is larger than 1 second */ if (fabs(sys_offset) >= 1.0) { if (LCL_ApplyStepOffset(sys_offset)) LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f", accumulated_error); } } else { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970"); return 0; } } return 1; } /* ================================================== */ int RTC_Linux_GetReport(RPT_RTC_Report *report) { report->ref_time.tv_sec = coef_ref_time; report->ref_time.tv_usec = 0; report->n_samples = n_samples; report->n_runs = n_runs; if (n_samples > 1) { report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) + (long)(rtc_trim[n_samples-1] - rtc_trim[0])); } else { report->span_seconds = 0; } report->rtc_seconds_fast = coef_seconds_fast; report->rtc_gain_rate_ppm = 1.0e6 * coef_gain_rate; return 1; } /* ================================================== */ int RTC_Linux_Trim(void) { struct timeval now; /* Remember the slope coefficient - we won't be able to determine a good one in a few seconds when we determine the new offset! */ saved_coef_gain_rate = coef_gain_rate; if (fabs(coef_seconds_fast) > 1.0) { LOG(LOGS_INFO, LOGF_RtcLinux, "Trimming RTC, error = %.3f seconds", coef_seconds_fast); /* Do processing to set clock. Let R be the value we set the RTC to, then in 500ms the RTC ticks (R+1) (see comments in arch/i386/kernel/time.c about the behaviour of the real time clock chip). If S is the system time now, the error at the next RTC tick is given by E = (R+1) - (S+0.5). Ideally we want |E| <= 0.5, which implies R <= S <= R+1, i.e. R is just the rounded down part of S, i.e. the seconds part. */ LCL_ReadCookedTime(&now, NULL); set_rtc(now.tv_sec); /* All old samples will now look bogus under the new regime. */ n_samples = 0; operating_mode = OM_AFTERTRIM; /* Estimate the offset in case writertc is called or chronyd is terminated during rapid sampling */ coef_seconds_fast = -now.tv_usec / 1e6 + 0.5; coef_ref_time = now.tv_sec; /* And start rapid sampling, interrupts on now */ if (timeout_running) { SCH_RemoveTimeout(timeout_id); timeout_running = 0; } switch_interrupts(1); } return 1; } chrony-2.1.1/rtc_linux.h0000644000000000000000000000305412542263351013700 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ====================================================================== */ #ifndef _GOT_RTC_LINUX_H #define _GOT_RTC_LINUX_H #include "reports.h" extern int RTC_Linux_Initialise(void); extern void RTC_Linux_Finalise(void); extern int 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 /* _GOT_RTC_LINUX_H */ chrony-2.1.1/sched.c0000644000000000000000000004514612542263351012762 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011, 2013-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This file contains the scheduling loop and the timeout queue. */ #include "config.h" #include "sysincl.h" #include "array.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; #ifndef FD_SETSIZE /* If FD_SETSIZE is not defined, assume that fd_set is implemented as a fixed size array of bits, possibly embedded inside a record */ #define FD_SETSIZE (sizeof(fd_set) * 8) #endif typedef struct { SCH_FileHandler handler; SCH_ArbitraryArgument arg; } FileHandlerEntry; static ARR_Instance file_handlers; /* Timestamp when last select() returned */ static struct timeval last_select_ts, last_select_ts_raw; static double last_select_ts_err; /* ================================================== */ /* 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; /* Timestamp when was last timeout dispatched for each class */ static struct timeval last_class_dispatch[SCH_NumberOfClasses]; /* ================================================== */ static int need_to_exit; /* ================================================== */ static void handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything); /* ================================================== */ void SCH_Initialise(void) { FD_ZERO(&read_fds); n_read_fds = 0; file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry)); 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); LCL_ReadRawTime(&last_select_ts_raw); last_select_ts = last_select_ts_raw; srandom(last_select_ts.tv_sec << 16 ^ last_select_ts.tv_usec); initialised = 1; } /* ================================================== */ void SCH_Finalise(void) { ARR_DestroyInstance(file_handlers); initialised = 0; } /* ================================================== */ void SCH_AddInputFileHandler (int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg) { FileHandlerEntry *ptr; assert(initialised); if (fd >= FD_SETSIZE) LOG_FATAL(LOGF_Scheduler, "Too many file descriptors"); /* 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. */ assert(!FD_ISSET(fd, &read_fds)); ++n_read_fds; if (ARR_GetSize(file_handlers) < fd + 1) ARR_SetSize(file_handlers, fd + 1); ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd); ptr->handler = handler; ptr->arg = arg; FD_SET(fd, &read_fds); if ((fd + 1) > one_highest_fd) { one_highest_fd = fd + 1; } } /* ================================================== */ void SCH_RemoveInputFileHandler(int fd) { int fds_left, fd_to_check; assert(initialised); /* Check that a handler was registered for the fd in question */ assert(FD_ISSET(fd, &read_fds)); --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; } /* ================================================== */ void SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw) { if (cooked) { *cooked = last_select_ts; if (err) *err = last_select_ts_err; } if (raw) *raw = last_select_ts_raw; } /* ================================================== */ #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; inext; return result; } /* ================================================== */ static void release_tqe(TimerQueueEntry *node) { node->next = tqe_free_list; tqe_free_list = node; } /* ================================================== */ SCH_TimeoutID SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg) { TimerQueueEntry *new_tqe; TimerQueueEntry *ptr; assert(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; assert(initialised); assert(delay >= 0.0); LCL_ReadRawTime(&now); UTI_AddDoubleToTimeval(&now, delay, &then); if (UTI_CompareTimevals(&now, &then) > 0) { LOG_FATAL(LOGF_Scheduler, "Timeout overflow"); } return SCH_AddTimeout(&then, handler, arg); } /* ================================================== */ SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation, double randomness, SCH_TimeoutClass class, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg) { TimerQueueEntry *new_tqe; TimerQueueEntry *ptr; struct timeval now; double diff, r; double new_min_delay; assert(initialised); assert(min_delay >= 0.0); assert(class < SCH_NumberOfClasses); if (randomness > 0.0) { r = random() % 0xffff / (0xffff - 1.0) * randomness + 1.0; min_delay *= r; separation *= r; } LCL_ReadRawTime(&now); new_min_delay = min_delay; /* Check the separation from the last dispatched timeout */ UTI_DiffTimevalsToDouble(&diff, &now, &last_class_dispatch[class]); if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) { new_min_delay = separation - diff; } /* 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; } } else { 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; assert(initialised); 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); break; } } } /* ================================================== */ /* 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). */ static void dispatch_timeouts(struct timeval *now) { TimerQueueEntry *ptr; SCH_TimeoutHandler handler; SCH_ArbitraryArgument arg; int n_done = 0, n_entries_on_start = n_timer_queue_entries; while (1) { LCL_ReadRawTime(now); if (!(n_timer_queue_entries > 0 && UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) { break; } ptr = timer_queue.next; last_class_dispatch[ptr->class] = *now; handler = ptr->handler; arg = ptr->arg; SCH_RemoveTimeout(ptr->id); /* Dispatch the handler */ (handler)(arg); /* Increment count of timeouts handled */ ++n_done; /* If more timeouts were handled than there were in the timer queue on start and there are now, assume some code is scheduling timeouts with negative delays and abort. Make the actual limit higher in case the machine is temporarily overloaded and dispatching the handlers takes more time than was delay of a scheduled timeout. */ if (n_done > n_timer_queue_entries * 4 && n_done > n_entries_on_start * 4) { LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling"); } } } /* ================================================== */ /* nfh is the number of bits set in fhs */ static void dispatch_filehandlers(int nfh, fd_set *fhs) { FileHandlerEntry *ptr; int fh = 0; while (nfh > 0) { if (FD_ISSET(fh, fhs)) { /* This descriptor can be read from, dispatch its handler */ ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fh); (ptr->handler)(ptr->arg); /* Decrement number of readable files still to find */ --nfh; } ++fh; } } /* ================================================== */ static void handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { TimerQueueEntry *ptr; double delta; int i; if (change_type != LCL_ChangeAdjust) { /* Make sure this handler is invoked first in order to not shift new timers added from other handlers */ assert(LCL_IsFirstParameterChangeHandler(handle_slew)); /* If a step change occurs, just shift all raw time stamps by the offset */ for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { UTI_AddDoubleToTimeval(&ptr->tv, -doffset, &ptr->tv); } for (i = 0; i < SCH_NumberOfClasses; i++) { UTI_AddDoubleToTimeval(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]); } UTI_AddDoubleToTimeval(&last_select_ts_raw, -doffset, &last_select_ts_raw); } UTI_AdjustTimeval(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset); } /* ================================================== */ #define JUMP_DETECT_THRESHOLD 10 static int check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout, struct timeval *orig_select_tv, struct timeval *rem_select_tv) { struct timeval elapsed_min, elapsed_max; double step, elapsed; /* Get an estimate of the time spent waiting in the select() call. On some systems (e.g. Linux) the timeout timeval is modified to return the remaining time, use that information. */ if (timeout) { elapsed_max = elapsed_min = *orig_select_tv; } else if (rem_select_tv && rem_select_tv->tv_sec >= 0 && rem_select_tv->tv_sec <= orig_select_tv->tv_sec && (rem_select_tv->tv_sec != orig_select_tv->tv_sec || rem_select_tv->tv_usec != orig_select_tv->tv_usec)) { UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv); elapsed_max = elapsed_min; } else { if (rem_select_tv) elapsed_max = *orig_select_tv; else UTI_DiffTimevals(&elapsed_max, raw, prev_raw); elapsed_min.tv_sec = 0; elapsed_min.tv_usec = 0; } if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec > raw->tv_sec + JUMP_DETECT_THRESHOLD) { LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!"); } else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD < raw->tv_sec) { LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!"); } else { return 1; } UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw); UTI_TimevalToDouble(&elapsed_min, &elapsed); step += elapsed; /* Cooked time may no longer be valid after dispatching the handlers */ LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step)); return 0; } /* ================================================== */ void SCH_MainLoop(void) { fd_set rd; int status, errsv; struct timeval tv, saved_tv, *ptv; struct timeval now, saved_now, cooked; double err; assert(initialised); while (!need_to_exit) { /* Dispatch timeouts and fill now with current raw time */ dispatch_timeouts(&now); saved_now = now; /* The timeout handlers may request quit */ if (need_to_exit) break; /* 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; assert(tv.tv_sec > 0 || tv.tv_usec > 0); saved_tv = tv; } else { ptv = NULL; /* This is needed to fix a compiler warning */ saved_tv.tv_sec = 0; } /* 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) { LOG_FATAL(LOGF_Scheduler, "Nothing to do"); } /* Copy current set of read file descriptors */ memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set)); status = select(one_highest_fd, &rd, NULL, NULL, ptv); errsv = errno; LCL_ReadRawTime(&now); LCL_CookTime(&now, &cooked, &err); /* Check if the time didn't jump unexpectedly */ if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) { /* Cook the time again after handling the step */ LCL_CookTime(&now, &cooked, &err); } last_select_ts_raw = now; last_select_ts = cooked; last_select_ts_err = err; if (status < 0) { if (!need_to_exit && errsv != EINTR) { LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv)); } } else if (status > 0) { /* A file descriptor is ready to read */ dispatch_filehandlers(status, &rd); } else { /* No descriptors readable, timeout must have elapsed. Therefore, tv must be non-null */ assert(ptv); /* There's nothing to do here, since the timeouts will be dispatched at the top of the next loop cycle */ } } } /* ================================================== */ void SCH_QuitProgram(void) { assert(initialised); need_to_exit = 1; } /* ================================================== */ chrony-2.1.1/sched.h0000644000000000000000000000624312542263351012762 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Exported header file for sched.c */ #ifndef GOT_SCHED_H #define GOT_SCHED_H #include "sysincl.h" typedef unsigned long SCH_TimeoutID; typedef enum { SCH_ReservedTimeoutValue = 0, SCH_NtpSamplingClass, SCH_NtpBroadcastClass, SCH_NumberOfClasses /* needs to be last */ } SCH_TimeoutClass; 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); /* Get the time stamp taken after a file descriptor became ready or a timeout expired */ extern void SCH_GetLastEventTime(struct timeval *cooked, double *err, struct timeval *raw); /* 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, given randomness is added to the delay and separation */ extern SCH_TimeoutID SCH_AddTimeoutInClass(double min_delay, double separation, double randomness, 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 */ chrony-2.1.1/smooth.c0000644000000000000000000002061112542263351013173 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing time smoothing. */ #include "config.h" #include "sysincl.h" #include "conf.h" #include "local.h" #include "logging.h" #include "reference.h" #include "smooth.h" #include "util.h" /* Time smoothing determines an offset that needs to be applied to the cooked time to make it smooth for external observers. Observed offset and frequency change slowly and there are no discontinuities. This can be used on an NTP server to make it easier for the clients to track the time and keep their clocks close together even when large offset or frequency corrections are applied to the server's clock (e.g. after being offline for longer time). Accumulated offset and frequency are smoothed out in three stages. In the first stage, the frequency is changed at a constant rate (wander) up to a maximum, in the second stage the frequency stays at the maximum for as long as needed and in the third stage the frequency is brought back to zero. | max_freq +-------/--------\------------- | /| |\ freq | / | | \ | / | | \ | / | | \ 0 +--/----+--------+----\-------- | / | | | time |/ | | | stage 1 2 3 Integral of this function is the smoothed out offset. It's a continuous piecewise polynomial with two quadratic parts and one linear. */ struct stage { double wander; double length; }; #define NUM_STAGES 3 static struct stage stages[NUM_STAGES]; /* Enabled/disabled smoothing */ static int enabled; /* Enabled/disabled mode where only leap seconds are smoothed out and normal offset/frequency changes are ignored */ static int leap_only_mode; /* Maximum skew/max_wander ratio to start updating offset and frequency */ #define UNLOCK_SKEW_WANDER_RATIO 10000 static int locked; /* Maximum wander and frequency offset */ static double max_wander; static double max_freq; /* Frequency offset, time offset and the time of the last smoothing update */ static double smooth_freq; static double smooth_offset; static struct timeval last_update; static void get_smoothing(struct timeval *now, double *poffset, double *pfreq, double *pwander) { double elapsed, length, offset, freq, wander; int i; UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); offset = smooth_offset; freq = smooth_freq; wander = 0.0; for (i = 0; i < NUM_STAGES; i++) { if (elapsed <= 0.0) break; length = stages[i].length; if (length >= elapsed) length = elapsed; wander = stages[i].wander; offset -= length * (2.0 * freq + wander * length) / 2.0; freq += wander * length; elapsed -= length; } if (elapsed > 0.0) { wander = 0.0; offset -= elapsed * freq; } *poffset = offset; *pfreq = freq; if (pwander) *pwander = wander; } static void update_stages(void) { double s1, s2, s, l1, l2, l3, lc, f, f2; int i, dir; /* Prepare the three stages so that the integral of the frequency offset is equal to the offset that should be smoothed out */ s1 = smooth_offset / max_wander; s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander); l1 = l2 = l3 = 0.0; /* Calculate the lengths of the 1st and 3rd stage assuming there is no frequency limit. If length of the 1st stage comes out negative, switch its direction. */ for (dir = -1; dir <= 1; dir += 2) { s = dir * s1 + s2; if (s >= 0.0) { l3 = sqrt(s); l1 = l3 - dir * smooth_freq / max_wander; if (l1 >= 0.0) break; } } assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0); /* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */ f = dir * smooth_freq + l1 * max_wander - max_freq; if (f > 0.0) { lc = f / max_wander; /* No 1st stage if the frequency is already above the maximum */ if (lc > l1) { lc = l1; f2 = dir * smooth_freq; } else { f2 = max_freq; } l2 = lc * (2.0 + f / f2); l1 -= lc; l3 -= lc; } stages[0].wander = dir * max_wander; stages[0].length = l1; stages[1].wander = 0.0; stages[1].length = l2; stages[2].wander = -dir * max_wander; stages[2].length = l3; for (i = 0; i < NUM_STAGES; i++) { DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f", i + 1, stages[i].wander, stages[i].length); } } static void update_smoothing(struct timeval *now, double offset, double freq) { /* Don't accept offset/frequency until the clock has stabilized */ if (locked) { if (REF_GetSkew() / max_wander < UNLOCK_SKEW_WANDER_RATIO || leap_only_mode) SMT_Activate(now); return; } get_smoothing(now, &smooth_offset, &smooth_freq, NULL); smooth_offset += offset; smooth_freq = (smooth_freq - freq) / (1.0 - freq); last_update = *now; update_stages(); DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq); } static void handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { double delta; if (change_type == LCL_ChangeAdjust) { if (leap_only_mode) update_smoothing(cooked, 0.0, 0.0); else update_smoothing(cooked, doffset, dfreq); } UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset); } void SMT_Initialise(void) { CNF_GetSmooth(&max_freq, &max_wander, &leap_only_mode); if (max_freq <= 0.0 || max_wander <= 0.0) { enabled = 0; return; } enabled = 1; locked = 1; /* Convert from ppm */ max_freq *= 1e-6; max_wander *= 1e-6; LCL_AddParameterChangeHandler(handle_slew, NULL); } void SMT_Finalise(void) { } int SMT_IsEnabled(void) { return enabled; } double SMT_GetOffset(struct timeval *now) { double offset, freq; if (!enabled) return 0.0; get_smoothing(now, &offset, &freq, NULL); return offset; } void SMT_Activate(struct timeval *now) { if (!enabled || !locked) return; LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ? " (leap seconds only)" : ""); locked = 0; last_update = *now; } void SMT_Reset(struct timeval *now) { int i; if (!enabled) return; smooth_offset = 0.0; smooth_freq = 0.0; last_update = *now; for (i = 0; i < NUM_STAGES; i++) stages[i].wander = stages[i].length = 0.0; } void SMT_Leap(struct timeval *now, int leap) { /* When the leap-only mode is disabled, the leap second will be accumulated in handle_slew() as a normal offset */ if (!enabled || !leap_only_mode) return; update_smoothing(now, leap, 0.0); } int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now) { double length, elapsed; int i; if (!enabled) return 0; report->active = !locked; report->leap_only = leap_only_mode; get_smoothing(now, &report->offset, &report->freq_ppm, &report->wander_ppm); /* Convert to ppm and negate (positive values mean faster/speeding up) */ report->freq_ppm *= -1.0e6; report->wander_ppm *= -1.0e6; UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); if (!locked && elapsed >= 0.0) { for (i = 0, length = 0.0; i < NUM_STAGES; i++) length += stages[i].length; report->last_update_ago = elapsed; report->remaining_time = elapsed < length ? length - elapsed : 0.0; } else { report->last_update_ago = 0.0; report->remaining_time = 0.0; } return 1; } chrony-2.1.1/smooth.h0000644000000000000000000000273612542263351013210 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This module implements time smoothing. */ #ifndef GOT_SMOOTH_H #define GOT_SMOOTH_H #include "reports.h" extern void SMT_Initialise(void); extern void SMT_Finalise(void); extern int SMT_IsEnabled(void); extern double SMT_GetOffset(struct timeval *now); extern void SMT_Activate(struct timeval *now); extern void SMT_Reset(struct timeval *now); extern void SMT_Leap(struct timeval *now, int leap); extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now); #endif chrony-2.1.1/sources.c0000644000000000000000000011330012542263351013343 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "sources.h" #include "sourcestats.h" #include "memory.h" #include "ntp.h" /* For NTP_Leap */ #include "ntp_sources.h" #include "local.h" #include "reference.h" #include "util.h" #include "conf.h" #include "logging.h" #include "reports.h" #include "nameserv.h" #include "mkdirpp.h" #include "sched.h" #include "regress.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_distance; double lo_limit; double hi_limit; double last_sample_ago; }; /* ================================================== */ /* This enum contains the flag values that are used to label each source */ typedef enum { SRC_OK, /* OK so far, not a final status! */ SRC_UNSELECTABLE, /* Has noselect option set */ SRC_BAD_STATS, /* Doesn't have valid stats data */ SRC_WAITS_STATS, /* Others have bad stats, selection postponed */ SRC_STALE, /* Has older samples than others */ SRC_FALSETICKER, /* Doesn't agree with others */ SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */ SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */ SRC_NONPREFERRED, /* Others have prefer option */ SRC_WAITS_UPDATE, /* No updates, selection postponed */ SRC_DISTANT, /* Others have shorter root distance */ SRC_OUTLIER, /* Outlier in clustering (not used yet) */ SRC_UNSELECTED, /* Used for synchronisation, not system peer */ SRC_SELECTED, /* Used for synchronisation, selected as system peer */ } 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 */ uint32_t ref_id; /* The reference ID of this source (i.e. from its IP address, NOT the reference _it_ is sync'd to) */ IPAddr *ip_addr; /* Its IP address if NTP source */ /* Flag indicating that the source is updating reachability */ int active; /* Reachability register */ int reachability; /* Number of set bits in the reachability register */ int reachability_size; /* Updates since last reference update */ int updates; /* Updates left before allowing combining */ int distant; /* Flag indicating the status of the source */ SRC_Status status; /* Type of the source */ SRC_Type type; /* Options used when selecting sources */ SRC_SelectOption sel_option; /* Score against currently selected source */ double sel_score; struct SelectInfo sel_info; }; /* ================================================== */ /* Structure used to build the sort list for finding falsetickers */ struct Sort_Element { int index; double offset; enum { LOW = -1, 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) */ /* Score needed to replace the currently selected source */ #define SCORE_LIMIT 10.0 /* Number of updates needed to reset the distant status */ #define DISTANT_PENALTY 32 static double reselect_distance; static double stratum_weight; static double combine_limit; /* ================================================== */ /* Forward prototype */ static void slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything); static void add_dispersion(double dispersion, void *anything); static char * source_to_string(SRC_Instance inst); /* ================================================== */ /* Initialisation function */ void SRC_Initialise(void) { sources = NULL; sort_list = NULL; sel_sources = NULL; n_sources = 0; max_n_sources = 0; selected_source_index = INVALID_SOURCE; reselect_distance = CNF_GetReselectDistance(); stratum_weight = CNF_GetStratumWeight(); combine_limit = CNF_GetCombineLimit(); initialised = 1; LCL_AddParameterChangeHandler(slew_sources, NULL); LCL_AddDispersionNotifyHandler(add_dispersion, NULL); } /* ================================================== */ /* Finalisation function */ void SRC_Finalise(void) { LCL_RemoveParameterChangeHandler(slew_sources, NULL); LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL); Free(sources); Free(sort_list); Free(sel_sources); initialised = 0; } /* ================================================== */ /* Function to create a new instance. This would be called by one of the individual source-type instance creation routines. */ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOption sel_option, IPAddr *addr, int min_samples, int max_samples) { SRC_Instance result; assert(initialised); if (min_samples == SRC_DEFAULT_MINSAMPLES) min_samples = CNF_GetMinSamples(); if (max_samples == SRC_DEFAULT_MAXSAMPLES) max_samples = CNF_GetMaxSamples(); result = MallocNew(struct SRC_Instance_Record); result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples); if (n_sources == max_n_sources) { /* Reallocate memory */ max_n_sources = max_n_sources > 0 ? 2 * max_n_sources : 4; 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->type = type; result->sel_option = sel_option; SRC_SetRefid(result, ref_id, addr); SRC_ResetInstance(result); 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; assert(initialised); SST_DeleteInstance(instance->stats); dead_index = instance->index; for (i=dead_index; iindex = i; } --n_sources; Free(instance); /* If this was the previous reference source, we have to reselect! */ if (selected_source_index == dead_index) SRC_ReselectSource(); else if (selected_source_index > dead_index) --selected_source_index; } /* ================================================== */ void SRC_ResetInstance(SRC_Instance instance) { instance->leap_status = LEAP_Normal; instance->active = 0; instance->updates = 0; instance->reachability = 0; instance->reachability_size = 0; instance->distant = 0; instance->status = SRC_BAD_STATS; instance->sel_score = 1.0; SST_ResetInstance(instance->stats); } /* ================================================== */ void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr) { instance->ref_id = ref_id; instance->ip_addr = addr; SST_SetRefid(instance->stats, ref_id, addr); } /* ================================================== */ /* 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) { assert(initialised); SST_GetFrequencyRange(instance->stats, lo, 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. 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) { assert(initialised); inst->leap_status = leap_status; DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum); if (REF_IsLeapSecondClose()) { LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second"); return; } /* 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); } /* ================================================== */ void SRC_SetActive(SRC_Instance inst) { inst->active = 1; } /* ================================================== */ void SRC_UnsetActive(SRC_Instance inst) { inst->active = 0; } /* ================================================== */ static int special_mode_end(void) { int i; for (i = 0; i < n_sources; i++) { /* No updates from inactive sources */ if (!sources[i]->active) continue; /* Don't expect more updates than from an offline iburst NTP source */ if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1) continue; /* Check if the source could still have enough samples to be selectable */ if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size + SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS) return 0; } return 1; } void SRC_UpdateReachability(SRC_Instance inst, int reachable) { inst->reachability <<= 1; inst->reachability |= !!reachable; inst->reachability &= ~(-1 << SOURCE_REACH_BITS); if (inst->reachability_size < SOURCE_REACH_BITS) inst->reachability_size++; if (!reachable && inst->index == selected_source_index) { /* Try to select a better source */ SRC_SelectSource(NULL); } /* Check if special reference update mode failed */ if (REF_GetMode() != REF_ModeNormal && special_mode_end()) { REF_SetUnsynchronised(); } /* Try to replace NTP sources that are unreachable or falsetickers */ if (inst->type == SRC_NTP && (inst->status == SRC_FALSETICKER || (!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS))) { NSR_HandleBadSource(inst->ip_addr); } } /* ================================================== */ void SRC_ResetReachability(SRC_Instance inst) { inst->reachability = 0; inst->reachability_size = 0; SRC_UpdateReachability(inst, 0); } /* ================================================== */ static void log_selection_message(char *format, char *arg) { if (REF_GetMode() != REF_ModeNormal) return; LOG(LOGS_INFO, LOGF_Sources, format, arg); } /* ================================================== */ 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; } } /* ================================================== */ static char * source_to_string(SRC_Instance inst) { switch (inst->type) { case SRC_NTP: return UTI_IPToString(inst->ip_addr); case SRC_REFCLOCK: return UTI_RefidToString(inst->ref_id); default: assert(0); } return NULL; } /* ================================================== */ static void mark_ok_sources(SRC_Status status) { int i; for (i = 0; i < n_sources; i++) { if (sources[i]->status != SRC_OK) continue; sources[i]->status = status; } } /* ================================================== */ static int combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset, double *offset_sd, double *frequency, double *skew) { struct timeval src_ref_time; double src_offset, src_offset_sd, src_frequency, src_skew; double src_root_delay, src_root_dispersion, sel_src_distance, elapsed; double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd; double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew; int i, index, combined; if (n_sel_sources == 1) return 1; sum_offset_weight = sum_offset = sum2_offset_sd = 0.0; sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0; sel_src_distance = sources[selected_source_index]->sel_info.root_distance; if (sources[selected_source_index]->type == SRC_NTP) sel_src_distance += reselect_distance; for (i = combined = 0; i < n_sel_sources; i++) { index = sel_sources[i]; SST_GetTrackingData(sources[index]->stats, &src_ref_time, &src_offset, &src_offset_sd, &src_frequency, &src_skew, &src_root_delay, &src_root_dispersion); /* Don't include this source if its distance is longer than the distance of the selected source multiplied by the limit, their estimated frequencies are not close, or it was recently marked as distant */ if (index != selected_source_index && (sources[index]->sel_info.root_distance > combine_limit * sel_src_distance || fabs(*frequency - src_frequency) > combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) { /* Use a smaller penalty in first few updates */ sources[index]->distant = sources[index]->reachability_size >= SOURCE_REACH_BITS ? DISTANT_PENALTY : 1; } else if (sources[index]->distant) { sources[index]->distant--; } if (sources[index]->distant) { sources[index]->status = SRC_DISTANT; continue; } if (sources[index]->status == SRC_OK) sources[index]->status = SRC_UNSELECTED; UTI_DiffTimevalsToDouble(&elapsed, ref_time, &src_ref_time); src_offset += elapsed * src_frequency; offset_weight = 1.0 / sources[index]->sel_info.root_distance; frequency_weight = 1.0 / src_skew; DEBUG_LOG(LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e", index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew); sum_offset_weight += offset_weight; sum_offset += offset_weight * src_offset; sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd + (src_offset - *offset) * (src_offset - *offset)); sum_frequency_weight += frequency_weight; sum_frequency += frequency_weight * src_frequency; inv_sum2_skew += 1.0 / (src_skew * src_skew); combined++; } assert(combined); *offset = sum_offset / sum_offset_weight; *offset_sd = sqrt(sum2_offset_sd / sum_offset_weight); *frequency = sum_frequency / sum_frequency_weight; *skew = 1.0 / sqrt(inv_sum2_skew); DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e", *offset, *offset_sd, *frequency, *skew); return combined; } /* ================================================== */ /* This function selects the current reference from amongst the pool of sources we are holding and updates the local reference */ void SRC_SelectSource(SRC_Instance updated_inst) { struct SelectInfo *si; struct timeval now, ref_time; int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources; int n_badstats_sources, max_sel_reach, max_badstat_reach; int depth, best_depth, combined, stratum, min_stratum, max_score_index; double src_offset, src_offset_sd, src_frequency, src_skew; double src_root_delay, src_root_dispersion; double best_lo, best_hi, distance, sel_src_distance, max_score; double first_sample_ago, max_reach_sample_ago; NTP_Leap leap_status; if (updated_inst) updated_inst->updates++; if (n_sources == 0) { /* In this case, we clearly cannot synchronise to anything */ if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: no sources", NULL); selected_source_index = INVALID_SOURCE; } return; } /* This is accurate enough and cheaper than calling LCL_ReadCookedTime */ SCH_GetLastEventTime(&now, NULL, NULL); /* Step 1 - build intervals about each source */ n_endpoints = 0; n_sel_sources = 0; n_badstats_sources = 0; max_sel_reach = max_badstat_reach = 0; max_reach_sample_ago = 0.0; for (i = 0; i < n_sources; i++) { assert(sources[i]->status != SRC_OK); /* Ignore sources which were added with the noselect option */ if (sources[i]->sel_option == SRC_SelectNoselect) { sources[i]->status = SRC_UNSELECTABLE; continue; } si = &sources[i]->sel_info; SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->lo_limit, &si->hi_limit, &si->root_distance, &si->variance, &first_sample_ago, &si->last_sample_ago, &si->select_ok); if (!si->select_ok) { ++n_badstats_sources; sources[i]->status = SRC_BAD_STATS; if (max_badstat_reach < sources[i]->reachability) max_badstat_reach = sources[i]->reachability; continue; } sources[i]->status = SRC_OK; /* For now */ if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago) max_reach_sample_ago = first_sample_ago; if (max_sel_reach < sources[i]->reachability) max_sel_reach = sources[i]->reachability; } for (i = 0; i < n_sources; i++) { if (sources[i]->status != SRC_OK) continue; si = &sources[i]->sel_info; /* Reachability is not a requirement for selection. An unreachable source can still be selected if its newest sample is not older than the oldest sample from reachable sources. */ if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) { sources[i]->status = SRC_STALE; continue; } ++n_sel_sources; 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; } DEBUG_LOG(LOGF_Sources, "badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f", n_badstats_sources, n_sel_sources, max_badstat_reach, max_sel_reach, max_reach_sample_ago); /* Wait for the next call if we have no source selected and there is a source with bad stats (has less than 3 samples) with reachability equal to shifted maximum reachability of sources with valid stats. This delays selecting source on start with servers using the same polling interval until they all have valid stats. */ if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE && max_sel_reach >> 1 == max_badstat_reach) { mark_ok_sources(SRC_WAITS_STATS); return; } if (n_endpoints == 0) { /* No sources provided valid endpoints */ if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: no selectable sources", NULL); selected_source_index = INVALID_SOURCE; } return; } /* Now sort the endpoint list */ 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++) { switch (sort_list[i].tag) { case LOW: depth++; if (depth > best_depth) { best_depth = depth; best_lo = sort_list[i].offset; } break; case HIGH: if (depth == best_depth) best_hi = sort_list[i].offset; depth--; break; default: assert(0); } } if (best_depth <= n_sel_sources / 2) { /* Could not even get half the reachable sources to agree - clearly we can't synchronise. */ if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: no majority", NULL); REF_SetUnsynchronised(); selected_source_index = INVALID_SOURCE; } /* .. and mark all sources as falsetickers (so they appear thus on the outputs from the command client) */ mark_ok_sources(SRC_FALSETICKER); return; } /* 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) continue; /* 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; } else { sources[i]->status = SRC_FALSETICKER; } } if (n_sel_sources == 0 || n_sel_sources < CNF_GetMinSources()) { if (selected_source_index != INVALID_SOURCE) { log_selection_message("Can't synchronise: %s selectable sources", n_sel_sources ? "not enough" : "no"); selected_source_index = INVALID_SOURCE; } mark_ok_sources(SRC_WAITS_SOURCES); return; } /* Accept leap second status if more than half of selectable sources agree */ for (i = j1 = j2 = 0; i < n_sel_sources; i++) { index = sel_sources[i]; if (sources[index]->leap_status == LEAP_InsertSecond) j1++; else if (sources[index]->leap_status == LEAP_DeleteSecond) j2++; } if (j1 > n_sel_sources / 2) leap_status = LEAP_InsertSecond; else if (j2 > n_sel_sources / 2) leap_status = LEAP_DeleteSecond; else leap_status = LEAP_Normal; /* If there are any sources with prefer option, reduce the list again only to the preferred sources */ for (i = 0; i < n_sel_sources; i++) { if (sources[sel_sources[i]]->sel_option == SRC_SelectPrefer) break; } if (i < n_sel_sources) { for (i = j = 0; i < n_sel_sources; i++) { if (sources[sel_sources[i]]->sel_option != SRC_SelectPrefer) sources[sel_sources[i]]->status = SRC_NONPREFERRED; else sel_sources[j++] = sel_sources[i]; } assert(j > 0); n_sel_sources = j; sel_prefer = 1; } else { sel_prefer = 0; } /* Find minimum stratum */ 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; } /* Update scores and find the source with maximum score */ max_score_index = INVALID_SOURCE; max_score = 0.0; sel_src_distance = 0.0; if (selected_source_index != INVALID_SOURCE) sel_src_distance = sources[selected_source_index]->sel_info.root_distance + (sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight; for (i = 0; i < n_sources; i++) { /* Reset score for non-selectable sources */ if (sources[i]->status != SRC_OK || (sel_prefer && sources[i]->sel_option != SRC_SelectPrefer)) { sources[i]->sel_score = 1.0; sources[i]->distant = DISTANT_PENALTY; continue; } distance = sources[i]->sel_info.root_distance + (sources[i]->sel_info.stratum - min_stratum) * stratum_weight; if (sources[i]->type == SRC_NTP) distance += reselect_distance; if (selected_source_index != INVALID_SOURCE) { /* Update score, but only for source pairs where one source has a new sample */ if (sources[i] == updated_inst || sources[selected_source_index] == updated_inst) { sources[i]->sel_score *= sel_src_distance / distance; if (sources[i]->sel_score < 1.0) sources[i]->sel_score = 1.0; } } else { /* When there is no selected source yet, assign scores so that the source with minimum distance will have maximum score. The scores will be reset when the source is selected later in this function. */ sources[i]->sel_score = 1.0 / distance; } DEBUG_LOG(LOGF_Sources, "select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f", sources[i]->sel_score, sources[i]->ref_id, updated_inst ? updated_inst->ref_id : 0, sources[i]->status, distance); if (max_score < sources[i]->sel_score) { max_score = sources[i]->sel_score; max_score_index = i; } } assert(max_score_index != INVALID_SOURCE); /* Is the current source still a survivor and no other source has reached the score limit? */ if (selected_source_index == INVALID_SOURCE || sources[selected_source_index]->status != SRC_OK || (max_score_index != selected_source_index && max_score > SCORE_LIMIT)) { /* Before selecting the new synchronisation source wait until the reference can be updated */ if (sources[max_score_index]->updates == 0) { selected_source_index = INVALID_SOURCE; mark_ok_sources(SRC_WAITS_UPDATE); DEBUG_LOG(LOGF_Sources, "best source has no updates"); return; } selected_source_index = max_score_index; log_selection_message("Selected source %s", source_to_string(sources[selected_source_index])); /* New source has been selected, reset all scores */ for (i = 0; i < n_sources; i++) { sources[i]->sel_score = 1.0; sources[i]->distant = 0; } } sources[selected_source_index]->status = SRC_SELECTED; /* Don't update reference when the selected source has no new samples */ if (sources[selected_source_index]->updates == 0) { /* Mark the remaining sources as last combine_sources() call */ for (i = 0; i < n_sel_sources; i++) { index = sel_sources[i]; if (sources[index]->status == SRC_OK) sources[index]->status = sources[index]->distant ? SRC_DISTANT : SRC_UNSELECTED; } return; } for (i = 0; i < n_sources; i++) sources[i]->updates = 0; /* Now just use the statistics of the selected source combined with the other selectable sources for trimming the local clock */ SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time, &src_offset, &src_offset_sd, &src_frequency, &src_skew, &src_root_delay, &src_root_dispersion); combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd, &src_frequency, &src_skew); REF_SetReference(sources[selected_source_index]->sel_info.stratum, leap_status, combined, sources[selected_source_index]->ref_id, sources[selected_source_index]->ip_addr, &ref_time, src_offset, src_offset_sd, src_frequency, src_skew, src_root_delay, src_root_dispersion); } /* ================================================== */ /* Force reselecting the best source */ void SRC_ReselectSource(void) { selected_source_index = INVALID_SOURCE; SRC_SelectSource(NULL); } /* ================================================== */ void SRC_SetReselectDistance(double distance) { if (reselect_distance != distance) { reselect_distance = distance; LOG(LOGS_INFO, LOGF_Sources, "New reselect distance %f", distance); } } /* ================================================== */ 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); } /* ================================================== */ int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when) { return SST_IsGoodSample(inst->stats, offset, delay, max_delay_dev_ratio, clock_error, when); } /* ================================================== */ /* 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 doffset, LCL_ChangeType change_type, void *anything) { int i; for (i=0; istats); } else { SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset); } } if (change_type == LCL_ChangeUnknownStep) { /* After resetting no source is selectable, set reference unsynchronised */ SRC_SelectSource(NULL); } } /* ================================================== */ /* This routine is called when an indeterminate offset is introduced into the local time. */ static void add_dispersion(double dispersion, void *anything) { int i; for (i = 0; i < n_sources; i++) { SST_AddDispersion(sources[i]->stats, dispersion); } } /* ================================================== */ /* This is called to dump out the source measurement registers */ void SRC_DumpSources(void) { FILE *out; int direc_len, file_len; char *filename; unsigned int a, b, c, d; int i; char *direc; direc = CNF_GetDumpDir(); direc_len = strlen(direc); file_len = direc_len + 24; filename = MallocArray(char, file_len); /* a bit of slack */ if (mkdir_and_parents(direc)) { for (i=0; iref_id) >> 24; b = ((sources[i]->ref_id) >> 16) & 0xff; c = ((sources[i]->ref_id) >> 8) & 0xff; d = ((sources[i]->ref_id)) & 0xff; snprintf(filename, file_len-1, "%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, filelen; for (i=0; iref_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); filelen = dumpdirlen + 24; filename = MallocArray(char, filelen); snprintf(filename, filelen-1, "%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)) { 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_IsReachable(SRC_Instance inst) { return inst->reachability != 0; } /* ================================================== */ int SRC_ReadNumberOfSources(void) { return n_sources; } /* ================================================== */ int SRC_ActiveSources(void) { int i, r; for (i = r = 0; i < n_sources; i++) if (sources[i]->active) r++; return r; } /* ================================================== */ 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]; memset(&report->ip_addr, 0, sizeof (report->ip_addr)); if (src->ip_addr) report->ip_addr = *src->ip_addr; else { /* Use refid as an address */ report->ip_addr.addr.in4 = src->ref_id; report->ip_addr.family = IPADDR_INET4; } switch (src->status) { case SRC_UNSELECTABLE: case SRC_BAD_STATS: case SRC_STALE: case SRC_WAITS_STATS: report->state = RPT_UNREACH; break; case SRC_FALSETICKER: report->state = RPT_FALSETICKER; break; case SRC_JITTERY: report->state = RPT_JITTERY; break; case SRC_WAITS_SOURCES: case SRC_NONPREFERRED: case SRC_WAITS_UPDATE: case SRC_DISTANT: case SRC_OUTLIER: report->state = RPT_OUTLIER; break; case SRC_UNSELECTED: report->state = RPT_CANDIDATE; break; case SRC_SELECTED: report->state = RPT_SYNC; break; case SRC_OK: default: assert(0); break; } switch (src->sel_option) { case SRC_SelectNormal: report->sel_option = RPT_NORMAL; break; case SRC_SelectPrefer: report->sel_option = RPT_PREFER; break; case SRC_SelectNoselect: report->sel_option = RPT_NOSELECT; break; default: assert(0); } report->reachability = src->reachability; /* Call stats module to fill out estimates */ SST_DoSourceReport(src->stats, report, now); return 1; } } /* ================================================== */ int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now) { SRC_Instance src; if ((index >= n_sources) || (index < 0)) { return 0; } else { src = sources[index]; report->ref_id = src->ref_id; if (src->ip_addr) report->ip_addr = *src->ip_addr; else report->ip_addr.family = IPADDR_UNSPEC; SST_DoSourcestatsReport(src->stats, report, now); return 1; } } /* ================================================== */ SRC_Type SRC_GetType(int index) { if ((index >= n_sources) || (index < 0)) return -1; return sources[index]->type; } /* ================================================== */ int SRC_Samples(SRC_Instance inst) { return SST_Samples(inst->stats); } /* ================================================== */ chrony-2.1.1/sources.h0000644000000000000000000001615212542263351013357 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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" /* Size of the source reachability register */ #define SOURCE_REACH_BITS 8 /* 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); typedef enum { SRC_NTP, /* NTP client/peer */ SRC_REFCLOCK /* Rerefence clock */ } SRC_Type; /* Options used when selecting sources */ typedef enum { SRC_SelectNormal, SRC_SelectNoselect, SRC_SelectPrefer } SRC_SelectOption; /* 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(uint32_t ref_id, SRC_Type type, SRC_SelectOption sel_option, IPAddr *addr, int min_samples, int max_samples); /* 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 reset a source */ extern void SRC_ResetInstance(SRC_Instance instance); /* Function to change the sources's reference ID and IP address */ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr); /* 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 RFC 5905. 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 sets the source as receiving reachability updates */ extern void SRC_SetActive(SRC_Instance inst); /* This routine sets the source as not receiving reachability updates */ extern void SRC_UnsetActive(SRC_Instance inst); /* This routine updates the reachability register */ extern void SRC_UpdateReachability(SRC_Instance inst, int reachable); /* This routine marks the source unreachable */ extern void SRC_ResetReachability(SRC_Instance inst); /* 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. Updates are made to the local reference only when the selected source was updated (set as updated_inst) since the last reference update. 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(SRC_Instance updated_inst); /* Force reselecting the best source */ extern void SRC_ReselectSource(void); /* Set reselect distance */ extern void SRC_SetReselectDistance(double distance); /* 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); /* This routine determines if a new sample is good enough that it should be accumulated */ extern int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when); extern void SRC_DumpSources(void); extern void SRC_ReloadSources(void); extern int SRC_IsSyncPeer(SRC_Instance inst); extern int SRC_IsReachable(SRC_Instance inst); extern int SRC_ReadNumberOfSources(void); extern int SRC_ActiveSources(void); extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now); extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now); extern SRC_Type SRC_GetType(int index); extern int SRC_Samples(SRC_Instance inst); #endif /* GOT_SOURCES_H */ chrony-2.1.1/sourcestats.c0000644000000000000000000006774512542263351014264 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "config.h" #include "sysincl.h" #include "sourcestats.h" #include "memory.h" #include "regress.h" #include "util.h" #include "conf.h" #include "logging.h" #include "local.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) /* The minimum allowed skew */ #define MIN_SKEW 1.0e-12 /* ================================================== */ static LOG_FileID logfileid; /* ================================================== */ /* This data structure is used to hold the history of data from the source */ struct SST_Stats_Record { /* Reference ID and IP address of source, used for logging to statistics log */ uint32_t refid; IPAddr *ip_addr; /* User defined minimum and maximum number of samples */ int min_samples; int max_samples; /* Number of samples currently stored. The samples are stored in circular buffer. */ int n_samples; /* Number of extra samples stored in sample_times and offsets arrays that are used to extend runs test */ int runs_samples; /* The index of the newest sample */ int last_sample; /* Flag indicating whether last regression was successful */ int regression_ok; /* The best individual sample that we are holding, in terms of the minimum root distance at the present time */ int best_single_sample; /* The index of the sample with minimum delay in peer_delays */ int min_delay_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 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 * REGRESS_RUNS_RATIO]; /* 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. */ double offsets[MAX_SAMPLES * REGRESS_RUNS_RATIO]; /* 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 strata that were associated with the sources at the times the samples were generated */ int strata[MAX_SAMPLES]; }; /* ================================================== */ static void find_min_delay_sample(SST_Stats inst); static int get_buf_index(SST_Stats inst, int i); /* ================================================== */ void SST_Initialise(void) { logfileid = CNF_GetLogStatistics() ? LOG_FileOpen("statistics", " Date (UTC) Time IP Address Std dev'n Est offset Offset sd Diff freq Est skew Stress Ns Bs Nr") : -1; } /* ================================================== */ void SST_Finalise(void) { } /* ================================================== */ /* This function creates a new instance of the statistics handler */ SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples) { SST_Stats inst; inst = MallocNew(struct SST_Stats_Record); inst->min_samples = min_samples; inst->max_samples = max_samples; SST_SetRefid(inst, refid, addr); SST_ResetInstance(inst); return inst; } /* ================================================== */ /* This function deletes an instance of the statistics handler. */ void SST_DeleteInstance(SST_Stats inst) { Free(inst); } /* ================================================== */ void SST_ResetInstance(SST_Stats inst) { inst->n_samples = 0; inst->runs_samples = 0; inst->last_sample = 0; inst->regression_ok = 0; inst->best_single_sample = 0; inst->min_delay_sample = 0; inst->estimated_frequency = 0; inst->skew = 2000.0e-6; inst->estimated_offset = 0.0; inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */ inst->offset_time.tv_sec = 0; inst->offset_time.tv_usec = 0; inst->variance = 16.0; inst->nruns = 0; } /* ================================================== */ void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr) { inst->refid = refid; inst->ip_addr = addr; } /* ================================================== */ /* 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) { if (!new_oldest) return; assert(inst->n_samples >= new_oldest); inst->n_samples -= new_oldest; inst->runs_samples += new_oldest; if (inst->runs_samples > inst->n_samples * (REGRESS_RUNS_RATIO - 1)) inst->runs_samples = inst->n_samples * (REGRESS_RUNS_RATIO - 1); assert(inst->n_samples + inst->runs_samples <= MAX_SAMPLES * REGRESS_RUNS_RATIO); find_min_delay_sample(inst); } /* ================================================== */ 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, m; /* Make room for the new sample */ if (inst->n_samples > 0 && (inst->n_samples == MAX_SAMPLES || inst->n_samples == inst->max_samples)) { prune_register(inst, 1); } /* Make sure it's newer than the last sample */ if (inst->n_samples && UTI_CompareTimevals(&inst->sample_times[inst->last_sample], sample_time) >= 0) { LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s", inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid)); SST_ResetInstance(inst); } n = inst->last_sample = (inst->last_sample + 1) % (MAX_SAMPLES * REGRESS_RUNS_RATIO); m = n % MAX_SAMPLES; inst->sample_times[n] = *sample_time; inst->offsets[n] = offset; inst->orig_offsets[m] = offset; inst->peer_delays[m] = peer_delay; inst->peer_dispersions[m] = peer_dispersion; inst->root_delays[m] = root_delay; inst->root_dispersions[m] = root_dispersion; inst->strata[m] = stratum; if (!inst->n_samples || inst->peer_delays[m] < inst->peer_delays[inst->min_delay_sample]) inst->min_delay_sample = m; ++inst->n_samples; } /* ================================================== */ /* Return index of the i-th sample in the sample_times and offset buffers, i can be negative down to -runs_samples */ static int get_runsbuf_index(SST_Stats inst, int i) { return (unsigned int)(inst->last_sample + 2 * MAX_SAMPLES * REGRESS_RUNS_RATIO - inst->n_samples + i + 1) % (MAX_SAMPLES * REGRESS_RUNS_RATIO); } /* ================================================== */ /* Return index of the i-th sample in the other buffers */ static int get_buf_index(SST_Stats inst, int i) { return (unsigned int)(inst->last_sample + MAX_SAMPLES * REGRESS_RUNS_RATIO - inst->n_samples + i + 1) % MAX_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->last_sample]); for (i = -inst->runs_samples; i < inst->n_samples; i++) { /* The entries in times_back[] should end up negative */ UTI_DiffTimevalsToDouble(×_back[i], &inst->sample_times[get_runsbuf_index(inst, 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, j, best_index; if (!inst->n_samples) return; best_index = -1; best_root_distance = DBL_MAX; for (i = 0; i < inst->n_samples; i++) { j = get_buf_index(inst, i); elapsed = -times_back[i]; assert(elapsed >= 0.0); root_distance = inst->root_dispersions[j] + elapsed * inst->skew + 0.5 * inst->root_delays[j]; if (root_distance < best_root_distance) { best_root_distance = root_distance; best_index = i; } } assert(best_index >= 0); inst->best_single_sample = best_index; } /* ================================================== */ static void find_min_delay_sample(SST_Stats inst) { int i, index; inst->min_delay_sample = get_buf_index(inst, 0); for (i = 1; i < inst->n_samples; i++) { index = get_buf_index(inst, i); if (inst->peer_delays[index] < inst->peer_delays[inst->min_delay_sample]) inst->min_delay_sample = index; } } /* ================================================== */ /* 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 four times the fluctuation of the peer distance */ #define SD_TO_DIST_RATIO 1.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 * REGRESS_RUNS_RATIO]; double offsets[MAX_SAMPLES * REGRESS_RUNS_RATIO]; double peer_distances[MAX_SAMPLES]; double weights[MAX_SAMPLES]; int degrees_of_freedom; int best_start, times_back_start; double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd; int i, j, nruns; double min_distance, mean_distance; double sd_weight, sd; double old_skew, old_freq, stress; convert_to_intervals(inst, times_back + inst->runs_samples); if (inst->n_samples > 0) { for (i = -inst->runs_samples; i < inst->n_samples; i++) { offsets[i + inst->runs_samples] = inst->offsets[get_runsbuf_index(inst, i)]; } for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) { j = get_buf_index(inst, i); peer_distances[i] = 0.5 * inst->peer_delays[j] + inst->peer_dispersions[j]; mean_distance += peer_distances[i]; if (peer_distances[i] < min_distance) { min_distance = peer_distances[i]; } } mean_distance /= inst->n_samples; /* And now, work out the weight vector */ sd = mean_distance - min_distance; if (sd > min_distance || sd <= 0.0) sd = min_distance; for (i=0; in_samples; i++) { sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd; weights[i] = sd_weight * sd_weight; } } inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples, offsets + inst->runs_samples, weights, inst->n_samples, inst->runs_samples, inst->min_samples, &est_intercept, &est_slope, &est_var, &est_intercept_sd, &est_slope_sd, &best_start, &nruns, °rees_of_freedom); if (inst->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->last_sample]; inst->estimated_offset_sd = est_intercept_sd; inst->variance = est_var; inst->nruns = nruns; if (inst->skew < MIN_SKEW) inst->skew = MIN_SKEW; stress = fabs(old_freq - inst->estimated_frequency) / old_skew; if (logfileid != -1) { LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d", UTI_TimeToLogForm(inst->offset_time.tv_sec), inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid), sqrt(inst->variance), inst->estimated_offset, inst->estimated_offset_sd, inst->estimated_frequency, inst->skew, stress, inst->n_samples, best_start, nruns); } times_back_start = inst->runs_samples + best_start; prune_register(inst, best_start); } else { inst->estimated_frequency = 0.0; inst->skew = WORST_CASE_FREQ_BOUND; times_back_start = 0; } find_best_sample_index(inst, times_back + times_back_start); } /* ================================================== */ /* 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; /* This function is currently used only to determine the values of delta and epsilon in the ntp_core module. Limit the skew to a reasonable maximum to avoid failing the dispersion test too easily. */ if (skew > WORST_CASE_FREQ_BOUND) { *lo = -WORST_CASE_FREQ_BOUND; *hi = WORST_CASE_FREQ_BOUND; } } /* ================================================== */ void SST_GetSelectionData(SST_Stats inst, struct timeval *now, int *stratum, double *offset_lo_limit, double *offset_hi_limit, double *root_distance, double *variance, double *first_sample_ago, double *last_sample_ago, int *select_ok) { double offset, sample_elapsed; int i, j; if (!inst->n_samples) { *select_ok = 0; return; } i = get_runsbuf_index(inst, inst->best_single_sample); j = get_buf_index(inst, inst->best_single_sample); *stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)]; *variance = inst->variance; UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[i]); offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency; *root_distance = 0.5 * inst->root_delays[j] + inst->root_dispersions[j] + sample_elapsed * inst->skew; *offset_lo_limit = offset - *root_distance; *offset_hi_limit = offset + *root_distance; #if 0 double average_offset, elapsed; int average_ok; /* average_ok ignored for now */ UTI_DiffTimevalsToDouble(&elapsed, now, &(inst->offset_time)); average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed; if (fabs(average_offset - offset) <= inst->peer_dispersions[j] + 0.5 * inst->peer_delays[j]) { average_ok = 1; } else { average_ok = 0; } #endif i = get_runsbuf_index(inst, 0); UTI_DiffTimevalsToDouble(first_sample_ago, now, &inst->sample_times[i]); i = get_runsbuf_index(inst, inst->n_samples - 1); UTI_DiffTimevalsToDouble(last_sample_ago, now, &inst->sample_times[i]); *select_ok = inst->regression_ok; DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f first_ago=%f last_ago=%f selok=%d", inst->n_samples, offset, *root_distance, *variance, *first_sample_ago, *last_sample_ago, *select_ok); } /* ================================================== */ void SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time, double *average_offset, double *offset_sd, double *frequency, double *skew, double *root_delay, double *root_dispersion) { int i, j; double elapsed_sample; assert(inst->n_samples > 0); i = get_runsbuf_index(inst, inst->best_single_sample); j = get_buf_index(inst, inst->best_single_sample); *ref_time = inst->offset_time; *average_offset = inst->estimated_offset; *offset_sd = inst->estimated_offset_sd; *frequency = inst->estimated_frequency; *skew = inst->skew; *root_delay = inst->root_delays[j]; UTI_DiffTimevalsToDouble(&elapsed_sample, &inst->offset_time, &inst->sample_times[i]); *root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample; DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f", inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion); } /* ================================================== */ void SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffset) { int m, i; double delta_time; struct timeval *sample, prev; double prev_offset, prev_freq; if (!inst->n_samples) return; for (m = -inst->runs_samples; m < inst->n_samples; m++) { i = get_runsbuf_index(inst, m); sample = &(inst->sample_times[i]); prev = *sample; UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset); inst->offsets[i] += delta_time; } /* Update the regression estimates */ prev = inst->offset_time; prev_offset = inst->estimated_offset; prev_freq = inst->estimated_frequency; UTI_AdjustTimeval(&(inst->offset_time), when, &(inst->offset_time), &delta_time, dfreq, doffset); inst->estimated_offset += delta_time; inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq); DEBUG_LOG(LOGF_SourceStats, "n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f", inst->n_samples, inst->runs_samples, UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)), prev_offset, inst->estimated_offset, 1.0e6 * prev_freq, 1.0e6 * inst->estimated_frequency); } /* ================================================== */ void SST_AddDispersion(SST_Stats inst, double dispersion) { int m, i; for (m = 0; m < inst->n_samples; m++) { i = get_buf_index(inst, m); inst->root_dispersions[i] += dispersion; inst->peer_dispersions[i] += dispersion; } } /* ================================================== */ 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 or zero if we don't have any samples */ if (inst->n_samples > 0) { return inst->offsets[inst->last_sample]; } else { return 0.0; } } else { UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time); return inst->estimated_offset + elapsed * inst->estimated_frequency; } } /* ================================================== */ double SST_MinRoundTripDelay(SST_Stats inst) { if (!inst->n_samples) return DBL_MAX; return inst->peer_delays[inst->min_delay_sample]; } /* ================================================== */ int SST_IsGoodSample(SST_Stats inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when) { double elapsed, allowed_increase, delay_increase; if (inst->n_samples < 3) return 1; UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time); /* Require that the ratio of the increase in delay from the minimum to the standard deviation is less than max_delay_dev_ratio. In the allowed increase in delay include also skew and clock_error. */ allowed_increase = sqrt(inst->variance) * max_delay_dev_ratio + elapsed * (inst->skew + clock_error); delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0; if (delay_increase < allowed_increase) return 1; offset -= inst->estimated_offset + elapsed * inst->estimated_frequency; /* Before we decide to drop the sample, make sure the difference between measured offset and predicted offset is not significantly larger than the increase in delay */ if (fabs(offset) - delay_increase > allowed_increase) return 1; DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f", offset, delay, allowed_increase, delay_increase); return 0; } /* ================================================== */ /* 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 m, i, j; fprintf(out, "%d\n", inst->n_samples); for(m = 0; m < inst->n_samples; m++) { i = get_runsbuf_index(inst, m); j = get_buf_index(inst, m); 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[j], inst->peer_delays[j], inst->peer_dispersions[j], inst->root_delays[j], inst->root_dispersions[j], 1.0, /* used to be inst->weights[i] */ inst->strata[j]); } } /* ================================================== */ /* 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; double weight; assert(!inst->n_samples); if (fgets(line, sizeof(line), in) && sscanf(line, "%d", &inst->n_samples) == 1 && inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) { line_number = 2; for (i=0; in_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]), &weight, /* not used anymore */ &(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; } inst->last_sample = inst->n_samples - 1; inst->runs_samples = 0; find_min_delay_sample(inst); return 1; } /* ================================================== */ void SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now) { int i, j; struct timeval ago; if (inst->n_samples > 0) { i = get_runsbuf_index(inst, inst->n_samples - 1); j = get_buf_index(inst, inst->n_samples - 1); report->orig_latest_meas = inst->orig_offsets[j]; report->latest_meas = inst->offsets[i]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->stratum = inst->strata[j]; UTI_DiffTimevals(&ago, now, &inst->sample_times[i]); report->latest_meas_ago = ago.tv_sec; } else { report->latest_meas_ago = 86400 * 365 * 10; report->orig_latest_meas = 0; report->latest_meas = 0; report->latest_meas_err = 0; report->stratum = 0; } } /* ================================================== */ int SST_Samples(SST_Stats inst) { return inst->n_samples; } /* ================================================== */ void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timeval *now) { double dspan; double elapsed, sample_elapsed; int li, lj, bi, bj; report->n_samples = inst->n_samples; report->n_runs = inst->nruns; if (inst->n_samples > 1) { li = get_runsbuf_index(inst, inst->n_samples - 1); lj = get_buf_index(inst, inst->n_samples - 1); UTI_DiffTimevalsToDouble(&dspan, &inst->sample_times[li], &inst->sample_times[get_runsbuf_index(inst, 0)]); report->span_seconds = (unsigned long) (dspan + 0.5); if (inst->n_samples > 3) { UTI_DiffTimevalsToDouble(&elapsed, now, &inst->offset_time); bi = get_runsbuf_index(inst, inst->best_single_sample); bj = get_buf_index(inst, inst->best_single_sample); UTI_DiffTimevalsToDouble(&sample_elapsed, now, &inst->sample_times[bi]); report->est_offset = inst->estimated_offset + elapsed * inst->estimated_frequency; report->est_offset_err = (inst->estimated_offset_sd + sample_elapsed * inst->skew + (0.5*inst->root_delays[bj] + inst->root_dispersions[bj])); } else { report->est_offset = inst->offsets[li]; report->est_offset_err = 0.5*inst->root_delays[lj] + inst->root_dispersions[lj]; } } else { report->span_seconds = 0; report->est_offset = 0; report->est_offset_err = 0; } report->resid_freq_ppm = 1.0e6 * inst->estimated_frequency; report->skew_ppm = 1.0e6 * inst->skew; report->sd = sqrt(inst->variance); } /* ================================================== */ chrony-2.1.1/sourcestats.h0000644000000000000000000001277012542263351014255 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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(uint32_t refid, IPAddr *addr, int min_samples, int max_samples); /* This function deletes an instance of the statistics handler. */ extern void SST_DeleteInstance(SST_Stats inst); /* This function resets an instance */ extern void SST_ResetInstance(SST_Stats inst); /* This function changes the reference ID and IP address */ extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr); /* 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) 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); /* 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 *offset_lo_limit, double *offset_hi_limit, double *root_distance, double *variance, double *first_sample_ago, double *last_sample_ago, int *select_ok); /* Get data needed when setting up tracking on this source */ extern void SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time, double *average_offset, double *offset_sd, double *frequency, double *skew, double *root_delay, double *root_dispersion); /* 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); /* This routine is called when an indeterminate offset is introduced into the local time. */ extern void SST_AddDispersion(SST_Stats inst, double dispersion); /* 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); /* This routine determines if a new sample is good enough that it should be accumulated */ extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when); 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, struct timeval *now); extern int SST_Samples(SST_Stats inst); #endif /* GOT_SOURCESTATS_H */ chrony-2.1.1/srcparams.h0000644000000000000000000000371612542263351013671 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file defining parameters that can be set on a per source basis */ #ifndef GOT_SRCPARAMS_H #define GOT_SRCPARAMS_H #include "sources.h" typedef struct { int minpoll; int maxpoll; int online; int auto_offline; int presend_minpoll; int iburst; int min_stratum; int poll_target; int version; int max_sources; int min_samples; int max_samples; uint32_t authkey; double max_delay; double max_delay_ratio; double max_delay_dev_ratio; SRC_SelectOption sel_option; } SourceParameters; #define SRC_DEFAULT_PORT 123 #define SRC_DEFAULT_MINPOLL 6 #define SRC_DEFAULT_MAXPOLL 10 #define SRC_DEFAULT_PRESEND_MINPOLL 0 #define SRC_DEFAULT_MAXDELAY 3.0 #define SRC_DEFAULT_MAXDELAYRATIO 0.0 #define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0 #define SRC_DEFAULT_MINSTRATUM 0 #define SRC_DEFAULT_POLLTARGET 6 #define SRC_DEFAULT_MAXSOURCES 4 #define SRC_DEFAULT_MINSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1) #define INACTIVE_AUTHKEY 0 #endif /* GOT_SRCPARAMS_H */ chrony-2.1.1/strerror.c0000644000000000000000000000231312542263351013543 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Replacement strerror function for systems that don't have it */ #include "config.h" #ifdef SUNOS #include extern char *sys_errlist[]; char *strerror(int n) { return sys_errlist[n]; } #endif /* SUNOS */ chrony-2.1.1/stubs.c0000644000000000000000000001161512542263351013026 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Function replacements needed when optional features are disabled. */ #include "config.h" #include "clientlog.h" #include "cmdmon.h" #include "keys.h" #include "logging.h" #include "manual.h" #include "nameserv.h" #include "nameserv_async.h" #include "ntp_core.h" #include "ntp_io.h" #include "ntp_sources.h" #include "refclock.h" #ifndef FEAT_ASYNCDNS #define MAX_ADDRESSES 16 /* This is a blocking implementation used when asynchronous resolving is not available */ void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything) { IPAddr addrs[MAX_ADDRESSES]; DNS_Status status; int i; status = DNS_Name2IPAddress(name, addrs, MAX_ADDRESSES); for (i = 0; status == DNS_Success && i < MAX_ADDRESSES && addrs[i].family != IPADDR_UNSPEC; i++) ; (handler)(status, i, addrs, anything); } #endif /* !FEAT_ASYNCDNS */ #ifndef FEAT_CMDMON void CAM_Initialise(int family) { } void CAM_Finalise(void) { } int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all) { return 1; } void MNL_Initialise(void) { } void MNL_Finalise(void) { } #endif /* !FEAT_CMDMON */ #ifndef FEAT_NTP void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval) { } void NCR_Initialise(void) { } void NCR_Finalise(void) { } int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all) { return 1; } int NCR_CheckAccessRestriction(IPAddr *ip_addr) { return 0; } void NIO_Initialise(int family) { } void NIO_Finalise(void) { } void NSR_Initialise(void) { } void NSR_Finalise(void) { } NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) { return NSR_TooManySources; } void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params) { } NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr) { return NSR_NoSuchSource; } void NSR_RemoveAllSources(void) { } void NSR_HandleBadSource(IPAddr *address) { } void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler) { if (handler) (handler)(); } void NSR_ResolveSources(void) { } void NSR_StartSources(void) { } void NSR_AutoStartSources(void) { } int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address) { return 0; } int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address) { return 0; } int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address) { return 0; } int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll) { return 0; } int NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll) { return 0; } int NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay) { return 0; } int NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio) { return 0; } int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio) { return 0; } int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum) { return 0; } int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target) { return 0; } void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now) { memset(report, 0, sizeof (*report)); } void NSR_GetActivityReport(RPT_ActivityReport *report) { memset(report, 0, sizeof (*report)); } #ifndef FEAT_CMDMON void CLG_Initialise(void) { } void CLG_Finalise(void) { } void DNS_SetAddressFamily(int family) { } DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) { return DNS_Failure; } void KEY_Initialise(void) { } void KEY_Finalise(void) { } #endif /* !FEAT_CMDMON */ #endif /* !FEAT_NTP */ #ifndef FEAT_REFCLOCK void RCL_Initialise(void) { } void RCL_Finalise(void) { } int RCL_AddRefclock(RefclockParameters *params) { return 0; } void RCL_StartRefclocks(void) { } void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) { memset(report, 0, sizeof (*report)); } #endif /* !FEAT_REFCLOCK */ chrony-2.1.1/sys.c0000644000000000000000000000565512542263351012513 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This file contains all the conditionally compiled bits that pull in the various operating-system specific modules */ #include "config.h" #include "sys.h" #include "logging.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 #if defined (MACOSX) #include "sys_macosx.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 #if defined(MACOSX) SYS_MacOSX_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 #if defined(MACOSX) SYS_MacOSX_Finalise(); #endif } /* ================================================== */ void SYS_DropRoot(char *user) { #if defined(LINUX) && defined (FEAT_PRIVDROP) SYS_Linux_DropRoot(user); #else LOG_FATAL(LOGF_Sys, "dropping root privileges not supported"); #endif } /* ================================================== */ void SYS_SetScheduler(int SchedPriority) { #if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER) SYS_Linux_SetScheduler(SchedPriority); #else LOG_FATAL(LOGF_Sys, "scheduler priority setting not supported"); #endif } /* ================================================== */ void SYS_LockMemory(void) { #if defined(LINUX) && defined(HAVE_MLOCKALL) SYS_Linux_MemLockAll(1); #else LOG_FATAL(LOGF_Sys, "memory locking not supported"); #endif } /* ================================================== */ chrony-2.1.1/sys.h0000644000000000000000000000276612542263351012520 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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); /* Drop root privileges to the specified user */ extern void SYS_DropRoot(char *user); extern void SYS_SetScheduler(int SchedPriority); extern void SYS_LockMemory(void); #endif /* GOT_SYS_H */ chrony-2.1.1/sys_generic.c0000644000000000000000000002377012542263351014205 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Generic driver functions to complete system-specific drivers */ #include "config.h" #include "sysincl.h" #include "sys_generic.h" #include "conf.h" #include "local.h" #include "localp.h" #include "logging.h" #include "sched.h" #include "util.h" /* ================================================== */ /* System clock drivers */ static lcl_ReadFrequencyDriver drv_read_freq; static lcl_SetFrequencyDriver drv_set_freq; static lcl_SetSyncStatusDriver drv_set_sync_status; /* Current frequency as requested by the local module (in ppm) */ static double base_freq; /* Maximum frequency that can be set by drv_set_freq (in ppm) */ static double max_freq; /* Maximum expected delay in the actual frequency change (e.g. kernel ticks) in local time */ static double max_freq_change_delay; /* Maximum allowed frequency offset relative to the base frequency */ static double max_corr_freq; /* Amount of outstanding offset to process */ static double offset_register; /* Minimum offset to correct */ #define MIN_OFFSET_CORRECTION 1.0e-9 /* Current frequency offset between base_freq and the real clock frequency as set by drv_set_freq (not in ppm) */ static double slew_freq; /* Time (raw) of last update of slewing frequency and offset */ static struct timeval slew_start; /* Limits for the slew timeout */ #define MIN_SLEW_TIMEOUT 1.0 #define MAX_SLEW_TIMEOUT 1.0e4 /* Scheduler timeout ID and flag if the timer is currently running */ static SCH_TimeoutID slew_timeout_id; static int slew_timer_running; /* Suggested offset correction rate (correction time * offset) */ static double correction_rate; /* Maximum expected offset correction error caused by delayed change in the real frequency of the clock */ static double slew_error; /* ================================================== */ static void handle_end_of_slew(void *anything); static void update_slew(void); /* ================================================== */ /* Adjust slew_start on clock step */ static void handle_step(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, LCL_ChangeType change_type, void *anything) { if (change_type == LCL_ChangeUnknownStep) { /* Reset offset and slewing */ slew_start = *raw; offset_register = 0.0; update_slew(); } else if (change_type == LCL_ChangeStep) { UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start); } } /* ================================================== */ static double clamp_freq(double freq) { if (freq > max_freq) return max_freq; if (freq < -max_freq) return -max_freq; return freq; } /* ================================================== */ /* End currently running slew and start a new one */ static void update_slew(void) { struct timeval now, end_of_slew; double old_slew_freq, total_freq, corr_freq, duration; /* Remove currently running timeout */ if (slew_timer_running) SCH_RemoveTimeout(slew_timeout_id); LCL_ReadRawTime(&now); /* Adjust the offset register by achieved slew */ UTI_DiffTimevalsToDouble(&duration, &now, &slew_start); offset_register -= slew_freq * duration; /* Estimate how long should the next slew take */ if (fabs(offset_register) < MIN_OFFSET_CORRECTION) { duration = MAX_SLEW_TIMEOUT; } else { duration = correction_rate / fabs(offset_register); if (duration < MIN_SLEW_TIMEOUT) duration = MIN_SLEW_TIMEOUT; } /* Get frequency offset needed to slew the offset in the duration and clamp it to the allowed maximum */ corr_freq = offset_register / duration; if (corr_freq < -max_corr_freq) corr_freq = -max_corr_freq; else if (corr_freq > max_corr_freq) corr_freq = max_corr_freq; /* Get the new real frequency and clamp it */ total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq)); /* Set the new frequency (the actual frequency returned by the call may be slightly different from the requested frequency due to rounding) */ total_freq = (*drv_set_freq)(total_freq); /* Compute the new slewing frequency, it's relative to the real frequency to make the calculation in offset_convert() cheaper */ old_slew_freq = slew_freq; slew_freq = (total_freq - base_freq) / (1.0e6 - total_freq); /* Compute the dispersion introduced by changing frequency and add it to all statistics held at higher levels in the system */ slew_error = fabs((old_slew_freq - slew_freq) * max_freq_change_delay); if (slew_error >= MIN_OFFSET_CORRECTION) lcl_InvokeDispersionNotifyHandlers(slew_error); /* Compute the duration of the slew and clamp it. If the slewing frequency is zero or has wrong sign (e.g. due to rounding in the frequency driver or when base_freq is larger than max_freq), use maximum timeout and try again on the next update. */ if (fabs(offset_register) < MIN_OFFSET_CORRECTION || offset_register * slew_freq <= 0.0) { duration = MAX_SLEW_TIMEOUT; } else { duration = offset_register / slew_freq; if (duration < MIN_SLEW_TIMEOUT) duration = MIN_SLEW_TIMEOUT; else if (duration > MAX_SLEW_TIMEOUT) duration = MAX_SLEW_TIMEOUT; } /* Restart timer for the next update */ UTI_AddDoubleToTimeval(&now, duration, &end_of_slew); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); slew_start = now; slew_timer_running = 1; DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e", offset_register, correction_rate, base_freq, total_freq, slew_freq, duration, slew_error); } /* ================================================== */ static void handle_end_of_slew(void *anything) { slew_timer_running = 0; update_slew(); } /* ================================================== */ static double read_frequency(void) { return base_freq; } /* ================================================== */ static double set_frequency(double freq_ppm) { base_freq = freq_ppm; update_slew(); return base_freq; } /* ================================================== */ static void accrue_offset(double offset, double corr_rate) { offset_register += offset; correction_rate = corr_rate; update_slew(); } /* ================================================== */ /* Determine the correction to generate the cooked time for given raw time */ static void offset_convert(struct timeval *raw, double *corr, double *err) { double duration; UTI_DiffTimevalsToDouble(&duration, raw, &slew_start); *corr = slew_freq * duration - offset_register; if (err) *err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0; } /* ================================================== */ /* Positive means currently fast of true time, i.e. jump backwards */ static int apply_step_offset(double offset) { struct timeval old_time, new_time; double err; LCL_ReadRawTime(&old_time); UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); if (settimeofday(&new_time, NULL) < 0) { DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed"); return 0; } LCL_ReadRawTime(&old_time); UTI_DiffTimevalsToDouble(&err, &old_time, &new_time); lcl_InvokeDispersionNotifyHandlers(fabs(err)); return 1; } /* ================================================== */ static void set_sync_status(int synchronised, double est_error, double max_error) { double offset; offset = fabs(offset_register); if (est_error < offset) est_error = offset; max_error += offset; if (drv_set_sync_status) drv_set_sync_status(synchronised, est_error, max_error); } /* ================================================== */ void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay, lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, lcl_ApplyStepOffsetDriver sys_apply_step_offset, lcl_SetLeapDriver sys_set_leap, lcl_SetSyncStatusDriver sys_set_sync_status) { max_freq = max_set_freq_ppm; max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6); drv_read_freq = sys_read_freq; drv_set_freq = sys_set_freq; drv_set_sync_status = sys_set_sync_status; base_freq = (*drv_read_freq)(); slew_freq = 0.0; offset_register = 0.0; max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6; lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, sys_apply_step_offset ? sys_apply_step_offset : apply_step_offset, offset_convert, sys_set_leap, set_sync_status); LCL_AddParameterChangeHandler(handle_step, NULL); } /* ================================================== */ void SYS_Generic_Finalise(void) { /* Must *NOT* leave a slew running - clock could drift way off if the daemon is not restarted */ if (slew_timer_running) { SCH_RemoveTimeout(slew_timeout_id); slew_timer_running = 0; } (*drv_set_freq)(clamp_freq(base_freq)); } /* ================================================== */ chrony-2.1.1/sys_generic.h0000644000000000000000000000341712542263351014206 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for generic driver */ #ifndef GOT_SYS_GENERIC_H #define GOT_SYS_GENERIC_H #include "localp.h" /* Register a completed driver that implements offset functions on top of provided frequency functions */ extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay, lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, lcl_ApplyStepOffsetDriver sys_apply_step_offset, lcl_SetLeapDriver sys_set_leap, lcl_SetSyncStatusDriver sys_set_sync_status); extern void SYS_Generic_Finalise(void); #endif /* GOT_SYS_GENERIC_H */ chrony-2.1.1/sys_linux.c0000644000000000000000000003147512542263351013731 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) John G. Hasler 2009 * Copyright (C) Miroslav Lichvar 2009-2012, 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This is the module specific to the Linux operating system. */ #include "config.h" #include "sysincl.h" #include #if defined(HAVE_SCHED_SETSCHEDULER) # include int SchedPriority = 0; #endif #if defined(HAVE_MLOCKALL) # include #include int LockAll = 0; #endif #ifdef FEAT_PRIVDROP #include #include #include #include #include #endif #include "sys_generic.h" #include "sys_linux.h" #include "conf.h" #include "logging.h" #include "wrap_adjtimex.h" /* The threshold for adjtimex maxerror when the kernel sets the UNSYNC flag */ #define UNSYNC_MAXERROR 16.0 /* This is the uncompensated system tick value */ static int nominal_tick; /* Current tick value */ static int current_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; /* The kernel USER_HZ constant */ static int hz; static double dhz; /* And dbl prec version of same for arithmetic */ /* Flag indicating whether adjtimex() can step the clock */ static int have_setoffset; /* The assumed rate at which the effective frequency and tick values are updated in the kernel */ static int tick_update_hz; /* ================================================== */ inline static long our_round(double x) { long y; if (x > 0.0) y = x + 0.5; else y = x - 0.5; return y; } /* ================================================== */ /* Positive means currently fast of true time, i.e. jump backwards */ static int apply_step_offset(double offset) { if (TMX_ApplyStepOffset(-offset) < 0) { DEBUG_LOG(LOGF_SysLinux, "adjtimex() failed"); return 0; } return 1; } /* ================================================== */ /* 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 USER_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 double set_frequency(double freq_ppm) { long required_tick; double required_freq; int required_delta_tick; required_delta_tick = our_round(freq_ppm / dhz); /* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as set by adjtimex() and a scaling constant (that depends on the internal kernel HZ constant) would be needed to compensate for the error. Because chronyd is closed loop it doesn't matter much if we don't scale the required frequency, but we want to prevent thrashing between two states when the system's frequency error is close to a multiple of USER_HZ. With USER_HZ <= 250, the maximum frequency adjustment of 500 ppm overlaps at least two ticks and we can stick to the current tick if it's next to the required tick. */ if (hz <= 250 && (required_delta_tick + 1 == current_delta_tick || required_delta_tick - 1 == current_delta_tick)) { required_delta_tick = current_delta_tick; } required_freq = -(freq_ppm - dhz * required_delta_tick); required_tick = nominal_tick - required_delta_tick; if (TMX_SetFrequency(&required_freq, required_tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex failed for set_frequency, freq_ppm=%10.4e required_freq=%10.4e required_tick=%ld", freq_ppm, required_freq, required_tick); } current_delta_tick = required_delta_tick; return dhz * current_delta_tick - required_freq; } /* ================================================== */ /* Read the ppm frequency from the kernel */ static double read_frequency(void) { long tick; double freq; if (TMX_GetFrequency(&freq, &tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } current_delta_tick = nominal_tick - tick; return dhz * current_delta_tick - freq; } /* ================================================== */ static void set_leap(int leap) { int current_leap; if (TMX_GetLeap(¤t_leap) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap"); } if (current_leap == leap) return; if (TMX_SetLeap(leap) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap"); } LOG(LOGS_INFO, LOGF_SysLinux, "System clock status set to %s leap second", leap ? (leap > 0 ? "insert" : "delete") : "not insert/delete"); } /* ================================================== */ static void set_sync_status(int synchronised, double est_error, double max_error) { if (synchronised) { if (est_error > UNSYNC_MAXERROR) est_error = UNSYNC_MAXERROR; if (max_error >= UNSYNC_MAXERROR) { max_error = UNSYNC_MAXERROR; synchronised = 0; } } else { est_error = max_error = UNSYNC_MAXERROR; } /* Clear the UNSYNC flag only if rtcsync is enabled */ if (!CNF_GetRtcSync()) synchronised = 0; TMX_SetSync(synchronised, est_error, max_error); } /* ================================================== */ /* Estimate the value of USER_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/USER_HZ. */ static int guess_hz(int tick) { int i, tick_lo, tick_hi, ihz; double tick_nominal; /* Pick off the hz=100 case first */ if (tick >= 9000 && tick <= 11000) { return 100; } 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) { return ihz; } } /* oh dear. doomed. */ return 0; } /* ================================================== */ static int get_hz(void) { #ifdef _SC_CLK_TCK int hz; if ((hz = sysconf(_SC_CLK_TCK)) < 1) return 0; return hz; #else return 0; #endif } /* ================================================== */ static int kernelvercmp(int major1, int minor1, int patch1, int major2, int minor2, int patch2) { if (major1 != major2) return major1 - major2; if (minor1 != minor2) return minor1 - minor2; return patch1 - patch2; } /* ================================================== */ /* 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; long tick; double freq; struct utsname uts; hz = get_hz(); if (!hz) { if (TMX_GetFrequency(&freq, &tick) < 0) LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); hz = guess_hz(tick); if (!hz) LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %ld", tick); } dhz = (double) hz; nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */ max_tick_bias = nominal_tick / 10; /* We can't reliably detect the internal kernel HZ, it may not even be fixed (CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */ tick_update_hz = 100; if (uname(&uts) < 0) { LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry."); } patch = 0; if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) < 2) { LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry"); } DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch); if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) { LOG_FATAL(LOGF_SysLinux, "Kernel version not supported, sorry."); } if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 && kernelvercmp(major, minor, patch, 2, 6, 33) < 0) { /* Tickless kernels before 2.6.33 accumulated ticks only in half-second intervals */ tick_update_hz = 2; } /* ADJ_SETOFFSET support */ if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) { have_setoffset = 0; } else { have_setoffset = 1; } DEBUG_LOG(LOGF_SysLinux, "hz=%d nominal_tick=%d max_tick_bias=%d", hz, nominal_tick, max_tick_bias); } /* ================================================== */ /* Initialisation code for this module */ void SYS_Linux_Initialise(void) { get_version_specific_details(); if (TMX_ResetOffset() < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } if (have_setoffset && TMX_TestStepOffset() < 0) { LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET"); have_setoffset = 0; } SYS_Generic_CompleteFreqDriver(1.0e6 * max_tick_bias / nominal_tick, 1.0 / tick_update_hz, read_frequency, set_frequency, have_setoffset ? apply_step_offset : NULL, set_leap, set_sync_status); } /* ================================================== */ /* Finalisation code for this module */ void SYS_Linux_Finalise(void) { SYS_Generic_Finalise(); } /* ================================================== */ #ifdef FEAT_PRIVDROP void SYS_Linux_DropRoot(char *user) { struct passwd *pw; cap_t cap; if (user == NULL) return; if ((pw = getpwnam(user)) == NULL) { LOG_FATAL(LOGF_SysLinux, "getpwnam(%s) failed", user); } if (prctl(PR_SET_KEEPCAPS, 1)) { LOG_FATAL(LOGF_SysLinux, "prctl() failed"); } if (setgroups(0, NULL)) { LOG_FATAL(LOGF_SysLinux, "setgroups() failed"); } if (setgid(pw->pw_gid)) { LOG_FATAL(LOGF_SysLinux, "setgid(%d) failed", pw->pw_gid); } if (setuid(pw->pw_uid)) { LOG_FATAL(LOGF_SysLinux, "setuid(%d) failed", pw->pw_uid); } if ((cap = cap_from_text("cap_net_bind_service,cap_sys_time=ep")) == NULL) { LOG_FATAL(LOGF_SysLinux, "cap_from_text() failed"); } if (cap_set_proc(cap)) { LOG_FATAL(LOGF_SysLinux, "cap_set_proc() failed"); } cap_free(cap); DEBUG_LOG(LOGF_SysLinux, "Privileges dropped to user %s", user); } #endif /* ================================================== */ #if defined(HAVE_SCHED_SETSCHEDULER) /* Install SCHED_FIFO real-time scheduler with specified priority */ void SYS_Linux_SetScheduler(int SchedPriority) { int pmax, pmin; struct sched_param sched; if (SchedPriority < 1 || SchedPriority > 99) { LOG_FATAL(LOGF_SysLinux, "Bad scheduler priority: %d", SchedPriority); } else { sched.sched_priority = SchedPriority; pmax = sched_get_priority_max(SCHED_FIFO); pmin = sched_get_priority_min(SCHED_FIFO); if ( SchedPriority > pmax ) { sched.sched_priority = pmax; } else if ( SchedPriority < pmin ) { sched.sched_priority = pmin; } if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) { LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed"); } else { DEBUG_LOG(LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d", sched.sched_priority); } } } #endif /* HAVE_SCHED_SETSCHEDULER */ #if defined(HAVE_MLOCKALL) /* Lock the process into RAM so that it will never be swapped out */ void SYS_Linux_MemLockAll(int LockAll) { struct rlimit rlim; if (LockAll == 1 ) { /* Make sure that we will be able to lock all the memory we need */ /* even after dropping privileges. This does not actually reaerve any memory */ rlim.rlim_max = RLIM_INFINITY; rlim.rlim_cur = RLIM_INFINITY; if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) { LOG(LOGS_ERR, LOGF_SysLinux, "setrlimit() failed: not locking into RAM"); } else { if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) { LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed"); } else { DEBUG_LOG(LOGF_SysLinux, "Successfully locked into RAM"); } } } } #endif /* HAVE_MLOCKALL */ chrony-2.1.1/sys_linux.h0000644000000000000000000000251512542263351013727 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_DropRoot(char *user); extern void SYS_Linux_MemLockAll(int LockAll); extern void SYS_Linux_SetScheduler(int SchedPriority); #endif /* GOT_SYS_LINUX_H */ chrony-2.1.1/sys_macosx.c0000644000000000000000000001657712542263351014072 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) J. Hannken-Illjes 2001 * Copyright (C) Bryan Christianson 2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Driver file for the MacOS X operating system. */ #include "config.h" #ifdef MACOSX #include #include #include #include #include #include #include #include #include "sys_macosx.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; offset_register = 0.0; adjustment_requested = 0.0; current_freq = 0.0; if (gettimeofday(&T0, NULL) < 0) { LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed"); } newadj.tv_sec = 0; newadj.tv_usec = 0; if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); } } /* ================================================== */ static void clock_finalise(void) { /* Nothing to do yet */ } /* ================================================== */ static void start_adjust(void) { struct timeval newadj, oldadj; struct timeval T1; 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, NULL) < 0) { LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed"); } 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 = (int)delta; UTI_NormaliseTimeval(&newadj); /* Add rounding error back onto offset register. */ UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj); if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); } 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 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) { LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed"); } if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed"); } 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, double corr_rate) { stop_adjust(); offset_register += offset; start_adjust(); } /* ================================================== */ /* Positive offset means system clock is fast of true time, therefore step backwards */ static int apply_step_offset(double offset) { struct timeval old_time, new_time, T1; stop_adjust(); if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed"); } UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); if (settimeofday(&new_time, NULL) < 0) { DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed"); return 0; } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; start_adjust(); return 1; } /* ================================================== */ static double set_frequency(double new_freq_ppm) { stop_adjust(); current_freq = new_freq_ppm * 1.0e-6; start_adjust(); return current_freq * 1.0e6; } /* ================================================== */ static double read_frequency(void) { return current_freq * 1.0e6; } /* ================================================== */ static void get_offset_correction(struct timeval *raw, double *corr, double *err) { stop_adjust(); *corr = -offset_register; start_adjust(); if (err) *err = 0.0; } /* ================================================== */ void SYS_MacOSX_Initialise(void) { int result; size_t len; struct clockinfo clockinfo; int mib[2]; mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; len = sizeof(clockinfo); result = sysctl(mib, 2, &clockinfo, &len, NULL, 0); if(result < 0) { LOG_FATAL(LOGF_SysMacOSX, "Cannot read clockinfo"); } kern_tickadj = clockinfo.tickadj; kern_bigadj = clockinfo.tick; clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */, NULL /* set_sync_status */); } /* ================================================== */ void SYS_MacOSX_Finalise(void) { clock_finalise(); } /* ================================================== */ #endif chrony-2.1.1/sys_macosx.h0000644000000000000000000000234012542263351014056 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) J. Hannken-Illjes 2001 * Copyright (C) Bryan Christianson 2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for MacOS X driver */ #ifndef GOT_SYS_MACOSX_H #define GOT_SYS_MACOSX_H void SYS_MacOSX_Initialise(void); void SYS_MacOSX_Finalise(void); #endif chrony-2.1.1/sys_netbsd.c0000644000000000000000000001720312542263351014042 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Driver file for the NetBSD operating system. */ #include "config.h" #ifdef __NetBSD__ #include #include #include #include #include #include #include #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; offset_register = 0.0; adjustment_requested = 0.0; current_freq = 0.0; if (gettimeofday(&T0, NULL) < 0) { LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed"); } newadj.tv_sec = 0; newadj.tv_usec = 0; if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); } } /* ================================================== */ static void clock_finalise(void) { /* Nothing to do yet */ } /* ================================================== */ static void start_adjust(void) { struct timeval newadj, oldadj; struct timeval T1; 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, NULL) < 0) { LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed"); } 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) { LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); } 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 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) { LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed"); } if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed"); } 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, double corr_rate) { stop_adjust(); offset_register += offset; start_adjust(); } /* ================================================== */ /* Positive offset means system clock is fast of true time, therefore step backwards */ static int apply_step_offset(double offset) { struct timeval old_time, new_time, T1; stop_adjust(); if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed"); } UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); if (settimeofday(&new_time, NULL) < 0) { DEBUG_LOG(LOGF_SysNetBSD, "settimeofday() failed"); return 0; } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; start_adjust(); return 1; } /* ================================================== */ static double set_frequency(double new_freq_ppm) { stop_adjust(); current_freq = new_freq_ppm * 1.0e-6; start_adjust(); return current_freq * 1.0e6; } /* ================================================== */ static double read_frequency(void) { return current_freq * 1.0e6; } /* ================================================== */ static void get_offset_correction(struct timeval *raw, double *corr, double *err) { stop_adjust(); *corr = -offset_register; start_adjust(); if (err) *err = 0.0; } /* ================================================== */ void SYS_NetBSD_Initialise(void) { static struct nlist nl[] = { {"_tickadj"}, {"_bigadj"}, {NULL} }; kvm_t *kt; kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); if (!kt) { LOG_FATAL(LOGF_SysNetBSD, "Cannot open kvm"); } if (kvm_nlist(kt, nl) < 0) { LOG_FATAL(LOGF_SysNetBSD, "Cannot read kernel symbols"); } if (kvm_read(kt, nl[0].n_value, (char *)(&kern_tickadj), sizeof(int)) < 0) { LOG_FATAL(LOGF_SysNetBSD, "Cannot read from _tickadj"); } if (kvm_read(kt, nl[1].n_value, (char *)(&kern_bigadj), sizeof(long)) < 0) { /* kernel doesn't have the symbol, use one second instead */ kern_bigadj = 1000000; } kvm_close(kt); clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */, NULL /* set_sync_status */); } /* ================================================== */ void SYS_NetBSD_Finalise(void) { clock_finalise(); } /* ================================================== */ #endif /* __NetBSD__ */ chrony-2.1.1/sys_netbsd.h0000644000000000000000000000227112542263351014046 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 chrony-2.1.1/sys_solaris.c0000644000000000000000000002733512542263351014246 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Driver file for Solaris operating system */ #include "config.h" #ifdef SOLARIS #include #include #include #include #include #include #include #include "sys_solaris.h" #include "localp.h" #include "sched.h" #include "logging.h" #include "util.h" /* ================================================== */ /* This register contains the number of seconds by which the local clock was estimated to be fast of reference time at the epoch when gettimeofday() returned T0 */ static double offset_register; /* This register contains the epoch to which the offset is referenced */ static struct timeval T0; /* This register contains the current estimate of the system frequency, in absolute (NOT ppm) */ static double current_freq; /* This register contains the number of seconds of adjustment that were passed to adjtime last time it was called. */ static double adjustment_requested; /* ================================================== */ /* On Solaris 2.5 & 2.5.1, passing an argument of zero as the new delta to adjtime does not zero out the adjustment - the remaining adjustment is returned as the old delta arg, but the adjustment keeps running. To get round this, we set adjustments of +/-1us when we really want zero. Alternate adjustments are used to avoid a drift from building up. */ static struct timeval zeroes[2] = { {0, 1}, {-1, 999999} }; static int index=0; /* If 1, need to run dosynctodr(). If 0, don't */ static int need_dosynctodr = -1; #define GET_ZERO (zeroes[index^=1]) /* ================================================== */ static void clock_initialise(void) { struct timeval newadj, oldadj; offset_register = 0.0; adjustment_requested = 0.0; current_freq = 0.0; if (gettimeofday(&T0, NULL) < 0) { LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed"); } newadj = GET_ZERO; if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSolaris, "adjtime() failed"); } if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSolaris, "adjtime() failed"); } } /* ================================================== */ static void clock_finalise(void) { /* Nothing to do yet */ } /* ================================================== */ static void start_adjust(void) { struct timeval newadj, oldadj; struct timeval T1; double elapsed, accrued_error; double adjust_required; struct timeval exact_newadj; double rounding_error; double old_adjust_remaining; /* Determine the amount of error built up since the last adjustment */ if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed"); } UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0); accrued_error = elapsed * current_freq; adjust_required = - (accrued_error + offset_register); UTI_DoubleToTimeval(adjust_required, &exact_newadj); /* At this point, we will need to call the adjustment rounding algorithm in the system-specific layer. For now, just assume the adjustment can be applied exactly. */ newadj = exact_newadj; /* Want to *add* rounding error back onto offset register */ UTI_DiffTimevalsToDouble(&rounding_error, &exact_newadj, &newadj); if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSolaris, "adjtime() failed"); } 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 timeval zeroadj, remadj; double adjustment_remaining, adjustment_achieved; double elapsed, elapsed_plus_adjust; zeroadj = GET_ZERO; if (adjtime(&zeroadj, &remadj) < 0) { LOG_FATAL(LOGF_SysSolaris, "adjtime() failed"); } if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed"); } 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, double corr_rate) { stop_adjust(); offset_register += offset; start_adjust(); } /* ================================================== */ /* Positive offset means system clock is fast of true time, therefore step backwards */ static int apply_step_offset(double offset) { struct timeval old_time, new_time, rounded_new_time, T1; double rounding_error; stop_adjust(); if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed"); } UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); /* The settimeofday function (on Solaris 2.5/Sparc20 at least) does not work quite as we would want. The time we want to set is rounded to the nearest second and that time is used. Also, the clock appears to start from that second boundary plus about 4ms. For now we'll tolerate this small error. */ rounded_new_time.tv_usec = 0; if (new_time.tv_usec >= 500000) { rounded_new_time.tv_sec = new_time.tv_sec + 1; } else { rounded_new_time.tv_sec = new_time.tv_sec; } UTI_DiffTimevalsToDouble(&rounding_error, &rounded_new_time, &new_time); if (settimeofday(&new_time, NULL) < 0) { DEBUG_LOG(LOGF_SysSolaris, "settimeofday() failed"); return 0; } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; offset_register += rounding_error; start_adjust(); return 1; } /* ================================================== */ static double set_frequency(double new_freq_ppm) { stop_adjust(); current_freq = new_freq_ppm * 1.0e-6; start_adjust(); return current_freq * 1.0e6; } /* ================================================== */ static double read_frequency(void) { return current_freq * 1.0e6; } /* ================================================== */ static void get_offset_correction(struct timeval *raw, double *corr, double *err) { stop_adjust(); *corr = -offset_register; start_adjust(); if (err) *err = 0.0; } /* ================================================== */ static void immediate_step(void) { } /* ================================================== */ /* Interval in seconds between adjustments to cancel systematic drift */ #define DRIFT_REMOVAL_INTERVAL (4.0) static int drift_removal_running = 0; static SCH_TimeoutID drift_removal_id; /* ================================================== */ /* This is the timer callback routine which is called periodically to invoke a time adjustment to take out the machine's drift. Otherwise, times reported through this software (e.g. by running ntpdate from another machine) show the machine being correct (since they correct for drift build-up), but any program on this machine that reads the system time will be given an erroneous value, the degree of error depending on how long it is since get_offset_correction was last called. */ static void drift_removal_timeout(SCH_ArbitraryArgument not_used) { stop_adjust(); start_adjust(); drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL); } /* ================================================== */ static void check_need_dosynctodr(void) { struct utsname name; int result; int major, minor, veryminor, n_fields; result = uname(&name); if (result < 0) { LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot use uname to detect Solaris version"); need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */ return; } n_fields = sscanf(name.release, "%d.%d.%d\n", &major, &minor, &veryminor); if (n_fields < 2) { LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris version doesn't appear to be of the form X.Y[.Z]"); need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */ return; } if (major != 5) { LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris major version doesn't appear to be 5"); need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */ return; } /* The 'rule of thumb' is that from Solaris 2.6 onwards, dosynctodr() doesn't * need to be called, and in fact it is counter-productive to do so. For * earlier versions, it is required. */ if (minor < 6) { need_dosynctodr = 1; } else { need_dosynctodr = 0; } } /* ================================================== */ static void set_dosynctodr(unsigned long on_off) { static struct nlist nl[] = { {"dosynctodr"}, {NULL} }; kvm_t *kt; unsigned long read_back; assert(on_off == 1 || on_off == 0); kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL); if (!kt) { LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot open kvm to change dosynctodr"); return; } if (kvm_nlist(kt, nl) < 0) { LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read dosynctodr in nlist"); kvm_close(kt); return; } if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) { LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot write to dosynctodr"); kvm_close(kt); return; } if (kvm_read(kt, nl[0].n_value, (char *)(&read_back), sizeof(unsigned long)) < 0) { LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read from dosynctodr"); kvm_close(kt); return; } kvm_close(kt); assert(read_back == on_off); } /* ================================================== */ void SYS_Solaris_Initialise(void) { check_need_dosynctodr(); /* Need to do KVM stuff to turn off dosynctodr. */ clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */, NULL /* set_sync_status */); /* Turn off the kernel switch that keeps the system clock in step with the non-volatile clock */ if (need_dosynctodr) { set_dosynctodr(0); } drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL); drift_removal_running = 1; } /* ================================================== */ void SYS_Solaris_Finalise(void) { if (drift_removal_running) { SCH_RemoveTimeout(drift_removal_id); } clock_finalise(); /* When exiting, we want to return the machine to its 'autonomous' tracking mode */ if (need_dosynctodr) { set_dosynctodr(1); } } /* ================================================== */ #endif /* SOLARIS */ chrony-2.1.1/sys_solaris.h0000644000000000000000000000222512542263351014242 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for Solaris driver */ #ifndef GOT_SYS_SOLARIS_H #define GOT_SYS_SOLARIS_H void SYS_Solaris_Initialise(void); void SYS_Solaris_Finalise(void); #endif chrony-2.1.1/sys_sunos.c0000644000000000000000000002463112542263351013735 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Driver file for the SunOS 4.1.x operating system. */ #include "config.h" #ifdef SUNOS #include #include #include #include #include #include #include #include "sys_sunos.h" #include "localp.h" #include "logging.h" #include "util.h" #include "sched.h" /* ================================================== */ /* This register contains the number of seconds by which the local clock was estimated to be fast of reference time at the epoch when gettimeofday() returned T0 */ static double offset_register; /* This register contains the epoch to which the offset is referenced */ static struct timeval T0; /* This register contains the current estimate of the system frequency, in absolute (NOT ppm) */ static double current_freq; /* This register contains the number of seconds of adjustment that were passed to adjtime last time it was called. */ static double adjustment_requested; /* Eventually, this needs to be a user-defined parameter - e.g. user might want 5 to get much finer resolution like xntpd. We stick with a reasonable number so that slewing can work. This value has to be a factor of 1 million, otherwise the noddy method we use for rounding an adjustment to the nearest multiple of this value won't work!! */ static unsigned long our_tickadj = 100; /* ================================================== */ static void clock_initialise(void) { struct timeval newadj, oldadj; offset_register = 0.0; adjustment_requested = 0.0; current_freq = 0.0; if (gettimeofday(&T0, NULL) < 0) { LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed"); } newadj.tv_sec = 0; newadj.tv_usec = 0; if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSunOS, "adjtime() failed"); } if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSunOS, "adjtime() failed"); } } /* ================================================== */ static void clock_finalise(void) { /* Nothing to do yet */ } /* ================================================== */ static void start_adjust(void) { struct timeval newadj, oldadj; struct timeval T1; double elapsed, accrued_error; double adjust_required; struct timeval exact_newadj; double rounding_error; double old_adjust_remaining; long remainder, multiplier; /* Determine the amount of error built up since the last adjustment */ if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed"); } UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0); accrued_error = elapsed * current_freq; adjust_required = - (accrued_error + offset_register); UTI_DoubleToTimeval(adjust_required, &exact_newadj); /* At this point, we need to round the required adjustment to the closest multiple of _tickadj --- because SunOS can't process other adjustments exactly and will silently discard the residual. Obviously such behaviour can't be tolerated for us. */ newadj = exact_newadj; remainder = newadj.tv_usec % our_tickadj; multiplier = newadj.tv_usec / our_tickadj; if (remainder >= (our_tickadj >> 1)) { newadj.tv_usec = (multiplier + 1) * our_tickadj; } else { newadj.tv_usec = multiplier * our_tickadj; } UTI_NormaliseTimeval(&newadj); /* Want to *add* rounding error back onto offset register. Note that the exact adjustment was the offset register *negated* */ UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj); if (adjtime(&newadj, &oldadj) < 0) { LOG_FATAL(LOGF_SysSunOS, "adjtime() failed"); } 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 timeval zeroadj, remadj; double adjustment_remaining, adjustment_achieved; double gap; double elapsed, elapsed_plus_adjust; zeroadj.tv_sec = 0; zeroadj.tv_usec = 0; if (adjtime(&zeroadj, &remadj) < 0) { LOG_FATAL(LOGF_SysSunOS, "adjtime() failed"); } if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed"); } 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, double corr_rate) { stop_adjust(); offset_register += offset; start_adjust(); } /* ================================================== */ /* Positive offset means system clock is fast of true time, therefore step backwards */ static int apply_step_offset(double offset) { struct timeval old_time, new_time, T1; stop_adjust(); if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed"); } UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); if (settimeofday(&new_time, NULL) < 0) { DEBUG_LOG(LOGF_SysSunOS, "settimeofday() failed"); return 0; } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; start_adjust(); return 1; } /* ================================================== */ static double set_frequency(double new_freq_ppm) { stop_adjust(); current_freq = new_freq_ppm * 1.0e-6; start_adjust(); return current_freq * 1.0e6; } /* ================================================== */ static double read_frequency(void) { return current_freq * 1.0e6; } /* ================================================== */ static void get_offset_correction(struct timeval *raw, double *corr, double *err) { stop_adjust(); *corr = -offset_register; start_adjust(); if (err) *err = 0.0; } /* ================================================== */ static void immediate_step(void) { } /* ================================================== */ /* Interval in seconds between adjustments to cancel systematic drift */ #define DRIFT_REMOVAL_INTERVAL (4.0) static int drift_removal_running = 0; static SCH_TimeoutID drift_removal_id; /* ================================================== */ /* This is the timer callback routine which is called periodically to invoke a time adjustment to take out the machine's drift. Otherwise, times reported through this software (e.g. by running ntpdate from another machine) show the machine being correct (since they correct for drift build-up), but any program on this machine that reads the system time will be given an erroneous value, the degree of error depending on how long it is since get_offset_correction was last called. */ static void drift_removal_timeout(SCH_ArbitraryArgument not_used) { stop_adjust(); start_adjust(); drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL); } /* ================================================== */ static void setup_kernel(unsigned long on_off) { static struct nlist nl[] = { {"_dosynctodr"}, {"_tick"}, {"_tickadj"}, {NULL} }; kvm_t *kt; unsigned long read_back; unsigned long our_tick = 10000; unsigned long default_tickadj = 625; assert(on_off == 1 || on_off == 0); kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL); if (!kt) { LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot open kvm"); return; } if (kvm_nlist(kt, nl) < 0) { LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot read kernel symbols"); kvm_close(kt); return; } if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) { LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _dosynctodr"); kvm_close(kt); return; } if (kvm_write(kt, nl[1].n_value, (char *)(&our_tick), sizeof(unsigned long)) < 0) { LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tick"); kvm_close(kt); return; } if (kvm_write(kt, nl[2].n_value, (char *)(on_off ? &default_tickadj : &our_tickadj), sizeof(unsigned long)) < 0) { LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tickadj"); kvm_close(kt); return; } kvm_close(kt); } /* ================================================== */ void SYS_SunOS_Initialise(void) { /* Need to do KVM stuff to turn off dosynctodr. */ clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */, NULL /* set_sync_status */); /* Turn off the kernel switch that keeps the system clock in step with the non-volatile clock */ setup_kernel(0); drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL); drift_removal_running = 1; } /* ================================================== */ void SYS_SunOS_Finalise(void) { if (drift_removal_running) { SCH_RemoveTimeout(drift_removal_id); } /* Turn dosynctodr back on?? */ clock_finalise(); /* When exiting, we want to return the machine to its 'autonomous' tracking mode */ setup_kernel(1); } /* ================================================== */ #endif /* SUNOS */ chrony-2.1.1/sys_sunos.h0000644000000000000000000000221512542263351013734 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for Solaris driver */ #ifndef GOT_SYS_SUNOS_H #define GOT_SYS_SUNOS_H void SYS_SunOS_Initialise(void); void SYS_SunOS_Finalise(void); #endif chrony-2.1.1/sysincl.h0000644000000000000000000000616212542263351013360 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This file includes all system header files that the software requires. This allows us to isolate system dependencies to this file alone. */ #ifndef GOT_SYSINCL_H #define GOT_SYSINCL_H #if defined (SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(__NetBSD__) || defined (MACOSX) #if !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(MACOSX) #include #endif #include #include #include #include #include #if !defined(__FreeBSD__) && !defined(MACOSX) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INTTYPES_H #include #elif HAVE_STDINT_H #include #else /* Tough */ #endif /* One or other of these to make getsid() visible */ #define __EXTENSIONS__ 1 #define __USE_XOPEN_EXTENDED 1 #include #endif #ifdef FEAT_IPV6 /* For inet_ntop() */ #include #endif #if defined (SOLARIS) || defined(SUNOS) /* Only needed on these platforms, and doesn't exist on some Linux versions. */ #include #endif #if defined (WINNT) /* Designed to work with the GCC from the GNAT-3.10 for Win32 distribution */ #define Win32_Winsock #include #include #if 1 /* Cheat and inline the necessary bits from . We don't include it directly because it redefines some EXXX constants that conflict with (included by ) */ int* _errno(); int* __doserrno(); #define errno (*_errno()) #define _doserrno (*__doserrno()) #define ENOENT 2 #else #include #endif #include #include #include #include #include #include #include #include #include #include #endif #endif /* GOT_SYSINCL_H */ chrony-2.1.1/tempcomp.c0000644000000000000000000001027312542263351013511 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2011, 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Routines implementing temperature compensation. */ #include "config.h" #include "array.h" #include "conf.h" #include "local.h" #include "memory.h" #include "util.h" #include "logging.h" #include "sched.h" #include "tempcomp.h" /* Sanity limit (in ppm) */ #define MAX_COMP 10.0 static SCH_TimeoutID timeout_id; static LOG_FileID logfileid; static char *filename; static double update_interval; static double T0, k0, k1, k2; struct Point { double temp; double comp; }; static ARR_Instance points; static double get_tempcomp(double temp) { unsigned int i; struct Point *p1 = NULL, *p2 = NULL; /* If not configured with points, calculate the compensation from the specified quadratic function */ if (!points) return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2; /* Otherwise interpolate/extrapolate between two nearest points */ for (i = 1; i < ARR_GetSize(points); i++) { p2 = (struct Point *)ARR_GetElement(points, i); if (p2->temp >= temp) break; } p1 = p2 - 1; return (temp - p1->temp) / (p2->temp - p1->temp) * (p2->comp - p1->comp) + p1->comp; } static void read_timeout(void *arg) { FILE *f; double temp, comp; f = fopen(filename, "r"); if (f && fscanf(f, "%lf", &temp) == 1) { comp = get_tempcomp(temp); if (fabs(comp) <= MAX_COMP) { comp = LCL_SetTempComp(comp); DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp); if (logfileid != -1) { struct timeval now; LCL_ReadCookedTime(&now, NULL); LOG_FileWrite(logfileid, "%s %11.4e %11.4e", UTI_TimeToLogForm(now.tv_sec), temp, comp); } } else { LOG(LOGS_WARN, LOGF_TempComp, "Temperature compensation of %.3f ppm exceeds sanity limit of %.1f", comp, MAX_COMP); } } else { LOG(LOGS_WARN, LOGF_TempComp, "Could not read temperature from %s", filename); } if (f) fclose(f); timeout_id = SCH_AddTimeoutByDelay(update_interval, read_timeout, NULL); } static void read_points(const char *filename) { FILE *f; char line[256]; struct Point *p; f = fopen(filename, "r"); if (!f) { LOG_FATAL(LOGF_TempComp, "Could not open tempcomp point file %s", filename); return; } points = ARR_CreateInstance(sizeof (struct Point)); while (fgets(line, sizeof (line), f)) { p = (struct Point *)ARR_GetNewElement(points); if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) { LOG_FATAL(LOGF_Configure, "Could not read tempcomp point from %s", filename); break; } } fclose(f); if (ARR_GetSize(points) < 2) LOG_FATAL(LOGF_Configure, "Not enough points in %s", filename); } void TMC_Initialise(void) { char *point_file; CNF_GetTempComp(&filename, &update_interval, &point_file, &T0, &k0, &k1, &k2); if (filename == NULL) return; if (update_interval <= 0.0) update_interval = 1.0; if (point_file) read_points(point_file); logfileid = CNF_GetLogTempComp() ? LOG_FileOpen("tempcomp", " Date (UTC) Time Temp. Comp.") : -1; read_timeout(NULL); } void TMC_Finalise(void) { if (filename == NULL) return; if (points) ARR_DestroyInstance(points); SCH_RemoveTimeout(timeout_id); } chrony-2.1.1/tempcomp.h0000644000000000000000000000213312542263351013512 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Miroslav Lichvar 2011 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Header file for temperature compensation. */ extern void TMC_Initialise(void); extern void TMC_Finalise(void); chrony-2.1.1/test/0000755000000000000000000000000012542263351012475 5ustar rootrootchrony-2.1.1/test/compilation/0000755000000000000000000000000012542263351015013 5ustar rootrootchrony-2.1.1/test/compilation/001-features0000755000000000000000000000060212542263351017053 0ustar rootroot#!/bin/sh # Try to compile chrony in various combinations of disabled features cd ../.. for opts in \ "--disable-asyncdns" \ "--disable-rtc" \ "--disable-cmdmon" \ "--disable-ntp" \ "--disable-refclock" \ "--disable-cmdmon --disable-ntp" \ "--disable-cmdmon --disable-refclock" \ "--disable-cmdmon --disable-ntp --disable-refclock" do ./configure $opts make || exit 1 done chrony-2.1.1/test/simulation/0000755000000000000000000000000012542263351014661 5ustar rootrootchrony-2.1.1/test/simulation/001-defaults0000755000000000000000000000034012542263351016711 0ustar rootroot#!/bin/bash . test.common test_start "default test settings" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/002-largenetwork0000755000000000000000000000044712542263351017617 0ustar rootroot#!/bin/bash . test.common test_start "large network" time_rms_limit=5e-4 server_strata=3 servers=4 clients=5 client_start=2000 min_sync_time=2100 max_sync_time=2300 run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/003-largefreqoffset0000755000000000000000000000064012542263351020266 0ustar rootroot#!/bin/bash . test.common test_start "large frequency offset" max_sync_time=1000 for freq_offset in -5e-2 -5e-3 5e-3 5e-2; do # Adjust offset so it's close to 0 on first clock update time_offset=$(awk "BEGIN {print -($freq_offset * 130)}") run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/004-largetimeoffset0000755000000000000000000000045512542263351020274 0ustar rootroot#!/bin/bash . test.common test_start "large time offset" min_sync_time=1300 max_sync_time=1400 for time_offset in -1e2 1e2; do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/005-externalstep0000755000000000000000000000175312542263351017635 0ustar rootroot#!/bin/bash . test.common test_start "external time step" min_sync_time=1500 max_sync_time=1550 for step in -1e2 1e2; do # Make one step in 150th second client_step="(* $step (equal 0.1 (sum 1.0) 150))" run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail done min_sync_time=5120 max_sync_time=6200 client_conf="makestep 1 -1" for step in -1e8 -1e5 1e5 1e8; do # Make one step in 5000th second client_step="(* $step (equal 0.1 (sum 1.0) 5000))" run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail done min_sync_time=$default_min_sync_time max_sync_time=$default_max_sync_time time_max_limit=2e4 time_rms_limit=8e3 for step in -1e4 1e4; do # Make a step every 500 seconds client_step="(* $step (equal 0.1 (% (sum 1.0) 500) 0))" run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/006-largejitter0000755000000000000000000000050512542263351017426 0ustar rootroot#!/bin/bash . test.common test_start "large jitter" time_offset=1e0 jitter=1e-1 time_max_limit=5e-1 freq_max_limit=2e-1 time_rms_limit=1e-1 freq_rms_limit=5e-3 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/007-largewander0000755000000000000000000000046512542263351017413 0ustar rootroot#!/bin/bash . test.common test_start "large wander" wander=1e-7 time_max_limit=5e-3 freq_max_limit=5e-3 time_rms_limit=1e-3 freq_rms_limit=1e-4 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/008-ntpera0000755000000000000000000000212612542263351016406 0ustar rootroot#!/bin/bash . test.common test_start "NTP eras" # Assume NTP_ERA_SPLIT is between years 1960 and 1990 # Set date to 500 seconds before NTP second overflows, this should # work correctly with both 32-bit and 64-bit time_t export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s') run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail # The following tests need 64-bit time_t grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip for year in 1990 2090; do export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s') run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done for year in 1950 2130; do export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s') run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail # This check is expected to fail check_sync && test_fail done test_pass chrony-2.1.1/test/simulation/009-sourceselection0000755000000000000000000000155212542263351020326 0ustar rootroot#!/bin/bash . test.common test_start "source selection" # Falsetickers should be detected if their number is less than half of all base_delay=1e-3 servers=5 for falsetickers in 1 2; do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done for falsetickers in 3 4; do run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail # These check are expected to fail check_source_selection && test_fail check_sync && test_fail done # Sources with large asymmetric delay should be excluded servers=3 falsetickers=0 base_delay="(+ 1e-3 (equal 0.1 to 2) (equal 0.1 to 3))" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/101-poll0000755000000000000000000000104412542263351016053 0ustar rootroot#!/bin/bash . test.common test_start "minpoll/maxpoll options" wander=0.0 jitter=1e-6 time_max_limit=1e-5 freq_max_limit=1e-5 time_rms_limit=5e-6 freq_rms_limit=5e-6 client_conf="makestep 1e-2 1" for poll in $(seq 2 14); do client_server_options="minpoll $poll maxpoll $poll" limit=$[2**$poll * 10] min_sync_time=$[2**$poll * 2] max_sync_time=$[2**$poll * 21 / 10 + 1] run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/102-iburst0000755000000000000000000000057012542263351016421 0ustar rootroot#!/bin/bash . test.common test_start "iburst option" freq_offset=1e-4 client_conf="makestep 1e-2 1 driftfile tmp/drift" client_server_options="iburst" min_sync_time=4 max_sync_time=6 echo "100 1.0" > tmp/drift run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/103-initstepslew0000755000000000000000000000103212542263351017636 0ustar rootroot#!/bin/bash . test.common test_start "initstepslew directive" freq_offset=0.0 wander=0.0 time_rms_limit=1e-3 limit=100 client_conf="initstepslew 5 192.168.123.1" min_sync_time=6 max_sync_time=35 for time_offset in -2.0 -0.2 0.2 2.0; do run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail done min_sync_time=5 max_sync_time=5 for time_offset in -1e8 -1e2 1e2 1e8; do run_test || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/104-driftfile0000755000000000000000000000072012542263351017060 0ustar rootroot#!/bin/bash . test.common test_start "driftfile directive" servers=0 time_offset=0.0 wander=0.0 limit=10 freq_max_limit=1e-9 min_sync_time=1 max_sync_time=1 client_conf="driftfile tmp/drift" for freq_offset in -5e-2 -5e-4 -5e-6 5e-6 5e-4 5e-2; do awk "BEGIN {printf \"%.9e 1\", 1e6 - 1 / (1 + $freq_offset) * 1e6}" > tmp/drift run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/105-ntpauth0000755000000000000000000000205612542263351016600 0ustar rootroot#!/bin/bash . test.common test_start "NTP authentication" server_conf="keyfile tmp/keys" client_conf="keyfile tmp/keys" cat > tmp/keys <<-EOF 1 $(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24) 2 ASCII:$(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24) 3 MD5 ASCII:$(tr -c -d 'a-zA-Z' < /dev/urandom 2> /dev/null | head -c 24) 4 MD5 HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | head -c 32) EOF for key in 1 2 3 4; do client_server_options="key $key" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done server_conf="" run_test || test_fail check_chronyd_exit || test_fail # This check must fail as the server doesn't know the key check_sync && test_fail check_packet_interval || test_fail server_conf="keyfile tmp/keys" client_conf="" run_test || test_fail check_chronyd_exit || test_fail # This check must fail as the client doesn't know the key check_sync && test_fail check_packet_interval || test_fail test_pass chrony-2.1.1/test/simulation/106-refclock0000755000000000000000000000044012542263351016701 0ustar rootroot#!/bin/bash . test.common test_start "SHM refclock" servers=0 limit=1000 refclock_jitter=$jitter min_sync_time=45 max_sync_time=70 client_conf="refclock SHM 0" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/107-allowdeny0000755000000000000000000000150012542263351017106 0ustar rootroot#!/bin/bash . test.common test_start "allow/deny directives" limit=500 # Note that start_client in clknetsim.bash always adds allow to the config for server_conf in \ "deny" \ "deny all" \ "deny 192.168.0.0/16" \ "deny 192.168.123" \ "deny 192.168.123.2" \ "deny all allow 192.168.124.0/24" do run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail # These checks are expected to fail check_source_selection && test_fail check_sync && test_fail done for server_conf in \ "deny all allow" \ "deny all allow all" \ "deny all allow 192.168.123" \ "deny all allow 192.168.123/24" \ "deny 192.168.124.0/24" do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/108-peer0000755000000000000000000000132712542263351016053 0ustar rootroot#!/bin/bash . test.common test_start "NTP peers" # Allow and drop packets to the server in 1000 second intervals, so only one # client has access to it and the other is forced to switch to the peer. base_delay=$(cat <<-EOF | tr -d '\n' (+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1) (equal 0.1 (min (% time 2000) 1000) 1000)) (* -1 (equal 0.1 from 3) (equal 0.1 to 1) (equal 0.1 (max (% time 2000) 1000) 1000))) EOF ) clients=2 peers=2 max_sync_time=1000 client_server_options="minpoll 6 maxpoll 6" client_peer_options="minpoll 6 maxpoll 6" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/109-makestep0000755000000000000000000000143212542263351016727 0ustar rootroot#!/bin/bash . test.common test_start "makestep directive" client_conf="makestep 0 -1 corrtimeratio 1e10" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail limit=200 jitter=1e-5 client_conf="makestep 2 1" min_sync_time=130 max_sync_time=150 for time_offset in -1.0 -0.1 0.1 1.0; do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done min_sync_time=120 max_sync_time=140 for time_offset in -1e8 -1e2 1e2 1e8; do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/110-chronyc0000755000000000000000000000241212542263351016552 0ustar rootroot#!/bin/bash . test.common test_start "chronyc" chronyc_conf="tracking sources sourcestats" run_test || test_fail check_chronyd_exit || test_fail check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\) Stratum : 2 Ref time \(UTC\) : Fri Jan 1 00:1.:.. 2010 System time : 0\.0000..... seconds (slow|fast) of NTP time Last offset : [+-]0\.000...... seconds RMS offset : 0\.000...... seconds Frequency : (99|100)\.... ppm fast Residual freq : [+-][0-9]\.... ppm Skew : [0-9]\.... ppm Root delay : 0\.000... seconds Root dispersion : 0\.000... seconds Update interval : [0-9]+\.. seconds Leap status : Normal 210 Number of sources = 1 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== \^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s 210 Number of sources = 1 Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev ============================================================================== 192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s$" \ || test_fail test_pass chrony-2.1.1/test/simulation/111-knownclient0000755000000000000000000000051612542263351017444 0ustar rootroot#!/bin/bash . test.common test_start "reply to client configured as server" server_conf="server 192.168.123.2 noselect acquisitionport 123" client_conf="acquisitionport 123" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_port || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/112-port0000755000000000000000000000231012542263351016070 0ustar rootroot#!/bin/bash . test.common test_start "port and acquisitionport directives" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail # This check is expected to fail check_packet_port && test_fail client_conf="acquisitionport 123" run_test || test_fail check_chronyd_exit || test_fail check_packet_port || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail client_conf="" for server_conf in \ "port 0" \ "acquisitionport 123 port 0" do run_test || test_fail check_chronyd_exit || test_fail check_packet_port || test_fail check_packet_interval || test_fail # These checks are expected to fail check_source_selection && test_fail check_sync && test_fail done server_conf="port 124 acquisitionport 123" client_server_options="port 124" for client_conf in \ "acquisitionport 0" \ "acquisitionport 123" \ "acquisitionport 124" do run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail # This check is expected to fail check_packet_port && test_fail done test_pass chrony-2.1.1/test/simulation/113-leapsecond0000755000000000000000000000205012542263351017223 0ustar rootroot#!/bin/bash . test.common test_start "leap second" export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s') leap=$[2 * 24 * 3600] limit=$[4 * 24 * 3600] server_conf="refclock SHM 0 dpoll 10 poll 10 leapsectz right/UTC" refclock_jitter=1e-9 refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))" for leapmode in system step slew; do client_conf="leapsecmode $leapmode" if [ $leapmode = slew ]; then max_sync_time=$[$leap + 12] else max_sync_time=$[$leap] fi run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done for smoothmode in "" "leaponly"; do server_conf="refclock SHM 0 dpoll 10 poll 10 leapsectz right/UTC leapsecmode slew smoothtime 400 0.001 $smoothmode" client_conf="leapsecmode system" min_sync_time=230000 max_sync_time=240000 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail done test_pass chrony-2.1.1/test/simulation/114-presend0000755000000000000000000000071712542263351016557 0ustar rootroot#!/bin/bash . test.common test_start "presend option" min_sync_time=140 max_sync_time=260 client_server_options="presend 6 maxdelay 16" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail base_delay=5 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/115-cmdmontime0000755000000000000000000000103212542263351017243 0ustar rootroot#!/bin/bash . test.common test_start "cmdmon timestamps" # The following tests need 64-bit time_t grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip limit=2 client_server_options="noselect" client_conf="local stratum 1" chronyc_start="0.5" chronyc_conf="tracking" for year in `seq 1850 100 2300`; do date="Jan 1 00:00:00 $year" export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s') run_test || test_fail check_chronyd_exit || test_fail check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail done test_pass chrony-2.1.1/test/simulation/116-minsources0000755000000000000000000000067712542263351017315 0ustar rootroot#!/bin/bash . test.common test_start "minsources directive" client_conf="minsources 3" run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail # These check are expected to fail check_source_selection && test_fail check_sync && test_fail servers=3 run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_source_selection || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/117-fallbackdrift0000755000000000000000000000067212542263351017712 0ustar rootroot#!/bin/bash . test.common test_start "fallback drift" limit=100000 wander=0.0 jitter=1e-6 time_offset=10 freq_offset="(* 1e-4 (sine 1000))" base_delay="(* -1.0 (equal 0.1 (min time 4250) 4250))" client_server_options="minpoll 4 maxpoll 4" client_conf="fallbackdrift 6 10" time_max_limit=1e0 time_rms_limit=1e0 freq_max_limit=5e-4 freq_rms_limit=5e-4 run_test || test_fail check_chronyd_exit || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/118-maxdelay0000755000000000000000000000112512542263351016721 0ustar rootroot#!/bin/bash . test.common test_start "maxdelay options" max_sync_time=2000 base_delay=1e-5 jitter=1e-5 wander=0.0 freq_offset="(sum 1e-10)" time_rms_limit=2e-4 client_server_options="maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevratio 2.0" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail for client_server_options in "maxdelay 2e-5" \ "maxdelayratio 1.001"; do run_test || test_fail check_chronyd_exit || test_fail check_packet_interval || test_fail check_sync && test_fail done test_pass chrony-2.1.1/test/simulation/119-smoothtime0000755000000000000000000000302212542263351017304 0ustar rootroot#!/bin/bash . test.common test_start "smoothtime option" server_strata=2 server_conf="smoothtime 400 0.001" min_sync_time=250 max_sync_time=1000 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail limit=10000 refclock_jitter=1e-4 refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))" server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))" server_strata=1 server_conf="refclock SHM 0 dpoll 4 poll 6 smoothtime 2000 1" time_offset=-10 client_server_options="minpoll 6 maxpoll 6" client_conf="corrtimeratio 100" min_sync_time=8000 max_sync_time=8500 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail min_sync_time=$default_min_sync_time max_sync_time=$default_max_sync_time time_max_limit=11 time_rms_limit=11 freq_max_limit=1e-2 freq_rms_limit=2e-3 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_sync || test_fail refclock_jitter=1e-9 refclock_offset="(* 1e-1 (triangle 1000) (+ -1.0 (pulse 1000 10000)))" server_step="" server_conf="refclock SHM 0 dpoll 4 poll 6 minsamples 4 maxsamples 4 smoothtime 1e4 1e-6" client_server_options="minpoll 4 maxpoll 4" time_offset=0.1 jitter=1e-6 wander=0.0 min_sync_time=30 max_sync_time=40 time_max_limit=1e-5 time_rms_limit=5e-6 freq_max_limit=1e-6 freq_rms_limit=1e-7 run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/201-freqaccumulation0000755000000000000000000000116712542263351020456 0ustar rootroot#!/bin/bash . test.common # Test fix in commit 60d0fa299307076143da94d36deb7b908fa9bdb7 test_start "frequency accumulation" time_offset=100.0 jitter=1e-6 base_delay=1e-6 wander=0.0 limit=180 time_max_limit=1e-5 freq_max_limit=1e-7 time_rms_limit=1e-5 freq_rms_limit=1e-7 min_sync_time=120 max_sync_time=140 client_server_options="minpoll 6 maxpoll 6" client_conf="driftfile tmp/drift makestep 1 1" for freq_offset in -5e-2 -5e-4 5e-4 5e-2; do for drift in -1e+4 -1e+2 1e+2 1e+4; do echo "$drift 100000" > tmp/drift run_test || test_fail check_chronyd_exit || test_fail check_sync || test_fail done done test_pass chrony-2.1.1/test/simulation/202-prefer0000755000000000000000000000057612542263351016403 0ustar rootroot#!/bin/bash . test.common # Test fix in commit 4253075a97141edfa62043ab71bd0673587e6629 test_start "prefer option" servers=3 client_server_conf=" server 192.168.123.1 server 192.168.123.2 server 192.168.123.3 prefer" run_test || test_fail check_chronyd_exit || test_fail check_source_selection || test_fail check_packet_interval || test_fail check_sync || test_fail test_pass chrony-2.1.1/test/simulation/README0000644000000000000000000000065512542263351015547 0ustar rootrootThis is a collection of simulation tests. They use clknetsim to simulate multiple systems connected in a network. It's available at https://github.com/mlichvar/clknetsim If this directory doesn't have a clknetsim subdirectory, a known working revision will be downloaded and compiled automatically. Currently it runs only on Linux. The tests are written in bash and they can be run directly. The ./run script runs all tests. chrony-2.1.1/test/simulation/run0000755000000000000000000000111512542263351015411 0ustar rootroot#!/bin/bash . test.common passed=() failed=() skipped=() [ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_]) for test in "${tests[@]}"; do echo "$test ($[${#passed[@]} + ${#failed[@]} + 1]/${#tests[@]})" ./$test case $? in 0) passed=(${passed[@]} $test);; 9) skipped=(${skipped[@]} $test);; *) failed=(${failed[@]} $test);; esac echo done echo "SUMMARY:" echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]" echo " PASSED ${#passed[@]}" echo " FAILED ${#failed[@]} (${failed[@]})" echo " SKIPPED ${#skipped[@]} (${skipped[@]})" [ ${#failed[@]} -eq 0 ] chrony-2.1.1/test/simulation/test.common0000644000000000000000000002670212542263351017061 0ustar rootroot# Copyright (C) 2013-2014 Miroslav Lichvar # # 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, see . export LC_ALL=C export PATH=../../:$PATH export CLKNETSIM_PATH=clknetsim # Known working clknetsim revision clknetsim_revision=1e56224dee1db69c0027e9bd63c2a202d4765959 clknetsim_url=https://github.com/mlichvar/clknetsim/archive/$clknetsim_revision.tar.gz # Only Linux is supported if [ "$(uname -s)" != Linux ]; then echo "Simulation tests supported only on Linux" exit 3 fi # Try to download clknetsim if not found if [ ! -e $CLKNETSIM_PATH ]; then curl -L "$clknetsim_url" | tar xz || exit 3 ln -s clknetsim-$clknetsim_revision clknetsim || exit 3 fi # Try to build clknetsim if not built if [ ! -x $CLKNETSIM_PATH/clknetsim -o ! -e $CLKNETSIM_PATH/clknetsim.so ]; then make -C clknetsim || exit 3 fi . $CLKNETSIM_PATH/clknetsim.bash # Default test testings default_limit=10000 default_time_offset=1e-1 default_freq_offset=1e-4 default_base_delay=1e-4 default_jitter=1e-4 default_wander=1e-9 default_refclock_jitter="" default_refclock_offset=0.0 default_update_interval=0 default_shift_pll=2 default_server_strata=1 default_servers=1 default_clients=1 default_peers=0 default_falsetickers=0 default_server_start=0.0 default_client_start=0.0 default_chronyc_start=1000.0 default_server_step="" default_client_step="" default_client_server_conf="" default_server_server_options="" default_client_server_options="" default_server_peer_options="" default_client_peer_options="" default_server_conf="" default_client_conf="" default_chronyc_conf="" default_chronyd_options="" default_time_max_limit=1e-3 default_freq_max_limit=5e-4 default_time_rms_limit=3e-4 default_freq_rms_limit=1e-5 default_min_sync_time=120 default_max_sync_time=210 # Initialize test settings from their defaults for defopt in $(declare | grep '^default_'); do defoptname=${defopt%%=*} optname=${defoptname#default_} eval "[ -z \"\${$optname:+a}\" ] && $optname=\"\$$defoptname\"" done test_start() { rm -f tmp/* echo "Testing $@:" } test_pass() { echo "PASS" exit 0 } test_fail() { echo "FAIL" exit 1 } test_skip() { echo "SKIP" exit 9 } test_ok() { pad_line echo -e "\tOK" return 0 } test_bad() { pad_line echo -e "\tBAD" return 1 } test_error() { pad_line echo -e "\tERROR" return 1 } msg_length=0 pad_line() { local line_length=56 [ $msg_length -lt $line_length ] && \ printf "%$[$line_length - $msg_length]s" "" msg_length=0 } # Print aligned message test_message() { local level=$1 eol=$2 shift 2 local msg="$*" while [ $level -gt 0 ]; do echo -n " " level=$[$level - 1] msg_length=$[$msg_length + 2] done echo -n "$msg" msg_length=$[$msg_length + ${#msg}] if [ $eol -ne 0 ]; then echo msg_length=0 fi } get_wander_expr() { local scaled_wander scaled_wander=$(awk "BEGIN {print $wander / \ sqrt($update_interval < 0 ? 2^-($update_interval) : 1)}") echo "(+ $freq_offset (sum (* $scaled_wander (normal))))" } get_delay_expr() { echo "(+ $base_delay (* $jitter (exponential)))" } get_refclock_expr() { echo "(+ $refclock_offset (* $refclock_jitter (normal)))" } get_chronyd_nodes() { echo $[$servers * $server_strata + $clients] } get_chronyd_conf() { local i stratum=$1 peer=$2 if [ $stratum -eq 1 ]; then echo "local stratum 1" echo "$server_conf" elif [ $stratum -le $server_strata ]; then for i in $(seq 1 $servers); do echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $server_server_options" done for i in $(seq 1 $peers); do [ $i -eq $peer -o $i -gt $servers ] && continue echo "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $server_peer_options" done echo "$server_conf" else if [ -n "$client_server_conf" ]; then echo "$client_server_conf" else for i in $(seq 1 $servers); do echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $client_server_options" done fi for i in $(seq 1 $peers); do [ $i -eq $peer -o $i -gt $clients ] && continue echo "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $client_peer_options" done echo "$client_conf" fi } # Check if the clock was well synchronized check_sync() { local i sync_time max_time_error max_freq_error ret=0 local rms_time_error rms_freq_error test_message 2 1 "checking clock sync time, max/rms time/freq error:" for i in $(seq 1 $(get_chronyd_nodes)); do [ $i -gt $[$servers * $server_strata] ] || continue sync_time=$(find_sync tmp/log.offset tmp/log.freq $i \ $time_max_limit $freq_max_limit 1.0) max_time_error=$(get_stat 'Maximum absolute offset' $i) max_freq_error=$(get_stat 'Maximum absolute frequency' $i) rms_time_error=$(get_stat 'RMS offset' $i) rms_freq_error=$(get_stat 'RMS frequency' $i) test_message 3 0 "node $i: $sync_time $(printf '%.2e %.2e %.2e %.2e' \ $max_time_error $max_freq_error $rms_time_error $rms_freq_error)" check_stat $sync_time $min_sync_time $max_sync_time && \ check_stat $max_time_error 0.0 $time_max_limit && \ check_stat $max_freq_error 0.0 $freq_max_limit && \ check_stat $rms_time_error 0.0 $time_rms_limit && \ check_stat $rms_freq_error 0.0 $freq_rms_limit && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Check if chronyd exited properly check_chronyd_exit() { local i ret=0 test_message 2 1 "checking chronyd exit:" for i in $(seq 1 $(get_chronyd_nodes)); do test_message 3 0 "node $i:" tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Check for problems in source selection check_source_selection() { local i ret=0 test_message 2 1 "checking source selection:" for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do test_message 3 0 "node $i:" ! grep -q 'no majority\|no selectable sources' tmp/log.$i && \ grep -q 'Selected source' tmp/log.$i && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Check if incoming and outgoing packet intervals are sane check_packet_interval() { local i ret=0 mean_in_interval mean_out_interval min_in_interval min_out_interval test_message 2 1 "checking mean/min incoming/outgoing packet interval:" for i in $(seq 1 $(get_chronyd_nodes)); do mean_in_interval=$(get_stat 'Mean incoming packet interval' $i) mean_out_interval=$(get_stat 'Mean outgoing packet interval' $i) min_in_interval=$(get_stat 'Minimum incoming packet interval' $i) min_out_interval=$(get_stat 'Minimum outgoing packet interval' $i) test_message 3 0 "node $i: $(printf '%.2e %.2e %.2e %.2e' \ $mean_in_interval $mean_out_interval $min_in_interval $min_out_interval)" # Check that the mean intervals are non-zero and shorter than # limit, incoming is not longer than outgoing for stratum 1 # servers, outgoing is not longer than incoming for clients, # and the minimum outgoing interval is not shorter than the NTP # sampling separation or iburst interval for clients nodes=$[$servers * $server_strata + $clients] check_stat $mean_in_interval 0.1 inf && \ check_stat $mean_out_interval 0.1 inf && \ ([ $i -gt $servers ] || \ check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \ ([ $i -le $[$servers * $server_strata] ] || \ check_stat $mean_out_interval 0.0 $mean_in_interval 10*$jitter) && \ ([ $i -le $[$servers * $server_strata] ] || \ check_stat $min_out_interval \ $([ $servers -gt 1 ] && echo 0.18 || echo 1.8) inf) && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Compare chronyc output with specified pattern check_chronyc_output() { local i ret=0 pattern=$1 test_message 2 1 "checking chronyc output:" for i in $(seq $[$(get_chronyd_nodes) + 1] $[$(get_chronyd_nodes) + $clients]); do test_message 3 0 "node $i:" [[ "$(cat tmp/log.$i)" =~ $pattern ]] && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Check if only NTP port (123) was used check_packet_port() { local i ret=0 port=123 test_message 2 1 "checking port numbers in packet log:" for i in $(seq 1 $(get_chronyd_nodes)); do test_message 3 0 "node $i:" grep -E -q " $port [0-9]+\$" tmp/log.packets && \ ! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \ grep -E -q -v " $port [0-9]+\$" && \ test_ok || test_bad [ $? -eq 0 ] || ret=1 done return $ret } # Print test settings which differ from default value print_nondefaults() { local defopt defoptname optname test_message 2 1 "non-default settings:" declare | grep '^default_*' | while read defopt; do defoptname=${defopt%%=*} optname=${defoptname#default_} eval "[ \"\$$optname\" = \"\$$defoptname\" ]" || \ test_message 3 1 $(eval "echo $optname=\$$optname") done } run_simulation() { local nodes=$1 test_message 2 0 "running simulation:" start_server $nodes \ -o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \ -R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \ -r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \ -l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error } run_test() { local i j n stratum node nodes step start freq offset conf test_message 1 1 "network with $servers*$server_strata servers and $clients clients:" print_nondefaults nodes=$(get_chronyd_nodes) [ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients] for i in $(seq 1 $nodes); do echo "node${i}_shift_pll = $shift_pll" for j in $(seq 1 $nodes); do [ $i -eq $j ] && continue echo "node${i}_delay${j} = $(get_delay_expr)" echo "node${j}_delay${i} = $(get_delay_expr)" done done > tmp/conf node=1 for stratum in $(seq 1 $[$server_strata + 1]); do [ $stratum -le $server_strata ] && n=$servers || n=$clients for i in $(seq 1 $n); do test_message 2 0 "starting node $node:" if [ $stratum -eq 1 ]; then step=$server_step start=$server_start freq="" [ $i -le $falsetickers ] && offset=$i.0 || offset=0.0 elif [ $stratum -le $server_strata ]; then step=$server_step start=$server_start freq=$(get_wander_expr) offset=0.0 else step=$client_step start=$client_start freq=$(get_wander_expr) offset=$time_offset fi conf=$(get_chronyd_conf $stratum $i $n) [ -z "$freq" ] || echo "node${node}_freq = $freq" >> tmp/conf [ -z "$step" ] || echo "node${node}_step = $step" >> tmp/conf [ -z "$refclock_jitter" ] || \ echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf echo "node${node}_offset = $offset" >> tmp/conf echo "node${node}_start = $start" >> tmp/conf start_client $node chronyd "$conf" "" "$chronyd_options" && \ test_ok || test_error [ $? -ne 0 ] && return 1 node=$[$node + 1] done done for i in $(seq 1 $[$nodes - $node + 1]); do test_message 2 0 "starting node $node:" echo "node${node}_start = $chronyc_start" >> tmp/conf start_client $node chronyc "$chronyc_conf" "" \ "-n -h 192.168.123.$[$node - $clients]" && \ test_ok || test_error [ $? -ne 0 ] && return 1 node=$[$node + 1] done run_simulation $nodes } chrony-2.1.1/util.c0000644000000000000000000004773412542263351012656 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009, 2012-2015 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Various utility functions */ #include "config.h" #include "sysincl.h" #include "util.h" #include "hash.h" /* ================================================== */ INLINE_STATIC void UTI_TimevalToDouble(struct timeval *a, double *b) { *b = (double)(a->tv_sec) + 1.0e-6 * (double)(a->tv_usec); } /* ================================================== */ INLINE_STATIC void UTI_DoubleToTimeval(double a, struct timeval *b) { long int_part, frac_part; int_part = (long)(a); frac_part = (long)(0.5 + 1.0e6 * (a - (double)(int_part))); b->tv_sec = int_part; b->tv_usec = frac_part; UTI_NormaliseTimeval(b); } /* ================================================== */ INLINE_STATIC int UTI_CompareTimevals(struct timeval *a, struct timeval *b) { if (a->tv_sec < b->tv_sec) { return -1; } else if (a->tv_sec > b->tv_sec) { return +1; } else { if (a->tv_usec < b->tv_usec) { return -1; } else if (a->tv_usec > b->tv_usec) { return +1; } else { return 0; } } } /* ================================================== */ INLINE_STATIC void UTI_NormaliseTimeval(struct timeval *x) { /* Reduce tv_usec to within +-1000000 of zero. JGH */ if ((x->tv_usec >= 1000000) || (x->tv_usec <= -1000000)) { x->tv_sec += x->tv_usec/1000000; x->tv_usec = x->tv_usec%1000000; } /* Make tv_usec positive. JGH */ if (x->tv_usec < 0) { --x->tv_sec; x->tv_usec += 1000000; } } /* ================================================== */ INLINE_STATIC void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b) { result->tv_sec = a->tv_sec - b->tv_sec; result->tv_usec = a->tv_usec - b->tv_usec; /* Correct microseconds field to bring it into the range (0,1000000) */ UTI_NormaliseTimeval(result); /* JGH */ } /* ================================================== */ /* Calculate result = a - b and return as a double */ INLINE_STATIC void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b) { *result = (double)(a->tv_sec - b->tv_sec) + (double)(a->tv_usec - b->tv_usec) * 1.0e-6; } /* ================================================== */ INLINE_STATIC void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end) { long int_part, frac_part; /* Don't want to do this by using (long)(1000000 * increment), since that will only cope with increments up to +/- 2148 seconds, which is too marginal here. */ int_part = (long) increment; increment = (increment - int_part) * 1.0e6; frac_part = (long) (increment > 0.0 ? increment + 0.5 : increment - 0.5); end->tv_sec = int_part + start->tv_sec; end->tv_usec = frac_part + start->tv_usec; UTI_NormaliseTimeval(end); } /* ================================================== */ /* Calculate the average and difference (as a double) of two timevals */ INLINE_STATIC void UTI_AverageDiffTimevals (struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff) { struct timeval tvdiff; struct timeval tvhalf; UTI_DiffTimevals(&tvdiff, later, earlier); *diff = (double)tvdiff.tv_sec + 1.0e-6 * (double)tvdiff.tv_usec; if (*diff < 0.0) { /* Either there's a bug elsewhere causing 'earlier' and 'later' to be backwards, or something wierd has happened. Maybe when we change the frequency on Linux? */ /* Assume the required behaviour is to treat it as zero */ *diff = 0.0; } tvhalf.tv_sec = tvdiff.tv_sec / 2; tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2) * 500000; /* JGH */ average->tv_sec = earlier->tv_sec + tvhalf.tv_sec; average->tv_usec = earlier->tv_usec + tvhalf.tv_usec; /* Bring into range */ UTI_NormaliseTimeval(average); } /* ================================================== */ void UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b, struct timeval *c, struct timeval *result) { double diff; UTI_DiffTimevalsToDouble(&diff, a, b); UTI_AddDoubleToTimeval(c, diff, result); } /* ================================================== */ #define POOL_ENTRIES 16 #define BUFFER_LENGTH 64 static char buffer_pool[POOL_ENTRIES][BUFFER_LENGTH]; static int pool_ptr = 0; #define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)]) /* ================================================== */ /* Convert a timeval into a temporary string, largely for diagnostic display */ char * UTI_TimevalToString(struct timeval *tv) { char *result; result = NEXT_BUFFER; #ifdef HAVE_LONG_TIME_T snprintf(result, BUFFER_LENGTH, "%"PRId64".%06lu", (int64_t)tv->tv_sec, (unsigned long)tv->tv_usec); #else snprintf(result, BUFFER_LENGTH, "%ld.%06lu", (long)tv->tv_sec, (unsigned long)tv->tv_usec); #endif return result; } /* ================================================== */ /* Convert an NTP timestamp into a temporary string, largely for diagnostic display */ char * UTI_TimestampToString(NTP_int64 *ts) { struct timeval tv; UTI_Int64ToTimeval(ts, &tv); return UTI_TimevalToString(&tv); } /* ================================================== */ char * UTI_RefidToString(uint32_t ref_id) { unsigned int i, j, c; char *result; result = NEXT_BUFFER; for (i = j = 0; i < 4 && i < BUFFER_LENGTH - 1; i++) { c = (ref_id >> (24 - i * 8)) & 0xff; if (isprint(c)) result[j++] = c; } result[j] = '\0'; return result; } /* ================================================== */ char * UTI_IPToString(IPAddr *addr) { unsigned long a, b, c, d, ip; uint8_t *ip6; char *result; result = NEXT_BUFFER; switch (addr->family) { case IPADDR_UNSPEC: snprintf(result, BUFFER_LENGTH, "[UNSPEC]"); break; case IPADDR_INET4: ip = addr->addr.in4; a = (ip>>24) & 0xff; b = (ip>>16) & 0xff; c = (ip>> 8) & 0xff; d = (ip>> 0) & 0xff; snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d); break; case IPADDR_INET6: ip6 = addr->addr.in6; #ifdef FEAT_IPV6 inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH); #else snprintf(result, BUFFER_LENGTH, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]); #endif break; default: snprintf(result, BUFFER_LENGTH, "[UNKNOWN]"); } return result; } /* ================================================== */ int UTI_StringToIP(const char *addr, IPAddr *ip) { #ifdef FEAT_IPV6 struct in_addr in4; struct in6_addr in6; if (inet_pton(AF_INET, addr, &in4) > 0) { ip->family = IPADDR_INET4; ip->addr.in4 = ntohl(in4.s_addr); return 1; } if (inet_pton(AF_INET6, addr, &in6) > 0) { ip->family = IPADDR_INET6; memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6)); return 1; } #else unsigned long a, b, c, d, n; n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d); if (n == 4) { ip->family = IPADDR_INET4; ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff); return 1; } #endif return 0; } /* ================================================== */ uint32_t UTI_IPToRefid(IPAddr *ip) { static int MD5_hash = -1; unsigned char buf[16]; switch (ip->family) { case IPADDR_INET4: return ip->addr.in4; case IPADDR_INET6: if (MD5_hash < 0) { MD5_hash = HSH_GetHashId("MD5"); assert(MD5_hash >= 0); } if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof (ip->addr.in6), NULL, 0, buf, 16) != 16) { assert(0); return 0; }; return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; } return 0; } /* ================================================== */ void UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest) { /* Don't send uninitialized bytes over network */ memset(dest, 0, sizeof (IPAddr)); dest->family = htons(src->family); switch (src->family) { case IPADDR_INET4: dest->addr.in4 = htonl(src->addr.in4); break; case IPADDR_INET6: memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); break; } } /* ================================================== */ void UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest) { dest->family = ntohs(src->family); switch (dest->family) { case IPADDR_INET4: dest->addr.in4 = ntohl(src->addr.in4); break; case IPADDR_INET6: memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6)); break; } } /* ================================================== */ int UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask) { int i, d; if (a->family != b->family) return a->family - b->family; if (mask && mask->family != b->family) mask = NULL; switch (a->family) { case IPADDR_UNSPEC: return 0; case IPADDR_INET4: if (mask) return (a->addr.in4 & mask->addr.in4) - (b->addr.in4 & mask->addr.in4); else return a->addr.in4 - b->addr.in4; case IPADDR_INET6: for (i = 0, d = 0; !d && i < 16; i++) { if (mask) d = (a->addr.in6[i] & mask->addr.in6[i]) - (b->addr.in6[i] & mask->addr.in6[i]); else d = a->addr.in6[i] - b->addr.in6[i]; } return d; } return 0; } /* ================================================== */ void UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port) { switch (sa->sa_family) { case AF_INET: ip->family = IPADDR_INET4; ip->addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); *port = ntohs(((struct sockaddr_in *)sa)->sin_port); break; #ifdef FEAT_IPV6 case AF_INET6: ip->family = IPADDR_INET6; memcpy(ip->addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, sizeof (ip->addr.in6)); *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port); break; #endif default: ip->family = IPADDR_UNSPEC; *port = 0; } } /* ================================================== */ int UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa) { switch (ip->family) { case IPADDR_INET4: memset(sa, 0, sizeof (struct sockaddr_in)); sa->sa_family = AF_INET; ((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip->addr.in4); ((struct sockaddr_in *)sa)->sin_port = htons(port); #ifdef SIN6_LEN ((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in); #endif return sizeof (struct sockaddr_in); #ifdef FEAT_IPV6 case IPADDR_INET6: memset(sa, 0, sizeof (struct sockaddr_in6)); sa->sa_family = AF_INET6; memcpy(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip->addr.in6, sizeof (ip->addr.in6)); ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); #ifdef SIN6_LEN ((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6); #endif return sizeof (struct sockaddr_in6); #endif default: memset(sa, 0, sizeof (struct sockaddr)); sa->sa_family = AF_UNSPEC; return 0; } } /* ================================================== */ char * UTI_TimeToLogForm(time_t t) { struct tm stm; char *result; result = NEXT_BUFFER; stm = *gmtime(&t); strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", &stm); return result; } /* ================================================== */ void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta_time, double dfreq, double doffset) { double elapsed; UTI_DiffTimevalsToDouble(&elapsed, when, old_tv); *delta_time = elapsed * dfreq - doffset; UTI_AddDoubleToTimeval(old_tv, *delta_time, new_tv); } /* ================================================== */ uint32_t UTI_GetNTPTsFuzz(int precision) { uint32_t fuzz; int fuzz_bits; fuzz_bits = 32 - 1 + precision; fuzz = random() % (1 << fuzz_bits); return fuzz; } /* ================================================== */ double UTI_Int32ToDouble(NTP_int32 x) { return (double) ntohl(x) / 65536.0; } /* ================================================== */ #define MAX_NTP_INT32 (4294967295.0 / 65536.0) NTP_int32 UTI_DoubleToInt32(double x) { if (x > MAX_NTP_INT32) x = MAX_NTP_INT32; else if (x < 0) x = 0.0; return htonl((NTP_int32)(0.5 + 65536.0 * x)); } /* ================================================== */ /* Seconds part of NTP timestamp correponding to the origin of the struct timeval format. */ #define JAN_1970 0x83aa7e80UL void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, uint32_t fuzz) { uint32_t lo, sec, usec; sec = (uint32_t)src->tv_sec; usec = (uint32_t)src->tv_usec; /* Recognize zero as a special case - it always signifies an 'unknown' value */ if (!usec && !sec) { dest->hi = dest->lo = 0; } else { dest->hi = htonl(sec + JAN_1970); /* This formula gives an error of about 0.1us worst case */ lo = 4295 * usec - (usec>>5) - (usec>>9); /* Add the fuzz */ lo ^= fuzz; dest->lo = htonl(lo); } } /* ================================================== */ void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest) { uint32_t ntp_sec, ntp_frac; /* As yet, there is no need to check for zero - all processing that has to detect that case is in the NTP layer */ ntp_sec = ntohl(src->hi); ntp_frac = ntohl(src->lo); #ifdef HAVE_LONG_TIME_T dest->tv_sec = ntp_sec - (uint32_t)(NTP_ERA_SPLIT + JAN_1970) + (time_t)NTP_ERA_SPLIT; #else dest->tv_sec = ntp_sec - JAN_1970; #endif /* Until I invent a slick way to do this, just do it the obvious way */ dest->tv_usec = (int)(0.5 + (double)(ntp_frac) / 4294.967296); } /* ================================================== */ /* Maximum offset between two sane times */ #define MAX_OFFSET 4294967296.0 /* Minimum allowed distance from maximum 32-bit time_t */ #define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600) int UTI_IsTimeOffsetSane(struct timeval *tv, double offset) { double t; /* Handle nan correctly here */ if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET)) return 0; UTI_TimevalToDouble(tv, &t); t += offset; /* Time before 1970 is not considered valid */ if (t < 0.0) return 0; #ifdef HAVE_LONG_TIME_T /* Check if it's in the interval to which NTP time is mapped */ if (t < (double)NTP_ERA_SPLIT || t > (double)(NTP_ERA_SPLIT + (1LL << 32))) return 0; #else /* Don't get too close to 32-bit time_t overflow */ if (t > (double)(0x7fffffff - MIN_ENDOFTIME_DISTANCE)) return 0; #endif return 1; } /* ================================================== */ double UTI_Log2ToDouble(int l) { if (l >= 0) { if (l > 31) l = 31; return 1 << l; } else { if (l < -31) l = -31; return 1.0 / (1 << -l); } } /* ================================================== */ void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest) { uint32_t sec_low; #ifdef HAVE_LONG_TIME_T uint32_t sec_high; #endif dest->tv_usec = ntohl(src->tv_nsec) / 1000; sec_low = ntohl(src->tv_sec_low); #ifdef HAVE_LONG_TIME_T sec_high = ntohl(src->tv_sec_high); if (sec_high == TV_NOHIGHSEC) sec_high = 0; dest->tv_sec = (uint64_t)sec_high << 32 | sec_low; #else dest->tv_sec = sec_low; #endif } /* ================================================== */ void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest) { dest->tv_nsec = htonl(src->tv_usec * 1000); #ifdef HAVE_LONG_TIME_T dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32); #else dest->tv_sec_high = htonl(TV_NOHIGHSEC); #endif dest->tv_sec_low = htonl(src->tv_sec); } /* ================================================== */ #define FLOAT_EXP_BITS 7 #define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1))) #define FLOAT_EXP_MAX (-FLOAT_EXP_MIN - 1) #define FLOAT_COEF_BITS ((int)sizeof (int32_t) * 8 - FLOAT_EXP_BITS) #define FLOAT_COEF_MIN (-(1 << (FLOAT_COEF_BITS - 1))) #define FLOAT_COEF_MAX (-FLOAT_COEF_MIN - 1) double UTI_FloatNetworkToHost(Float f) { int32_t exp, coef, x; x = ntohl(f.f); exp = (x >> FLOAT_COEF_BITS) - FLOAT_COEF_BITS; coef = x << FLOAT_EXP_BITS >> FLOAT_EXP_BITS; return coef * pow(2.0, exp); } Float UTI_FloatHostToNetwork(double x) { int32_t exp, coef, neg; Float f; if (x < 0.0) { x = -x; neg = 1; } else if (x >= 0.0) { neg = 0; } else { /* Save NaN as zero */ x = 0.0; neg = 0; } if (x < 1.0e-100) { exp = coef = 0; } else if (x > 1.0e100) { exp = FLOAT_EXP_MAX; coef = FLOAT_COEF_MAX + neg; } else { exp = log(x) / log(2) + 1; coef = x * pow(2.0, -exp + FLOAT_COEF_BITS) + 0.5; assert(coef > 0); /* we may need to shift up to two bits down */ while (coef > FLOAT_COEF_MAX + neg) { coef >>= 1; exp++; } if (exp > FLOAT_EXP_MAX) { /* overflow */ exp = FLOAT_EXP_MAX; coef = FLOAT_COEF_MAX + neg; } else if (exp < FLOAT_EXP_MIN) { /* underflow */ if (exp + FLOAT_COEF_BITS >= FLOAT_EXP_MIN) { coef >>= FLOAT_EXP_MIN - exp; exp = FLOAT_EXP_MIN; } else { exp = coef = 0; } } } /* negate back */ if (neg) coef = (uint32_t)-coef << FLOAT_EXP_BITS >> FLOAT_EXP_BITS; f.f = htonl(exp << FLOAT_COEF_BITS | coef); return f; } /* ================================================== */ int UTI_FdSetCloexec(int fd) { int flags; flags = fcntl(fd, F_GETFD); if (flags != -1) { flags |= FD_CLOEXEC; return !fcntl(fd, F_SETFD, flags); } return 0; } /* ================================================== */ int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len, const unsigned char *data, int data_len, unsigned char *auth, int auth_len) { return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len); } /* ================================================== */ int UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len) { unsigned char buf[MAX_HASH_LENGTH]; return UTI_GenerateNTPAuth(hash_id, key, key_len, data, data_len, buf, sizeof (buf)) == auth_len && !memcmp(buf, auth, auth_len); } /* ================================================== */ int UTI_DecodePasswordFromText(char *key) { int i, j, len = strlen(key); char buf[3], *p; if (!strncmp(key, "ASCII:", 6)) { memmove(key, key + 6, len - 6); return len - 6; } else if (!strncmp(key, "HEX:", 4)) { if ((len - 4) % 2) return 0; for (i = 0, j = 4; j + 1 < len; i++, j += 2) { buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0'; key[i] = strtol(buf, &p, 16); if (p != buf + 2) return 0; } return i; } else { /* assume ASCII */ return len; } } chrony-2.1.1/util.h0000644000000000000000000001240712542263351012650 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Various utility functions */ #ifndef GOT_UTIL_H #define GOT_UTIL_H #include "sysincl.h" #include "addressing.h" #include "ntp.h" #include "candm.h" #include "hash.h" /* Convert a timeval into a floating point number of seconds */ extern void UTI_TimevalToDouble(struct timeval *a, double *b); /* Convert a number of seconds expressed in floating point into a timeval */ extern void UTI_DoubleToTimeval(double a, struct timeval *b); /* Returns -1 if a comes earlier than b, 0 if a is the same time as b, and +1 if a comes after b */ extern int UTI_CompareTimevals(struct timeval *a, struct timeval *b); /* Normalise a struct timeval, by adding or subtracting seconds to bring its microseconds field into range */ extern void UTI_NormaliseTimeval(struct timeval *x); /* Calculate result = a - b */ extern void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b); /* Calculate result = a - b and return as a double */ extern void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b); /* Add a double increment to a timeval to get a new one. 'start' is the starting time, 'end' is the result that we return. This is safe to use if start and end are the same */ extern void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end); /* Calculate the average and difference (as a double) of two timevals */ extern void UTI_AverageDiffTimevals(struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff); /* Calculate result = a - b + c */ extern void UTI_AddDiffToTimeval(struct timeval *a, struct timeval *b, struct timeval *c, struct timeval *result); /* Convert a timeval into a temporary string, largely for diagnostic display */ extern char *UTI_TimevalToString(struct timeval *tv); /* Convert an NTP timestamp into a temporary string, largely for diagnostic display */ extern char *UTI_TimestampToString(NTP_int64 *ts); /* Convert ref_id into a temporary string, for diagnostics */ extern char *UTI_RefidToString(uint32_t ref_id); /* Convert an IP address to string, for diagnostics */ extern char *UTI_IPToString(IPAddr *ip); extern int UTI_StringToIP(const char *addr, IPAddr *ip); extern uint32_t UTI_IPToRefid(IPAddr *ip); extern void UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest); extern void UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest); extern int UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask); extern void UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port); extern int UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa); extern char *UTI_TimeToLogForm(time_t t); /* Adjust time following a frequency/offset change */ extern void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double *delta, double dfreq, double doffset); /* Get a random value to fuzz an NTP timestamp in the given precision */ extern uint32_t UTI_GetNTPTsFuzz(int precision); extern double UTI_Int32ToDouble(NTP_int32 x); extern NTP_int32 UTI_DoubleToInt32(double x); extern void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest, uint32_t fuzz); extern void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest); /* Check if time + offset is sane */ extern int UTI_IsTimeOffsetSane(struct timeval *tv, double offset); /* Get 2 raised to power of a signed integer */ extern double UTI_Log2ToDouble(int l); extern void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest); extern void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest); extern double UTI_FloatNetworkToHost(Float x); extern Float UTI_FloatHostToNetwork(double x); /* Set FD_CLOEXEC on descriptor */ extern int UTI_FdSetCloexec(int fd); extern int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len, const unsigned char *data, int data_len, unsigned char *auth, int auth_len); extern int UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len); /* Decode password encoded in ASCII or HEX */ extern int UTI_DecodePasswordFromText(char *key); #if defined (INLINE_UTILITIES) #define INLINE_STATIC inline static #include "util.c" #else #define INLINE_STATIC #endif /* defined (INLINE_UTILITIES) */ #endif /* GOT_UTIL_H */ chrony-2.1.1/wrap_adjtimex.c0000644000000000000000000001056112542263351014523 0ustar rootroot/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2011-2012, 2014 * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= This is a wrapper around the Linux adjtimex system call. */ #include "config.h" #include "wrap_adjtimex.h" #include /* Definitions used if missing in the system headers */ #ifndef ADJ_TAI #define ADJ_TAI 0x0080 /* set TAI offset */ #endif #ifndef ADJ_SETOFFSET #define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ #endif #ifndef ADJ_NANO #define ADJ_NANO 0x2000 /* select nanosecond resolution */ #endif #ifndef ADJ_OFFSET_SS_READ #define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ #endif /* Frequency offset scale (shift) */ #define SHIFT_USEC 16 static int status = 0; int TMX_ResetOffset(void) { struct timex txc; /* Reset adjtime() offset */ txc.modes = ADJ_OFFSET_SINGLESHOT; txc.offset = 0; if (adjtimex(&txc) < 0) return -1; /* Reset PLL offset */ txc.modes = ADJ_OFFSET | ADJ_STATUS; txc.status = STA_PLL; txc.offset = 0; if (adjtimex(&txc) < 0) return -1; /* Set status back */ txc.modes = ADJ_STATUS; txc.status = status; if (adjtimex(&txc) < 0) return -1; return 0; } int TMX_SetFrequency(double *freq, long tick) { struct timex txc; txc.modes = ADJ_TICK | ADJ_FREQUENCY; txc.freq = (long)(*freq * (double)(1 << SHIFT_USEC)); *freq = txc.freq / (double)(1 << SHIFT_USEC); txc.tick = tick; return adjtimex(&txc); } int TMX_GetFrequency(double *freq, long *tick) { struct timex txc; int result; txc.modes = 0; /* pure read */ result = adjtimex(&txc); *freq = txc.freq / (double)(1 << SHIFT_USEC); *tick = txc.tick; return result; } int TMX_SetLeap(int leap) { struct timex txc; status &= ~(STA_INS | STA_DEL); if (leap > 0) { status |= STA_INS; } else if (leap < 0) { status |= STA_DEL; } txc.modes = ADJ_STATUS; txc.status = status; return adjtimex(&txc); } int TMX_GetLeap(int *leap) { struct timex txc; txc.modes = 0; if (adjtimex(&txc) < 0) return -1; status &= ~(STA_INS | STA_DEL); status |= txc.status & (STA_INS | STA_DEL); if (status & STA_INS) *leap = 1; else if (status & STA_DEL) *leap = -1; else *leap = 0; return 0; } int TMX_SetSync(int sync, double est_error, double max_error) { struct timex txc; if (sync) { status &= ~STA_UNSYNC; } else { status |= STA_UNSYNC; } txc.modes = ADJ_STATUS | ADJ_ESTERROR | ADJ_MAXERROR; txc.status = status; txc.esterror = est_error * 1.0e6; txc.maxerror = max_error * 1.0e6; return adjtimex(&txc); } int TMX_TestStepOffset(void) { struct timex txc; /* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET. This seems to be the only way how to verify that the kernel really supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown mode. */ txc.modes = ADJ_MAXERROR; txc.maxerror = 0; if (adjtimex(&txc) < 0 || txc.maxerror != 0) return -1; txc.modes = ADJ_SETOFFSET | ADJ_NANO; txc.time.tv_sec = 0; txc.time.tv_usec = 0; if (adjtimex(&txc) < 0 || txc.maxerror < 100000) return -1; return 0; } int TMX_ApplyStepOffset(double offset) { struct timex txc; txc.modes = ADJ_SETOFFSET | ADJ_NANO; txc.time.tv_sec = offset; txc.time.tv_usec = 1.0e9 * (offset - txc.time.tv_sec); if (txc.time.tv_usec < 0) { txc.time.tv_sec--; txc.time.tv_usec += 1000000000; } return adjtimex(&txc); } chrony-2.1.1/wrap_adjtimex.h0000644000000000000000000000266012542263351014531 0ustar rootroot/* 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= The header file for the adjtimex wrapper */ #ifndef GOT_WRAP_ADJTIMEX_H #define GOT_WRAP_ADJTIMEX_H int TMX_ResetOffset(void); int TMX_SetFrequency(double *freq, long tick); int TMX_GetFrequency(double *freq, long *tick); int TMX_SetLeap(int leap); int TMX_GetLeap(int *leap); int TMX_SetSync(int sync, double est_error, double max_error); int TMX_TestStepOffset(void); int TMX_ApplyStepOffset(double offset); #endif /* GOT_WRAP_ADJTIMEX_H */ chrony-2.1.1/version.txt0000644000000000000000000000000612542263372013743 0ustar rootroot2.1.1 chrony-2.1.1/chrony.txt0000644000000000000000000055013212542263375013575 0ustar rootrootUser guide for the chrony suite ******************************* 1 Introduction ************** 1.1 Overview ============ chrony is a versatile implementation of the Network Time Protocol (NTP). It can synchronize the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network. It is designed to perform well in a wide range of conditions, including intermittent network connections, heavily congested networks, changing temperatures (ordinary computer clocks are sensitive to temperature), and systems that do not run continuosly, or run on a virtual machine. Typical accuracy between two machines on a LAN is in tens, or a few hundreds, of microseconds; over the Internet, accuracy is typically within a few milliseconds. With a good hardware reference clock sub-microsecond accuracy is possible. Two programs are included in chrony, 'chronyd' is a daemon that can be started at boot time and 'chronyc' is a command-line interface program which can be used to monitor 'chronyd''s performance and to change various operating parameters whilst it is running. The IP addresses from which 'chronyc' clients may connect can be tightly controlled. The default is just the computer that 'chronyd' itself is running on. 1.2 Acknowledgements ==================== The 'chrony' suite makes use of the algorithm known as _RSA Data Security, Inc. MD5 Message-Digest Algorithm_ for authenticating messages between different machines on the network. In writing the 'chronyd' program, extensive use has been made of RFC 1305 and RFC 5905, written by David Mills. The source code of the NTP reference implementation has been used to check details of the protocol. 1.3 Availability ================ 1.3.1 Getting the software -------------------------- Links on the chrony home page (http://chrony.tuxfamily.org) describe how to obtain the software. 1.3.2 Platforms --------------- Although most of the program is portable between Unix-like systems, there are parts that have to be tailored to each specific vendor's system. These are the parts that interface with the operating system's facilities for adjusting the system clock; different operating systems may provide different function calls to achieve this, and even where the same function is used it may have different quirks in its behaviour. The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and Solaris. Closely related systems may work too. Porting the software to other systems (particularly to those supporting an 'adjtime' system call) should not be difficult, however it requires access to such systems to test out the driver. 1.4 Relationship to other software packages =========================================== 1.4.1 ntpd ---------- The 'reference' implementation of the Network Time Protocol is the program 'ntpd', available via The NTP home page (http://www.ntp.org/). One of the main differences between 'ntpd' and 'chronyd' is in the algorithms used to control the computer's clock. Things 'chronyd' can do better than 'ntpd': * 'chronyd' can perform usefully in an environment where access to the time reference is intermittent. 'ntpd' needs regular polling of the reference to work well. * 'chronyd' can usually synchronise the clock faster and with better time accuracy. * 'chronyd' quickly adapts to sudden changes in the rate of the clock (e.g. due to changes in the temperature of the crystal oscillator). 'ntpd' may need a long time to settle down again. * 'chronyd' can perform well even when the network is congested for longer periods of time. * 'chronyd' in the default configuration never steps the time to not upset other running programs. 'ntpd' can be configured to never step the time too, but it has to use a different means of adjusting the clock, which has some disadvantages. * 'chronyd' can adjust the rate of the clock on Linux in a larger range, which allows it to operate even on machines with broken or unstable clock (e.g. in some virtual machines). Things 'chronyd' can do that 'ntpd' can't: * 'chronyd' provides support for isolated networks whether the only method of time correction is manual entry (e.g. by the administrator looking at a clock). 'chronyd' can look at the errors corrected at different updates to work out the rate at which the computer gains or loses time, and use this estimate to trim the computer clock subsequently. * 'chronyd' provides support to work out the gain or loss rate of the 'real-time clock', i.e. the clock that maintains the time when the computer is turned off. It can use this data when the system boots to set the system time from a corrected version of the real-time clock. These real-time clock facilities are only available on Linux, so far. Things 'ntpd' can do that 'chronyd' can't: * 'ntpd' supports all operating modes from RFC 5905, including broadcast, multicast and manycast client / server. It supports the orphan mode and it also supports authentication based on public-key cryptography described in RFC 5906. * 'ntpd' has been ported to more types of computer / operating system. * 'ntpd' includes drivers for many reference clocks. 'chronyd' relies on other programs (e.g. gpsd) to access the data from the reference clocks. 1.4.2 timed ----------- 'timed' is a program that is part of the BSD networking suite. It uses broadcast packets to find all machines running the daemon within a subnet. The machines elect a master which periodically measures the system clock offsets of the other computers using ICMP timestamps. Corrections are sent to each member as a result of this process. Problems that may arise with 'timed' are : * Because it uses broadcasts, it is not possible to isolate its functionality to a particular group of computers; there is a risk of upsetting other computers on the same network (e.g. where a whole company is on the same subnet but different departments are independent from the point of view of administering their computers.) * The update period appears to be 10 minutes. Computers can build up significant offsets relative to each other in that time. If a computer can estimate its rate of drift it can keep itself closer to the other computers between updates by adjusting its clock every few seconds. 'timed' does not seem to do this. * 'timed' does not have any integrated capability for feeding real-time into its estimates, or for estimating the average rate of time loss/gain of the machines relative to real-time (unless one of the computers in the group has access to an external reference and is always appointed as the 'master'). 'timed' does have the benefit over 'chronyd' that for isolated networks of computers, they will track the 'majority vote' time. For such isolated networks, 'chronyd' requires one computer to be the 'master' with the others slaved to it. If the master has a particular defective clock, the whole set of computers will tend to slip relative to real time (but they _will_ stay accurate relative to one another). 1.5 Distribution rights and (lack of) warranty ============================================== Chrony may be distributed in accordance with the GNU General Public License version 2, reproduced in *Note GPL::. 1.6 Bug reporting and suggestions ================================= If you think you've found a bug in chrony, or have a suggestion, please let us know. You can join chrony users mailing list by sending a message with the subject subscribe to . Only subscribers can post to the list. When you are reporting a bug, please send us all the information you can. Unfortunately, chrony has proven to be one of those programs where it is very difficult to reproduce bugs in a different environment. So we may have to interact with you quite a lot to obtain enough extra logging and tracing to pin-point the problem in some cases. Please be patient and plan for this! Of course, if you can debug the problem yourself and send us a source code patch to fix it, we will be very grateful! 1.7 Contributions ================= Although chrony is now a fairly mature and established project, there are still areas that could be improved. If you can program in C and have some expertise in these areas, you might be able to fill the gaps. Particular areas that need addressing are : 1. Porting to other Unices This involves creating equivalents of sys_solaris.c, sys_linux.c etc for the new system. 2. Porting to Windows NT A small amount of work on this was done under Cygwin. Only the sorting out of the include files has really been achieved so far. The two main areas still to address are 1. The system clock driver. 2. How to make chronyd into an NT service (i.e. what to replace fork(), setsid() etc with so that chronyd can be automatically started in the system bootstrap. 3. More drivers for reference clock support 2 Installation ************** The software is distributed as source code which has to be compiled. The source code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. After unpacking the source code, change directory into it, and type ./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, 'Makefile' will be generated. If editline or readline library is available, chronyc will be built with line editing support. If you don't want this, specify the -disable-readline flag to configure. Please refer to *note line editing support:: for more information. If a 'timepps.h' header is available (e.g. from the LinuxPPS project (http://linuxpps.org/)), 'chronyd' will be built with PPS API reference clock driver. If the header is installed in a location that isn't normally searched by the compiler, you can add it to the searched locations by setting 'CPPFLAGS' variable to '-I/path/to/timepps'. 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 '/share/info/chrony.info' on the end. The second argument is the location of the file called 'dir'. This will typically be '/usr/share/info/dir'. So the typical command line would be install-info /usr/local/share/info/chrony.info /usr/share/info/dir Now that the software is successfully installed, the next step is to set up a configuration file. The default location of the file is '/etc/chrony.conf'. Several examples of configuration with comments are included in the examples directory. Suppose you want to use public NTP servers from the pool.ntp.org project as your time reference. A minimal useful configuration file could be pool pool.ntp.org iburst makestep 10 3 rtcsync Then, 'chronyd' can be run. 2.1 Support for line editing libraries ====================================== Chronyc can be built with support for line editing, this allows you to use the cursor keys to replay and edit old commands. Two libraries are supported which provide such functionality, editline and GNU readline. Please note that readline since version 6.0 is licensed under GPLv3+ which is incompatible with chrony's license GPLv2. You should use editline instead if you don't want to use older readline versions. The configure script will automatically enable the line editing support if one of the supported libraries is available. If they are both available, the editline library will be used. If you don't want to use it (in which case chronyc will use a minimal command line interface), invoke configure like this: ./configure --disable-readline other-options... If you have editline, readline or ncurses installed in locations that aren't normally searched by the compiler and linker, you need to use extra options: '--with-readline-includes=directory_name' This defines the name of the directory above the one where 'readline.h' is. 'readline.h' is assumed to be in 'editline' or 'readline' subdirectory of the named directory. '--with-readline-library=directory_name' This defines the directory containing the 'libedit.a' or 'libedit.so' file, or 'libreadline.a' or 'libreadline.so' file. '--with-ncurses-library=directory_name' This defines the directory containing the 'libncurses.a' or 'libncurses.so' file. 2.2 Extra options for package builders ====================================== The configure and make procedures have some extra options that may be useful if you are building a distribution package for chrony. The -infodir=DIR option to configure specifies an install directory for the info files. This overrides the 'info' subdirectory of the argument to the -prefix option. For example, you might use ./configure --prefix=/usr --infodir=/usr/share/info The -mandir=DIR option to configure specifies an install directory for the man pages. This overrides the 'man' subdirectory of the argument to the -prefix option. ./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man to set both options together. The final option is the DESTDIR option to the make command. For example, you could use the commands ./configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man make all docs make install DESTDIR=./tmp cd tmp tar cvf - . | gzip -9 > chrony.tar.gz to build a package. When untarred within the root directory, this will install the files to the intended final locations. 3 Typical operating scenarios ***************************** 3.1 Computers connected to the internet ======================================= In this section we discuss how to configure chrony for computers that are connected to the Internet (or to any network containing true NTP servers which ultimately derive their time from a reference clock) permanently or most of the time. To operate in this mode, you will need to know the names of the NTP server machines you wish to use. You may be able to find names of suitable servers by one of the following methods: * Your institution may already operate servers on its network. Contact your system administrator to find out. * Your ISP probably has one or more NTP servers available for its customers. * Somewhere under the NTP homepage there is a list of public stratum 1 and stratum 2 servers. You should find one or more servers that are near to you -- check that their access policy allows you to use their facilities. * Use public servers from the pool.ntp.org project (http://www.pool.ntp.org/). Assuming that you have found some servers, you need to set up a configuration file to run chrony. The (compiled-in) default location for this file is '/etc/chrony.conf'. Assuming that your NTP servers are called 'foo.example.net', 'bar.example.net' and 'baz.example.net', your 'chrony.conf' file could contain as a minimum server foo.example.net server bar.example.net server baz.example.net However, you will probably want to include some of the other directives described later. The following directives may be particularly useful : 'driftfile', 'makestep', 'rtcsync'. Also, the 'iburst' server option is useful to speed up the initial synchronization. The smallest useful configuration file would look something like server foo.example.net iburst server bar.example.net iburst server baz.example.net iburst driftfile /var/lib/chrony/drift makestep 10 3 rtcsync When using a pool of NTP servers (one name is used for multiple servers which may change over time), it's better to specify them with the 'pool' directive instead of multiple 'server' directives. The configuration file could in this case look like pool pool.ntp.org iburst driftfile /var/lib/chrony/drift makestep 10 3 rtcsync 3.2 Infrequent connection to true NTP servers ============================================= In this section we discuss how to configure chrony for computers that have occasional connections to the internet. 3.2.1 Setting up the configuration file for infrequent connections ------------------------------------------------------------------ As in the previous section, you will need access to NTP servers on the internet. The same remarks apply for how to find them. In this case, you will need some additional configuration to tell 'chronyd' when the connection to the internet goes up and down. This saves the program from continuously trying to poll the servers when they are inaccessible. Again, assuming that your NTP servers are called 'foo.example.net', 'bar.example.net' and 'baz.example.net', your 'chrony.conf' file would need to contain something like server foo.example.net server bar.example.net server baz.example.net However, your computer will keep trying to contact the servers to obtain timestamps, even whilst offline. If you operate a dial-on-demand system, things are even worse, because the link to the internet will keep getting established. For this reason, it would be better to specify this part of your configuration file in the following way: server foo.example.net offline server bar.example.net offline server baz.example.net offline The 'offline' keyword indicates that the servers start in an offline state, and that they should not be contacted until 'chronyd' receives notification that the link to the internet is present. In order to notify 'chronyd' of the presence of the link, you will need to be able to log in to it with the program 'chronyc'. To do this, 'chronyd' needs to be configured with an administrator password. The password is read from a file specified by the 'keyfile' directive. The 'generatecommandkey' directive can be used to generate a random password automatically on the first 'chronyd' start. The smallest useful configuration file would look something like server foo.example.net offline server bar.example.net offline server baz.example.net offline keyfile /etc/chrony.keys generatecommandkey driftfile /var/lib/chrony/drift makestep 10 3 The next section describes how to tell 'chronyd' when the internet link goes up and down. 3.2.2 How to tell chronyd when the internet link is available. -------------------------------------------------------------- To use this option, you will need to configure a command key in 'chronyd's' configuration file '/etc/chrony.conf', as described in the previous section. To tell 'chronyd' when to start and finish sampling the servers, the 'online' and 'offline' commands of chronyc need to be used. To give an example of their use, we assume that 'pppd' is the program being used to connect to the internet, and that chronyc has been installed at its default location '/usr/local/bin/chronyc'. We also assume that the command key has been set up as described in the previous section. In the file '/etc/ppp/ip-up' we add the command sequence /usr/local/bin/chronyc -a online and in the file '/etc/ppp/ip-down' we add the sequence /usr/local/bin/chronyc -a offline 'chronyd's' polling of the servers will now only occur whilst the machine is actually connected to the Internet. 3.3 Isolated networks ===================== In this section we discuss how to configure chrony for computers that never have network conectivity to any computer which ultimately derives its time from a reference clock. In this situation, one computer is selected to be the master timeserver. The other computers are either direct clients of the master, or clients of clients. The rate value in the master's drift file needs to be set to the average rate at which the master gains or loses time. 'chronyd' includes support for this, in the form of the 'manual' directive in the configuration file and the 'settime' command in the 'chronyc' program. If the master is rebooted, 'chronyd' can re-read the drift rate from the drift file. However, the master has no accurate estimate of the current time. To get around this, the system can be configured so that the master can initially set itself to a 'majority-vote' of selected clients' times; this allows the clients to 'flywheel' the master across its outage. A typical configuration file for the master (called 'master') might be (assuming the clients are in the 192.168.165.x subnet and that the master's address is 192.168.169.170) driftfile /var/lib/chrony/drift generatecommandkey keyfile /etc/chrony.keys initstepslew 10 client1 client3 client6 local stratum 8 manual allow 192.168.165 For the clients that have to resynchronise the master when it restarts, the configuration file might be server master driftfile /var/lib/chrony/drift logdir /var/log/chrony log measurements statistics tracking keyfile /etc/chrony.keys generatecommandkey local stratum 10 initstepslew 20 master allow 192.168.169.170 The rest of the clients would be the same, except that the 'local' and 'allow' directives are not required. 3.4 The home PC with a dial-up connection ========================================= 3.4.1 Assumptions/how the software works ---------------------------------------- This section considers the home computer which has a dial-up connection. It assumes that Linux is run exclusively on the computer. Dual-boot systems may work; it depends what (if anything) the other system does to the system's real-time clock. Much of the configuration for this case is discussed earlier (*note Infrequent connection::). This section addresses specifically the case of a computer which is turned off between 'sessions'. In this case, 'chronyd' relies on the computer's real-time clock (RTC) to maintain the time between the periods when it is powered up. The arrangement is shown in the figure below. trim if required PSTN +---------------------------+ +----------+ | | | | v | | | +---------+ +-------+ +-----+ +---+ | System's| measure error/ |chronyd| |modem| |ISP| |real-time|------------------->| |-------| | | | | clock | drift rate +-------+ +-----+ +---+ +---------+ ^ | | | | +---------------------------+ --o-----o--- set time at boot up | +----------+ |NTP server| +----------+ When the computer is connected to the Internet (via the modem), 'chronyd' has access to external NTP servers which it makes measurements from. These measurements are saved, and straight-line fits are performed on them to provide an estimate of the computer's time error and rate of gaining/losing time. When the computer is taken offline from the Internet, the best estimate of the gain/loss rate is used to free-run the computer until it next goes online. Whilst the computer is running, 'chronyd' makes measurements of the real-time clock (RTC) (via the '/dev/rtc' interface, which must be compiled into the kernel). An estimate is made of the RTC error at a particular RTC second, and the rate at which the RTC gains or loses time relative to true time. On 2.6 and later kernels, if your motherboard has a HPET, you need to enable the 'HPET_EMULATE_RTC' option in your kernel configuration. Otherwise, chrony will not be able to interact with the RTC device and will give up using it. When the computer is powered down, the measurement histories for all the NTP servers are saved to files (if the 'dumponexit' directive is specified in the configuration file), and the RTC tracking information is also saved to a file (if the 'rtcfile' directive has been specified). These pieces of information are also saved if the 'dump' and 'writertc' commands respectively are issued through 'chronyc'. When the computer is rebooted, 'chronyd' reads the current RTC time and the RTC information saved at the last shutdown. This information is used to set the system clock to the best estimate of what its time would have been now, had it been left running continuously. The measurement histories for the servers are then reloaded. The next time the computer goes online, the previous sessions' measurements can contribute to the line-fitting process, which gives a much better estimate of the computer's gain/loss rate. One problem with saving the measurements and RTC data when the machine is shut down is what happens if there is a power failure; the most recent data will not be saved. Although 'chronyd' is robust enough to cope with this, some performance may be lost. (The main danger arises if the RTC has been changed during the session, with the 'trimrtc' command in 'chronyc'. Because of this, 'trimrtc' will make sure that a meaningful RTC file is saved out after the change is completed). The easiest protection against power failure is to put the 'dump' and 'writertc' commands in the same place as the 'offline' command is issued to take 'chronyd' offline; because 'chronyd' free-runs between online sessions, no parameters will change significantly between going offline from the Internet and any power failure. A final point regards home computers which are left running for extended periods and where it is desired to spin down the hard disc when it is not in use (e.g. when not accessed for 15 minutes). 'chronyd' has been planned so it supports such operation; this is the reason why the RTC tracking parameters are not saved to disc after every update, but only when the user requests such a write, or during the shutdown sequence. The only other facility that will generate periodic writes to the disc is the 'log rtc' facility in the configuration file; this option should not be used if you want your disc to spin down. 3.4.2 Typical configuration files. ---------------------------------- To illustrate how a dial-up home computer might be configured, example configuration files are shown in this section. For the '/etc/chrony.conf' file, the following can be used as an example. server foo.example.net maxdelay 0.4 offline server bar.example.net maxdelay 0.4 offline server baz.example.net maxdelay 0.4 offline logdir /var/log/chrony log statistics measurements tracking driftfile /var/lib/chrony/drift keyfile /etc/chrony.keys generatecommandkey makestep 10 3 maxupdateskew 100.0 dumponexit dumpdir /var/lib/chrony rtcfile /var/lib/chrony/rtc 'pppd' is used for connecting to the internet. This runs two scripts '/etc/ppp/ip-up' and '/etc/ppp/ip-down' when the link goes online and offline respectively. The relevant part of the '/etc/ppp/ip-up' file is /usr/local/bin/chronyc -a online and the relevant part of the '/etc/ppp/ip-down' script is /usr/local/bin/chronyc -a -m offline dump writertc To start 'chronyd' during the boot sequence, the following is in '/etc/rc.d/rc.local' (this is a Slackware system) if [ -f /usr/local/sbin/chronyd -a -f /etc/chrony.conf ]; then /usr/local/sbin/chronyd -r -s echo "Start chronyd" fi The placement of this command may be important on some systems. In particular, 'chronyd' may need to be started before any software that depends on the system clock not jumping or moving backwards, depending on the directives in 'chronyd's' configuration file. For the system shutdown, 'chronyd' should receive a SIGTERM several seconds before the final SIGKILL; the SIGTERM causes the measurement histories and RTC information to be saved out. 3.5 Other important configuration options ========================================= The most common option to include in the configuration file is the 'driftfile' option. One of the major tasks of 'chronyd' is to work out how fast or how slow the system clock runs relative to real time - e.g. in terms of seconds gained or lost per day. Measurements over a long period are usually required to refine this estimate to an acceptable degree of accuracy. Therefore, it would be bad if 'chronyd' had to work the value out each time it is restarted, because the system clock would not run so accurately whilst the determination is taking place. To avoid this problem, 'chronyd' allows the gain or loss rate to be stored in a file, which can be read back in when the program is restarted. This file is called the drift file, and might typically be stored in '/var/lib/chrony/drift'. By specifying an option like the following driftfile /var/lib/chrony/drift in the configuration file ('/etc/chrony.conf'), the drift file facility will be activated. 4 Usage reference ***************** 4.1 Starting chronyd ==================== If 'chronyd' has been installed to its default location '/usr/local/sbin/chronyd', starting it is simply a matter of entering the command /usr/local/sbin/chronyd Information messages and warnings will be logged to syslog. If no configuration commands are specified on the command line, 'chronyd' will read the commands from the configuration file (default '/etc/chrony.conf'). The command line options supported are as follows: '-n' When run in this mode, the program will not detach itself from the terminal. '-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. When 'chronyd' was compiled with debugging support, this option can be used twice to print also debugging messages. '-f ' This option can be used to specify an alternate location for the configuration file (default '/etc/chrony.conf'). '-r' This option will reload sample histories for each of the servers and refclocks being used. These histories are created by using the 'dump' command in 'chronyc', or by setting the 'dumponexit' directive in the configuration file. This option is useful if you want to stop and restart 'chronyd' briefly for any reason, e.g. to install a new version. However, it only makes sense on systems where the kernel can maintain clock compensation whilst not under 'chronyd's' control. The only version where this happens so far is Linux. On other systems this option should not be used. '-R' When this option is used, the 'initstepslew' directive and the 'makestep' directive used with a positive limit will be ignored. This option is useful when restarting 'chronyd' and can be used in conjunction with the '-r' option. '-s' This option will set the system clock from the computer's real-time clock. This is analogous to supplying the '-s' flag to the '/sbin/hwclock' program during the Linux boot sequence. Support for real-time clocks is limited at present - the criteria are described in the section on the 'rtcfile' directive (*note rtcfile directive::). If used in conjunction with the '-r' flag, 'chronyd' will attempt to preserve the old samples after setting the system clock from the real time clock (RTC). This can be used to allow 'chronyd' to perform long term averaging of the gain or loss rate across system reboots, and is useful for dial-up systems that are shut down when not in use. For this to work well, it relies on 'chronyd' having been able to determine accurate statistics for the difference between the RTC and system clock last time the computer was on. If 'chronyd' doesn't support the RTC on your computer or there is no RTC installed, the system clock will be set with this option forward to the time of the last modification of the drift file (specified by the 'driftfile' directive) to restore the system time at which 'chronyd' was previously stopped. '-u ' This option sets the name of the user to which will 'chronyd' switch to drop root privileges if compiled with Linux capabilities support (default 'root'). '-q' When run in this mode, 'chronyd' will set the system clock once and exit. It will not detach from the terminal. '-Q' This option is similar to '-q', but it will only print the offset and not correct the clock. '-v' This option displays 'chronyd's' version number to the terminal and exits. '-P ' This option will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). This mode is supported only on Linux. '-m' This option will lock chronyd into RAM so that it will never be paged out. This mode is only supported on Linux. '-4' With this option hostnames will be resolved only to IPv4 addresses and only IPv4 sockets will be created. '-6' With this option hostnames will be resolved only to IPv6 addresses and only IPv6 sockets will be created. On systems that support an '/etc/rc.local' file for starting programs at boot time, 'chronyd' can be started from there. On systems with a System V style initialisation, a suitable start/stop script might be as shown below. This might be placed in the file '/etc/rc2.d/S83chrony'. #!/bin/sh # This file should have uid root, gid sys and chmod 744 # killproc() { # kill the named process(es) pid=`/usr/bin/ps -e | /usr/bin/grep -w $1 | /usr/bin/sed -e 's/^ *//' -e 's/ .*//'` [ "$pid" != "" ] && kill $pid } case "$1" in 'start') if [ -f /opt/free/sbin/chronyd -a -f /etc/chrony.conf ]; then /opt/free/sbin/chronyd fi ;; 'stop') killproc chronyd ;; *) echo "Usage: /etc/rc2.d/S83chrony { start | stop }" ;; esac (In both cases, you may want to bear in mind that 'chronyd' can step the time when it starts. There may be other programs started at boot time that could be upset by this, so you may need to consider the ordering carefully. However, 'chronyd' will need to start after daemons providing services that it may require, e.g. the domain name service.) 4.2 The chronyd configuration file ================================== The configuration file is normally called '/etc/chrony.conf'; in fact, this is the compiled-in default. However, other locations can be specified with a command line option. Each command in the configuration file is placed on a separate line. The following sections describe each of the commands in turn. The directives can occur in any order in the file and they are not case-sensitive. The configuration commands can also be specified directly on the 'chronyd' command line, each argument is parsed as a line and the configuration file is ignored. 4.2.1 Comments in the configuration file ---------------------------------------- The configuration file may contain comment lines. A comment line is any line that starts with zero or more spaces followed by any one of the following characters: * ! * ; * # * % Any line with this format will be ignored. 4.2.2 acquisitionport --------------------- By default, 'chronyd' uses a separate client socket for each configured server and their source port is chosen arbitrarily by the operating system. However, you can use the 'acquisitionport' directive to explicitly specify a port and use only one socket (per IPv4/IPv6 address family) for all configured servers. This may be useful for getting through firewalls. If set to 0, the source port of the socket will be chosen arbitrarily. It may be set to the same port as used by the NTP server (*note port directive::) to use only one socket for all NTP packets. An example of the 'acquisitionport' command is acquisitionport 1123 This would change the source port used for client requests to udp/1123. You could then persuade the firewall administrator to let that port through. 4.2.3 allow ----------- The 'allow' command is used to designate a particular subnet from which NTP clients are allowed to access the computer as an NTP server. The default is that no clients are allowed access, i.e. 'chronyd' operates purely as an NTP client. If the 'allow' directive is used, 'chronyd' will be both a client of its servers, and a server to other clients. Examples of use of the command are as follows: allow foo.example.net allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 2001:db8::/32 allow 0/0 allow ::/0 allow The first command allows the named node to be an NTP client of this computer. The second command allows any node with an IPv4 address of the form 1.2.x.y (with x and y arbitrary) to be an NTP client of this computer. Likewise, the third command allows any node with an IPv4 address of the form 3.4.5.x to have client NTP access. The fourth and fifth forms allow access from any node with an IPv4 address of the form 6.7.8.x, 6.7.9.x, 6.7.10.x or 6.7.11.x (with x arbitrary), i.e. the value 22 is the number of bits defining the specified subnet. (In the fifth form, the final byte is ignored). The sixth form is used for IPv6 addresses. The seventh and eighth forms allow access by any IPv4 and IPv6 node respectively. The ninth forms allows access by any node (IPv4 or IPv6). A second form of the directive, 'allow all', has a greater effect, depending on the ordering of directives in the configuration file. To illustrate the effect, consider the two examples allow 1.2.3.4 deny 1.2.3 allow 1.2 and allow 1.2.3.4 deny 1.2.3 allow all 1.2 In the first example, the effect is the same regardles of what order the three directives are given in. So the 1.2.x.y subnet is allowed access, except for the 1.2.3.x subnet, which is denied access, however the host 1.2.3.4 is allowed access. In the second example, the 'allow all 1.2' directives overrides the effect of _any_ previous directive relating to a subnet within the specified subnet. Within a configuration file this capability is probably rather moot; however, it is of greater use for reconfiguration at run-time via 'chronyc' (*note allow all command::). Note, if the 'initstepslew' directive (*note initstepslew directive::) is used in the configuration file, each of the computers listed in that directive must allow client access by this computer for it to work. 4.2.4 bindacqaddress -------------------- The 'bindacqaddress' directive sets the network interface to which will 'chronyd' bind its NTP client sockets. The syntax is similar to the 'bindaddress' and 'bindcmdaddress' directives. For each of IPv4 and IPv6 protocols, only one 'bindacqaddress' directive can be specified. 4.2.5 bindaddress ----------------- The 'bindaddress' directive allows you to restrict the network interface to which 'chronyd' will listen for NTP requests. This provides an additional level of access restriction above that available through the 'deny' mechanism. Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with an internet connection. The ethernet interface's IP address is 192.168.1.1. Suppose you want to block all access through the internet connection. You could add the line bindaddress 192.168.1.1 to the configuration file. For each of IPv4 and IPv6 protocols, only one 'bindaddress' directive can be specified. Therefore, it's not useful on computers which should serve NTP on multiple network interfaces. 4.2.6 bindcmdaddress -------------------- The 'bindcmdaddress' directive allows you to specify the network interface to which 'chronyd' will listen for command packets (issued by 'chronyc'). This provides an additional level of access restriction above that available through 'cmddeny' mechanism. By default, 'chronyd' binds to the loopback interface (with addresses '127.0.0.1' and '::1'). This blocks all access except from localhost. To listen for command packets on all interfaces, you can add the lines bindcmdaddress 0.0.0.0 bindcmdaddress :: to the configuration file. For each of IPv4 and IPv6 protocols, only one 'bindcmdaddress' directive can be specified. 4.2.7 broadcast --------------- The 'broadcast' directive is used to declare a broadcast address to which chronyd should send packets in NTP broadcast mode (i.e. make chronyd act as a broadcast server). Broadcast clients on that subnet will be able to synchronise. The syntax is as follows broadcast 30 192.168.1.255 broadcast 60 192.168.2.255 12123 broadcast 60 ff02::101 In the first example, the destination port defaults to 123/udp (the normal NTP port). In the second example, the destionation port is specified as 12123. The first parameter in each case (30 or 60 respectively) is the interval in seconds between broadcast packets being sent. The second parameter in each case is the broadcast address to send the packet to. This should correspond to the broadcast address of one of the network interfaces on the computer where chronyd is running. You can have more than 1 'broadcast' directive if you have more than 1 network interface onto which you wish to send NTP broadcast packets. 'chronyd' itself cannot currently act as a broadcast client; it must always be configured as a point-to-point client by defining specific NTP servers and peers. This broadcast server feature is intended for providing a time source to other NTP software (e.g. various MS Windows clients). If ntpd is used as the broadcast client, it will try to use a point-to-point client/server NTP access to measure the round-trip delay. Thus, the broadcast subnet should also be the subject of an 'allow' directive (*note allow directive::). 4.2.8 clientloglimit -------------------- This directive specifies the maximum size of the memory allocated to log client accesses. When the limit is reached, only information for clients that have already been logged will be updated. If 0 is specified, the memory size will be unlimited. The default is 524288 bytes. An example of the use of this directive is clientloglimit 1048576 4.2.9 cmdallow -------------- This is similar to the 'allow' directive (*note allow directive::), except that it allows control access (rather than NTP client access) to a particular subnet or host. (By 'control access' is meant that chronyc can be run on those hosts and successfully connect to chronyd on this computer.) The syntax is identical to the 'allow' directive. There is also a 'cmdallow all' directive with similar behaviour to the 'allow all' directive (but applying to control access in this case, of course). Note that 'chronyd' has to be configured with the 'bindcmdaddress' directive to not listen only on the loopback interface to actually allow remote access. 4.2.10 cmddeny -------------- This is similar to the 'cmdallow' directive (*note cmdallow directive::), except that it denies control access to a particular subnet or host, rather than allowing it. The syntax is identical. There is also a 'cmddeny all' directive with similar behaviour to the 'cmdallow all' directive. 4.2.11 cmdport -------------- The 'cmdport' directive allows the port that is used for run-time command and monitoring (via the program 'chronyc') to be altered from its default (323/udp). If set to 0, 'chronyd' will not open the port, this is useful to disable the 'chronyc' access completely. An example shows the syntax cmdport 257 This would make 'chronyd' use 257/udp as its command port. ('chronyc' would need to be run with the '-p 257' switch to inter-operate correctly). 4.2.12 combinelimit ------------------- When 'chronyd' has multiple sources available for synchronization, it has to select one source as the synchronization source. The measured offsets and frequencies of the system clock relative to the other sources, however, can be combined with the selected source to improve the accuracy of the system clock. The 'combinelimit' directive limits which sources are included in the combining algorithm. Their synchronization distance has to be shorter than the distance of the selected source multiplied by the value of the limit. Also, their measured frequencies have to be close to the frequency of the selected source. By default, the limit is 3. Setting the limit to 0 effectively disables the source combining algorithm and only the selected source will be used to control the system clock. The syntax is combinelimit 4.2.13 commandkey ----------------- The commandkey command is used to set the key number used for authenticating user commands via the chronyc program at run time. This allows certain actions of the chronyc program to be restricted to administrators. An example of the commandkey command is commandkey 20 By default, the key number is 0. In the key file (see the keyfile command) there should be a line of the form 20 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC When running the chronyc program to perform run-time configuration, the command password HEX:B028F91EA5C38D06C2E140B26C7F41EC must be entered before any commands affecting the operation of the daemon can be entered, or chronyc must be started with the '-a' option to run the password command automatically. 4.2.14 corrtimeratio -------------------- When 'chronyd' makes a time correction, it controls how quickly the system clock is slewed (so far only on Linux). This rate affects the frequency error of the system clock. The 'corrtimeratio' directive sets the ratio between the duration in which the clock is slewed for an average correction according to the source history and the interval in which the corrections are done (usually the NTP polling interval). Corrections larger than the average take less time and smaller corrections take more time, the amount of the correction and the correction time are inversely proportional. Increasing 'corrtimeratio' improves the overall frequency error of the system clock, but increases the overall time error as the corrections take longer. By default, the ratio is set to 3, the time accuracy of the clock is preferred over its frequency accuracy. The syntax is corrtimeratio 100 The maximum allowed slew rate can be set by the 'maxslewrate' directive (*note maxslewrate directive::. The current remaining correction is shown in the 'tracking' report (*note tracking command::) as the 'System time' value. 4.2.15 deny ----------- This is similar to the 'allow' directive (*note allow directive::), except that it denies NTP client access to a particular subnet or host, rather than allowing it. The syntax is identical. There is also a 'deny all' directive with similar behaviour to the 'allow all' directive. 4.2.16 driftfile ---------------- One of the main activities of the 'chronyd' program is to work out the rate at which the system clock gains or loses time relative to real time. Whenever 'chronyd' computes a new value of the gain/loss rate, it is desirable to record it somewhere. This allows 'chronyd' to begin compensating the system clock at that rate whenever it is restarted, even before it has had a chance to obtain an equally good estimate of the rate during the new run. (This process may take many minutes, at least). The driftfile command allows a file to be specified into which 'chronyd' can store the rate information. Two parameters are recorded in the file. The first is the rate at which the system clock gains or loses time, expressed in parts per million, with gains positive. Therefore, a value of 100.0 indicates that when the system clock has advanced by a second, it has gained 100 microseconds on reality (so the true time has only advanced by 999900 microseconds). The second is an estimate of the error bound around the first value in which the true rate actually lies. An example of the driftfile command is driftfile /var/lib/chrony/drift 4.2.17 dumpdir -------------- To compute the rate of gain or loss of time, 'chronyd' has to store a measurement history for each of the time sources it uses. Certain systems (so far only Linux) have operating system support for setting the rate of gain or loss to compensate for known errors. (On other systems, 'chronyd' must simulate such a capability by periodically slewing the system clock forwards or backwards by a suitable amount to compensate for the error built up since the previous slew). For such systems, it is possible to save the measurement history across restarts of 'chronyd' (assuming no changes are made to the system clock behaviour whilst it is not running). If this capability is to be used (via the dumponexit command in the configuration file, or the dump command in chronyc), the dumpdir command should be used to define the directory where the measurement histories are saved. An example of the command is dumpdir /var/lib/chrony A source whose reference id (the IP address for IPv4 sources) is 1.2.3.4 would have its measurement history saved in the file '/var/lib/chrony/1.2.3.4.dat'. 4.2.18 dumponexit ----------------- If this command is present, it indicates that 'chronyd' should save the measurement history for each of its time sources recorded whenever the program exits. (See the dumpdir command above). 4.2.19 fallbackdrift -------------------- Fallback drifts are long-term averages of the system clock drift calculated over exponentially increasing intervals. They are used when the clock is no longer synchronised to avoid quickly drifting away from true time if there was a short-term deviation in the drift before the synchronisation was lost. The directive specifies the minimum and maximum interval since last clock update to switch between fallback drifts. They are defined as a power of 2 (in seconds). The syntax is as follows fallbackdrift 16 19 In this example, the minimum interval is 16 (18 hours) and maximum interval is 19 (6 days). The system clock frequency will be set to the first fallback 18 hours after last clock update, to the second after 36 hours, etc. This might be a good setting to cover daily and weekly temperature fluctuations. By default (or if the specified maximum or minimum is 0), no fallbacks are used and the clock frequency changes only with new measurements from NTP, reference clocks or manual input. 4.2.20 generatecommandkey ------------------------- With this directive, if the command key is not found on start in the file specified by the 'keyfile' directive, 'chronyd' will generate a new command key from the /dev/urandom file and write it to the key file. The generated key will use SHA1 if 'chronyd' is compiled with the support, otherwise MD5 will be used. 4.2.21 hwclockfile ------------------ The 'hwclockfile' directive sets the location of the adjtime file which is used by the '/sbin/hwclock' program. With this directive, 'chronyd' will parse the file to find out if the RTC keeps local time or UTC. It overrides the 'rtconutc' directive (*note rtconutc directive::). An example of the command is hwclockfile /etc/adjtime 4.2.22 include -------------- The 'include' directive includes a specified configuration file. This is useful when maintaining configuration on multiple hosts to keep the differences in a separate file. include /etc/chrony/local.conf 4.2.23 initstepslew ------------------- In normal operation, 'chronyd' slews the time when it needs to adjust the system clock. For example, to correct a system clock which is 1 second slow, 'chronyd' slightly increases the amount by which the system clock is advanced on each clock interrupt, until the error is removed. (Actually, this is done by calling the 'adjtime()' or similar system function which does it for us.) Note that at no time does time run backwards with this method. On most Unix systems it is not desirable to step the system clock, because many programs rely on time advancing monotonically forwards. When the 'chronyd' daemon is initially started, it is possible that the system clock is considerably in error. Attempting to correct such an error by slewing may not be sensible, since it may take several hours to correct the error by this means. The purpose of the 'initstepslew' directive is to allow 'chronyd' to make a rapid measurement of the system clock error at boot time, and to correct the system clock by stepping before normal operation begins. Since this would normally be performed only at an appropriate point in the system boot sequence, no other software should be adversely affected by the step. If the correction required is less than a specified threshold, a slew is used instead. This makes it easier to restart 'chronyd' whilst the system is in normal operation. The 'initstepslew' directive takes a threshold and a list of NTP servers as arguments. Each of the servers is rapidly polled several times, and a majority voting mechanism used to find the most likely range of system clock error that is present. A step (or slew) is applied to the system clock to correct this error. 'chronyd' then enters its normal operating mode. An example of use of the command is initstepslew 30 foo.example.net bar.example.net where 2 NTP servers are used to make the measurement. The '30' indicates that if the system's error is found to be 30 seconds or less, a slew will be used to correct it; if the error is above 30 seconds, a step will be used. The 'initstepslew' directive can also be used in an isolated LAN environment, where the clocks are set manually. The most stable computer is chosen as the master, and the other computers are slaved to it. If each of the slaves is configured with the local option (see below), the master can be set up with an 'initstepslew' directive which references some or all of the slaves. Then, if the master machine has to be rebooted, the slaves can be relied on to 'flywheel' the time for the master. The 'initstepslew' directive is functionally similar to a combination of the 'makestep' and 'server' directives with the 'iburst' option. The main difference is that the 'initstepslew' servers are used only before normal operation begins and that the foreground 'chronyd' process waits for 'initstepslew' to finish before exiting. This is useful to prevent programs started in the boot sequence after 'chronyd' from reading the clock before it's stepped. 4.2.24 keyfile -------------- This command is used to specify the location of the file containing ID/key pairs for the following 2 uses: * Authentication of NTP packets. * Authentication of administrator commands entered via chronyc. The format of the command is shown in the example below keyfile /etc/chrony.keys The argument is simply the name of the file containing the ID/key pairs. The format of the file is shown below 10 tulip 11 hyacinth 20 MD5 ASCII:crocus 25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c ... Each line consists of an ID, a name of authentication hash function (optional) and a password. The ID can be any unsigned integer in the range 0 through 2**32-1, but ID of 0 can be used only for the command key and not for the NTP authentication. The hash function is MD5 by default, depending on how was 'chronyd' compiled other allowed hash functions may be SHA1, SHA256, SHA384, SHA512, RMD128, RMD160, RMD256, RMD320, TIGER and WHIRLPOOL. The password can be encoded as a string of characters not containing a space with optional 'ASCII:' prefix or as a hexadecimal number with 'HEX:' prefix. The password is used with the hash function to generate and verify a message authentication code (MAC) in NTP and command packets. For maximum security, it's recommended to use SHA1 or stronger hash function. The passwords should be random and they should be as long as the output size of the configured hash function, e.g. 160 bits with SHA1. The ID for the chronyc authentication key is specified with the commandkey command (see earlier). The command key can be generated automatically on start with the 'generatecommandkey' directive. 4.2.25 leapsecmode ------------------ A leap second is an adjustment that is occasionally applied to UTC to keep it close to the mean solar time. When a leap second is inserted, the last day of June or December has an extra second 23:59:60. For computer clocks that is a problem. The Unix time is defined as number of seconds since 00:00:00 UTC on 1 January 1970 without leap seconds. The system clock cannot have time 23:59:60, every minute has 60 seconds and every day has 86400 seconds by definition. The inserted leap second is skipped and the clock is suddenly ahead of UTC by one second. The 'leapsecmode' directive selects how that error is corrected. There are four options: 'system' When inserting a leap second, the kernel steps the system clock backwards by one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it steps forward by one second when the clock gets to 23:59:59 UTC. This is the default mode when the system driver supports leap seconds (currently Linux only). 'step' This is similar to the 'system' mode, except the clock is stepped by 'chronyd' instead of the kernel. It can be useful to avoid bugs in the kernel code that would be executed in the 'system' mode. This is the default mode when the system driver doesn't support leap seconds. 'slew' The clock is corrected by slewing started at 00:00:00 UTC when a leap second is inserted or 23:59:59 UTC when a leap second is deleted. This may be preferred over the 'system' and 'step' modes when applications running on the system are sensitive to jumps in the system time and it's acceptable that the clock will be off for a longer time. On Linux with the default 'maxslewrate' value (*note maxslewrate directive::) the correction takes 12 seconds. 'ignore' No correction is applied to the clock for the leap second. The clock will be corrected later in normal operation when new measurements are made and the estimated offset includes the one second error. An example of the command is leapsecmode slew When serving time to NTP clients that can't be configured to correct their clocks for a leap second by slewing or they would correct them at slightly different rates when it's necessary to keep them close together, the 'slew' mode can be combined with the 'smoothtime' directive (*note smoothtime directive::) to enable a server leap smear. When smearing a leap second, the leap status is suppressed on the server and the served time is corrected slowly be slewing instead of stepping. The clients don't need any special configuration as they don't know there is any leap second and they follow the server time which eventually brings them back to UTC. Care must be taken to ensure they use for synchronization only NTP servers which smear the leap second in exactly the same way. This feature needs to be used carefully, because the server is intentionally not serving its best estimate of the true time. A recommended configuration to enable a server leap smear is: leapsecmode slew maxslewrate 1000 smoothtime 400 0.001 leaponly The first directive is necessary to disable the clock step which would reset the smoothing process. The second directive limits the slewing rate of the local clock to 1000 ppm, which improves the stability of the smoothing process when the local correction starts and ends. The third directive enables the server time smoothing process. It will start when the clock gets to 00:00:00 UTC and it will take 17 hours 34 minutes to finish. The frequency offset will be changing by 0.001 ppm per second and will reach maximum of 31.623 ppm. The 'leaponly' option makes the duration of the leap smear constant and allows the clients to safely synchronise with multiple identically configured leap smearing servers. 4.2.26 leapsectz ---------------- This directive is used to set the name of the timezone in the system tz database which 'chronyd' can use to find out when will the next leap second occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid on Jun 30 and Dec 31 in the timezone. A useful timezone is 'right/UTC'. This is mainly useful with reference clocks which don't provide the leap second information. It is not necessary to restart 'chronyd' if the tz database is updated with a new leap second at least 12 hours before the event. An example of the command is leapsectz right/UTC The following shell command verifies that the timezone contains leap seconds and can be used with this directive $ TZ=right/UTC date -d 'Dec 31 2008 23:59:60' Wed Dec 31 23:59:60 UTC 2008 4.2.27 local ------------ The local keyword is used to allow 'chronyd' to appear synchronised to real time (from the viewpoint of clients polling it), even if it has no current synchronisation source. This option is normally used on computers in an isolated network, where several computers are required to synchronise to one other, this being the "master" which is kept vaguely in line with real time by manual input. An example of the command is local stratum 10 The value 10 may be substituted with other values in the range 1 through 15. Stratum 1 indicates a computer that has a true real-time reference directly connected to it (e.g. GPS, atomic clock etc) – such computers are expected to be very close to real time. Stratum 2 computers are those which have a stratum 1 server; stratum 3 computers have a stratum 2 server and so on. A large value of 10 indicates that the clock is so many hops away from a reference clock that its time is fairly unreliable. Put another way, if the computer ever has access to another computer which is ultimately synchronised to a reference clock, it will almost certainly be at a stratum less than 10. Therefore, the choice of a high value like 10 for the local command prevents the machine's own time from ever being confused with real time, were it ever to leak out to clients that have visibility of real servers. 4.2.28 lock_all --------------- The 'lock_all' directive will lock chronyd into RAM so that it will never be paged out. This mode is only supported on Linux. This directive uses the Linux mlockall() system call to prevent 'chronyd' from ever being swapped out. This should result in lower and more consistent latency. It should not have significant impact on performance as 'chronyd's' memory usage is modest. The mlockall man page has more details. 4.2.29 log ---------- The log command indicates that certain information is to be logged. 'measurements' This option logs the raw NTP measurements and related information to a file called measurements.log. 'statistics' This option logs information about the regression processing to a file called statistics.log. 'tracking' This option logs changes to the estimate of the system's gain or loss rate, and any slews made, to a file called tracking.log. 'rtc' This option logs information about the system's real-time clock. 'refclocks' This option logs the raw and filtered reference clock measurements to a file called refclocks.log. 'tempcomp' This option logs the temperature measurements and system rate compensations to a file called tempcomp.log. The files are written to the directory specified by the logdir command. An example of the command is log measurements statistics tracking 4.2.29.1 Measurements log file format ..................................... An example line (which actually appears as a single line in the file) from the measurements log file is shown below. 2014-10-13 05:40:50 158.152.1.76 N 2 111 111 1111 10 10 1.0 \ -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [2014-10-13] 2. Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. IP address of server/peer from which measurement comes [158.152.1.76] 4. Leap status ('N' means normal, '+' means that the last minute of the current month has 61 seconds, '-' means that the last minute of the month has 59 seconds, '?' means the remote computer is not currently synchronised.) [N] 5. Stratum of remote computer. [2] 6. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111] 7. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111] 8. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, against defined parameters, and a test for synchronisation loop (1=pass, 0=fail) [1111] 9. Local poll [10] 10. Remote poll [10] 11. 'Score' (an internal score within each polling level used to decide when to increase or decrease the polling level. This is adjusted based on number of measurements currently being used for the regression algorithm). [1.0] 12. The estimated local clock error ('theta' in RFC 5905). Positive indicates that the local clock is slow of the remote source. [-4.966e-03]. 13. The peer delay ('delta' in RFC 5905). [2.296e-01] 14. The peer dispersion ('epsilon' in RFC 5905). [1.577e-05] 15. The root delay ('DELTA' in RFC 5905). [1.615e-01] 16. The root dispersion ('EPSILON' in RFC 5905). [7.446e-03] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.29.2 Statistics log file format ................................... An example line (which actually appears as a single line in the file) from the statistics log file is shown below. 1998-07-22 05:40:50 158.152.1.76 6.261e-03 -3.247e-03 \ 2.220e-03 1.874e-06 1.080e-06 7.8e-02 16 0 8 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [1998-07-22] 2. Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. IP address of server/peer from which measurement comes [158.152.1.76] 4. The estimated standard deviation of the measurements from the source (in seconds). [6.261e-03] 5. The estimated offset of the source (in seconds, positive means the local clock is estimated to be fast, in this case). [-3.247e-03] 6. The estimated standard deviation of the offset estimate (in seconds). [2.220e-03] 7. The estimated rate at which the local clock is gaining or losing time relative to the source (in seconds per second, positive means the local clock is gaining). This is relative to the compensation currently being applied to the local clock, _not_ to the local clock without any compensation. [1.874e-06] 8. The estimated error in the rate value (in seconds per second). [1.080e-06]. 9. The ration of |old_rate - new_rate| / old_rate_error. Large values indicate the statistics are not modelling the source very well. [7.8e-02] 10. The number of measurements currently being used for the regression algorithm. [16] 11. The new starting index (the oldest sample has index 0; this is the method used to prune old samples when it no longer looks like the measurements fit a linear model). [0, i.e. no samples discarded this time] 12. The number of runs. The number of runs of regression residuals with the same sign is computed. If this is too small it indicates that the measurements are no longer represented well by a linear model and that some older samples need to be discarded. The number of runs for the data that is being retained is tabulated. Values of approximately half the number of samples are expected. [8] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.29.3 Tracking log file format ................................. An example line (which actually appears as a single line in the file) from the tracking log file is shown below. 2012-02-23 05:40:50 158.152.1.76 3 340.529 1.606 1.046e-03 N \ 4 6.849e-03 -4.670e-04 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [2012-02-03] 2. Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. The IP address of the server/peer to which the local system is synchronised. [158.152.1.76] 4. The stratum of the local system. [3] 5. The local system frequency (in ppm, positive means the local system runs fast of UTC). [340.529] 6. The error bounds on the frequency (in ppm) [1.606] 7. The estimated local offset at the epoch (which is rapidly corrected by slewing the local clock. (In seconds, positive indicates the local system is fast of UTC). [1.046e-3] 8. Leap status ('N' means normal, '+' means that the last minute of this month has 61 seconds, '-' means that the last minute of the month has 59 seconds, '?' means the clock is not currently synchronised.) [N] 9. The number of combined sources. [4] 10. The estimated standard deviation of the combined offset (in seconds). [6.849e-03] 11. The remaining offset correction from the previous update (in seconds, positive means the system clock is slow of UTC). [-4.670e-04] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.29.4 Real-time clock log file format ........................................ An example line (which actually appears as a single line in the file) from the measurements log file is shown below. 1998-07-22 05:40:50 -0.037360 1 -0.037434\ -37.948 12 5 120 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [1998-07-22] 2. Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. The measured offset between the system's real time clock and the system ('gettimeofday()') time. In seconds, positive indicates that the RTC is fast of the system time. [-0.037360]. 4. Flag indicating whether the regression has produced valid coefficients. (1 for yes, 0 for no). [1] 5. Offset at the current time predicted by the regression process. A large difference between this value and the measured offset tends to indicate that the measurement is an outlier with a serious measurement error. [-0.037434]. 6. The rate at which the RTC is losing or gaining time relative to the system clock. In ppm, with positive indicating that the RTC is gaining time. [-37.948] 7. The number of measurements used in the regression. [12] 8. The number of runs of regression residuals of the same sign. Low values indicate that a straight line is no longer a good model of the measured data and that older measurements should be discarded. [5] 9. The measurement interval used prior to the measurement being made (in seconds). [120] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.29.5 Refclocks log file format .................................. An example line (which actually appears as a single line in the file) from the refclocks log file is shown below. 2009-11-30 14:33:27.000000 PPS2 7 N 1 4.900000e-07 -6.741777e-07 1.000e-06 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [2009-11-30] 2. Hour:Minute:Second.Microsecond [14:33:27.000000]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. Reference ID of refclock from which measurement comes. [PPS2] 4. Sequence number of driver poll within one polling interval for raw samples, or '-' for filtered samples. [7] 5. Leap status ('N' means normal, '+' means that the last minute of the current month has 61 seconds, '-' means that the last minute of the month has 59 seconds). [N] 6. Flag indicating whether the sample comes from PPS source. (1 for yes, 0 for no, or '-' for filtered sample). [1] 7. Local clock error measured by refclock driver, or '-' for filtered sample. [4.900000e-07] 8. Local clock error with applied corrections. Positive indicates that the local clock is slow. [-6.741777e-07] 9. Assumed dispersion of the sample. [1.000e-06] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.29.6 Tempcomp log file format ................................. An example line (which actually appears as a single line in the file) from the tempcomp log file is shown below. 2010-04-19 10:39:48 2.8000e+04 3.6600e-01 The columns are as follows (the quantities in square brackets are the values from the example line above) : 1. Date [2010-04-19] 2. Hour:Minute:Second [10:39:48]. Note that the date/time pair is expressed in UTC, not the local time zone. 3. Temperature read from tempcomp file. [2.8000e+04] 4. Applied compensation in ppm, positive means the system clock is running faster than it would be without the compensation. [3.6600e-01] A banner is periodically written to the log file to indicate the meanings of the columns. 4.2.30 logbanner ---------------- A banner is periodically written to the log files enabled by the 'log' directive to indicate the meanings of the columns. The 'logbanner' directive specifies after how many entries in the log file should be the banner written. The default is 32, and 0 can be used to disable it entirely. 4.2.31 logchange ---------------- This directive forces 'chronyd' to send a message to syslog if it makes a system clock adjustment larger than a threshold value. An example of use is logchange 0.5 which would cause a syslog message to be generated a system clock error of over 0.5 seconds starts to be compensated. Clock errors detected either via NTP packets or via timestamps entered via the 'settime' command of 'chronyc' are logged. This directive assumes that syslog messages are appearing where somebody can see them. This allows that person to see if a large error has arisen, e.g. because of a fault, or because of faulty timezone handling, for example when summer time (daylight saving) starts or ends. 4.2.32 logdir ------------- This directive allows the directory where log files are written to be specified. An example of the use of this directive is logdir /var/log/chrony 4.2.33 mailonchange ------------------- This directive defines an email address to which mail should be sent if chronyd applies a correction exceeding a particular threshold to the system clock. An example of use of this directive is mailonchange root@localhost 0.5 This would send a mail message to root if a change of more than 0.5 seconds were applied to the system clock. 4.2.34 makestep --------------- Normally chronyd will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, the system clock may be so far adrift that this slewing process would take a very long time to correct the system clock. This directive forces 'chronyd' to step system clock if the adjustment is larger than a threshold value, but only if there were no more clock updates since 'chronyd' was started than a specified limit (a negative value can be used to disable the limit). This is particularly useful when using reference clocks, because the 'initstepslew' directive (*note initstepslew directive::) works only with NTP sources. An example of the use of this directive is makestep 1000 10 This would step system clock if the adjustment is larger than 1000 seconds, but only in the first ten clock updates. 4.2.35 manual ------------- The 'manual' directive enables support at run-time for the 'settime' command in chronyc (*note settime command::). If no 'manual' directive is included, any attempt to use the 'settime' command in chronyc will be met with an error message. Note that the 'settime' command can be enabled at run-time using the 'manual' command in chronyc (*note manual command::). (The idea of the two commands is that the 'manual' command controls the manual clock driver's behaviour, whereas the 'settime' command allows samples of manually entered time to be provided). 4.2.36 maxchange ---------------- This directive sets the maximum allowed offset corrected on a clock update. The check is performed only after the specified number of updates to allow a large initial adjustment of the system clock. When an offset larger than the specified maximum occurs, it will be ignored for the specified number of times and then 'chronyd' will give up and exit (a negative value can be used to never exit). In both cases a message is sent to syslog. An example of the use of this directive is maxchange 1000 1 2 After the first clock update, 'chronyd' will check the offset on every clock update, it will ignore two adjustments larger than 1000 seconds and exit on another one. 4.2.37 maxclockerror -------------------- The 'maxclockerror' directive sets the maximum assumed frequency error of the local clock. This is a frequency stability of the clock, not an absolute frequency error. By default, the maximum assumed error is set to 1 ppm. The syntax is maxclockerror Typical values for might be 10 for a low quality clock to 0.1 for a high quality clock using a temperature compensated crystal oscillator. 4.2.38 maxsamples ----------------- The 'maxsamples' directive sets the default maximum number of samples 'chronyd' should keep for each source. This setting can be overriden for individual sources in the 'server' and 'refclock' directives (*note server directive::, *note refclock directive::). The default value is 0, which disables the configurable limit. The useful range is 4 to 64. The syntax is maxsamples 4.2.39 maxslewrate ------------------ The 'maxslewrate' directive sets the maximum rate at which 'chronyd' is allowed to slew the time. It limits the slew rate controlled by the correction time ratio (*note corrtimeratio directive::) and is effective only on systems where 'chronyd' is able to control the rate (so far only Linux). By default, the maximum slew rate is 83333.333 ppm (one twelfth). The syntax is maxslewrate 4.2.40 maxupdateskew -------------------- One of 'chronyd's' tasks is to work out how fast or slow the computer's clock runs relative to its reference sources. In addition, it computes an estimate of the error bounds around the estimated value. If the range of error is too large, it probably indicates that the measurements have not settled down yet, and that the estimated gain or loss rate is not very reliable. The 'maxupdateskew' parameter allows the threshold for determining whether an estimate may be so unreliable that it should not be used. By default, the threshold is 1000 ppm. The syntax is maxupdateskew Typical values for might be 100 for a dial-up connection to servers over a phone line, and 5 or 10 for a computer on a LAN. It should be noted that this is not the only means of protection against using unreliable estimates. At all times, 'chronyd' keeps track of both the estimated gain or loss rate, and the error bound on the estimate. When a new estimate is generated following another measurement from one of the sources, a weighted combination algorithm is used to update the master estimate. So if 'chronyd' has an existing highly-reliable master estimate and a new estimate is generated which has large error bounds, the existing master estimate will dominate in the new master estimate. 4.2.41 minsamples ----------------- The 'minsamples' directive sets the default minimum number of samples 'chronyd' should keep for each source. This setting can be overriden for individual sources in the 'server' and 'refclock' directives (*note server directive::, *note refclock directive::). The default value is 0. The useful range is 4 to 64. The syntax is minsamples 4.2.42 minsources ----------------- The 'minsources' directive sets the minimum number of sources that need to be considered as selectable in the source selection algorithm before the local clock is updated. The default value is 1. Setting this option to a larger number can be used to improve the reliability. More sources will have to agree with each other and the clock will not be updated when only one source (which could be serving wrong time) is reachable. The syntax is minsources 4.2.43 noclientlog ------------------ This directive, which takes no arguments, specifies that client accesses are not to be logged. Normally they are logged, allowing statistics to be reported using the 'clients' command in 'chronyc'. 4.2.44 peer ----------- The syntax of this directive is identical to that for the 'server' directive (*note server directive::), except that it is used to specify an NTP peer rather than an NTP server. Please note that NTP peers that are not configured with a key to enable authentication are vulnerable to a denial-of-service attack. An attacker knowing that NTP hosts A and B are peering with each other can send a packet with random timestamps to host A with source address of B which will set the NTP state variables on A to the values sent by the attacker. Host A will then send on its next poll to B a packet with originate timestamp that doesn't match the transmit timestamp of B and the packet will be dropped. If the attacker does this periodically for both hosts, they won't be able to synchronize to each other. This attack can be prevented by enabling authentication with the key option, or using the 'server' directive on both sides to specify the other host as a server instead of peer, the only drawback is that it will double the network traffic between the two hosts. 4.2.45 pidfile -------------- chronyd always writes its process ID (pid) to a file, and checks this file on startup to see if another chronyd may already be running on the system. By default, the file used is '/var/run/chronyd.pid'. The 'pidfile' directive allows the name to be changed, e.g. pidfile /var/tmp/chronyd.pid 4.2.46 pool ----------- The syntax of this directive is similar to that for the 'server' directive (*note server directive::), except that it is used to specify a pool of NTP servers rather than a single NTP server. The pool name is expected to resolve to multiple addresses which may change over time. All options valid in the 'server' directive can be used in this directive too. There is one option specific to 'pool' directive: 'maxsources' sets the maximum number of sources that can be used from the pool, the default value is 4. On start, when the pool name is resolved, 'chronyd' will add up to 16 sources, one for each resolved address. When the number of sources from which at least one valid reply was received reaches 'maxsources', the other sources will be removed. When a pool source is unreachable or marked as falseticker, 'chronyd' will try to replace the source with a newly resolved address of the pool. An example of the pool directive is pool pool.ntp.org iburst maxsources 3 4.2.47 port ----------- This option allows you to configure the port on which 'chronyd' will listen for NTP requests. The port will be open only when an address is allowed by the 'allow' directive or command, an NTP peer is configured, or the broadcast server mode is enabled. The compiled in default is udp/123, the standard NTP port. If set to 0, 'chronyd' will never open the server port and will operate strictly in a client-only mode. The source port used in NTP client requests can be set by the 'acquisitionport' directive. An example of the port command is port 11123 This would change the NTP port served by 'chronyd' on the computer to udp/11123. 4.2.48 refclock --------------- Reference clocks allows very accurate synchronisation and 'chronyd' can function as a stratum 1 server. They are specified by the 'refclock' directive. It has two mandatory parameters, a refclock driver name and a driver specific parameter. There are currently four drivers included: 'PPS' PPSAPI (pulse per second) driver. The parameter is the path to a PPS device. Assert events are used by default. Driver option ':clear' can be appended to the path if clear events should be used instead. As PPS refclock gets only sub-second time information, it needs another source (NTP or non-PPS refclock) or local directive (*note local directive::) enabled to work. For example: refclock PPS /dev/pps0 lock NMEA refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect 'SHM' NTP shared memory driver. This driver uses a shared memory segment to receive data from another daemon which communicates with an actual reference clock. The parameter is the number of a shared memory segment, usually 0, 1, 2 or 3. For example: refclock SHM 1 poll 3 refid GPS1 A driver option in form ':perm=NNN' can be appended to the segment number to create the segment with permissions other than the default '0600'. Some examples of applications that can be used as SHM sources are 'gpsd' (http://catb.org/gpsd/), 'shmpps' and 'radioclk' (http://www.buzzard.me.uk/jonathan/radioclock.html). 'SOCK' Unix domain socket driver. It is similar to the SHM driver, but uses a different format and uses a socket instead of shared memory. It does not require polling and it supports transmitting of PPS data. The parameter is a path to the socket which will be created by 'chronyd' and used to receive the messages. The format of messages sent over the socket is described in the 'refclock_sock.c' file. Recent versions of the 'gpsd' daemon include support for the SOCK protocol. The path where the socket should be created is described in the 'gpsd(8)' man page. For example: refclock SOCK /var/run/chrony.ttyS0.sock 'PHC' PTP hardware clock (PHC) driver. The parameter is the path to the device of the PTP clock, which can be synchronised by a PTP daemon (e.g. 'ptp4l' from the Linux PTP project (http://linuxptp.sourceforge.net/). The PTP clocks are typically kept in TAI instead of UTC. The 'offset' option can be used to compensate for the current UTC/TAI offset. For example: refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -35 The 'refclock' command also supports a number of subfields (which may be defined in any order): 'poll' Timestamps produced by refclock drivers are not used immediately, but they are stored and processed by a median filter in the polling interval specified by this option. This is defined as a power of 2 and may be negative to specify a sub-second interval. The default is 4 (16 seconds). A shorter interval allows 'chronyd' to react faster to changes in clock frequency, but it may decrease the accuracy if the source is too noisy. 'dpoll' Some drivers don't listen for external events and try to produce samples in their own polling interval. This is defined as a power of 2 and may be negative to specify a sub-second interval. The default is 0 (1 second). 'refid' This option is used to specify a reference id of the refclock, as up to four ASCII characters. By default, first three characters from driver name and the number of the refclock are used as refid. Each refclock must have an unique refid. 'filter' This option sets the length of the median filter which is used to reduce noise. With each poll about 40 percent of the stored samples is discarded and one final sample is calculated as average of the remaining samples. If the length is 4 or above, at least 4 samples have to be collected between polls. For lengths below 4, the filter has to be full. The default is 64. 'rate' PPS signal frequency (in Hz). This option only controls how the received pulses are aligned. To actually receive more than one pulse per second, a negative 'dpoll' has to be specified (-3 for 5Hz signal). The default is 1. 'lock' This option can be used to lock a PPS refclock to another refclock whose reference id is specified by this option. In this mode received pulses are aligned directly to unfiltered samples from the refclock. By default, pulses are aligned to local clock, but only when it is well synchronised. 'offset' This option can be used to compensate a constant error. The specified offset (in seconds) is applied to all samples produced by the refclock. The default is 0.0. 'delay' This option sets the NTP delay of the source (in seconds). Half of this value is included in the maximum assumed error which is used in the source selection algorithm. Increasing the delay is useful to avoid having no majority in the algorithm or to make it prefer other sources. The default is 1e-9 (1 nanosecond). 'precision' Refclock precision (in seconds). The default is 1e-6 (1 microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC refclocks. 'maxdispersion' Maximum allowed dispersion for filtered samples (in seconds). Samples with larger estimated dispersion are ignored. By default, this limit is disabled. 'prefer' Prefer this source over sources without prefer option. 'noselect' Never select this source. This is useful for monitoring or with sources which are not very accurate, but are locked with a PPS refclock. 'minsamples' Set the minimum number of samples kept for this source. This overrides the 'minsamples' directive (*note minsamples directive::). 'maxsamples' Set the maximum number of samples kept for this source. This overrides the 'maxsamples' directive (*note maxsamples directive::). 4.2.49 reselectdist ------------------- When 'chronyd' selects synchronisation source from available sources, it will prefer the one with minimum synchronisation distance. However, to avoid frequent reselecting when there are sources with similar distance, a fixed distance is added to the distance for sources that are currently not selected. This can be set with the 'reselectdist' option. By default, the distance is 100 microseconds. The syntax is reselectdist 4.2.50 rtcautotrim ------------------ The 'rtcautotrim' directive is used to keep the real time clock (RTC) close to the system clock automatically. When the system clock is synchronized and the estimated error between the two clocks is larger than the specified threshold, 'chronyd' will trim the RTC as if the 'trimrtc' (*note trimrtc command::) command was issued. This directive is effective only with the 'rtcfile' directive. An example of the use of this directive is rtcautotrim 30 This would set the threshold error to 30 seconds. 4.2.51 rtcdevice ---------------- The 'rtcdevice' directive defines the name of the device file for accessing the real time clock. By default this is '/dev/rtc', unless the directive is used to set a different value. This applies to Linux systems with devfs. An example of use is rtcdevice /dev/misc/rtc 4.2.52 rtcfile -------------- The 'rtcfile' directive defines the name of the file in which 'chronyd' can save parameters associated with tracking the accuracy of the system's real-time clock (RTC). The syntax is illustrated in the following example rtcfile /var/lib/chrony/rtc 'chronyd' saves information in this file when it exits and when the 'writertc' command is issued in 'chronyc'. The information saved is the RTC's error at some epoch, that epoch (in seconds since January 1 1970), and the rate at which the RTC gains or loses time. So far, the support for real-time clocks is limited - their code is even more system-specific than the rest of the software. You can only use the real time clock facilities (the 'rtcfile' directive and the '-s' command line option to 'chronyd') if the following three conditions apply: 1. You are running Linux version 2.2.x or later. 2. You have compiled the kernel with extended real-time clock support (i.e. the '/dev/rtc' device is capable of doing useful things). 3. You don't have other applications that need to make use of '/dev/rtc' at all. 4.2.53 rtconutc --------------- 'chronyd' assumes by default that the real time clock (RTC) keeps local time (including any daylight saving changes). This is convenient on PCs running Linux which are dual-booted with DOS or Windows. NOTE : IF YOU KEEP THE REAL TIME CLOCK ON LOCAL TIME AND YOUR COMPUTER IS OFF WHEN DAYLIGHT SAVING (SUMMER TIME) STARTS OR ENDS, THE COMPUTER'S SYSTEM TIME WILL BE ONE HOUR IN ERROR WHEN YOU NEXT BOOT AND START CHRONYD. An alternative is for the RTC to keep Universal Coordinated Time (UTC). This does not suffer from the 1 hour problem when daylight saving starts or ends. If the 'rtconutc' directive appears, it means the RTC is required to keep UTC. The directive takes no arguments. It is equivalent to specifying the '-u' switch to the Linux '/sbin/hwclock' program. Note that this setting is overriden when the 'hwclockfile' directive (*note hwclockfile directive::) is used. 4.2.54 rtcsync -------------- The 'rtcsync' directive will enable a kernel mode where the system time is copied to the real time clock (RTC) every 11 minutes. This directive is supported only on Linux and cannot be used when the normal RTC tracking is enabled, i.e. when the 'rtcfile' directive is used. 4.2.55 sched_priority --------------------- The 'sched_priority' directive will select the SCHED_FIFO real-time scheduler at the specified priority (which must be between 0 and 100). This mode is supported only on Linux. This directive uses the Linux sched_setscheduler() system call to instruct the kernel to use the SCHED_FIFO first-in, first-out real-time scheduling policy for 'chronyd' with the specified priority. This means that whenever 'chronyd' is ready to run it will run, interrupting whatever else is running unless it is a higher priority real-time process. This should not impact performance as 'chronyd's' resource requirements are modest, but it should result in lower and more consistent latency since 'chronyd' will not need to wait for the scheduler to get around to running it. You should not use this unless you really need it. The sched_setscheduler man page has more details. 4.2.56 server ------------- The 'server' directive allows NTP servers to be specified. The client/server relationship is strictly hierarchical : a client may synchronise its system time to that of the server, but the server's system time will never be influenced by that of a client. The 'server' directive is immediately followed by either the name of the server, or its IP address. The server command also supports a number of subfields (which may be defined in any order): 'port' This option allows the UDP port on which the server understands NTP requests to be specified. For normal servers this option should not be required (the default is 123, the standard NTP port). 'minpoll' Although 'chronyd' will trim the rate at which it samples the server during normal operation, the user may wish to constrain the minimum polling interval. This is always defined as a power of 2, so 'minpoll 5' would mean that the polling interval cannot drop below 32 seconds. The default is 6 (64 seconds). 'maxpoll' In a similar way, the user may wish to constrain the maximum polling interval. Again this is specified as a power of 2, 'maxpoll 9' indicates that the polling interval must stay at or below 512 seconds. The default is 10 (1024 seconds). 'maxdelay' 'chronyd' uses the network round-trip delay to the server to determine how accurate a particular measurement is likely to be. Long round-trip delays indicate that the request, or the response, or both were delayed. If only one of the messages was delayed the measurement error is likely to be substantial. For small variations in round trip delay, 'chronyd' uses a weighting scheme when processing the measurements. However, beyond a certain level of delay the measurements are likely to be so corrupted as to be useless. (This is particularly so on dial-up or other slow links, where a long delay probably indicates a highly asymmetric delay caused by the response waiting behind a lot of packets related to a download of some sort). If the user knows that round trip delays above a certain level should cause the measurement to be ignored, this level can be defined with the maxdelay command. For example, 'maxdelay 0.3' would indicate that measurements with a round-trip delay of 0.3 seconds or more should be ignored. The default value is 3 seconds. 'maxdelayratio' This option is similar to the maxdelay option above. 'chronyd' keeps a record of the minimum round-trip delay amongst the previous measurements that it has buffered. If a measurement has a round trip delay that is greater than the maxdelayratio times the minimum delay, it will be rejected. 'maxdelaydevratio' If a measurement has ratio of the increase in round-trip delay from the minimum delay amongst the previous measurements to the standard deviation of the previous measurements that is greater than maxdelaydevratio, it will be rejected. The default is 10.0. 'presend' If the timing measurements being made by 'chronyd' are the only network data passing between two computers, you may find that some measurements are badly skewed due to either the client or the server having to do an ARP lookup on the other party prior to transmitting a packet. This is more of a problem with long sampling intervals, which may be similar in duration to the lifetime of entries in the ARP caches of the machines. In order to avoid this problem, the 'presend' option may be used. It takes a single integer argument, which is the smallest polling interval for which an extra pair of NTP packets will be exchanged between the client and the server prior to the actual measurement. For example, with the following option included in a 'server' directive : presend 9 when the polling interval is 512 seconds or more, an extra NTP client packet will be sent to the server a short time (currently 4 seconds) before making the actual measurement. 'key' The NTP protocol supports the inclusion of checksums in the packets, to prevent computers having their system time upset by rogue packets being sent to them. The checksums are generated as a function of a password, using the cryptographic hash function set in the key file. The association between key numbers and passwords is contained in the keys file, defined by the keyfile command. If the key option is present, 'chronyd' will attempt to use authenticated packets when communicating with this server. The key number used will be the single argument to the key option (an unsigned integer in the range 1 through 2**32-1). The server must have the same password for this key number configured, otherwise no relationship between the computers will be possible. 'offline' If the server will not be reachable when 'chronyd' is started, the offline option may be specified. 'chronyd' will not try to poll the server until it is enabled to do so (by using the online option of 'chronyc'). 'auto_offline' If this option is set, the server will be assumed to have gone offline when 2 requests have been sent to it without receiving a response. This option avoids the need to run the 'offline' (*note offline command::) command from chrony when disconnecting the dial-up link. (It will still be necessary to use chronyc's 'online' (*note online command::) command when the link has been established, to enable measurements to start.) 'iburst' On start, make four measurements over a short duration (rather than the usual periodic measurements). 'minstratum' When the synchronisation source is selected from available sources, sources with lower stratum are normally preferred. This option can be used to increase stratum of the source to the specified minimum, so 'chronyd' will avoid selecting that source. This is useful with low stratum sources that are known to be unrealiable or inaccurate and which should be used only when other sources are unreachable. 'polltarget' Target number of measurements to use for the regression algorithm which 'chronyd' will try to maintain by adjusting polling interval between 'minpoll' and 'maxpoll'. A higher target makes 'chronyd' prefer shorter polling intervals. The default is 6 and a useful range is 6 to 60. 'version' This option sets the NTP version number used in packets sent to the server. This can be useful when the server runs an old NTP implementation that doesn't respond to newer versions. The default version number is 4. 'prefer' Prefer this source over sources without prefer option. 'noselect' Never select this source. This is particularly useful for monitoring. 'minsamples' Set the minimum number of samples kept for this source. This overrides the 'minsamples' directive (*note minsamples directive::). 'maxsamples' Set the maximum number of samples kept for this source. This overrides the 'maxsamples' directive (*note maxsamples directive::). 4.2.57 smoothtime ----------------- The 'smoothtime' directive can be used to enable smoothing of the time that 'chronyd' serves to its clients to make it easier for them to track it and keep their clocks close together even when large offset or frequency corrections are applied to the server's clock, for example after being offline for a longer time. BE WARNED - the server is intentionally not serving its best estimate of the true time. If a large offset has been accumulated, it may take a very long time to smooth it out. This directive should be used only when the clients are not configured to poll also another NTP server, because they could reject this server as a falseticker or fail to select a source completely. The smoothing process is implemented with a quadratic spline function with two or three pieces. It's independent from any slewing applied to the local system clock, but the accumulated offset and frequency will be reset when the clock is corrected by stepping, e.g. by the 'makestep' directive or command. The process can be reset without stepping the clock by the 'smoothtime reset' command (*note smoothtime command::). The first two arguments of the directive are the maximum frequency offset of the smoothed time to the tracked NTP time (in ppm) and the maximum rate at which the frequency offset is allowed to change (in ppm per second). 'leaponly' is an optional third argument which enables a mode where only leap seconds are smoothed out and normal offset/frequency changes are ignored. The 'leaponly' option is useful in a combination with the 'leapsecmode slew' option (*note leapsecmode directive::) to allow clients use multiple time smoothing servers safely. The smoothing process is activated automatically when 1/10000 of the estimated skew of the local clock falls below the maximum rate of frequency change. It can be also activated manually by the 'smoothtime activate' command, which is particularly useful when the clock is synchronized only with manual input and the skew is always larger than the threshold. The 'smoothing' command (*note smoothing command::) can be used to monitor the process. An example suitable for clients using 'ntpd' and 1024 second polling interval could be smoothtime 400 0.001 An example suitable for clients using 'chronyd' on Linux could be smoothtime 50000 0.01 4.2.58 stratumweight -------------------- The 'stratumweight' directive sets how much distance should be added per stratum to the synchronisation distance when 'chronyd' selects the synchronisation source from available sources. The syntax is stratumweight By default, the weight is 0.001 seconds. This means that stratum of the sources in the selection process matters only when the differences between the distances are in milliseconds. 4.2.59 tempcomp --------------- Normally, changes in the rate of drift of the system clock are caused mainly by changes in the temperature of the crystal oscillator on the mainboard. If there are temperature measurements available from a sensor close to the oscillator, the 'tempcomp' directive can be used to compensate for the changes in the temperature and improve the stability and accuracy of the clock. The result depends on many factors, including the resolution of the sensor, the amount of noise in the measurements, the polling interval of the time source, the compensation update interval, how well is the compensation specified, and how close is the sensor to the oscillator. When it's working well, the frequency reported in the 'tracking.log' file is more stable and the maximum reached offset is smaller. There are two forms of the directive. The first one has six parameters: a path to the file containing the current temperature from the sensor (in text format), the compensation update interval (in seconds), and temperature coefficients T0, k0, k1, k2. The frequency compensation is calculated (in ppm) as 'k0 + (T - T0) * k1 + (T - T0)^2 * k2' The result has to be between -10 ppm and 10 ppm, otherwise the measurement is considered invalid and will be ignored. The k0 coefficient can be used to get the results in that range. An example of use is tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0 The measured temperature will be read from the file in the Linux sysfs filesystem every 30 seconds. When the temperature is 26000 (26 degrees Celsius), the frequency correction will be zero. When it is 27000 (27 degrees Celsius), the clock will be set to run 0.183ppm faster, etc. The second form has three parameters, the path to the sensor file, the update interval and a path to a file containing a list of (temperature, compensation) points, from which the compensation is linearly interpolated or extrapolated. An example is tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp where the 'chrony.tempcomp' file could have 20000 1.0 21000 0.64 22000 0.36 23000 0.16 24000 0.04 25000 0.0 26000 0.04 27000 0.16 28000 0.36 29000 0.64 30000 1.0 Valid measurements with corresponding compensations are logged to the 'tempcomp.log' file if enabled by the 'log tempcomp' directive. 4.2.60 user ----------- The 'user' directive sets the name of the user to which will 'chronyd' switch on initialisation to drop root privileges. So far, it works only on Linux when compiled with capabilities support. Setting the name to root will disable it. The default value is 'root'. 4.3 Running chronyc =================== Chronyc is the program that can be used to reconfigure options within the 'chronyd' program whilst it is running. Chronyc can also be used to generate status reports about the operation of 'chronyd'. 4.3.1 Basic use --------------- The program chronyc is run by entering chronyc at the command line. The prompt 'chronyc' is displayed whilst chronyc is expecting input from the user, when it is being run from a terminal. If chronyc's input or output are redirected from/to a file, the prompt is now shown. When you are finished entering commands, the commands 'exit' or 'quit' will terminate the program. (Entering will also terminate the program.) 4.3.2 Command line options -------------------------- Chronyc supports the following command line options. '-v' Displays the version number of chronyc on the terminal, and exists. '-h ' This option allows the user to specify which host running the 'chronyd' program is to be contacted. This allows for remote configuration, without having to ssh to the other host first. The default is to contact 'chronyd' running on the same host as that where chronyc is being run. '-p ' This option allows the user to specify the UDP port number which the target 'chronyd' is using for its command & monitoring connections. This defaults to the compiled-in default; there would rarely be a need to change this. '-n' This option disables resolving IP addresses to hostnames. '-4' With this option hostnames will be resolved only to IPv4 addresses. '-6' With this option hostnames will be resolved only to IPv6 addresses. '-m' With this option multiple commands can be specified on the command line. Each argument will be interpreted as a whole command. '-f ' This option can be used to specify an alternate location of the 'chronyd' configuration file (default '/etc/chrony.conf'). The configuration file is needed for the '-a' option. '-a' With this option 'chronyc' will try to authenticate automatically on start. It will read the configuration file, read the command key from the keyfile and run the authhash and password commands. 4.3.3 Security with chronyc --------------------------- Many of the commands available through chronyc have a fair amount of power to reconfigure the run-time behaviour of 'chronyd'. Consequently, 'chronyc' is quite dangerous for the integrity of the target system's clock performance. Having access to 'chronyd' via chronyc is more or less equivalent to being able to modify 'chronyd's' configuration file (typically '/etc/chrony.conf') and to restart 'chronyd'. Chronyc also provides a number of monitoring (as opposed to commanding) commands, which will not affect the behaviour of 'chronyd'. However, you may still want to restrict access to these commands. In view of this, access to some of the capabilities of chronyc will usually be tightly controlled. There are two mechanisms supported: 1. The set of hosts from which 'chronyd' will accept commands can be restricted. By default, commands will only be accepted from the same host that 'chronyd' is running on. 2. Any command that actually reconfigures some aspect of 'chronyd's' behaviour requires the user of chronyc to know a password. This password is specified in 'chronyd's' keys file (*note keyfile directive::) and specified via the commandkey option in its configuration file (*note commandkey directive::). Only the following commands can be used _without_ providing a password: * 'activity' * 'authhash' * 'dns' * 'exit' * 'help' * 'password' * 'quit' * 'rtcdata' * 'smoothing' * 'sources' * 'sourcestats' * 'tracking' * 'waitsync' All other commands require a password to have been specified previously, because they affect 'chronyd's' operation. 4.3.4 Command reference ----------------------- This section describes each of the commands available within the chronyc program. Chronyc offers the user a simple command-line driven interface. 4.3.4.1 accheck ............... This command allows you to check whether client NTP access is allowed from a particular host. Examples of use, showing a named host and a numeric IP address, are as follows: accheck foo.example.net accheck 1.2.3.4 accheck 2001:db8::1 This command can be used to examine the effect of a series of 'allow', 'allow all', 'deny' and 'deny all' commands specified either via chronyc, or in 'chronyd's' configuration file. 4.3.4.2 activity ................ This command reports the number of servers/peers that are online and offline. If the auto_offline option is used in specifying some of the servers/peers, the 'activity' command may be useful for detecting when all of them have entered the offline state after the PPP link has been disconnected. The report shows the number of servers/peers in 5 states: * 'online' : the server/peer is currently online (i.e. assumed by chronyd to be reachable) * 'offline' : the server/peer is currently offline (i.e. assumed by chronyd to be unreachable, and no measurements from it will be attempted.) * 'burst_online' : a burst command has been initiated for the server/peer and is being performed; after the burst is complete, the server/peer will be returned to the online state. * 'burst_offline' : a burst command has been initiated for the server/peer and is being performed; after the burst is complete, the server/peer will be returned to the offline state. * 'unresolved' : the name of the server/peer wasn't resolved to an address yet; this server is not visible in the 'sources' and 'sourcestats' reports. 4.3.4.3 add peer ................ The 'add peer' command allows a new NTP peer to be added whilst 'chronyd' is running. Following the words 'add peer', the syntax of the following parameters and options is similar to that for the 'peer' directive in the configuration file (*note peer directive::). The following peer options can be set in the command: 'port', 'minpoll', 'maxpoll', 'presend', 'maxdelayratio', 'maxdelay', 'key' An example of using this command is shown below. add peer foo.example.net minpoll 6 maxpoll 10 key 25 4.3.4.4 add server .................. The 'add server' command allows a new NTP server to be added whilst 'chronyd' is running. Following the words 'add server', the syntax of the following parameters and options is similar to that for the 'server' directive in the configuration file (*note server directive::). The following server options can be set in the command: 'port', 'minpoll', 'maxpoll', 'presend', 'maxdelayratio', 'maxdelay', 'key' An example of using this command is shown below. add server foo.example.net minpoll 6 maxpoll 10 key 25 4.3.4.5 allow all ................. The effect of the allow command is identical to the 'allow all' directive in the configuration file (*note allow directive::). 4.3.4.6 allow ............. The effect of the allow command is identical to the 'allow' directive in the configuration file (*note allow directive::). The syntax is illustrated in the following examples: allow foo.example.net allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 2001:db8:789a::/48 allow 0/0 allow ::/0 allow The effect of each of these examples is the same as that of the 'allow' directive in the configuration file. 4.3.4.7 authhash ................ This command selects the hash function used for authenticating user commands. For successful authentication the hash function has to be the same as the function specified for the command key in the keys file on the server (*note keyfile directive::). It needs to be selected before the 'password' command is used. The default hash function is MD5. An example is authhash SHA1 The authhash command is run automatically on start if 'chronyc' was started with the '-a' option. 4.3.4.8 burst ............. The 'burst' command tells 'chronyd' to make a set of measurements to each of its NTP sources over a short duration (rather than the usual periodic measurements that it makes). After such a burst, 'chronyd' will revert to the previous state for each source. This might be either online, if the source was being periodically measured in the normal way, or offline, if the source had been indicated as being offline. (Switching a source between the online and offline states is described in *note online command::, *note offline command::). The syntax of the burst command is as follows burst / [/] burst / [/] burst / [
] The mask and masked-address arguments are optional, in which case 'chronyd' will initiate a burst for all of its currently defined sources. The arguments have the following meaning and format. 'n-good-measurements' This defines the number of good measurements that 'chronyd' will want to obtain from each source. A measurement is good if it passes certain tests, for example, the round trip time to the source must be acceptable. (This allows 'chronyd' to reject measurements that are likely to be bogus.) 'max-measurements' This defines the maximum number of measurements that 'chronyd' will attempt to make, even if the required number of good measurements has not been obtained. 'mask' This is an IP address with which the IP address of each of 'chronyd''s sources is to be masked. 'masked-address' This is an IP address. If the masked IP address of a source matches this value then the burst command is applied to that source. 'masked-bits' This can be used with 'masked-address' for CIDR notation, which is a shorter alternative to the form with mask. 'address' This is an IP address or a hostname. The burst command is applied only to that source. If no mask or masked address arguments are provided, every source will be matched. An example of the two-argument form of the command is burst 2/10 This will cause 'chronyd' to attempt to get two good measurements from each source, stopping after two have been obtained, but in no event will it try more than ten probes to the source. Examples of the four-argument form of the command are burst 2/10 255.255.0.0/1.2.0.0 burst 2/10 2001:db8:789a::/48 In the first case, the two out of ten sampling will only be applied to sources whose IPv4 addresses are of the form '1.2.x.y', where x and y are arbitrary. In the second case, the sampling will be applied to sources whose IPv6 addresses have first 48 bits equal to '2001:db8:789a'. Example of the three-argument form of the command is burst 2/10 foo.example.net 4.3.4.9 clients ............... This command shows a list of all clients that have accessed the server, through either the NTP or command/monitoring ports. There are no arguments. An example of the output is Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC ========================= ====== ====== ====== ====== ====== ==== ==== localhost 0 0 15 1 0 29y 0 aardvark.xxx 4 0 0 0 0 49 29y badger.xxx 4 0 0 0 0 6 29y Each row shows the data for a single host. Only hosts that have passed the host access checks (set with the 'allow', 'deny', 'cmdallow' and 'cmddeny' commands or configuration file directives) are logged. The columns are as follows: 1. The hostname of the client 2. The number of times the client has accessed the server using an NTP client mode packet. 3. The number of times the client has accessed the server using an NTP symmetric active mode packet. 4. The number of authenticated command packets that have been processed from the client (i.e. those following a successful 'password' command). 5. The number of unauthenticated command packets that have been processed from the client. 6. The number of bad command packets received from the client (not all forms of bad packet are logged). 7. Time since the last NTP packet was received 8. Time since the last command packet was received The last two entries will be shown as the time since 1970 if no packet of that type has ever been received. 4.3.4.10 cmdaccheck ................... This command is similar to the 'accheck' command, except that it is used to check whether command access is permitted from a named host. Examples of use are as follows: cmdaccheck foo.example.net cmdaccheck 1.2.3.4 cmdaccheck 2001:db8::1 4.3.4.11 cmdallow all ..................... This is similar to the 'allow all' command, except that it is used toallow particular hosts or subnets to use the chronyc program to interactwith 'chronyd' on the current host. 4.3.4.12 cmdallow ................. This is similar to the 'allow' command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with 'chronyd' on the current host. 4.3.4.13 cmddeny all .................... This is similar to the 'deny all' command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with 'chronyd' on the current host. 4.3.4.14 cmddeny ................ This is similar to the 'deny' command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with 'chronyd' on the current host. 4.3.4.15 cyclelogs .................. The 'cyclelogs' command causes all of 'chronyd's' open log files to be closed and re-opened. This allows them to be renamed so that they can be periodically purged. An example of how to do this is shown below. % mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log % chronyc -a cyclelogs % ls -l /var/log/chrony -rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log -rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log % rm -f measurements1.log 4.3.4.16 delete ............... The 'delete' command allows an NTP server or peer to be removed from the current set of sources. The syntax is illustrated in the examples below. delete foo.example.net delete 1.2.3.4 delete 2001:db8::1 There is one parameter, the name or IP address of the server or peer to be deleted. 4.3.4.17 deny all ................. The effect of the allow command is identical to the 'deny all' directive in the configuration file (*note deny directive::). 4.3.4.18 deny ............. The effect of the allow command is identical to the 'deny' directive in the configuration file (*note deny directive::). The syntax is illustrated in the following examples: deny foo.example.net deny 1.2 deny 3.4.5 deny 6.7.8/22 deny 6.7.8.9/22 deny 2001:db8:789a::/48 deny 0/0 deny ::/0 deny 4.3.4.19 dns ............ The 'dns' command configures how are hostnames and IP addresses resolved in 'chronyc'. IP addresses can be resolved to hostnames when printing results of 'sources', 'sourcestats', 'tracking' and 'clients' commands. Hostnames are resolved in commands that take an address as argument. There are five forms of the command: 'dns -n' Disables resolving IP addresses to hostnames. Raw IP addresses will be displayed. 'dns +n' Enables resolving IP addresses to hostnames. This is the default unless 'chronyc' was started with '-n' option. 'dns -4' Resolves hostnames only to IPv4 addresses. 'dns -6' Resolves hostnames only to IPv6 addresses. 'dns -46' Resolves hostnames to both address families. This is the default unless 'chronyc' was started with '-4' or '-6' option. 4.3.4.20 dump ............. The 'dump' command causes 'chronyd' to write its current history of measurements for each of its sources to dump files, either for inspection or to support the '-r' option when 'chronyd' is restarted. The 'dump' command is somewhat equivalent to the 'dumponexit' directive in the chrony configuration file. *Note dumponexit directive::. To use the 'dump', you probably want to configure the name of the directory into which the dump files will be written. This can only be done in the configuration file, see *note dumpdir directive::. 4.3.4.21 exit ............. The exit command exits from chronyc and returns the user to the shell (same as the quit command). 4.3.4.22 help ............. The help command displays a summary of the commands and their arguments. 4.3.4.23 local .............. The 'local' command allows 'chronyd' to be told that it is to appear as a reference source, even if it is not itself properly synchronised to an external source. (This can be used on isolated networks, to allow one computer to be a master time server with the other computers slaving to it.) The 'local' command is somewhat equivalent to the 'local' directive in the configuration file, see *note local directive::. The syntax is as shown in the following examples. local stratum 10 local off The first example enables the local reference mode on the host, and sets the stratum at which it should claim to be synchronised. The second example disables the local reference mode. 4.3.4.24 makestep ................. Normally chronyd will cause the system to gradually correct any time offset, by slowing down or speeding up the clock as required. In certain situations, the system clock may be so far adrift that this slewing process would take a very long time to correct the system clock. The 'makestep' command can be used in this situation. There are two forms of the command. The first form has no parameters. It tells 'chronyd' to cancel any remaining correction that was being slewed and jump the system clock by the equivalent amount, making it correct immediately. The second form configures the automatic stepping, similarly to the 'makestep' directive (*note makestep directive::). It has two parameters, stepping threshold (in seconds) and number of future clock updates for which will be the threshold active. This can be used with the 'burst' command to quickly make a new measurement and correct the clock by stepping if needed, without waiting for 'chronyd' to complete the measurement and update the clock. makestep 0.1 1 burst 1/2 BE WARNED - certain software will be seriously affected by such jumps to the system time. (That is the reason why chronyd uses slewing normally.) 4.3.4.25 manual ............... The manual command enables and disables use of the 'settime' command (*note settime command::), and is used to modify the behaviour of the manual clock driver. Examples of the command are shown below. manual on manual off manual delete 1 manual list manual reset The 'on' form of the command enables use of the 'settime' command. The 'off' form of the command disables use of the 'settime' command. The 'list' form of the command lists all the samples currently stored in 'chronyd'. The output is illustrated below. 210 n_samples = 1 # Date Time(UTC) Slewed Original Residual ==================================================== 0 27Jan99 22:09:20 0.00 0.97 0.00 The columns as as follows : 1. The sample index (used for the 'manual delete' command) 2. The date and time of the sample 3. The system clock error when the timestamp was entered, adjusted to allow for changes made to the system clock since. 4. The system clock error when the timestamp was entered, as it originally was (without allowing for changes to the system clock since). 5. The regression residual at this point, in seconds. This allows 'outliers' to be easily spotted, so that they can be deleted using the 'manual delete' command. The 'delete' form of the command deletes a single sample. The parameter is the index of the sample, as shown in the first column of the output from 'manual list'. Following deletion of the data point, the current error and drift rate are re-estimated from the remaining data points and the system clock trimmed if necessary. This option is intended to allow 'outliers' to be discarded, i.e. samples where the administrator realises he/she has entered a very poor timestamp. The 'reset' form of the command deletes all samples at once. The system clock is left running as it was before the command was entered. 4.3.4.26 maxdelay ................. This allows the 'maxdelay' option for one of the sources to be modified, in the same way as specifying the 'maxdelay' option for the 'server' directive in the configuration file (*note server directive::). The following examples illustrate the syntax maxdelay foo.example.net 0.3 maxdelay 1.2.3.4 0.0015 maxdelay 2001:db8::1 0.0015 The first example sets the maximum network delay allowed for a measurement to the host 'foo.example.net' to 0.3 seconds. The second and third examples set the maximum network delay for a measurement to the host with IPv4 address '1.2.3.4' and the host with IPv6 address '2001:db8::1' to 1.5 milliseconds. (Any measurement whose network delay exceeds the specified value is discarded.) 4.3.4.27 maxdelaydevratio ......................... This allows the 'maxdelaydevratio' option for one of the sources to be modified, in the same way as specifying the 'maxdelaydevratio' option for the 'server' directive in the configuration file (*note server directive::). The following examples illustrate the syntax maxdelaydevratio foo.example.net 0.1 maxdelaydevratio 1.2.3.4 1.0 maxdelaydevratio 2001:db8::1 100.0 4.3.4.28 maxdelayratio ...................... This allows the 'maxdelayratio' option for one of the sources to be modified, in the same way as specifying the 'maxdelayratio' option for the 'server' directive in the configuration file (*note server directive::). The following examples illustrate the syntax maxdelayratio foo.example.net 1.5 maxdelayratio 1.2.3.4 2.0 maxdelayratio 2001:db8::1 2.0 The first example sets the maximum network delay for a measurement to the host 'foo.example.net' to be 1.5 times the minimum delay found amongst the previous measurements that have been retained. The second and third examples set the maximum network delay for a measurement to the host with IPv4 address '1.2.3.4' and the host with IPv6 address '2001:db8::1' to be double the retained minimum. As for 'maxdelay', any measurement whose network delay is too large will be discarded. 4.3.4.29 maxpoll ................ The 'maxpoll' command is used to modify the minimum polling interval for one of the current set of sources. It is equivalent to the 'maxpoll' option in the 'server' directive in the configuration file (*note server directive::). The syntax is as follows maxpoll where the host can be specified as either a machine name or IP address. The new minimum poll is specified as a base-2 logarithm of the number of seconds between polls (e.g. specify 6 for 64 second sampling). An example is maxpoll foo.example.net 10 which sets the maximum polling interval for the host 'foo.example.net' to 1024 seconds. Note that the new maximum polling interval only takes effect after the next measurement has been made. 4.3.4.30 maxupdateskew ...................... This command has the same effect as the 'maxupdateskew' directive in the configuration file, see *note maxupdateskew directive::. 4.3.4.31 minpoll ................ The 'minpoll' command is used to modify the minimum polling interval for one of the current set of sources. It is equivalent to the 'minpoll' option in the 'server' directive in the configuration file (*note server directive::). The syntax is as follows minpoll where the host can be specified as either a machine name or IP address. The new minimum poll is specified as a base-2 logarithm of the number of seconds between polls (e.g. specify 6 for 64 second sampling). An example is minpoll foo.example.net 5 which sets the minimum polling interval for the host 'foo.example.net' to 32 seconds. Note that the new minimum polling interval only takes effect after the next measurement has been made. 4.3.4.32 minstratum ................... The 'minstratum' command is used to modify the minimum stratum for one of the current set of sources. It is equivalent to the 'minstratum' option in the 'server' directive in the configuration file (*note server directive::). The syntax is as follows minstratum where the host can be specified as either a machine name or IP address. An example is minpoll foo.example.net 5 which sets the minimum stratum for the host 'foo.example.net' to 5. Note that the new minimum stratum only takes effect after the next measurement has been made. 4.3.4.33 offline ................ The 'offline' command is used to warn 'chronyd' that the network connection to a particular host or hosts is about to be lost. It should be used on computers with a dial-up or similar connection to their time sources, to warn 'chronyd' that the connection is about to be broken. An example of how to use 'offline' in this case is shown in *note Advising chronyd of internet availability::. Another case where 'offline' could be used is where a computer serves time to a local group of computers, and has a permanant connection to true time servers outside the organisation. However, the external connection is heavily loaded at certain times of the day and the measurements obtained are less reliable at those times. In this case, it is probably most useful to determine the gain/loss rate during the quiet periods and let the whole network coast through the loaded periods. The 'offline' and 'online' commands can be used to achieve this. The situation is shown in the figure below. +----------+ |Ext source| +----------+ | | |/| <-- Link with variable | reliability | +-------------------+ |Local master server| +-------------------+ | +---+---+-----+-----+----+----+ | | | | | | | Local clients If the source to which 'chronyd' is currently synchronised is indicated offline in this way, 'chronyd' will continue to treat it as the synchronisation source. If the network connection were broken without the 'offline' command being used, 'chronyd' would assume that the source had failed and would attempt to pick another synchronisation source. There are four forms of the 'offline' command. The first form is a wildcard, meaning all sources. The second form allows an IP address mask and a masked address to be specified. The third form uses the CIDR notation. The fourth form uses an IP address or a hostname. These forms are illustrated below. offline offline 255.255.255.0/1.2.3.0 offline 2001:db8:789a::/48 offline foo.example.net The second form means that the 'offline' command is to be applied to any source whose IPv4 address is in the '1.2.3' subnet. (The host's address is logically and-ed with the mask, and if the result matches the masked-address the host is processed). The third form means that the command is to be applied to all sources whose IPv6 addresses have first 48 bits equal to '2001:db8:789a'. The fourth form means that the command is to be applied only to that one source. The wildcard form of the address is actually equivalent to offline 0.0.0.0/0.0.0.0 offline ::/0 4.3.4.34 online ............... The 'online' command is opposite in function to the 'offline' command. It is used to advise 'chronyd' that network connectivity to a particular source or sources has been restored. The syntax is identical to that of the 'offline' command, see *note offline command::. 4.3.4.35 password ................. The password command is used to allow chronyc to send privileged commands to 'chronyd'. The password can either be entered on the command line, or can be entered without echoing. The syntax for entering the password on the command line is as follows password xyzzy password ASCII:xyzzy password HEX:78797a7a79 To enter the password without it being echoed, enter password The computer will respond with a 'Password:' prompt, at which you should enter the password and press return. The password can be encoded as a string of characters not containing a space with optional 'ASCII:' prefix or as a hexadecimal number with 'HEX:' prefix. It has to match 'chronyd's' currently defined command key (*note commandkey directive::). If the command key was specified with a different hash function than MD5, it's necessary to select the hash function with the 'authhash' command (*note authhash command::) before entering the password. The password command is run automatically on start if 'chronyc' was started with the '-a' option. 4.3.4.36 polltarget ................... The 'polltarget' command is used to modify the poll target for one of the current set of sources. It is equivalent to the 'polltarget' option in the 'server' directive in the configuration file (*note server directive::). The syntax is as follows polltarget where the host can be specified as either a machine name or IP address. An example is polltarget foo.example.net 12 which sets the poll target for the host 'foo.example.net' to 12. 4.3.4.37 quit ............. The quit command exits from chronyc and returns the user to the shell (same as the exit command). 4.3.4.38 reselect ................. To avoid excessive switching between sources, 'chronyd' may stay synchronised to a source even when it is not currently the best one among the available sources. The 'reselect' command can be used to force 'chronyd' to reselect the best synchronisation source. 4.3.4.39 reselectdist ..................... The 'reselectdist' command sets the reselect distance. It is equivalent to the 'reselectdist' directive in the configuration file (*note reselectdist directive::). 4.3.4.40 retries ................ The 'retries' command sets the maximum number of retries for 'chronyc' requests before giving up. The response timeout is controlled by 'timeout' command (*note timeout command::). The default is 2. 4.3.4.41 rtcdata ................ The 'rtcdata' command displays the current real time clock RTC parameters. An example output is shown below. RTC ref time (GMT) : Sat May 30 07:25:56 1998 Number of samples : 10 Number of runs : 5 Sample span period : 549 RTC is fast by : -1.632736 seconds RTC gains time at : -107.623 ppm The fields have the following meaning 'RTC ref time (GMT)' This is the RTC reading the last time its error was measured. 'Number of samples' This is the number of previous measurements being used to determine the RTC gain/loss rate. 'Number of runs' This is the number of runs of residuals of the same sign following the regression fit for (RTC error) versus (RTC time). A value which is small indicates that the measurements are not well approximated by a linear model, and that the algorithm will tend to delete the older measurements to improve the fit. 'Sample span period' This is the period that the measurements span (from the oldest to the newest). Without a unit the value is in seconds; suffixes 'm' for minutes, 'h' for hours, 'd' for days or 'y' for years may be used. 'RTC is fast by' This is the estimate of how many seconds fast the RTC when it thought the time was at the reference time (above). If this value is large, you may (or may not) want to use the 'trimrtc' command to bring the RTC into line with the system clock. (Note, a large error will not affect 'chronyd's' operation, unless it becomes so big as to start causing rounding errors. 'RTC gains time at' This is the amount of time gained (positive) or lost (negative) by the real time clock for each second that it ticks. It is measured in parts per million. So if the value shown was +1, suppose the RTC was exactly right when it crosses a particular second boundary. Then it would be 1 microsecond fast when it crosses its next second boundary. 4.3.4.42 settime ................ The 'settime' command allows the current time to be entered manually, if this option has been configured into 'chronyd'. (It may be configured either with the 'manual' directive in the configuration file (*note manual directive::), or with the 'manual' command of chronyc (*note manual command::). It should be noted that the computer's sense of time will only be as accurate as the reference you use for providing this input (e.g. your watch), as well as how well you can time the press of the return key. Providing your computer's time zone is set up properly, you will be able to enter a local time (rather than UTC). The response to a successful 'settime' command indicates the amount that the computer's clock was wrong. It should be apparent from this if you have entered the time wrongly, e.g. with the wrong time zone. The rate of drift of the system clock is estimated by a regression process using the entered measurement and all previous measurements entered during the present run of 'chronyd'. However, the entered measurement is used for adjusting the current clock offset (rather than the estimated intercept from the regression, which is ignored). Contrast what happens with the 'manual delete' command, where the intercept is used to set the current offset (since there is no measurement that has just been typed in in that case). The time is parsed by the public domain 'getdate' algorithm. Consequently, you can only specify time to the nearest second. Examples of inputs that are valid are shown below. settime 16:30 settime 16:30:05 settime Nov 21, 1997 16:30:05 For a full description of 'getdate', get hold of the getdate documentation (bundled, for example, with the source for GNU tar). 4.3.4.43 smoothing .................. The 'smoothing' command displays the current state of the NTP server time smoothing. An example of the output is shown below. Active : Yes Offset : +1.000268817 seconds Frequency : -0.142859 ppm Wander : -0.010000 ppm per second Last update : 17.8 seconds ago Remaining time : 19988.4 seconds The fields are explained as follows. 'Active' This shows if the server time smoothing is currently active. Possible values are 'Yes' and 'No'. If the 'leaponly' option is included in the 'smoothtime' directive, '(leap second only)' will be shown on the line. 'Offset' This is the current offset applied to the time sent to NTP clients. Positive value means the clients are getting time that's ahead of true time. 'Frequency' The current frequency offset of the served time. Negative value means the time observed by clients is running slower than true time. 'Wander' The current frequency wander of the served time. Negative value means the time observed by clients is slowing down. 'Last update' This field shows how long ago was the time smoothing process updated, e.g. 'chronyd' accumulated a new measurement. 'Remaining time' The time it would take for the smoothing process to get to zero offset and frequency if there were no more updates. 4.3.4.44 smoothtime ................... The 'smoothtime' command can be used to reset or activate the server time smoothing process if it is configured with the 'smoothtime' directive (*note smoothtime directive::). The syntax is as follows smoothtime reset smoothtime activate 4.3.4.45 sources ................ This command displays information about the current time sources that 'chronyd' is accessing. The optional argument '-v' can be specified, meaning _verbose_. In this case, extra caption lines are shown as a reminder of the meanings of the columns. 210 Number of sources = 3 MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== #* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns ^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms ^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms The columns are as follows: 'M' This indicates the mode of the source. '^' means a server, '=' means a peer and '#' indicates a locally connected reference clock. 'S' This column indicates the state of the sources. '*' indicates the source to which 'chronyd' is currently synchronised. '+' indicates acceptable sources which are combined with the selected source. '-' indicates acceptable sources which are excluded by the combining algorithm. '?' indicates sources to which connectivity has been lost or whose packets don't pass all tests. 'x' indicates a clock which 'chronyd' thinks is is a falseticker (i.e. its time is inconsistent with a majority of other sources). '~' indicates a source whose time appears to have too much variability. The '?' condition is also shown at start-up, until at least 3 samples have been gathered from it. 'Name/IP address' This shows the name or the IP address of the source, or refid for reference clocks. 'Stratum' This shows the stratum of the source, as reported in its most recently received sample. Stratum 1 indicates a computer with a locally attached reference clock. A computer that is synchronised to a stratum 1 computer is at stratum 2. A computer that is synchronised to a stratum 2 computer is at stratum 3, and so on. 'Poll' This shows the rate at which the source is being polled, as a base-2 logarithm of the interval in seconds. Thus, a value of 6 would indicate that a measurement is being made every 64 seconds. 'chronyd' automatically varies the polling rate in response to prevailing conditions. 'Reach' This shows the source's reachability register printed as octal number. The register has 8 bits and is updated on every received or missed packet from the source. A value of 377 indicates that a valid reply was received for all from the last eight transmissions. 'LastRx' This column shows how long ago the last sample was received from the source. This is normally in seconds. The letters 'm', 'h', 'd' or 'y' indicate minutes, hours, days or years. A value of 10 years indicates there were no samples received from this source yet. 'Last sample' This column shows the offset between the local clock and the source at the last measurement. The number in the square brackets shows the actual measured offset. This may be suffixed by 'ns' (indicating nanoseconds), 'us' (indicating microseconds), 'ms' (indicating milliseconds), or 's' (indicating seconds). The number to the left of the square brackets shows the original measurement, adjusted to allow for any slews applied to the local clock since. The number following the '+/-' indicator shows the margin of error in the measurement. Positive offsets indicate that the local clock is fast of the source. 4.3.4.46 sourcestats .................... The 'sourcestats' command displays information about the drift rate and offset estimatation process for each of the sources currently being examined by 'chronyd'. The optional argument '-v' can be specified, meaning _verbose_. In this case, extra caption lines are shown as a reminder of the meanings of the columns. An example report is 210 Number of sources = 1 Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev =============================================================================== abc.def.ghi 11 5 46m -0.001 0.045 1us 25us The columns are as follows 'Name/IP Address' This is the name or IP address of the NTP server (or peer) or refid of the refclock to which the rest of the line relates. 'NP' This is the number of sample points currently being retained for the server. The drift rate and current offset are estimated by performing a linear regression through these points. 'NR' This is the number of runs of residuals having the same sign following the last regression. If this number starts to become too small relative to the number of samples, it indicates that a straight line is no longer a good fit to the data. If the number of runs is too low, 'chronyd' discards older samples and re-runs the regression until the number of runs becomes acceptable. 'Span' This is the interval between the oldest and newest samples. If no unit is shown the value is in seconds. In the example, the interval is 46 minutes. 'Frequency' This is the estimated residual frequency for the server, in parts per million. In this case, the computer's clock is estimated to be running 1 part in 10**9 slow relative to the server. 'Freq Skew' This is the estimated error bounds on 'Freq' (again in parts per million). 'Offset' This is the estimated offset of the source. 'Std Dev' This is the estimated sample standard deviation. 4.3.4.47 timeout ................ The 'timeout' command sets the initial timeout for 'chronyc' requests in milliseconds. If no response is received from 'chronyd', the timeout is doubled and the request is resent. The maximum number of retries is configured with the 'retries' command (*note retries command::). By default, the timeout is 1000 milliseconds or 100 milliseconds if 'chronyc' is contacting localhost (i.e. the '-h' option wasn't specified) and 'chronyd' was compiled with asynchronous name resolving. 4.3.4.48 tracking ................. The 'tracking' command displays parameters about the system's clock performance. An example of the output is shown below. Reference ID : 1.2.3.4 (foo.example.net) Stratum : 3 Ref time (UTC) : Fri Feb 3 15:00:29 2012 System time : 0.000001501 seconds slow of NTP time Last offset : -0.000001632 seconds RMS offset : 0.000002360 seconds Frequency : 331.898 ppm fast Residual freq : 0.004 ppm Skew : 0.154 ppm Root delay : 0.373169 seconds Root dispersion : 0.024780 seconds Update interval : 64.2 seconds Leap status : Normal The fields are explained as follows. 'Reference ID' This is the refid and name (or IP address) if available, of the server to which the computer is currently synchronised. If this is '127.127.1.1' it means the computer is not synchronised to any external source and that you have the 'local' mode operating (via the 'local' command in 'chronyc' (*note local command::), or the 'local' directive in the '/etc/chrony.conf' file (*note local directive::)). 'Stratum' The stratum indicates how many hops away from a computer with an attached reference clock we are. Such a computer is a stratum-1 computer, so the computer in the example is two hops away (i.e. 'foo.example.net' is a stratum-2 and is synchronised from a stratum-1). 'Ref time' This is the time (UTC) at which the last measurement from the reference source was processed. 'System time' In normal operation, 'chronyd' _never_ steps the system clock, because any jump in the timescale can have adverse consequences for certain application programs. Instead, any error in the system clock is corrected by slightly speeding up or slowing down the system clock until the error has been removed, and then returning to the system clock's normal speed. A consequence of this is that there will be a period when the system clock (as read by other programs using the 'gettimeofday()' system call, or by the 'date' command in the shell) will be different from 'chronyd's' estimate of the current true time (which it reports to NTP clients when it is operating in server mode). The value reported on this line is the difference due to this effect. On systems other than Linux, 'chronyd' doesn't adjust the fundamental rate of the system clock, so keeps the system time correct by periodically making offsets to it as though an error had been measured. The build up of these offsets will be observed in this report. 'Last offset' This is the estimated local offset on the last clock update. 'RMS offset' This is a long-term average of the offset value. 'Frequency' The 'frequency' is the rate by which the system's clock would be would be wrong if 'chronyd' was not correcting it. It is expressed in ppm (parts per million). For example, a value of 1ppm would mean that when the system's clock thinks it has advanced 1 second, it has actually advanced by 1.000001 seconds relative to true time. As you can see in the example, the clock in the computer is not a very good one - it gains about 30 seconds per day! 'Residual freq' This shows the 'residual frequency' for the currently selected reference source. This reflects any difference between what the measurements from the reference source indicate the frequency should be and the frequency currently being used. The reason this is not always zero is that a smoothing procedure is applied to the frequency. Each time a measurement from the reference source is obtained and a new residual frequency computed, the estimated accuracy of this residual is compared with the estimated accuracy (see 'skew' next) of the existing frequency value. A weighted average is computed for the new frequency, with weights depending on these accuracies. If the measurements from the reference source follow a consistent trend, the residual will be driven to zero over time. 'Skew' This is the estimated error bound on the the frequency. 'Root delay' This is the total of the network path delays to the stratum-1 computer from which the computer is ultimately synchronised. 'Root dispersion' This is the total dispersion accumulated through all the computers back to the stratum-1 computer from which the computer is ultimately synchronised. Dispersion is due to system clock resolution, statistical measurement variations etc. An absolute bound on the computer's clock accuracy (assuming the stratum-1 computer is correct) is given by clock_error <= root_dispersion + (0.5 * |root_delay|) 'Update interval' This is the interval between the last two clock updates. 'Leap status' This is the leap status, which can be 'Normal', 'Insert second', 'Delete second' or 'Not synchronised'. 4.3.4.49 trimrtc ................ The 'trimrtc' command is used to correct the system's real time clock (RTC) to the main system clock. It has no effect if the error between the two clocks is currently estimated at less than a second (the resolution of the RTC is only 1 second). The command takes no arguments. It performs the following steps (if the RTC is more than 1 second away from the system clock): 1. Remember the currently estimated gain/loss rate of the RTC and flush the previous measurements. 2. Step the real time clock to bring it within a second of the system clock. 3. Make several measurements to accurately determine the new offset between the RTC and the system clock (i.e. the remaining fraction of a second error) 4. Save the RTC parameters to the RTC file (specified with the 'rtcfile' directive in the configuration file (*note rtcfile directive::). The last step is done as a precaution against the computer suffering a power failure before either the daemon exits or the 'writertc' command is issued. 'chronyd' will still work perfectly well both whilst operating and across machine reboots even if the 'trimrtc' command is never used (and the RTC is allowed to drift away from true time). The 'trimrtc' command is provided as a method by which it can be corrected, in a manner compatible with 'chronyd' using it to maintain accurate time across machine reboots. The 'trimrtc' command can be executed automatically by 'chronyd' with the 'rtcautotrim' directive (*note rtcautotrim directive::). 4.3.4.50 waitsync ................. The 'waitsync' command waits for 'chronyd' to synchronise. Up to three optional arguments can be specified, the first is the maximum number of tries in 10 second intervals before giving up and returning a non-zero error code. When 0 is specified, or there are no arguments, the number of tries will not be limited. The second and third arguments are the maximum allowed remaining correction of the system clock and the maximum allowed skew (in ppm) as reported by the 'tracking' command (*note tracking command::) in the 'System time' and 'Skew' fields. If not specified or zero, the value will not be checked. An example is waitsync 60 0.01 which will wait up to about 10 minutes for 'chronyd' to synchronise to a source and the remaining correction to be less than 10 milliseconds. 4.3.4.51 writertc ................. The 'writertc' command writes the currently estimated error and gain/loss rate parameters for the RTC to the RTC file (specified with the 'rtcfile' directive (*note rtcfile directive::)). This information is also written automatically when 'chronyd' is killed (with SIGHUP, SIGINT, SIGQUIT or SIGTERM) or when the 'trimrtc' command is issued. Appendix A GNU General Public License ************************************* GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 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. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. , 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 Lesser General Public License instead of this License. 1 Introduction 1.1 Overview 1.2 Acknowledgements 1.3 Availability 1.3.1 Getting the software 1.3.2 Platforms 1.4 Relationship to other software packages 1.4.1 ntpd 1.4.2 timed 1.5 Distribution rights and (lack of) warranty 1.6 Bug reporting and suggestions 1.7 Contributions 2 Installation 2.1 Support for line editing libraries 2.2 Extra options for package builders 3 Typical operating scenarios 3.1 Computers connected to the internet 3.2 Infrequent connection to true NTP servers 3.2.1 Setting up the configuration file for infrequent connections 3.2.2 How to tell chronyd when the internet link is available. 3.3 Isolated networks 3.4 The home PC with a dial-up connection 3.4.1 Assumptions/how the software works 3.4.2 Typical configuration files. 3.5 Other important configuration options 4 Usage reference 4.1 Starting chronyd 4.2 The chronyd configuration file 4.2.1 Comments in the configuration file 4.2.2 acquisitionport 4.2.3 allow 4.2.4 bindacqaddress 4.2.5 bindaddress 4.2.6 bindcmdaddress 4.2.7 broadcast 4.2.8 clientloglimit 4.2.9 cmdallow 4.2.10 cmddeny 4.2.11 cmdport 4.2.12 combinelimit 4.2.13 commandkey 4.2.14 corrtimeratio 4.2.15 deny 4.2.16 driftfile 4.2.17 dumpdir 4.2.18 dumponexit 4.2.19 fallbackdrift 4.2.20 generatecommandkey 4.2.21 hwclockfile 4.2.22 include 4.2.23 initstepslew 4.2.24 keyfile 4.2.25 leapsecmode 4.2.26 leapsectz 4.2.27 local 4.2.28 lock_all 4.2.29 log 4.2.29.1 Measurements log file format 4.2.29.2 Statistics log file format 4.2.29.3 Tracking log file format 4.2.29.4 Real-time clock log file format 4.2.29.5 Refclocks log file format 4.2.29.6 Tempcomp log file format 4.2.30 logbanner 4.2.31 logchange 4.2.32 logdir 4.2.33 mailonchange 4.2.34 makestep 4.2.35 manual 4.2.36 maxchange 4.2.37 maxclockerror 4.2.38 maxsamples 4.2.39 maxslewrate 4.2.40 maxupdateskew 4.2.41 minsamples 4.2.42 minsources 4.2.43 noclientlog 4.2.44 peer 4.2.45 pidfile 4.2.46 pool 4.2.47 port 4.2.48 refclock 4.2.49 reselectdist 4.2.50 rtcautotrim 4.2.51 rtcdevice 4.2.52 rtcfile 4.2.53 rtconutc 4.2.54 rtcsync 4.2.55 sched_priority 4.2.56 server 4.2.57 smoothtime 4.2.58 stratumweight 4.2.59 tempcomp 4.2.60 user 4.3 Running chronyc 4.3.1 Basic use 4.3.2 Command line options 4.3.3 Security with chronyc 4.3.4 Command reference 4.3.4.1 accheck 4.3.4.2 activity 4.3.4.3 add peer 4.3.4.4 add server 4.3.4.5 allow all 4.3.4.6 allow 4.3.4.7 authhash 4.3.4.8 burst 4.3.4.9 clients 4.3.4.10 cmdaccheck 4.3.4.11 cmdallow all 4.3.4.12 cmdallow 4.3.4.13 cmddeny all 4.3.4.14 cmddeny 4.3.4.15 cyclelogs 4.3.4.16 delete 4.3.4.17 deny all 4.3.4.18 deny 4.3.4.19 dns 4.3.4.20 dump 4.3.4.21 exit 4.3.4.22 help 4.3.4.23 local 4.3.4.24 makestep 4.3.4.25 manual 4.3.4.26 maxdelay 4.3.4.27 maxdelaydevratio 4.3.4.28 maxdelayratio 4.3.4.29 maxpoll 4.3.4.30 maxupdateskew 4.3.4.31 minpoll 4.3.4.32 minstratum 4.3.4.33 offline 4.3.4.34 online 4.3.4.35 password 4.3.4.36 polltarget 4.3.4.37 quit 4.3.4.38 reselect 4.3.4.39 reselectdist 4.3.4.40 retries 4.3.4.41 rtcdata 4.3.4.42 settime 4.3.4.43 smoothing 4.3.4.44 smoothtime 4.3.4.45 sources 4.3.4.46 sourcestats 4.3.4.47 timeout 4.3.4.48 tracking 4.3.4.49 trimrtc 4.3.4.50 waitsync 4.3.4.51 writertc Appendix A GNU General Public License chrony-2.1.1/INSTALL0000644000000000000000000000652712542263375012567 0ustar rootrootThe software is distributed as source code which has to be compiled. The source code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. After unpacking the source code, change directory into it, and type ./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, 'Makefile' will be generated. If editline or readline library is available, chronyc will be built with line editing support. If you don't want this, specify the -disable-readline flag to configure. Please refer to *note line editing support:: for more information. If a 'timepps.h' header is available (e.g. from the LinuxPPS project (http://linuxpps.org/)), 'chronyd' will be built with PPS API reference clock driver. If the header is installed in a location that isn't normally searched by the compiler, you can add it to the searched locations by setting 'CPPFLAGS' variable to '-I/path/to/timepps'. 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 '/share/info/chrony.info' on the end. The second argument is the location of the file called 'dir'. This will typically be '/usr/share/info/dir'. So the typical command line would be install-info /usr/local/share/info/chrony.info /usr/share/info/dir Now that the software is successfully installed, the next step is to set up a configuration file. The default location of the file is '/etc/chrony.conf'. Several examples of configuration with comments are included in the examples directory. Suppose you want to use public NTP servers from the pool.ntp.org project as your time reference. A minimal useful configuration file could be pool pool.ntp.org iburst makestep 10 3 rtcsync Then, 'chronyd' can be run. chrony-2.1.1/FAQ0000644000000000000000000003107312542263375012062 0ustar rootroot Frequently Asked Questions __________________________________________________________________ 1. Chrony compared to other programs 1.1. How does chrony compare to ntpd? chrony can usually synchronise the system clock faster and with better time accuracy, but it doesn’t implement all NTP features, e.g. broadcast/multicast mode, or authentication based on public-key cryptography. For a more detailed comparison, see the [1]comparison page on the chrony website and section [2]Comparison with ntpd in the manual. If your computer connects to the 'net only for few minutes at a time, you turn your Linux computer off or suspend it frequently, the clock is not very stable (e.g. it is a virtual machine), or you want to use NTP on an isolated network with no hardware clocks in sight, chrony will probably work much better for you. The original reason chrony was written was that ntpd (called xntpd at the time) could not to do anything sensible on a PC which was connected to the 'net only for about 5 minutes once or twice a day, mainly to upload/download email and news. The requirements were * slew the time to correct it when going 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. Also, when working with isolated networks with no true time references at all ntpd was found to give no help with managing the local clock’s gain/loss rate on the NTP master node (which was set from watch). Some automated support was added to chrony to deal with this. __________________________________________________________________ 2. Configuration issues 2.1. I have several computers on a LAN. Should be all clients of an external server? The best configuration is usually 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. 2.2. Must I specify servers by IP address if DNS is not available on chronyd start? No. Starting from version 1.25, chronyd will keep trying to resolve the hostnames specified in the server and peer directives in increasing intervals until it succeeds. The online command can be issued from chronyc to try to resolve them immediately. 2.3. How can I make chronyd more secure? If you don’t need to serve time to NTP clients or peers, you can add port 0 to the chrony.conf file to completely disable the NTP server functionality and prevent NTP requests from reaching chronyd. Starting from version 2.0, the NTP server port is open only when client access is allowed by the allow directive or command, an NTP peer is configured, or the broadcast directive is used. If you don’t need to use chronyc remotely, you can add the following directives to the configuration file to bind the command sockets to the loopback interface. This is done by default since version 2.0. bindcmdaddress 127.0.0.1 bindcmdaddress ::1 If you don’t need to use chronyc at all, you can disable the command sockets by adding cmdport 0 to the configuration file. On Linux, if chronyd is compiled with support for Linux capabilities (available in the libcap library), you can specify an unprivileged user with the -u option or user directive in the chrony.conf file to drop root privileges after start. The configure option --with-user can be used to drop the privileges by default. 2.4. How can I improve the accuracy of the system clock with NTP sources? Select NTP servers that are well synchronised, stable and close to your network. It’s better to use more than one server, three or four is usually recommended as the minimum, so chronyd can detect falsetickers and combine measurements from multiple sources. There are also useful options which can be set in the server directive, they are minpoll, maxpoll, polltarget, maxdelay, maxdelayratio and maxdelaydevratio. The first three options set the minimum and maximum allowed polling interval, and how should be the actual interval adjusted in the specified range. Their default values are 6 (64 seconds) for minpoll, 10 (1024 seconds) for maxpoll and 6 (samples) for polltarget. The default values should be used for general servers on the internet. With your own NTP servers or if have permission to poll some servers more frequently, setting these options for shorter polling intervals may significantly improve the accuracy of the system clock. The optimal polling interval depends on many factors, including the ratio between the wander of the clock and the network jitter (sometimes expressed in NTP documents as the Allan intercept), the temperature sensitivity of the crystal oscillator and the maximum rate of change of the temperature. An example of the directive for an NTP server on the internet that you are allowed to poll frequently could be server foo.example.net minpoll 4 maxpoll 6 polltarget 16 An example using very short polling intervals for a server located in the same LAN could be server ntp.local minpoll 2 maxpoll 4 polltarget 30 The maxdelay options are useful to ignore measurements with larger delay (e.g. due to congestion in the network) and improve the stability of the synchronisation. The maxdelaydevratio option could be added to the example with local NTP server server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2 __________________________________________________________________ 3. Computer is not synchronising This is the most common problem. There are a number of reasons, see the following questions. 3.1. 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 wireshark 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. 3.2. Are NTP servers specified with the offline option? 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. __________________________________________________________________ 4. Issues with chronyc 4.1. I keep getting the error 506 Cannot talk to daemon When accessing chronyd remotely, 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 and an appropriate bindcmdaddress directive. This isn’t necessary for localhost. 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. 4.2. Is the chronyc / chronyd protocol documented anywhere? Only by the source code :-) See cmdmon.c (chronyd side) and client.c (chronyc side). __________________________________________________________________ 5. Real-time clock issues 5.1. 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. 5.2. 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 shutdown 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. 5.3. 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 __________________________________________________________________ 6. Microsoft Windows 6.1. 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 a service. 6.2. Are there any plans to support Windows? We have no plans to do this. Anyone is welcome to pick this work up and contribute it back to the project. __________________________________________________________________ 7. NTP-specific issues 7.1. Can chrony be driven from broadcast NTP servers? No, this NTP mode is not implemented yet. 7.2. 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. 7.3. Can chrony keep the system clock a fixed offset away from real time? This is not possible as the program currently stands. 7.4. What happens if the network connection is dropped without using chronyc's offline command first? chronyd will keep trying to access the server(s) that it thinks are online. When the network is connected again, it will take some time (on average half of the current polling interval) before new measurements are made and the clock is corrected. If the servers were set to offline and the online command was issued when the network was connected, chronyd would make new measurements immediately. The auto_offline option to the server entry in the chrony.conf file may be useful to switch the servers to the offline state automatically. __________________________________________________________________ 8. Linux-specific issues 8.1. I get Could not open /dev/rtc, Device or resource busy in my syslog file Some other program running on the system may be using the device. __________________________________________________________________ 9. Solaris-specific issues 9.1. 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. We no longer have root access to any Solaris machines to work on this, and we are reliant on somebody developing the patch and testing it. __________________________________________________________________ Last updated 2015-06-23 16:02:17 CEST References 1. http://chrony.tuxfamily.org/comparison.html 2. http://chrony.tuxfamily.org/manual.html#Comparison-with-ntpd