chrony-1.29/0000755000076400007640000000000012200726270012021 5ustar mirosmiroschrony-1.29/chrony.texi.in0000644000076400007640000055517512200721757014652 0ustar mirosmiros\input texinfo @c {{{ Main header stuff @afourwide @paragraphindent 0 @setfilename chrony.info @settitle User guide for the chrony suite @c @setchapternewpage off @ifinfo @dircategory Net Utilities @direntry * chrony: (chrony). How to use chronyd and chronyc * chronyd: (chrony)Starting chronyd. Reference for chronyd * chronyc: (chrony)Running chronyc. Reference for chronyc @end direntry @end ifinfo @titlepage @sp 10 @title The chrony suite @subtitle This manual describes how to use @subtitle the programs chronyd and chronyc @author Richard P. Curnow @page @vskip 0pt plus 1filll Copyright @copyright{} 1997-1999 Richard P. Curnow @end titlepage @c }}} @c {{{ Top node @node Top @top @menu * Introduction:: What the chrony suite does * Installation:: How to compile and install the software * Typical scenarios:: How to configure the software for some common cases * Usage reference:: Reference manual * Porting guide:: Hints to help with porting the software * GPL:: The GNU General Public License @end menu @c }}} @c {{{ Ch:Introduction @c {{{ Chapter top @node Introduction @chapter Introduction @menu * Overview:: What the programs do * Acknowledgements:: Credit where credit is due * Availability:: Where to get the software * Other time synchronisation packages:: Comparision with other software * Distribution and warranty:: There is no warranty * Bug reporting:: How to report bugs and make suggestions * Contributing:: Areas where contributions are particularly welcome @end menu @c }}} @c {{{ S:Overview @node Overview @section Overview Chrony is a software package for maintaining the accuracy of computer system clocks. It consists of a pair of programs : @itemize @bullet @item @code{chronyd}. This is a daemon which runs in background on the system. It obtains measurements (e.g. via the network) of the system's offset relative to other systems, and adjusts the system time accordingly. For isolated systems, the user can periodically enter the correct time by hand (using @code{chronyc}). In either case, @code{chronyd} determines the rate at which the computer gains or loses time, and compensates for this. @code{chronyd} can also act as an NTP server, and provide a time-of-day service to other computers. A typical set-up is to run @code{chronyd} on a gateway computer that has a dial-up link to the Internet, and use it to serve time to computers on a private LAN sitting behind the gateway. The IP addresses that can act as clients of @code{chronyd} can be tightly controlled. The default is no client access. @item @code{chronyc}. This is a command-line driven control and monitoring program. An administrator can use this to fine-tune various parameters within the daemon, add or delete servers etc whilst the daemon is running. The IP addresses from which @code{chronyc} clients may connect can be tightly controlled. The default is just the computer that @code{chronyd} itself is running on. @end itemize @c }}} @c {{{ S:Acknowledgments @node Acknowledgements @section Acknowledgements The @code{chrony} suite makes use of the algorithm known as @emph{RSA Data Security, Inc. MD5 Message-Digest Algorithm} for authenticating messages between different machines on the network. In writing the @code{chronyd} program, extensive use has been made of RFC1305, written by David Mills. The @code{ntp} suite's source code has been occasionally used to check details of the protocol that the RFC did not make absolutely clear. The core algorithms in @code{chronyd} are all completely distinct from @code{ntp}, however. @c }}} @c {{{ S:Availability @node Availability @section Availability @menu * Getting the software:: Where can I get the software from? * Platforms:: Which platforms will it run on? @end menu @node Getting the software @subsection Getting the software Links on @uref{http://chrony.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 in the following environments: @itemize @bullet @item Linux 2.2 and newer @item NetBSD @item BSD/386 @item Solaris 2.3/2.5/2.5.1/2.6/2.7/2.8 on Sparc (Sparc 20, Ultrasparc) and i386 @item SunOS 4.1.4 on Sparc 2 and Sparc20. @end itemize Closely related systems may work too, but they have not been tested. Porting the software to other system (particularly to those supporting an @code{adjtime} system call) should not be difficult, however it requires access to such systems to test out the driver. @c }}} @c {{{ S:Other programs @node Other time synchronisation packages @section Relationship to other software packages @menu * Comparison with 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} fully supports NTP version 4 (RFC5905), including broadcast, multicast, manycast clients / servers and the orphan mode. It also supports extra authentication schemes based on public-key cryptography (RFC5906). @code{chronyd} uses NTP version 3 (RFC1305), which is compatible with version 4. @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. Note, the Linux driver has been reported as working on a range of different architectures (Alpha, Sparc, MIPS as well as x86 of course). @item Porting to Windows NT 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 @item Automation of the trimrtc and writertc mechanisms Currently, the RTC trimming mechanism is a manual operation, because there has to be a reasonable guarantee that the system will stay up for a reasonable length of time afterwards. (If it is shut down too soon, a poor characterisation of the RTC drift rate will be stored on disc, giving a bad system clock error when the system is next booted.) To make chrony more automated for the non-expert user, it would be useful if this problem could be avoided so that trimrtc could be done automatically (e.g. in a crontab, or as part of the ip-up or ip-down scripts.) @end enumerate @c }}} @c }}} @c {{{ Ch:Installation @node Installation @chapter Installation @c {{{ main introduction text The software is distributed as source code which has to be compiled. The source code is supplied in the form of a gzipped tar file, which unpacks to a subdirectory identifying the name and version of the program. After unpacking the source code, change directory into it, and type @example ./configure @end example This is a shell script that automatically determines the system type. There is a single optional parameter, @code{--prefix} which indicates the directory tree where the software should be installed. For example, @example ./configure --prefix=/opt/free @end example will install the @code{chronyd} daemon into /opt/free/sbin and the chronyc control program into /opt/free/bin. The default value for the prefix is /usr/local. The configure script assumes you want to use gcc as your compiler. If you want to use a different compiler, you can configure this way: @example CC=cc CFLAGS=-O ./configure --prefix=/opt/free @end example for Bourne-family shells, or @example setenv CC cc setenv CFLAGS -O ./configure --prefix=/opt/free @end example for C-family shells. If the software cannot (yet) be built on your system, an error message will be shown. Otherwise, @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, 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 contents of this depend on the network environment in which the computer operates. Typical scenarios are described in the following section of the document. @c }}} @menu * 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 permanently on the Internet (or on a private network with NTP servers) * Infrequent connection:: You connect to the Internet sometimes (e.g. via a modem) * Isolated networks:: You have an isolated network with no reference clocks * Dial-up home PCs:: Additional considerations if you turn your computer off when it's not in use. * Configuration options overview:: Overview of some configuration options. @end menu @c }}} @c {{{ S:Permanent connection @node Computers on the net @section Computers connected to the internet In this section we discuss how to configure chrony for computers that have permanent connections to the internet (or to any network containing true NTP servers which ultimately derive their time from a reference clock). To operate in this mode, you will need to know the names of the NTP server machines you wish to use. You may be able to find names of suitable servers by one of the following methods: @itemize @bullet @item Your institution may already operate servers on its network. Contact your system administrator to find out. @item Your ISP probably has one or more NTP servers available for its customers. @item Somewhere under the NTP homepage there is a list of public stratum 1 and stratum 2 servers. You should find one or more servers that are near to you --- check that their access policy allows you to use their facilities. @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{a.b.c} and @code{d.e.f}, your @file{chrony.conf} file could contain as a minimum @example server a.b.c server d.e.f server g.h.i @end example However, you will probably want to include some of the other directives described later. The following directives will be particularly useful : @code{driftfile}, @code{commandkey}, @code{keyfile}. The smallest useful configuration file would look something like @example server a.b.c server d.e.f server g.h.i keyfile @SYSCONFDIR@/chrony.keys commandkey 1 driftfile @CHRONYVARDIR@/drift @end example @c }}} @c {{{ S:Infrequent connection @node Infrequent connection @section Infrequent connection to true NTP servers In this section we discuss how to configure chrony for computers that have occasional connections to the internet. @menu * Configuration for infrequent connections:: How to set up the @code{@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{a.b.c} and @code{d.e.f}, your @file{chrony.conf} file would need to contain something like @example server a.b.c server d.e.f server g.h.i @end example However, 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 a.b.c offline server d.e.f offline server g.h.i 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 chronyc. To do this, @code{chronyd} needs to be configured with an administrator password. To set up an administrator password, you can create a file @file{@SYSCONFDIR@/chrony.keys} containing a single line @example 1 ALongAndRandomPassword @end example and add the following line to @file{@SYSCONFDIR@/chrony.conf} (the order of the lines does not matter) @example commandkey 1 @end example The smallest useful configuration file would look something like @example server a.b.c offline server d.e.f offline server g.h.i offline keyfile @SYSCONFDIR@/chrony.keys commandkey 1 driftfile @CHRONYVARDIR@/drift @end example The next section describes how to tell @code{chronyd} when the internet link goes up and down. @node Advising chronyd of internet availability @subsection How to tell chronyd when the internet link is available. To use this option, you will need to configure a command key in @code{chronyd's} configuration file @file{@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 commandkey 25 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 commandkey 24 local stratum 10 initstepslew 20 master allow 192.168.169.170 @end example The rest of the clients would be the same, except that the @code{local} and @code{allow} directives are not required. @c }}} @c {{{ S:Dial-up home PCs @node Dial-up home PCs @section The home PC with a dial-up connection @menu * Dial-up overview:: General discussion of how the software operates in this mode * Dial-up configuration:: Typical configuration files @end menu @node Dial-up overview @subsection Assumptions/how the software works This section considers the home computer which has a dial-up connection. It assumes that Linux is run exclusively on the computer. Dual-boot systems may work; it depends what (if anything) the other system does to the system's real-time clock. Much of the configuration for this case is discussed earlier (@pxref{Infrequent connection}). This section addresses specifically the case of a computer which is turned off between 'sessions'. In this case, @code{chronyd} relies on the computer's real-time clock (RTC) to maintain the time between the periods when it is powered up. The arrangement is shown in the figure below. @example @group trim if required PSTN +---------------------------+ +----------+ | | | | v | | | +---------+ +-------+ +-----+ +---+ | System's| measure error/ |chronyd| |modem| |ISP| |real-time|------------------->| |-------| | | | | clock | drift rate +-------+ +-----+ +---+ +---------+ ^ | | | | +---------------------------+ --o-----o--- set time at boot up | +----------+ |NTP server| +----------+ @end group @end example When the computer is connected to the Internet (via the modem), @code{chronyd} has access to external NTP servers which it makes measurements from. These measurements are saved, and straight-line fits are performed on them to provide an estimate of the computer's time error and rate of gaining/losing time. When the computer is taken offline from the Internet, the best estimate of the gain/loss rate is used to free-run the computer until it next goes online. Whilst the computer is running, @code{chronyd} makes measurements of the real-time clock (RTC) (via the @file{/dev/rtc} interface, which must be compiled into the kernel). An estimate is made of the RTC error at a particular RTC second, and the rate at which the RTC gains or loses time relative to true time. 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 0.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline server 1.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline server 2.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline logdir /var/log/chrony log statistics measurements tracking driftfile @CHRONYVARDIR@/drift keyfile @SYSCONFDIR@/chrony.keys commandkey 25 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. 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. @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 being used. These histories are created by using the @code{dump} command in @code{chronyc}, or by setting the @code{dumponexit} directive in the configuration file. This option is useful if you want to stop and restart @code{chronyd} briefly for any reason, e.g. to install a new version. However, it only makes sense on systems where the kernel can maintain clock compensation whilst not under @code{chronyd's} control. The only version where this happens so far is Linux. On systems where this is not the case, e.g. Solaris and SunOS the option should not be used. @item -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 conjuction 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/clock} program during the Linux boot sequence. Support for real-time clocks is limited at present - the criteria are described in the section on the @code{rtcfile} directive (@pxref{rtcfile directive}). If @code{chronyd} cannot support the real time clock on your computer, this option cannot be used and a warning message will be logged to the syslog. If used in conjunction with the `-r' flag, @code{chronyd} will attempt to preserve the old samples after setting the system clock from the real time clock. This can be used to allow @code{chronyd} to perform long term averaging of the gain or loss rate across system reboots, and is useful for dial-up systems that are shut down when not in use. For this to work well, it relies on @code{chronyd} having been able to determine accurate statistics for the difference between the real time clock and system clock last time the computer was on. @item -u When this option is used, chronyd will drop root privileges to the specified user. So far, it works only on Linux when compiled with capabilities support. @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. @menu * comments in config file:: How to write a comment * acquisitionport directive:: Set port to use for initial time probes * allow directive:: Give access to NTP clients * bindaddress directive:: Limit the network interface that is used for NTP * bindcmdaddress directive:: Limit the network interface that is used for commands * broadcast directive:: Make chronyd act as an NTP broadcast server * cmdallow directive:: Give control access to chronyc on other computers * cmddeny directive:: Deny control access to chronyc on other computers * combinelimit directive:: Limit sources included in combining algorithm * commandkey directive:: Set runtime command key * corrtimeratio directive:: Set correction time ratio * cmdport directive:: Set port to use for runtime commanding * deny directive:: Deny access to NTP clients * driftfile directive:: Specify location of file containing drift data * dumpdir directive:: Specify directory for dumping measurements * dumponexit directive:: Dump measurements when daemon exits * fallbackdrift directive:: Specify fallback drift intervals * generatecommandkey directive:: Generate command key automatically * include directive:: Include a configuration file * initstepslew directive:: Trim the system clock on boot-up. * keyfile directive:: Specify location of file containing keys * leapsectz directive:: Read leap second data from tz database * linux_hz directive:: Define a non-standard value of the kernel HZ constant * linux_freq_scale directive:: Define a non-standard value to compensate the kernel frequency bias * local directive:: Allow unsynchronised machine to act as server * log directive:: Make daemon log certain sets of information * 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 * maxchange directive:: Set maximum allowed offset * manual directive:: Allow manual entry using chronyc's settime cmd. * maxclockerror directive:: Set maximum frequency error of local clock * maxsamples directive:: Set maximum number of samples per source * maxupdateskew directive:: Stop bad estimates upsetting machine clock * minsamples directive:: Set minimum number of samples per source * noclientlog directive:: Prevent chronyd from gathering data about clients * clientloglimit directive:: Set client log memory limit * peer directive:: Specify an NTP peer * pidfile directive:: Specify the file where chronyd's pid is written * port directive:: Set port to use for NTP packets * refclock directive:: Specify a reference clock * reselectdist directive:: Set improvement in distance needed to reselect a source * 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 * server directive:: Specify an NTP server * sched_priority directive:: Require real-time scheduling and specify a priority for it. * stratumweight directive:: Specify how important is stratum when selecting source * lock_all directive:: Require that chronyd be locked into RAM. * 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 @code{chronyd} uses a separate client-side port for the rapid-fire measurements requested with the @code{initstepslew} directive (@pxref{initstepslew directive}). Normally, that port is chosen arbitrarily by the operating system. However, you can use @code{acquisitionport} to explicitly specify a port. This may be useful for getting through firewalls. Do not make acquisition and regular NTP service (@pxref{port directive}) use the same port. An example of the @code{acquisitionport} command is @example acquisitionport 1123 @end example This would change the port used for rapid queries to udp/1123. You could then persuade the firewall administrator to let that port through. @c }}} @c {{{ allow @node allow directive @subsection allow The @code{allow} command is used to designate a particular subnet from which NTP clients are allowed to access the computer as an NTP server. The default is that no clients are allowed access, i.e. @code{chronyd} operates purely as an NTP client. If the @code{allow} directive is used, @code{chronyd} will be both a client of its servers, and a server to other clients. Examples of use of the command are as follows: @example allow foo.bar.com allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 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 {{{ bindaddress @node bindaddress directive @subsection bindaddress The bindaddress allows you to restrict the network interface to which chronyd will listen for NTP packets. This provides an additional level of access restriction above that available through the 'deny' mechanism. Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with a dial-up connection. The ethernet interface's IP address is 192.168.1.1. Suppose (for some reason) you want to block all access through the dialup connection (note, this will even block replies from servers on the dialup side, so you will not be able to synchronise to an external source). You could add the line @example bindaddress 192.168.1.1 @end example to the configuration file. This directive affects NTP (UDP port 123) packets. If no @code{bindcmdaddress} directive is present, the address supplied by @code{bindaddress} will be used to control binding of the command socket (UDP port 323) as well. The @code{bindaddress} directive has been found to cause problems when used on computers that need to pass NTP traffic over multiple network interfaces (e.g. firewalls). It is, therefore, not particularly useful. Use of the @code{allow} and @code{deny} directives together with a network firewall is more likely to be successful. For each of IPv4 and IPv6 protocols, only one @code{bindaddress} directive can be specified. @c }}} @c {{{ bindcmdaddress @node bindcmdaddress directive @subsection bindcmdaddress The bindcmdaddress allows you to restrict the network interface to which chronyd will listen for command packets (issued by chronyc). Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with a dial-up connection. The ethernet interface's IP address is 192.168.1.1. Suppose you want to block all access through the dialup connection. You could add the line @example bindcmdaddress 192.168.1.1 @end example to the configuration file. The @code{bindcmdaddress} directive has been found to cause problems when used on computers that need to pass command traffic over multiple network interfaces. It is, therefore, not particularly useful. Use of the @code{cmdallow} and @code{cmddeny} directives together with a network firewall is more likely to be successful. 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 {{{ cmdallow @node cmdallow directive @subsection cmdallow This is similar to the @code{allow} directive (@pxref{allow directive}), except that it allows control access (rather than NTP client access) to a particular subnet or host. (By 'control access' is meant that chronyc can be run on those hosts and successfully connect to chronyd on this computer.) The syntax is identical to the @code{allow} directive. There is also a @code{cmdallow all} directive with similar behaviour to the @code{allow all} directive (but applying to control access in this case, of course). @c }}} @c {{{ cmddeny @node cmddeny directive @subsection cmddeny This is similar to the @code{cmdallow} directive (@pxref{cmdallow directive}), except that it denies control access to a particular subnet or host, rather than allowing it. The syntax is identical. There is also a @code{cmddeny all} directive with similar behaviour to the @code{cmdallow all} directive. @c }}} @c {{{ 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 foobar @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 {{{ cmdport @node cmdport directive @subsection cmdport The @code{cmdport} directive allows the port that is used for run-time command and monitoring (via the program @code{chronyc}) to be altered from its default (323/udp). An example shows the syntax @example cmdport 257 @end example This would make @code{chronyd} use 257/udp as its command port. (@code{chronyc} would need to be run with the @code{-p 257} switch to inter-operate correctly). @c }}} @c {{{ 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 temporarily affects the frequency error of the system clock. The @code{corrtimeratio} directive controls 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} makes the overall frequency error of the system clock smaller, but increases the overall time error as the corrections will take longer. By default, the ratio is 1, which means the duration of an average correction will be close to the update interval. The syntax is @example corrtimeratio 10 @end example 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 unsynchronised to avoid quickly drifting away from true time if there was a short-term deviation in drift before the synchronisation was lost. The directive specifies the minimum and maximum interval for how long the system clock has to be unsynchronised 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 the synchronisation was lost, 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 will be used and the clock frequency will stay at the last value calculated before synchronisation was lost. @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 {{{ 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} always slews the time when it needs to adjust the system clock. For example, to correct a system clock which is 1 second slow, @code{chronyd} slightly increases the amount by which the system clock is advanced on each clock interrupt, until the error is removed. (Actually, this is done by calling the @code{adjtime()} or similar system function which does it for us.) Note that at no time does time run backwards with this method. On most Unix systems it is not desirable to step the system clock, because many programs rely on time advancing monotonically forwards. When the @code{chronyd} daemon is initially started, it is possible that the system clock is considerably in error. Attempting to correct such an error by slewing may not be sensible, since it may take several hours to correct the error by this means. The purpose of the @code{initstepslew} directive is to allow @code{chronyd} to make a rapid measurement of the system clock error at boot time, and to correct the system clock by stepping before normal operation begins. Since this would normally be performed only at an appropriate point in the system boot sequence, no other software should be adversely affected by the step. If the correction required is less than a specified threshold, a slew is used instead. This makes it easier to restart @code{chronyd} whilst the system is in normal operation. The @code{initstepslew} directive takes a threshold and a list of NTP servers as arguments. A maximum of 8 will be used. Each of the servers is rapidly polled several times, and a majority voting mechanism used to find the most likely range of system clock error that is present. A step (or slew) is applied to the system clock to correct this error. @code{chronyd} then enters its normal operating mode (where only slews are used). An example of use of the command is @example initstepslew 30 foo.bar.com baz.quz.com @end example where 2 NTP servers are used to make the measurement. The @code{30} indicates that if the system's error is found to be 30 seconds or less, a slew will be used to correct it; if the error is above 30 seconds, a step will be used. The @code{initstepslew} directive can also be used in an isolated LAN environment, where the clocks are set manually. The most stable computer is chosen as the master, and the other computers are slaved to it. If each of the slaves is configured with the local option (see below), the master can be set up with an @code{initstepslew} directive which references some or all of the slaves. Then, if the master machine has to be rebooted, the slaves can be relied on to 'flywheel' the time for the master. @c }}} @c {{{ keyfile @node keyfile directive @subsection keyfile This command is used to specify the location of the file containing ID/key pairs for the following 2 uses: @itemize @bullet @item Authentication of NTP packets. @item Authentication of administrator commands entered via chronyc. @end itemize The format of the command is shown in the example below @example keyfile @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. 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. 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 {{{ 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 {{{ linux_hz @node linux_hz directive @subsection linux_hz (This option only applies to Linux). By default, chronyd will find the value of @code{HZ} from a kernel header file at compile time. @code{HZ} is the nominal number of timer interrupts per second. If you're running chronyd on the system where it was built, the value it has should be right, and you don't need to worry about this option. This option is provided for people who move a pre-built chronyd onto a system where the value of HZ in the kernel headers has been changed from the default value. An example of the command is @example linux_hz 100 @end example @c }}} @c {{{ linux_freq_scale @node linux_freq_scale directive @subsection linux_freq_scale (This option only applies to Linux). By default, chronyd will find the value of @code{HZ} and @code{SHIFT_HZ} from kernel header files at compile time. An internal value called @code{freq_scale} is calculated from this. By default it is (1< @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 maximum number of samples @code{chronyd} should keep for each source. The default is 0, which disables the configurable limit, and the useful range is 4 to 64. The syntax is @example maxsamples @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 minimum number of samples @code{chronyd} should try to keep for each source. The default is 0 and the useful range is 4 to 64. The syntax is @example minsamples @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 {{{ 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 {{{ peer @node peer directive @subsection peer The syntax of this directive is identical to that for the @code{server} directive (@pxref{server directive}), except that it is used to specify an NTP peer rather than an NTP server. @c }}} @c {{{ pidfile @node pidfile directive @subsection pidfile chronyd always writes its process ID (pid) to a file, and checks this file on startup to see if another chronyd may already be running on the system. By default, the file used is @code{/var/run/chronyd.pid}. The @code{pidfile} directive allows the name to be changed, e.g. @example pidfile /var/tmp/chronyd.pid @end example @c }}} @c {{{ port @node port directive @subsection port This option allows you to configure the port used for the NTP service on your machine. The compiled in default is udp/123, the standard NTP port. It is unlikely that you would ever need to change this value. A possible exception would be if you wanted to operate strictly in client-only mode and never be available as a server to ntpd clients. If set to 0, the kernel will assign a random port. An example of the port command is @example port 11123 @end example This would change the NTP port served by chronyd on the computer to udp/11123. @c }}} @c {{{ 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 three 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.1 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 @code{gpsd}, @code{shmpps} and @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 @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 intervals specified by this option. This is defined as a power of 2. 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 are not controlled by external events and thus require polling. Again this is defined as a power of 2 and can be negative for sub-second intervals. 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 is used to specify how the refclock is assumed to be inaccurate (in seconds). Increasing the value is useful to avoid having no majority in the source selection algorithm or to make the algorithm prefer other refclocks. 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 and PPS refclocks. @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. @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 {{{ 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/clock} program. @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 {{{ 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, it is 1 second. This usually means that sources with lower stratum will be preferred to sources with higher stratum even when their distance is significantly worse. Setting @code{stratumweight} to 0 makes @code{chronyd} ignore stratum when selecting the source. @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 {{{ 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 This option allows the user to specify which host running the @code{chronyd} program is to be contacted. This allows for remote configuration, without having to telnet or rlogin to the other host first. The default is to contact @code{chronyd} running on the same host as that where chronyc is being run. @item -p 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{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 command:: Allowing NTP client access * allow all 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 command:: Allowing command client access * cmdallow all command:: Allowing command client access * cmddeny command:: Denying command client access * cmddeny all command:: Denying command client access * cyclelogs command:: Close and re-open open log files * delete command:: Remove an NTP server or peer * deny command :: Denying NTP client access * deny all command:: Denying NTP client access * 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:: Immediately correct the system clock instead of slewing * manual command:: Enable/disable/configure options for settime * maxdelay command:: Set max measurement delay for a source * maxdelayratio command:: Set max measurement delay for a source as ratio * maxdelaydevratio command:: Set max measurement delay for a source as ratio to deviation * 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 * 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 a.b.c 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 identical to that for the @code{peer} directive in the configuration file (@pxref{peer directive}). An example of using this command is shown below. @example add peer foo.bar.com minpoll 6 maxpoll 10 authkey 25 @end example @c }}} @c {{{ add server @node add server command @subsubsection add server The @code{add server} command allows a new NTP server to be added whilst @code{chronyd} is running. Following the words @code{add server}, the syntax of the following parameters and options is identical to that for the @code{server} directive in the configuration file (@pxref{server directive}). An example of using this command is shown below. @example add server foo.bar.com minpoll 6 maxpoll 10 authkey 25 @end example @c }}} @c {{{ allow @node allow command @subsubsection allow The effect of the allow command is identical to the @code{allow} directive in the configuration file (@pxref{allow directive}). The syntax is illustrated in the following examples: @example allow foo.bar.com allow 1.2 allow 3.4.5 allow 6.7.8/22 allow 6.7.8.9/22 allow 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 {{{ 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 {{{ authhash @node authhash command @subsubsection authhash This command sets the hash function used for authenticating user commands. For successful authentication the hash function has to be the same as the one set for the command key in the keys file on the server. It needs to be set 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.bar.com @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 a.b.c cmdaccheck 1.2.3.4 cmdaccheck 2001:db8::1 @end example @c }}} @c {{{ cmdallow @node cmdallow command @subsubsection cmdallow This is similar to the @code{allow} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cmdallow all @node cmdallow all command @subsubsection cmdallow all This is similar to the @code{allow all} command, except that it is used to@c {{{ allow particular hosts or subnets to use the chronyc program to interact@c }}} with @code{chronyd} on the current host. @c }}} @c {{{ cmddeny @node cmddeny command @subsubsection cmddeny This is similar to the @code{deny} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cmddeny all @node cmddeny all command @subsubsection cmddeny all This is similar to the @code{deny all} command, except that it is used to allow particular hosts or subnets to use the chronyc program to interact with @code{chronyd} on the current host. @c }}} @c {{{ cyclelogs @node cyclelogs command @subsubsection cyclelogs The @code{cyclelogs} command causes all of @code{chronyd's} open log files to be closed and re-opened. This allows them to be renamed so that they can be periodically purged. An example of how to do this is shown below. @example % mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log % chronyc -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.bar.com 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 @node deny command @subsubsection deny The effect of the allow command is identical to the @code{deny} directive in the configuration file (@pxref{deny directive}). The syntax is illustrated in the following examples: @example deny foo.bar.com deny 1.2 deny 3.4.5 deny 6.7.8/22 deny 6.7.8.9/22 deny 2001:db8:789a::/48 deny 0/0 deny ::/0 deny @end example @c }}} @c {{{ deny all @node deny all command @subsubsection deny all The effect of the allow command is identical to the @code{deny all} directive in the configuration file (@pxref{deny directive}). @c }}} @c {{{ 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. It cancels any remaining correction that was being slewed, and jumps the system clock by the equivalent amount, making it correct immediately. BE WARNED - certain software will be seriously affected by such jumps to the system time. (That is the reason why chronyd uses slewing normally.) The @code{makestep} directive in the configuration file can be used to step the clock automatically when the adjustment is larger than a specified threshold, see @ref{makestep directive}. @c }}} @c {{{ manual @node manual command @subsubsection manual The manual command enables and disables use of the @code{settime} command (@pxref{settime command}), and is used to modify the behaviour of the manual clock driver. Examples of the command are shown below. @example manual on manual off manual delete 1 manual list manual reset @end example The @code{on} form of the command enables use of the @code{settime} command. The @code{off} form of the command disables use of the @code{settime} command. The @code{list} form of the command lists all the samples currently stored in @code{chronyd}. The output is illustrated below. @example 210 n_samples = 1 # Date Time(UTC) Slewed Original Residual ==================================================== 0 27Jan99 22:09:20 0.00 0.97 0.00 @end example The columns as as follows : @enumerate 1 @item The sample index (used for the @code{manual delete} command) @item The date and time of the sample @item The system clock error when the timestamp was entered, adjusted to allow for changes made to the system clock since. @item The system clock error when the timestamp was entered, as it originally was (without allowing for changes to the system clock since). @item The regression residual at this point, in seconds. This allows 'outliers' to be easily spotted, so that they can be deleted using the @code{manual delete} command. @end enumerate The @code{delete} form of the command deletes a single sample. The parameter is the index of the sample, as shown in the first column of the output from @code{manual list}. Following deletion of the data point, the current error and drift rate are re-estimated from the remaining data points and the system clock trimmed if necessary. This option is intended to allow 'outliers' to be discarded, i.e. samples where the administrator realises he/she has entered a very poor timestamp. The @code{reset} form of the command deletes all samples at once. The system clock is left running as it was before the command was entered. @c }}} @c {{{ maxdelay @node maxdelay command @subsubsection maxdelay This allows the @code{maxdelay} option for one of the sources to be modified, in the same way as specifying the @code{maxdelay} option for the @code{server} directive in the configuration file (@pxref{server directive}). The following examples illustrate the syntax @example maxdelay foo.bar.com 0.3 maxdelay 1.2.3.4 0.0015 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.bar.com} 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 {{{ maxdelayratio @node maxdelayratio command @subsubsection maxdelayratio This allows the @code{maxdelayratio} option for one of the sources to be modified, in the same way as specifying the @code{maxdelayratio} option for the @code{server} directive in the configuration file (@pxref{server directive}). The following examples illustrate the syntax @example maxdelayratio foo.bar.com 1.5 maxdelayratio 1.2.3.4 2.0 maxdelayratio 2001:db8::1 2.0 @end example The first example sets the maximum network delay for a measurement to the host @code{foo.bar.com} to be 1.5 times the minimum delay found amongst the previous measurements that have been retained. The second 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 {{{ 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.bar.com 0.1 maxdelaydevratio 1.2.3.4 1.0 maxdelaydevratio 2001:db8::1 100.0 @end example @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.bar.com 10 @end example which sets the maximum polling interval for the host @code{foo.bar.com} to 1024 seconds. Note that the new maximum polling interval only takes effect after the next measurement has been made. @c }}} @c {{{ maxupdateskew @node maxupdateskew command @subsubsection maxupdateskew This command has the same effect as the @code{maxupdateskew} directive in the configuration file, see @ref{maxupdateskew directive}. @c }}} @c {{{ minpoll @node minpoll command @subsubsection minpoll The @code{minpoll} command is used to modify the minimum polling interval for one of the current set of sources. It is equivalent to the @code{minpoll} option in the @code{server} directive in the configuration file (@pxref{server directive}). The syntax is as follows @example minpoll @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.bar.com 5 @end example which sets the minimum polling interval for the host @code{foo.bar.com} to 32 seconds. Note that the new minimum polling interval only takes effect after the next measurement has been made. @c }}} @c {{{ 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.bar.com 5 @end example which sets the minimum stratum for the host @code{foo.bar.com} 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.bar.com @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. (Note that the no-echo mode is limited to 8 characters on SunOS 4.1 due to limitations in the system library. Other systems do not have this restriction.) The password 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}). 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.bar.com 12 @end example which sets the poll target for the host @code{foo.bar.com} 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 {{{ 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 ^? a.b.c 2 6 377 23 -923us[ -924us] +/- 43ms ^+ d.e.f 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}). The default is 1000 milliseconds. @c }}} @c {{{ tracking @node tracking command @subsubsection tracking The @code{tracking} command displays parameters about the system's clock performance. An example of the output is shown below. @example Reference ID : 1.2.3.4 (a.b.c) Stratum : 3 Ref time (UTC) : 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{a.b.c} 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 such as Solaris and SunOS, @code{chronyd} has no means to adjust the fundamental rate of the system clock, so keeps the system time correct by periodically making offsets to it as though an error had been measured. The build up of these offsets will be observed in this report. @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. In certain extreme situations, this value can be negative. (This can arise in a symmetric peer arrangement where the computers' frequencies are not tracking each other and the network delay is very short relative to the turn-around time at each computer.) @item Root dispersion This is the total dispersion accumulated through all the computers back to the stratum-1 computer from which the computer is ultimately synchronised. Dispersion is due to system clock resolution, statistical measurement variations etc. An absolute bound on the computer's clock accuracy (assuming the stratum-1 computer is correct) is given by @example clock_error <= root_dispersion + (0.5 * |root_delay|) @end example @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. @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). @c }}} @c }}} @c }}} @c }}} @c {{{ apx: porting guide @node Porting guide @appendix Porting guide @c {{{ section top This appendix discusses issues that have arisen in writing the system-specific parts of the existing ports. This will provide useful information for those attempting to write ports to other systems. @menu * System driver files:: What needs to go in a driver file for a particular type of system * Quirks of particular systems:: Problem areas that have been found on ports already written. @end menu @c }}} @c {{{ S:system driver files @node System driver files @section System driver files The system specific parts of the software are contained in files with names like @code{sys_linux.c}. The following functions are required in a system driver file: @enumerate @item A function to read the current frequency @item A function to set the current frequency @item A function to slew the system time by a specified delta @item A function to step the system time by a specified delta @item A function to work out the error at a particular time between the system's clock and @code{chronyd's} estimate of real time. (This is required because some systems have to track real time by making the system time follow it in a 'sawtooth' fashion). @end enumerate The @dfn{frequency} is the rate at which the system gains or loses time, measured relative to the system when running uncompensated. @c }}} @c {{{ system quirks @node Quirks of particular systems @section Quirks of particular systems @c {{{ section top These sections describe quirks in each system type that needed to be investigated to port the software to each system type. @menu * Linux porting quirks:: * Solaris 2.5 porting quirks:: * SunOS 4.1.4 porting quirks:: @end menu @c }}} @c {{{ linux @node Linux porting quirks @subsection Linux The following quirks have been found in developing the Linux port. @enumerate 1 @item In order to avoid floating point arithmetic, the kernel uses shifting and adding to approximate a scaling of 100/128. This approximation implies that the frequency set via the @code{adjtimex()} system call is not the frequency that is actually obtained. The method of approximation varies between kernel versions and must be determined by examining the kernel source. An inverse factor must be included in the driver to compensate. @item In some kernel versions, an @code{adjtimex()} system call with the flags bits all zeroed will return the amount of offset still to be corrected. In others (e.g. the 2.0 series beyond 2.0.32), the offset must be changed in order to get the old offset returned (similar to @code{adjtime()} on other systems). @end enumerate @c }}} @c {{{ solaris 2.5 @node Solaris 2.5 porting quirks @subsection Solaris 2.5 The following quirks have been found in developing the Solaris port. @enumerate 1 @item The @code{adjtime()} system call with a zero argument does not cancel an adjustment that is in progress - it just reports the remaining adjustment. @item The @code{settimeofday()} system call only observes the seconds part of the argument - any fractional seconds part is lost. second. @item The kernel variable @code{dosynctodr} has to be set to zero, otherwise the system clock is periodically reset to the real-time clock. @end enumerate @c }}} @c {{{ sunos 4.1.4 @node SunOS 4.1.4 porting quirks @subsection SunOS 4.1.4 The following quirks have been found in developing the SunOS port. @enumerate 1 @item The @code{adjtime()} system call truncates its argument to a multiple of the system's @code{tickadj} variable. (@code{chronyd} sets that to 100, giving a 1 part in 100 slewing capability for correcting offsets.) @item The kernel variable @code{dosynctodr} has to be set to zero, otherwise the system clock is periodically reset to the real-time clock. @end enumerate @c }}} @c }}} @c }}} @c {{{ apx:GNU General Public License @node GPL @appendix GNU General Public License @center GNU GENERAL PUBLIC LICENSE @center Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 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-1.29/sys_solaris.h0000644000076400007640000000222512200721757014552 0ustar mirosmiros/* 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-1.29/clientlog.c0000644000076400007640000003010512200721757014151 0ustar mirosmiros/* 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) { } /* ================================================== */ 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]; } } /* ================================================== */ /* Search for the record for a particular subnet, but return NULL if one of the parents does not exist - never open a node out */ static void * find_subnet_dont_open(Subnet *subnet, uint32_t *addr, int addr_len, int bits_consumed) { uint32_t this_subnet; if (bits_consumed >= 32 * addr_len) { return subnet; } else { this_subnet = get_subnet(addr, bits_consumed); bits_consumed += NBITS; if (!subnet->entry[this_subnet]) { return NULL; } else { return find_subnet_dont_open((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed); } } } /* ================================================== */ 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_GetSubnetBitmap(IPAddr *subnet, int bits, CLG_Bitmap result) { Subnet *s; uint32_t ip6[4]; unsigned long i; unsigned long word, bit, mask; if (bits >= 0 && bits % 8 == 0) { memset (result, 0, TABLE_SIZE/8); if (active) { switch (subnet->family) { case IPADDR_INET4: if (bits >= 32) return CLG_BADSUBNET; s = find_subnet_dont_open(&top_subnet4, &subnet->addr.in4, 1, 32 - bits); break; case IPADDR_INET6: if (bits >= 128) return CLG_BADSUBNET; split_ip6(subnet, ip6); s = find_subnet_dont_open(&top_subnet6, ip6, 4, 128 - bits); break; default: return CLG_BADSUBNET; } if (s) { for (i=0; i<256; i++) { if (s->entry[i]) { word = i / 32; bit = i % 32; mask = 1UL << bit; result[word] |= mask; } } return CLG_SUCCESS; } else { return CLG_EMPTYSUBNET; } } else { return CLG_INACTIVE; } } else { return CLG_BADSUBNET; } } /* ================================================== */ CLG_Status CLG_GetClientAccessReportByIP(IPAddr *ip, RPT_ClientAccess_Report *report, time_t now) { uint32_t ip6[4]; Node *node; if (!active) { return CLG_INACTIVE; } else { switch (ip->family) { case IPADDR_INET4: node = (Node *) find_subnet_dont_open(&top_subnet4, &ip->addr.in4, 1, 0); break; case IPADDR_INET6: split_ip6(ip, ip6); node = (Node *) find_subnet_dont_open(&top_subnet6, ip6, 4, 0); break; default: return CLG_EMPTYSUBNET; } if (!node) { return CLG_EMPTYSUBNET; } else { report->client_hits = node->client_hits; report->peer_hits = node->peer_hits; report->cmd_hits_auth = node->cmd_hits_auth; report->cmd_hits_normal = node->cmd_hits_normal; report->cmd_hits_bad = node->cmd_hits_bad; report->last_ntp_hit_ago = now - node->last_ntp_hit; report->last_cmd_hit_ago = now - node->last_cmd_hit; return CLG_SUCCESS; } } } /* ================================================== */ CLG_Status CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, time_t now, unsigned long *n_indices) { Node *node; *n_indices = n_nodes; if (!active) { return CLG_INACTIVE; } else { if ((index < 0) || (index >= n_nodes)) { return CLG_INDEXTOOLARGE; } node = nodes[index]; report->ip_addr = node->ip_addr; report->client_hits = node->client_hits; report->peer_hits = node->peer_hits; report->cmd_hits_auth = node->cmd_hits_auth; report->cmd_hits_normal = node->cmd_hits_normal; report->cmd_hits_bad = node->cmd_hits_bad; report->last_ntp_hit_ago = now - node->last_ntp_hit; report->last_cmd_hit_ago = now - node->last_cmd_hit; return CLG_SUCCESS; } } chrony-1.29/wrap_adjtimex.h0000644000076400007640000000442012200721757015035 0ustar mirosmiros/* 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 /* Cut-down version of struct timex */ struct tmx_params { long tick; long offset; long freq; double dfreq; long maxerror; long esterror; unsigned sta_pll:1; unsigned sta_ppsfreq:1; unsigned sta_ppstime:1; unsigned sta_fll:1; unsigned sta_ins:1; unsigned sta_del:1; unsigned sta_unsync:1; unsigned sta_freqhold:1; unsigned sta_ppssignal:1; unsigned sta_ppsjitter:1; unsigned sta_ppswander:1; unsigned sta_ppserror:1; unsigned sta_clockerr:1; int status; long constant; long precision; long tolerance; long ppsfreq; long jitter; int shift; long stabil; long jitcnt; long calcnt; long errcnt; long stbcnt; }; int TMX_SetTick(long tick); int TMX_ApplyOffset(long *offset); int TMX_SetFrequency(double *freq, long tick); int TMX_GetFrequency(double *freq, long *tick); int TMX_GetOffsetLeft(long *offset); int TMX_ReadCurrentParams(struct tmx_params *params); int TMX_SetLeap(int leap); int TMX_SetSync(int sync); int TMX_EnableNanoPLL(void); int TMX_ApplyPLLOffset(long offset, long constant); int TMX_GetPLLOffsetLeft(long *offset); int TMX_TestStepOffset(void); int TMX_ApplyStepOffset(double offset); #endif /* GOT_WRAP_ADJTIMEX_H */ chrony-1.29/local.h0000644000076400007640000002001712200721757013271 0ustar mirosmiros/* 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) is_step_change : true if change is being applied as a jump (using settimeofday rather than adjtime) anything : Passthrough argument from call to registration routine */ typedef void (*LCL_ParameterChangeHandler) (struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything ); /* Add a handler. Then handler MUST NOT deregister itself!!! */ extern void LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything); /* Remove a handler */ extern void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler, void *anything); /* Function type for handlers to be called back when an indeterminate offset is introduced into the local time. This situation occurs when the frequency must be adjusted to effect a clock slew and there is doubt about one of the endpoints of the interval over which the frequency change was applied.It is expected that such handlers will add extra dispersion to any existing samples stored in their registers. dispersion : The bound on how much error has been introduced in the local clock, in seconds. anything : passthrough from the registration routine */ typedef void (*LCL_DispersionNotifyHandler)(double dispersion, void *anything); /* Register a handler for being notified of dispersion being added to the local clock. The handler MUST NOT unregister itself!!! */ extern void LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything); /* Delete a handler */ extern void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything); /* Read the absolute system frequency, relative to the uncompensated system. Returned in units of parts per million. Thus the result of this is how many seconds fast the uncompensated system would be after its own time has reached 1 million seconds from the start of the measurement. */ extern double LCL_ReadAbsoluteFrequency(void); /* Routine to set the absolute frequency. Only expected to be used when either (i) reading the drift from a file at the start of a run, or (ii) responsing to a user parameter 'poke'. This is defined in ppm, as for the absolute frequency reading routine. */ extern void LCL_SetAbsoluteFrequency(double afreq); /* Routine to apply a change of frequency to the local clock. The argument is the estimated gain (positive) or loss (negative) of the local clock relative to true time, per unit time of the PREVIOUS frequency setting of the local clock. This is assumed to be based on a regression of y=offset v x=cooked local time. */ extern void LCL_AccumulateDeltaFrequency(double dfreq); /* Routine to apply an offset (in seconds) to the local clock. The argument should be positive to move the clock backwards (i.e. the local clock is currently fast of true time), or negative to move it forwards (i.e. it is currently slow of true time). 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 void 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); /* 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(double threshold); /* Routine to schedule a leap second. Leap second will be inserted at the end of the day if argument is positive, deleted if negative, and zero cancels scheduled leap second. */ extern void LCL_SetLeap(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); #endif /* GOT_LOCAL_H */ chrony-1.29/pktlength.c0000644000076400007640000001756712200721757014212 0ustar mirosmiros/* 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" /* ================================================== */ int PKL_CommandLength(CMD_Request *r) { int type; type = ntohs(r->command); if (type < 0 || type >= N_REQUEST_TYPES) { return 0; } else { switch (type) { case REQ_NULL: return offsetof(CMD_Request, data); case REQ_ONLINE: return offsetof(CMD_Request, data.online.EOR); case REQ_OFFLINE: return offsetof(CMD_Request, data.offline.EOR); case REQ_BURST: return offsetof(CMD_Request, data.burst.EOR); case REQ_MODIFY_MINPOLL: return offsetof(CMD_Request, data.modify_minpoll.EOR); case REQ_MODIFY_MAXPOLL: return offsetof(CMD_Request, data.modify_maxpoll.EOR); case REQ_DUMP: return offsetof(CMD_Request, data.dump.EOR); case REQ_MODIFY_MAXDELAY: return offsetof(CMD_Request, data.modify_maxdelay.EOR); case REQ_MODIFY_MAXDELAYRATIO: return offsetof(CMD_Request, data.modify_maxdelayratio.EOR); case REQ_MODIFY_MAXDELAYDEVRATIO: return offsetof(CMD_Request, data.modify_maxdelaydevratio.EOR); case REQ_MODIFY_MAXUPDATESKEW: return offsetof(CMD_Request, data.modify_maxupdateskew.EOR); case REQ_LOGON : return offsetof(CMD_Request, data.logon.EOR); case REQ_SETTIME : return offsetof(CMD_Request, data.settime.EOR); case REQ_LOCAL : return offsetof(CMD_Request, data.local.EOR); case REQ_MANUAL : return offsetof(CMD_Request, data.manual.EOR); case REQ_N_SOURCES : return offsetof(CMD_Request, data.n_sources.EOR); case REQ_SOURCE_DATA : return offsetof(CMD_Request, data.source_data.EOR); case REQ_REKEY : return offsetof(CMD_Request, data.rekey.EOR); case REQ_ALLOW : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_ALLOWALL : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_DENY : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_DENYALL : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_CMDALLOW : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_CMDALLOWALL : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_CMDDENY : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_CMDDENYALL : return offsetof(CMD_Request, data.allow_deny.EOR); case REQ_ACCHECK : return offsetof(CMD_Request, data.ac_check.EOR); case REQ_CMDACCHECK : return offsetof(CMD_Request, data.ac_check.EOR); case REQ_ADD_SERVER : return offsetof(CMD_Request, data.ntp_source.EOR); case REQ_ADD_PEER : return offsetof(CMD_Request, data.ntp_source.EOR); case REQ_DEL_SOURCE : return offsetof(CMD_Request, data.del_source.EOR); case REQ_WRITERTC : return offsetof(CMD_Request, data.writertc.EOR); case REQ_DFREQ : return offsetof(CMD_Request, data.dfreq.EOR); case REQ_DOFFSET : return offsetof(CMD_Request, data.doffset.EOR); case REQ_TRACKING : return offsetof(CMD_Request, data.tracking.EOR); case REQ_SOURCESTATS : return offsetof(CMD_Request, data.sourcestats.EOR); case REQ_RTCREPORT : return offsetof(CMD_Request, data.rtcreport.EOR); case REQ_TRIMRTC : return offsetof(CMD_Request, data.trimrtc.EOR); case REQ_CYCLELOGS : return offsetof(CMD_Request, data.cyclelogs.EOR); case REQ_SUBNETS_ACCESSED : 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.manual_list.EOR); case REQ_MANUAL_DELETE: return offsetof(CMD_Request, data.manual_delete.EOR); case REQ_MAKESTEP: return offsetof(CMD_Request, data.make_step.EOR); case REQ_ACTIVITY: return offsetof(CMD_Request, data.activity.EOR); case REQ_RESELECT: return offsetof(CMD_Request, data.reselect.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); default: /* If we fall through the switch, it most likely means we've forgotten to implement a new case */ assert(0); } } /* Catch-all case */ return 0; } /* ================================================== */ int PKL_ReplyLength(CMD_Reply *r) { int type; type = ntohs(r->reply); /* Note that reply type codes start from 1, not 0 */ if (type < 1 || type >= N_REPLY_TYPES) { return 0; } else { switch (type) { case RPY_NULL: return offsetof(CMD_Reply, data.null.EOR); case RPY_N_SOURCES: return offsetof(CMD_Reply, data.n_sources.EOR); case RPY_SOURCE_DATA: return offsetof(CMD_Reply, data.source_data.EOR); case RPY_MANUAL_TIMESTAMP: return offsetof(CMD_Reply, data.manual_timestamp.EOR); case RPY_TRACKING: return offsetof(CMD_Reply, data.tracking.EOR); case RPY_SOURCESTATS: return offsetof(CMD_Reply, data.sourcestats.EOR); case RPY_RTC: return offsetof(CMD_Reply, data.rtc.EOR); case RPY_SUBNETS_ACCESSED : 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); default: assert(0); } } return 0; } /* ================================================== */ chrony-1.29/COPYING0000644000076400007640000004310312200721757013062 0ustar mirosmiros 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-1.29/logging.h0000644000076400007640000000661612200721757013636 0ustar mirosmiros/* 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 diagnostic logging module */ #ifndef GOT_LOGGING_H #define GOT_LOGGING_H /* Definition of severity */ typedef enum { LOGS_INFO, LOGS_WARN, LOGS_ERR } LOG_Severity; /* Definition of facility. Each message is tagged with who generated it, so that the user can customise what level of reporting he gets for each area of the software */ typedef enum { LOGF_Reference, LOGF_NtpIO, LOGF_NtpCore, LOGF_NtpSources, LOGF_Scheduler, LOGF_SourceStats, LOGF_Sources, LOGF_Local, LOGF_Util, LOGF_Main, LOGF_ClientLog, LOGF_Configure, LOGF_CmdMon, LOGF_Acquire, LOGF_Manual, LOGF_Keys, LOGF_Logging, LOGF_Rtc, LOGF_Regress, LOGF_Sys, LOGF_SysLinux, LOGF_SysNetBSD, LOGF_SysSolaris, LOGF_SysSunOS, LOGF_SysWinnt, LOGF_RtcLinux, LOGF_Refclock } LOG_Facility; /* Init function */ extern void LOG_Initialise(void); /* Fini function */ extern void LOG_Finalise(void); /* Line logging function */ extern void LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...); /* Logging function for fatal errors */ extern void LOG_Fatal_Function(LOG_Facility facility, const char *format, ...); /* Position in code reporting function */ extern void LOG_Position(const char *filename, int line_number, const char *function_name); /* 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); /* Return zero once per 10 seconds */ extern int LOG_RateLimited(void); /* Line logging macro. If the compiler is GNU C, we take advantage of being able to get the function name also. */ #if defined(__GNUC__) #define LOG LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Line_Function #define LOG_FATAL LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Fatal_Function #else #define LOG LOG_Position(__FILE__, __LINE__, ""); LOG_Line_Function #define LOG_FATAL LOG_Position(__FILE__, __LINE__, ""); LOG_Fatal_Function #endif /* defined (__GNUC__) */ /* File logging functions */ typedef int LOG_FileID; extern LOG_FileID LOG_FileOpen(const char *name, const char *banner); 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-1.29/chrony.conf.5.in0000644000076400007640000000307412200726264014751 0ustar mirosmiros.TH chrony.conf 5 "August 2013" "chrony 1.29" "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 `a.b.c' and `d.e.f', your \fBchrony.conf\fR file could contain as a minimum server a.b.c server d.e.f server g.h.i However, you will probably want to include some of the other directives described in detail in the documentation supplied with the distribution (\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives will be particularly useful : `driftfile', `commandkey', `keyfile'. The smallest useful configuration file would look something like server a.b.c server d.e.f server g.h.i keyfile @SYSCONFDIR@/chrony.keys commandkey 1 driftfile @CHRONYVARDIR@/drift .SH "SEE ALSO" .BR chrony(1), .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-1.29/sources.c0000644000076400007640000011330612200721757013661 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-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. * ********************************************************************** ======================================================================= 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 "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" /* ================================================== */ /* 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; }; /* ================================================== */ /* This enum contains the flag values that are used to label each source */ typedef enum { SRC_OK, /* OK so far */ SRC_UNREACHABLE, /* Source is not reachable */ SRC_BAD_STATS, /* Stats driver could not supply valid data */ SRC_FALSETICKER, /* Source is found to be a falseticker */ SRC_JITTERY, /* Source scatter worse than other's dispersion */ SRC_SELECTABLE, /* Source is acceptable candidate */ SRC_SYNC /* Current synchronisation source */ } SRC_Status; /* ================================================== */ /* Define the instance structure used to hold information about each source */ struct SRC_Instance_Record { SST_Stats stats; NTP_Leap leap_status; /* Leap status */ int index; /* Index back into the array of source */ 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 we can use this source as a reference */ int selectable; /* Reachability register */ int reachability; /* Flag indicating that only few samples were accumulated so far */ int beginning; /* Updates left before allowing combining */ int outlier; /* 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, CENTRE=0, HIGH=1} tag; }; /* ================================================== */ /* Table of sources */ static struct SRC_Instance_Record **sources; static struct Sort_Element *sort_list; static int *sel_sources; static int n_sources; /* Number of sources currently in the table */ static int max_n_sources; /* Capacity of the table */ #define INVALID_SOURCE (-1) static int selected_source_index; /* Which source index is currently selected (set to INVALID_SOURCE if no current valid reference) */ /* Keep reachability status for last 8 samples */ #define REACH_BITS 8 /* Score needed to replace the currently selected source */ #define SCORE_LIMIT 10.0 /* Number of updates needed to reset the outlier status */ #define OUTLIER_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, int is_step_change, 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; 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); 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) { SRC_Instance result; assert(initialised); result = MallocNew(struct SRC_Instance_Record); result->stats = SST_CreateInstance(ref_id, addr); if (n_sources == max_n_sources) { /* Reallocate memory */ max_n_sources += 32; if (sources) { sources = ReallocArray(struct SRC_Instance_Record *, max_n_sources, sources); sort_list = ReallocArray(struct Sort_Element, 3*max_n_sources, sort_list); sel_sources = ReallocArray(int, max_n_sources, sel_sources); } else { sources = MallocArray(struct SRC_Instance_Record *, max_n_sources); sort_list = MallocArray(struct Sort_Element, 3*max_n_sources); sel_sources = MallocArray(int, max_n_sources); } } sources[n_sources] = result; result->index = n_sources; result->leap_status = LEAP_Normal; result->ref_id = ref_id; result->ip_addr = addr; result->selectable = 0; result->reachability = 0; result->beginning = 1; result->outlier = 0; result->status = SRC_BAD_STATS; result->type = type; result->sel_score = 1.0; result->sel_option = sel_option; 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); SRC_UnsetSelectable(instance); SST_DeleteInstance(instance->stats); dead_index = instance->index; for (i=dead_index; iindex = i; } --n_sources; Free(instance); if (selected_source_index > dead_index) { --selected_source_index; } } /* ================================================== */ /* Function to get the range of frequencies, relative to the given source, that we believe the local clock lies within. The return values are in terms of the number of seconds fast (+ve) or slow (-ve) relative to the source that the local clock becomes after a given amount of local time has elapsed. Suppose the initial offset relative to the source is U (fast +ve, slow -ve) and a time interval T elapses measured in terms of the local clock. Then the error relative to the source at the end of the interval should lie in the interval [U+T*lo, U+T*hi]. */ void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi) { 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; #ifdef TRACEON LOG(LOGS_INFO, 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); #endif /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET IS FLIPPED */ SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum); SST_DoNewRegression(inst->stats); /* And redo clock selection */ SRC_SelectSource(inst->ref_id); } /* ================================================== */ void SRC_SetSelectable(SRC_Instance inst) { inst->selectable = 1; #ifdef TRACEON LOG(LOGS_INFO, LOGF_Sources, "%s", source_to_string(inst)); #endif /* Don't do selection at this point, though - that will come about in due course when we get some useful data from the source */ } /* ================================================== */ void SRC_UnsetSelectable(SRC_Instance inst) { inst->selectable = 0; #ifdef TRACEON LOG(LOGS_INFO, LOGF_Sources, "%s%s", source_to_string(inst), (inst->index == selected_source_index) ? "(REF)":""); #endif /* If this was the previous reference source, we have to reselect! */ if (inst->index == selected_source_index) { SRC_SelectSource(0); } } /* ================================================== */ void SRC_UpdateReachability(SRC_Instance inst, int reachable) { inst->reachability <<= 1; inst->reachability |= !!reachable; inst->reachability &= ~(-1 << REACH_BITS); /* The beginning is over when the first sample is at the end of the register */ if (inst->reachability & (1 << (REACH_BITS - 1))) inst->beginning = 0; if (!reachable && inst->index == selected_source_index) { /* Try to select a better source */ SRC_SelectSource(0); } } /* ================================================== */ void SRC_ResetReachability(SRC_Instance inst) { /* This should be disabled until source selection is modified to keep a peer selected even when not reachable */ #if 0 inst->reachability = 0; SRC_UpdateReachability(inst, 0); #endif } /* ================================================== */ 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 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, 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; 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 outlier */ if (index != selected_source_index && (sources[index]->sel_info.root_distance > combine_limit * (reselect_distance + sources[selected_source_index]->sel_info.root_distance) || fabs(*frequency - src_frequency) > combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) { sources[index]->outlier = !sources[index]->beginning ? OUTLIER_PENALTY : 1; } else if (sources[index]->outlier) { sources[index]->outlier--; } if (sources[index]->outlier) continue; 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; #ifdef TRACEON LOG(LOGS_INFO, 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); #endif 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); #ifdef TRACEON LOG(LOGS_INFO, LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e", *offset, *offset_sd, *frequency, *skew); #endif return combined; } /* ================================================== */ /* This function selects the current reference from amongst the pool of sources we are holding. Updates are only made to the local reference if a new source is selected or match_refid is equal to the selected reference source refid */ void SRC_SelectSource(uint32_t match_refid) { int i, j, index, old_selected_index; struct timeval now, ref_time; double src_offset, src_offset_sd, src_frequency, src_skew; double src_root_delay, src_root_dispersion; int n_endpoints, j1, j2; double best_lo, best_hi; int depth, best_depth; int n_sel_sources, combined; double distance, sel_src_distance; int stratum, min_stratum; struct SelectInfo *si; int n_badstats_sources; int max_sel_reach, max_badstat_reach; int max_score_index; double max_score; NTP_Leap leap_status = LEAP_Normal; old_selected_index = selected_source_index; if (n_sources == 0) { /* In this case, we clearly cannot synchronise to anything */ if (selected_source_index != INVALID_SOURCE) { LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no sources"); selected_source_index = INVALID_SOURCE; REF_SetUnsynchronised(); } return; } /* 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; for (i=0; iselectable && sources[i]->reachability && sources[i]->sel_option != SRC_SelectNoselect) { si = &(sources[i]->sel_info); SST_GetSelectionData(sources[i]->stats, &now, &(si->stratum), &(si->lo_limit), &(si->hi_limit), &(si->root_distance), &(si->variance), &(si->select_ok)); #if 0 LOG(LOGS_INFO, LOGF_Sources, "%s dist=%f lo=%f hi=%f", source_to_string(sources[i]), si->root_distance, si->lo_limit, si->hi_limit); #endif if (si->select_ok) { ++n_sel_sources; sources[i]->status = SRC_OK; /* For now */ /* Otherwise it will be hard to pick this one later! However, this test might be too strict, we might want to dump it */ j1 = n_endpoints; j2 = j1 + 1; sort_list[j1].index = i; sort_list[j1].offset = si->lo_limit; sort_list[j1].tag = LOW; sort_list[j2].index = i; sort_list[j2].offset = si->hi_limit; sort_list[j2].tag = HIGH; n_endpoints += 2; if (max_sel_reach < sources[i]->reachability) { max_sel_reach = sources[i]->reachability; } } else { ++n_badstats_sources; sources[i]->status = SRC_BAD_STATS; if (max_badstat_reach < sources[i]->reachability) { max_badstat_reach = sources[i]->reachability; } } } else { /* If the source is not reachable, there is no way we will pick it. */ sources[i]->status = SRC_UNREACHABLE; } } #if 0 LOG(LOGS_INFO, LOGF_Sources, "badstat_sources=%d sel_sources=%d badstat_reach=%x sel_reach=%x", n_badstats_sources, n_sel_sources, max_badstat_reach, max_sel_reach); #endif /* 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) { return; } #if 0 LOG(LOGS_INFO, LOGF_Sources, "n_endpoints=%d", n_endpoints); #endif /* Now sort the endpoint list */ if (n_endpoints > 0) { /* Sort the list into order */ qsort((void *) sort_list, n_endpoints, sizeof(struct Sort_Element), compare_sort_elements); /* Now search for the interval which is contained in the most individual source intervals. Any source which overlaps this will be a candidate. If we get a case like <-----------------------> <--> <--> <===========> we will build the interval as shown with '=', whereas with an extra source we get <-----------------------> <-------> <--> <--> <==> The first case is just bad luck - we need extra sources to detect the falseticker, so just make an arbitrary choice based on stratum & stability etc. */ depth = best_depth = 0; best_lo = best_hi = 0.0; for (i=0; i best_depth) { best_depth = depth; best_lo = sort_list[i].offset; } break; case CENTRE: assert(0); break; case HIGH: if (depth == best_depth) { best_hi = sort_list[i].offset; } depth--; break; } } #if 0 LOG(LOGS_INFO, LOGF_Sources, "best_depth=%d best_lo=%f best_hi=%f", best_depth, best_lo, best_hi); #endif if (best_depth <= n_sel_sources/2) { /* Could not even get half the reachable sources to agree - clearly we can't synchronise. srcs #to agree 1 1 2 2 3 2 4 3 etc */ if (selected_source_index != INVALID_SOURCE) { LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no majority"); } selected_source_index = INVALID_SOURCE; /* .. and mark all sources as falsetickers (so they appear thus on the outputs from the command client) */ for (i=0; istatus = SRC_FALSETICKER; } } else { /* We have our interval, now work out which source are in it, i.e. build list of admissible sources. */ n_sel_sources = 0; for (i=0; istatus == SRC_OK) { /* This should be the same condition to get into the endpoint list */ /* Check if source's interval contains the best interval, or is wholly contained within it */ if (((sources[i]->sel_info.lo_limit <= best_lo) && (sources[i]->sel_info.hi_limit >= best_hi)) || ((sources[i]->sel_info.lo_limit >= best_lo) && (sources[i]->sel_info.hi_limit <= best_hi))) { sel_sources[n_sel_sources++] = i; #if 0 LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is valid", i, source_to_string(sources[i])); #endif } else { sources[i]->status = SRC_FALSETICKER; #if 0 LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is a falseticker", i, source_to_string(sources[i])); #endif } } } #if 0 /* We now have a list of indices for the sources which pass the false-ticker test. Now go on to reject those whose variance is greater than the minimum distance of any other */ /* Find minimum distance */ index = sel_sources[0]; min_distance = sources[index]->sel_info.root_distance; for (i=1; isel_info.root_distance; if (distance < min_distance) { min_distance = distance; } } #if 0 LOG(LOGS_INFO, LOGF_Sources, "min_distance=%f", min_distance); #endif /* Now go through and prune any NTP sources that have excessive variance */ for (i=0; itype == SRC_NTP && sqrt(sources[index]->sel_info.variance) > min_distance) { sel_sources[i] = INVALID_SOURCE; sources[index]->status = SRC_JITTERY; #if 0 LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s has too much variance", i, source_to_string(sources[i])); #endif } } #endif /* Now crunch the list and mark all sources as selectable */ for (i=j=0; istatus = SRC_SELECTABLE; sel_sources[j++] = sel_sources[i]; index++; } } n_sel_sources = j; if (n_sel_sources > 0) { /* Accept leap second status if more than half of selectable sources agree */ for (i=j1=j2=0; ileap_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; } /* If there are any sources with prefer option, reduce the list again only to the prefer sources */ for (i=j=0; isel_option == SRC_SelectPrefer) { sel_sources[j++] = sel_sources[i]; } } if (j > 0) { n_sel_sources = j; } /* Now find minimum stratum. If none are left now, tough. RFC1305 is not so harsh on pruning sources due to excess variance, which prevents this from happening */ index = sel_sources[0]; min_stratum = sources[index]->sel_info.stratum; for (i=1; isel_info.stratum; if (stratum < min_stratum) min_stratum = stratum; } #if 0 LOG(LOGS_INFO, LOGF_Sources, "min_stratum=%d", min_stratum); #endif /* Update scores and find 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_SELECTABLE) { sources[i]->sel_score = 1.0; sources[i]->outlier = OUTLIER_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]->ref_id == match_refid || sources[selected_source_index]->ref_id == match_refid) { 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 the source with minimum distance will have maximum score. The scores will be immediately reset. */ sources[i]->sel_score = 1.0 / distance; } #if 0 LOG(LOGS_INFO, LOGF_Sources, "select score=%f refid=%lx match_refid=%lx status=%d dist=%f", sources[i]->sel_score, sources[i]->ref_id, match_refid, sources[i]->status, distance); #endif 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_SELECTABLE) || (max_score_index != selected_source_index && max_score > SCORE_LIMIT)) { /* We have to elect a new synchronisation source */ selected_source_index = max_score_index; LOG(LOGS_INFO, LOGF_Sources, "Selected source %s", source_to_string(sources[selected_source_index])); #if 0 LOG(LOGS_INFO, LOGF_Sources, "new_sel_index=%d", selected_source_index); #endif /* New source has been selected, reset all scores */ for (i=0; i < n_sources; i++) { sources[i]->sel_score = 1.0; sources[i]->outlier = 0; } } sources[selected_source_index]->status = SRC_SYNC; /* Update local reference only when a new source was selected or a new sample was received (i.e. match_refid is equal to selected refid) */ if (selected_source_index != old_selected_index || match_refid == sources[selected_source_index]->ref_id) { /* 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); } } else { if (selected_source_index != INVALID_SOURCE) { LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no selectable sources"); } selected_source_index = INVALID_SOURCE; } } } else { /* No sources provided valid endpoints */ if (selected_source_index != INVALID_SOURCE) { LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no reachable sources"); } selected_source_index = INVALID_SOURCE; } if (selected_source_index == INVALID_SOURCE && selected_source_index != old_selected_index) { REF_SetUnsynchronised(); } } /* ================================================== */ /* Force reselecting the best source */ void SRC_ReselectSource(void) { selected_source_index = INVALID_SOURCE; SRC_SelectSource(0); } /* ================================================== */ 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, int is_step_change, void *anything) { int i; for (i=0; istats, cooked, dfreq, doffset); } } /* ================================================== */ /* 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_ReadNumberOfSources(void) { return n_sources; } /* ================================================== */ int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now) { SRC_Instance src; if ((index >= n_sources) || (index < 0)) { return 0; } else { src = sources[index]; 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_SYNC: report->state = RPT_SYNC; break; case SRC_JITTERY: report->state = RPT_JITTERY; break; case SRC_OK: case SRC_BAD_STATS: case SRC_UNREACHABLE: report->state = RPT_UNREACH; break; case SRC_FALSETICKER: report->state = RPT_FALSETICKER; break; case SRC_SELECTABLE: report->state = src->outlier ? RPT_OUTLIER : RPT_CANDIDATE; break; default: assert(0); break; } switch (src->sel_option) { case SRC_SelectNormal: report->sel_option = RPT_NOSELECT; 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; } /* ================================================== */ SRC_Skew_Direction SRC_LastSkewChange(SRC_Instance inst) { SRC_Skew_Direction result = SRC_Skew_Nochange; switch (SST_LastSkewChange(inst->stats)) { case SST_Skew_Decrease: result = SRC_Skew_Decrease; break; case SST_Skew_Nochange: result = SRC_Skew_Nochange; break; case SST_Skew_Increase: result = SRC_Skew_Increase; break; } return result; } /* ================================================== */ int SRC_Samples(SRC_Instance inst) { return SST_Samples(inst->stats); } /* ================================================== */ chrony-1.29/keys.h0000644000076400007640000000322512200721757013154 0ustar mirosmiros/* 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 extern void KEY_Initialise(void); extern void KEY_Finalise(void); extern void KEY_Reload(void); extern unsigned long KEY_GetCommandKey(void); extern int KEY_GetKey(unsigned long key_id, char **key, int *len); extern int KEY_KeyKnown(unsigned long key_id); extern int KEY_GetAuthDelay(unsigned long key_id); extern int KEY_GenerateAuth(unsigned long key_id, const unsigned char *data, int data_len, unsigned char *auth, int auth_len); extern int KEY_CheckAuth(unsigned long key_id, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len); #endif /* GOT_KEYS_H */ chrony-1.29/util.h0000644000076400007640000001161712200721757013162 0ustar mirosmiros/* 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 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); 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 void 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-1.29/cmdparse.c0000644000076400007640000001606212200721757013775 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * 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. * ********************************************************************** ======================================================================= 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 "util.h" /* ================================================== */ CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) { char *hostname, *cmd; int ok, 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.sel_option = SRC_SelectNormal; result = CPS_Success; hostname = line; line = CPS_SplitWord(line); if (!*hostname) { result = CPS_BadHost; ok = 0; } else { /* Parse subfields */ ok = 1; 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; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "minpoll")) { if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) { result = CPS_BadMinpoll; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxpoll")) { if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) { result = CPS_BadMaxpoll; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "presend")) { if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) { result = CPS_BadPresend; ok = 0; 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; ok = 0; 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; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "maxdelay")) { if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) { result = CPS_BadMaxdelay; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "key")) { if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1) { result = CPS_BadKey; ok = 0; 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; ok = 0; done = 1; } else { line += n; } } else if (!strcasecmp(cmd, "polltarget")) { if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) { result = CPS_BadPolltarget; ok = 0; 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 { result = CPS_BadOption; ok = 0; done = 1; } } else { done = 1; } } while (!done); } if (ok) { src->name = strdup(hostname); } return result; } /* ================================================== */ 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(*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(*q)) q++; /* Move the word to the beginning */ while (*q && !isspace(*q)) *p++ = *q++; /* Find the next word */ while (*q && isspace(*q)) q++; *p = '\0'; /* Return pointer to the next word or NUL */ return q; } /* ================================================== */ int CPS_ParseKey(char *line, unsigned long *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, "%lu", id) != 1) return 0; if (*s3) { *hash = s2; *key = s3; } else { *hash = "MD5"; *key = s2; } return 1; } chrony-1.29/examples/0000755000076400007640000000000012200721757013644 5ustar mirosmiroschrony-1.29/examples/chrony.conf.example20000644000076400007640000000216112200721757017531 0ustar mirosmiros# Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). server 0.pool.ntp.org iburst server 1.pool.ntp.org iburst server 2.pool.ntp.org iburst server 3.pool.ntp.org iburst # Ignore stratum in source selection. stratumweight 0 # Record the rate at which the system clock gains/losses time. driftfile /var/lib/chrony/drift # Enable kernel RTC synchronization. rtcsync # In first three updates step the system clock instead of slew # if the adjustment is larger than 10 seconds. makestep 10 3 # Allow NTP client access from local network. #allow 192.168/16 # Listen for commands only on localhost. bindcmdaddress 127.0.0.1 bindcmdaddress ::1 # Serve time even if not synchronized to any NTP server. #local stratum 10 keyfile /etc/chrony.keys # Specify the key used as password for chronyc. commandkey 1 # Generate command key if missing. generatecommandkey # Disable logging of client accesses. noclientlog # Send a message to syslog if a clock adjustment is larger than 0.5 seconds. logchange 0.5 logdir /var/log/chrony #log measurements statistics tracking chrony-1.29/examples/chrony.conf.example0000644000076400007640000003122312200721757017450 0ustar mirosmiros####################################################################### # # 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 0.pool.ntp.org iburst ! server 1.pool.ntp.org iburst ! server 2.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 0.pool.ntp.org offline ! server 1.pool.ntp.org offline ! server 2.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 ntp0.my-company.com # There are other options to the 'server' and 'peer' directives that you # might want to use. For example, you can ignore measurements whose # round-trip-time is too large (indicating that the measurement is # probably useless, because you don't know which way the measurement # message got held up.) Consult the full documentation for details. ####################################################################### ### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK # # To avoid changes being made to your computer's gain/loss compensation # when the measurement history is too erratic, you might want to enable # one of the following lines. The first seems good for dial-up (or # other high-latency connections like slow leased lines), the second # seems OK for a LAN environment. ! maxupdateskew 100 ! maxupdateskew 5 ####################################################################### ### FILENAMES ETC # Chrony likes to keep information about your computer's clock in files. # The 'driftfile' stores the computer's clock gain/loss rate in parts # per million. When chronyd starts, the system clock can be tuned # immediately so that it doesn't gain or lose any more time. You # generally want this, so it is uncommented. driftfile /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 # 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@foobar.org 0.5 ####################################################################### ### COMMAND ACCESS # The program chronyc is used to show the current operation of chronyd # and to change parts of its configuration whilst it is running. # Normally, chronyd will only allow connections from chronyc on the same # machine as itself. This is for security. If you have a subnet # 192.168.*.* and you want to be able to use chronyc from any machine on # it, you could uncomment the following line. (Edit this to your own # situation.) ! cmdallow 192.168/16 # You can add as many 'cmdallow' and 'cmddeny' lines as you like. The # syntax and meaning is the same as for 'allow' and 'deny', except that # 'cmdallow' and 'cmddeny' control access to the chronyd's command port. # NOTE, even if the host where you run chronyc is granted access, you # still need a command key set up and you have to know the password to # put into chronyc to allow you to modify chronyd's parameters. By # default all you can do is view information about chronyd's operation. ####################################################################### ### 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-1.29/examples/chrony.keys.example0000644000076400007640000000246412200721757017503 0ustar mirosmiros####################################################################### # # 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-1.29/reference.h0000644000076400007640000000756112200721757014146 0ustar mirosmiros/* 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 file for the module that keeps track of the current reference. */ #ifndef GOT_REFERENCE_H #define GOT_REFERENCE_H #include "sysincl.h" #include "ntp.h" #include "reports.h" /* Init function */ extern void REF_Initialise(void); /* Fini function */ extern void REF_Finalise(void); /* Function which takes a local cooked time and returns the estimated time of the reference. It also returns the other parameters required for forming the outgoing NTP packet. local_time is the cooked local time returned by the LCL module is_synchronised indicates whether we are synchronised to anything at the moment. leap indicates the current leap status stratum is the stratum of this machine, when considered to be sync'd to the reference ref_id is the reference_id of the source ref_time is the time at which the we last set the reference source up root_delay is the root delay of the sample we are using root_dispersion is the root dispersion of the sample we are using, with all the skew etc added on. */ extern void REF_GetReferenceParams ( struct timeval *local_time, int *is_synchronised, NTP_Leap *leap, int *stratum, 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 zero if the host is not synchronised */ extern int REF_GetOurStratum(void); /* Modify the setting for the maximum skew we are prepared to allow updates on (in ppm). */ extern void REF_ModifyMaxupdateskew(double new_max_update_skew); extern void REF_EnableLocal(int stratum); extern void REF_DisableLocal(void); extern int REF_IsLocalActive(void); extern void REF_GetTrackingReport(RPT_TrackingReport *rep); #endif /* GOT_REFERENCE_H */ chrony-1.29/Makefile.in0000644000076400007640000001232212200721757014073 0ustar mirosmiros################################################## # # 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 = util.o sched.o regress.o local.o \ sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \ sources.o sourcestats.o reference.o \ logging.o conf.o cmdmon.o keys.o \ nameserv.o acquire.o manual.o addrfilt.o \ cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \ broadcast.o refclock.o refclock_shm.o refclock_sock.o \ refclock_pps.o tempcomp.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) @HASH_LINK@ $(LIBS) $(EXTRA_LIBS) chronyc : $(CLI_OBJS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) @READLINE_LINK@ @HASH_LINK@ $(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 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 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 [ -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 chrony.1 $(DESTDIR)$(MANDIR)/man1 chmod 644 $(DESTDIR)$(MANDIR)/man1/chrony.1 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 $< 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 # This is only relevant if you're maintaining the website! faq.php : faq.txt faqgen.pl perl faqgen.pl < faq.txt > faq.php .deps: @mkdir .deps .deps/%.d: %.c | .deps @$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@ -include $(ALL_OBJS:%.o=.deps/%.d) chrony-1.29/wrap_adjtimex.c0000644000076400007640000001450312200721757015033 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Miroslav Lichvar 2011-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. * ********************************************************************** ======================================================================= This is a wrapper around the Linux adjtimex system call. It isolates the inclusion of from the need to include other header files, many of which conflict with those in on some recent distributions (as of Jul 2000) using kernels around 2.2.16 onwards. */ #include "config.h" #include "chrony_timex.h" #include "wrap_adjtimex.h" static int status = 0; int TMX_SetTick(long tick) { struct timex txc; txc.modes = ADJ_TICK; txc.tick = tick; return adjtimex(&txc); } int TMX_ApplyOffset(long *offset) { struct timex txc; int result; txc.modes = ADJ_OFFSET_SINGLESHOT; txc.offset = *offset; result = adjtimex(&txc); *offset = txc.offset; return result; } int TMX_SetFrequency(double *freq, long tick) { struct timex txc; txc.modes = ADJ_TICK | ADJ_FREQUENCY | ADJ_STATUS; txc.freq = (long)(*freq * (double)(1 << SHIFT_USEC)); *freq = txc.freq / (double)(1 << SHIFT_USEC); txc.tick = tick; txc.status = status; if (!(status & STA_UNSYNC)) { /* maxerror has to be reset periodically to prevent kernel from enabling UNSYNC flag */ txc.modes |= ADJ_MAXERROR; txc.maxerror = 0; } 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_GetOffsetLeft(long *offset) { struct timex txc; int result; txc.modes = ADJ_OFFSET_SS_READ; result = adjtimex(&txc); *offset = txc.offset; return result; } int TMX_ReadCurrentParams(struct tmx_params *params) { struct timex txc; int result; txc.modes = 0; /* pure read */ result = adjtimex(&txc); params->tick = txc.tick; params->offset = txc.offset; params->freq = txc.freq; params->dfreq = txc.freq / (double)(1 << SHIFT_USEC); params->maxerror = txc.maxerror; params->esterror = txc.esterror; params->sta_pll = !!(txc.status & STA_PLL); params->sta_ppsfreq = !!(txc.status & STA_PPSFREQ); params->sta_ppstime = !!(txc.status & STA_PPSTIME); params->sta_fll = !!(txc.status & STA_FLL); params->sta_ins = !!(txc.status & STA_INS); params->sta_del = !!(txc.status & STA_DEL); params->sta_unsync = !!(txc.status & STA_UNSYNC); params->sta_freqhold = !!(txc.status & STA_FREQHOLD); params->sta_ppssignal = !!(txc.status & STA_PPSSIGNAL); params->sta_ppsjitter = !!(txc.status & STA_PPSJITTER); params->sta_ppswander = !!(txc.status & STA_PPSWANDER); params->sta_ppserror = !!(txc.status & STA_PPSERROR); params->sta_clockerr = !!(txc.status & STA_CLOCKERR); params->constant = txc.constant; params->precision = txc.precision; params->tolerance = txc.tolerance; params->ppsfreq = txc.ppsfreq; params->jitter = txc.jitter; params->shift = txc.shift; params->stabil = txc.stabil; params->jitcnt = txc.jitcnt; params->calcnt = txc.calcnt; params->errcnt = txc.errcnt; params->stbcnt = txc.stbcnt; return result; } 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_SetSync(int sync) { struct timex txc; if (sync) { status &= ~STA_UNSYNC; } else { status |= STA_UNSYNC; } txc.modes = ADJ_STATUS; txc.status = status; return adjtimex(&txc); } int TMX_EnableNanoPLL(void) { struct timex txc; int result; txc.modes = ADJ_STATUS | ADJ_OFFSET | ADJ_TIMECONST | ADJ_NANO; txc.status = STA_PLL | STA_FREQHOLD; txc.offset = 0; txc.constant = 0; result = adjtimex(&txc); if (result < 0 || !(txc.status & STA_NANO) || txc.offset || txc.constant) return -1; status |= STA_PLL | STA_FREQHOLD; return result; } int TMX_ApplyPLLOffset(long offset, long constant) { struct timex txc; txc.modes = ADJ_OFFSET | ADJ_TIMECONST | ADJ_NANO; txc.offset = offset; txc.constant = constant; return adjtimex(&txc); } int TMX_GetPLLOffsetLeft(long *offset) { struct timex txc; int result; txc.modes = 0; result = adjtimex(&txc); *offset = txc.offset; return result; } 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; 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; if (offset >= 0) { txc.time.tv_sec = offset; } else { txc.time.tv_sec = offset - 1; } /* ADJ_NANO changes the status even with ADJ_SETOFFSET, use it only when STA_NANO is already enabled */ if (status & STA_PLL) { txc.modes |= ADJ_NANO; txc.time.tv_usec = 1e9 * (offset - txc.time.tv_sec); } else { txc.time.tv_usec = 1e6 * (offset - txc.time.tv_sec); } return adjtimex(&txc); } chrony-1.29/cmdmon.h0000644000076400007640000000256212200721757013461 0ustar mirosmiros/* 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-1.29/rtc.h0000644000076400007640000000272412200721757012774 0ustar mirosmiros/* 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(void); extern void RTC_Finalise(void); extern void RTC_TimePreInit(void); extern void RTC_TimeInit(void (*after_hook)(void *), void *anything); extern void RTC_StartMeasurements(void); extern int RTC_GetReport(RPT_RTC_Report *report); #define RTC_ST_OK 0 #define RTC_ST_NODRV 1 #define RTC_ST_BADFILE 2 extern int RTC_WriteParameters(void); extern int RTC_Trim(void); #endif /* GOT_RTC_H */ chrony-1.29/acquire.h0000644000076400007640000000304412200721757013631 0ustar mirosmiros/* 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 acquisition module */ #ifndef GOT_ACQUIRE_H #define GOT_ACQUIRE_H #include "addressing.h" typedef struct ACQ_SourceRecord *ACQ_Source; extern void ACQ_Initialise(void); extern void ACQ_Finalise(void); extern void ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double init_slew_threshold, void (*after_hook)(void *), void *anything); extern void ACQ_AccumulateSample(ACQ_Source acq_source, double offset, double root_distance); extern void ACQ_MissedSample(ACQ_Source acq_source); #endif /* GOT_ACQUIRE_H */ chrony-1.29/broadcast.h0000644000076400007640000000242612200721757014145 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= Deal with broadcast server functions. */ #ifndef GOT_BROADCAST_H #define GOT_BROADCAST_H #include "addressing.h" extern void BRD_Initialise(void); extern void BRD_Finalise(void); extern void BRD_AddDestination(IPAddr *addr, unsigned short port, int interval); #endif /* GOT_BROADCAST_H */ chrony-1.29/localp.h0000644000076400007640000000560412200721757013456 0ustar mirosmiros/* 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 void (*lcl_ApplyStepOffsetDriver)(double offset); /* System driver to convert a raw time to an adjusted (cooked) time. The number of seconds returned in 'corr' have to be added to the raw time to get the corrected time */ typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err); /* System driver to schedule leap second */ typedef void (*lcl_SetLeapDriver)(int leap); 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); #endif /* GOT_LOCALP_H */ chrony-1.29/cmdmon.c0000644000076400007640000020020012200721757013441 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-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. * ********************************************************************** ======================================================================= 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 "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 HAVE_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; /* File descriptors for command and monitoring sockets */ static int sock_fd4; #ifdef HAVE_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 int permissions[] = { PERMIT_OPEN, /* NULL */ PERMIT_AUTH, /* ONLINE */ PERMIT_AUTH, /* OFFLINE */ PERMIT_AUTH, /* BURST */ PERMIT_AUTH, /* MODIFY_MINPOLL */ PERMIT_AUTH, /* MODIFY_MAXPOLL */ PERMIT_AUTH, /* DUMP */ PERMIT_AUTH, /* MODIFY_MAXDELAY */ PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */ PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */ PERMIT_OPEN, /* LOGON */ PERMIT_AUTH, /* SETTIME */ PERMIT_AUTH, /* LOCAL */ PERMIT_AUTH, /* MANUAL */ PERMIT_OPEN, /* N_SOURCES */ PERMIT_OPEN, /* SOURCE_DATA */ PERMIT_AUTH, /* REKEY */ PERMIT_AUTH, /* ALLOW */ PERMIT_AUTH, /* ALLOWALL */ PERMIT_AUTH, /* DENY */ PERMIT_AUTH, /* DENYALL */ PERMIT_AUTH, /* CMDALLOW */ PERMIT_AUTH, /* CMDALLOWALL */ PERMIT_AUTH, /* CMDDENY */ PERMIT_AUTH, /* CMDDENYALL */ PERMIT_AUTH, /* ACCHECK */ PERMIT_AUTH, /* CMDACCHECK */ PERMIT_AUTH, /* ADD_SERVER */ PERMIT_AUTH, /* ADD_PEER */ PERMIT_AUTH, /* DEL_SOURCE */ PERMIT_AUTH, /* WRITERTC */ PERMIT_AUTH, /* DFREQ */ PERMIT_AUTH, /* DOFFSET */ PERMIT_OPEN, /* TRACKING */ PERMIT_OPEN, /* SOURCESTATS */ PERMIT_OPEN, /* RTCREPORT */ PERMIT_AUTH, /* TRIMRTC */ PERMIT_AUTH, /* CYCLELOGS */ PERMIT_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 */ }; /* ================================================== */ /* 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 int prepare_socket(int family); static void read_from_cmd_socket(void *anything); /* ================================================== */ static int prepare_socket(int family) { int port_number, sock_fd; socklen_t my_addr_len; union sockaddr_in46 my_addr; IPAddr bind_address; int on_off = 1; port_number = CNF_GetCommandPort(); 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 HAVE_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_ANY); break; #ifdef HAVE_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_any; 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) { assert(!initialised); initialised = 1; assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES); 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; if (family == IPADDR_UNSPEC || family == IPADDR_INET4) sock_fd4 = prepare_socket(AF_INET); else sock_fd4 = -1; #ifdef HAVE_IPV6 if (family == IPADDR_UNSPEC || family == IPADDR_INET6) sock_fd6 = prepare_socket(AF_INET6); else sock_fd6 = -1; #endif if (sock_fd4 < 0 #ifdef HAVE_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 HAVE_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 32 static void get_more_replies(void) { ResponseCell *new_replies; int i; if (!free_replies) { new_replies = MallocArray(ResponseCell, REPLY_EXTEND_QUANTUM); for (i=1; 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; } } /* ================================================== */ #if 0 /* These two routines are not legal if the program is operating as a daemon, since stderr is no longer open */ static void print_command_packet(CMD_Request *pkt, int length) { unsigned char *x; int i; x = (unsigned char *) pkt; for (i=0; iu.sa_family) { case AF_INET: sock_fd = sock_fd4; addrlen = sizeof (where_to->in4); break; #ifdef HAVE_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 && !LOG_RateLimited()) { unsigned short port; IPAddr ip; switch (where_to->u.sa_family) { case AF_INET: ip.family = IPADDR_INET4; ip.addr.in4 = ntohl(where_to->in4.sin_addr.s_addr); port = ntohs(where_to->in4.sin_port); break; #ifdef HAVE_IPV6 case AF_INET6: ip.family = IPADDR_INET6; memcpy(ip.addr.in6, (where_to->in6.sin6_addr.s6_addr), sizeof(ip.addr.in6)); port = ntohs(where_to->in6.sin6_port); break; #endif default: assert(0); } LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port); } } /* ================================================== */ static void handle_null(CMD_Request *rx_message, CMD_Reply *tx_message) { tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_online(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.online.address, &address); status = NSR_TakeSourcesOnline(&mask, &address); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.offline.address, &address); status = NSR_TakeSourcesOffline(&mask, &address); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address, mask; UTI_IPNetworkToHost(&rx_message->data.burst.mask, &mask); UTI_IPNetworkToHost(&rx_message->data.burst.address, &address); status = NSR_InitiateSampleBurst(ntohl(rx_message->data.burst.n_good_samples), ntohl(rx_message->data.burst.n_total_samples), &mask, &address); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_minpoll(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); status = NSR_ModifyMinpoll(&address, ntohl(rx_message->data.modify_minpoll.new_minpoll)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_maxpoll(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); status = NSR_ModifyMaxpoll(&address, ntohl(rx_message->data.modify_minpoll.new_minpoll)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_maxdelay(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelay.address, &address); status = NSR_ModifyMaxdelay(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelay.new_max_delay)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_maxdelayratio(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelayratio.address, &address); status = NSR_ModifyMaxdelayratio(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelayratio.new_max_delay_ratio)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_maxdelaydevratio(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_maxdelaydevratio.address, &address); status = NSR_ModifyMaxdelaydevratio(&address, UTI_FloatNetworkToHost(rx_message->data.modify_maxdelaydevratio.new_max_delay_dev_ratio)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_minstratum(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address); status = NSR_ModifyMinstratum(&address, ntohl(rx_message->data.modify_minstratum.new_min_stratum)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_polltarget(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; IPAddr address; UTI_IPNetworkToHost(&rx_message->data.modify_polltarget.address, &address); status = NSR_ModifyPolltarget(&address, ntohl(rx_message->data.modify_polltarget.new_poll_target)); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NOSUCHSOURCE); } } /* ================================================== */ static void handle_modify_maxupdateskew(CMD_Request *rx_message, CMD_Reply *tx_message) { REF_ModifyMaxupdateskew(UTI_FloatNetworkToHost(rx_message->data.modify_maxupdateskew.new_max_update_skew)); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message) { struct timeval ts; long offset_cs; double dfreq_ppm, new_afreq_ppm; UTI_TimevalNetworkToHost(&rx_message->data.settime.ts, &ts); if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) { tx_message->status = htons(STT_SUCCESS); tx_message->reply = htons(RPY_MANUAL_TIMESTAMP); tx_message->data.manual_timestamp.centiseconds = htonl((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_NOTENABLED); } } /* ================================================== */ static void handle_local(CMD_Request *rx_message, CMD_Reply *tx_message) { int on_off, stratum; on_off = ntohl(rx_message->data.local.on_off); if (on_off) { stratum = ntohl(rx_message->data.local.stratum); REF_EnableLocal(stratum); } else { REF_DisableLocal(); } tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_manual(CMD_Request *rx_message, CMD_Reply *tx_message) { int option; option = ntohl(rx_message->data.manual.option); switch (option) { case 0: MNL_Disable(); break; case 1: MNL_Enable(); break; case 2: MNL_Reset(); break; } tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_n_sources(CMD_Request *rx_message, CMD_Reply *tx_message) { int n_sources; n_sources = SRC_ReadNumberOfSources(); tx_message->status = htons(STT_SUCCESS); tx_message->reply = htons(RPY_N_SOURCES); tx_message->data.n_sources.n_sources = htonl(n_sources); } /* ================================================== */ static void handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_SourceReport report; struct timeval now_corr; /* Get data */ LCL_ReadCookedTime(&now_corr, 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->status = htons(STT_SUCCESS); 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_PREFER); 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) { tx_message->status = htons(STT_SUCCESS); KEY_Reload(); } /* ================================================== */ static void handle_allow(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 1, 0)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_allowall(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 1, 1)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_deny(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 0, 0)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_denyall(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 0, 1)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_cmdallow(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 1, 0)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_cmdallowall(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 1, 1)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_cmddeny(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 0, 0)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_cmddenyall(CMD_Request *rx_message, CMD_Reply *tx_message) { 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, 0, 1)) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_BADSUBNET); } } /* ================================================== */ static void handle_accheck(CMD_Request *rx_message, CMD_Reply *tx_message) { 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.local_ip_addr.family = IPADDR_UNSPEC; 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; status = NSR_AddSource(&rem_addr, type, ¶ms); switch (status) { case NSR_Success: tx_message->status = htons(STT_SUCCESS); break; case NSR_AlreadyInUse: tx_message->status = htons(STT_SOURCEALREADYKNOWN); break; case NSR_TooManySources: tx_message->status = htons(STT_TOOMANYSOURCES); break; case NSR_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.local_ip_addr.family = IPADDR_UNSPEC; rem_addr.port = 0; status = NSR_RemoveSource(&rem_addr); switch (status) { case NSR_Success: tx_message->status = htons(STT_SUCCESS); break; case NSR_NoSuchSource: tx_message->status = htons(STT_NOSUCHSOURCE); break; case NSR_TooManySources: case NSR_AlreadyInUse: 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: tx_message->status = htons(STT_SUCCESS); break; case RTC_ST_NODRV: tx_message->status = htons(STT_NORTC); break; case RTC_ST_BADFILE: tx_message->status = htons(STT_BADRTCFILE); break; } } /* ================================================== */ static void handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message) { double dfreq; dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq); LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6); LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message) { long sec, usec; double doffset; sec = (long)(ntohl(rx_message->data.doffset.sec)); usec = (long)(ntohl(rx_message->data.doffset.usec)); doffset = (double) sec + 1.0e-6 * (double) usec; LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset); LCL_AccumulateOffset(doffset, 0.0); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message) { RPT_TrackingReport rpt; REF_GetTrackingReport(&rpt); tx_message->status = htons(STT_SUCCESS); tx_message->reply = htons(RPY_TRACKING); tx_message->data.tracking.ref_id = htonl(rpt.ref_id); 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_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; RPT_SourcestatsReport report; struct timeval now_corr; LCL_ReadCookedTime(&now_corr, NULL); status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index), &report, &now_corr); if (status) { tx_message->status = htons(STT_SUCCESS); 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->status = htons(STT_SUCCESS); 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) { int status; status = RTC_Trim(); if (status) { tx_message->status = htons(STT_SUCCESS); } else { tx_message->status = htons(STT_NORTC); } } /* ================================================== */ static void handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message) { LOG_CycleLogFiles(); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message) { CLG_Status result; RPT_ClientAccessByIndex_Report report; unsigned long first_index, n_indices, last_index, n_indices_in_table; int i, j; struct timeval now; LCL_ReadCookedTime(&now, 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); last_index = first_index + n_indices - 1; tx_message->status = htons(STT_SUCCESS); tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX); for (i = first_index, j = 0; (i <= last_index) && (j < MAX_CLIENT_ACCESSES); i++) { result = CLG_GetClientAccessReportByIndex(i, &report, now.tv_sec, &n_indices_in_table); tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table); switch (result) { case CLG_SUCCESS: 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(i); tx_message->data.client_accesses_by_index.n_clients = htonl(j); } /* ================================================== */ static void handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message) { int n_samples; int i; RPY_ManualListSample *sample; RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES]; tx_message->status = htons(STT_SUCCESS); tx_message->reply = htons(RPY_MANUAL_LIST); MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples); tx_message->data.manual_list.n_samples = htonl(n_samples); for (i=0; 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 status; int index; index = ntohl(rx_message->data.manual_delete.index); status = MNL_DeleteSample(index); if (!status) { tx_message->status = htons(STT_BADSAMPLE); } else { tx_message->status = htons(STT_SUCCESS); } } /* ================================================== */ static void handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message) { LCL_MakeStep(0.0); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ 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->status = htons(STT_SUCCESS); 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); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ static void handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message) { SRC_ReselectSource(); tx_message->status = htons(STT_SUCCESS); } /* ================================================== */ #if 0 /* ================================================== */ static void handle_(CMD_Request *rx_message, CMD_Reply *tx_message) { int status; } #endif /* ================================================== */ /* Read a packet and process it */ static void read_from_cmd_socket(void *anything) { int status; int read_length; /* Length of packet read */ int expected_length; /* Expected length of packet 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; } read_length = status; LCL_ReadRawTime(&now); LCL_CookTime(&now, &cooked_now, NULL); switch (where_from.u.sa_family) { case AF_INET: remote_ip.family = IPADDR_INET4; remote_ip.addr.in4 = ntohl(where_from.in4.sin_addr.s_addr); remote_port = ntohs(where_from.in4.sin_port); localhost = (remote_ip.addr.in4 == 0x7f000001UL); break; #ifdef HAVE_IPV6 case AF_INET6: remote_ip.family = IPADDR_INET6; memcpy(&remote_ip.addr.in6, where_from.in6.sin6_addr.s6_addr, sizeof (remote_ip.addr.in6)); remote_port = ntohs(where_from.in6.sin6_port); /* Check for ::1 */ for (localhost = 0; localhost < 16; localhost++) if (remote_ip.addr.in6[localhost] != 0) break; localhost = (localhost == 15 && remote_ip.addr.in6[localhost] == 1); break; #endif default: assert(0); } allowed = ADF_IsAllowed(access_auth_table, &remote_ip) || localhost; /* 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) || 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 */ if (allowed) 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.number = htons(1); tx_message.total = htons(1); tx_message.pad1 = 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) { tx_message.status = htons(STT_NOHOSTACCESS); if (!LOG_RateLimited()) { LOG(LOGS_WARN, 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); } if (allowed) CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec); if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT) { tx_message.status = htons(STT_BADPKTVERSION); /* add empty MD5 auth so older clients will not drop the reply due to bad length */ memset(((char *)&tx_message) + PKL_ReplyLength(&tx_message), 0, 16); transmit_reply(&tx_message, &where_from, 16); } return; } if (rx_command >= N_REQUEST_TYPES) { if (!LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_CmdMon, "Read command packet with invalid command %d from %s:%hu", rx_command, UTI_IPToString(&remote_ip), remote_port); } if (allowed) 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) { if (!LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port); } if (allowed) 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; } if (!allowed) { /* 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! */ if (!LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_CmdMon, "Command packet received from unauthorised host %s port %d", UTI_IPToString(&remote_ip), remote_port); } tx_message.status = htons(STT_NOHOSTACCESS); 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 && !LOG_RateLimited()) { LOG(LOGS_WARN, 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; if (auth_ok) { struct timeval ts; UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts); if ((utoken_ok && token_ok) || ((ntohl(rx_message.utoken) == SPECIAL_UTOKEN) && (rx_command == REQ_LOGON) && (valid_ts = ts_is_unique_and_not_stale(&ts, &now)))) issue_token = 1; else issue_token = 0; } else { issue_token = 0; } 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 { allowed = 0; /* Check level of authority required to issue the command */ switch(permissions[rx_command]) { case PERMIT_AUTH: if (authenticated) { allowed = 1; } else { allowed = 0; } break; case PERMIT_LOCAL: if (authenticated || localhost) { allowed = 1; } else { allowed = 0; } break; case PERMIT_OPEN: allowed = 1; break; default: assert(0); } if (allowed) { switch(rx_command) { case REQ_NULL: handle_null(&rx_message, &tx_message); break; case REQ_ONLINE: handle_online(&rx_message, &tx_message); break; case REQ_OFFLINE: handle_offline(&rx_message, &tx_message); break; case REQ_BURST: handle_burst(&rx_message, &tx_message); break; case REQ_MODIFY_MINPOLL: handle_modify_minpoll(&rx_message, &tx_message); break; case REQ_MODIFY_MAXPOLL: handle_modify_maxpoll(&rx_message, &tx_message); break; case REQ_DUMP: SRC_DumpSources(); tx_message.status = htons(STT_SUCCESS); break; case REQ_MODIFY_MAXDELAY: handle_modify_maxdelay(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYRATIO: handle_modify_maxdelayratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXDELAYDEVRATIO: handle_modify_maxdelaydevratio(&rx_message, &tx_message); break; case REQ_MODIFY_MAXUPDATESKEW: handle_modify_maxupdateskew(&rx_message, &tx_message); break; case REQ_LOGON: /* If the log-on fails, record the reason why */ if (!issue_token && !LOG_RateLimited()) { LOG(LOGS_WARN, 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_allow(&rx_message, &tx_message); break; case REQ_ALLOWALL: handle_allowall(&rx_message, &tx_message); break; case REQ_DENY: handle_deny(&rx_message, &tx_message); break; case REQ_DENYALL: handle_denyall(&rx_message, &tx_message); break; case REQ_CMDALLOW: handle_cmdallow(&rx_message, &tx_message); break; case REQ_CMDALLOWALL: handle_cmdallowall(&rx_message, &tx_message); break; case REQ_CMDDENY: handle_cmddeny(&rx_message, &tx_message); break; case REQ_CMDDENYALL: handle_cmddenyall(&rx_message, &tx_message); break; case REQ_ACCHECK: handle_accheck(&rx_message, &tx_message); break; case REQ_CMDACCHECK: handle_cmdaccheck(&rx_message, &tx_message); break; case REQ_ADD_SERVER: handle_add_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_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-1.29/candm.h0000644000076400007640000003613712200721757013273 0ustar mirosmiros/* 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 N_REQUEST_TYPES 50 /* 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 { 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 { 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 EOR; } REQ_N_Sources; typedef struct { int32_t index; int32_t EOR; } REQ_Source_Data; typedef struct { int32_t EOR; } REQ_Rekey; 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 { int32_t EOR; } REQ_WriteRtc; typedef struct { Float dfreq; int32_t EOR; } REQ_Dfreq; typedef struct { int32_t sec; int32_t usec; int32_t EOR; } REQ_Doffset; typedef struct { int32_t EOR; } REQ_Tracking; typedef struct { uint32_t index; int32_t EOR; } REQ_Sourcestats; typedef struct { int32_t EOR; } REQ_RTCReport; typedef struct { int32_t EOR; } REQ_TrimRTC; typedef struct { int32_t EOR; } REQ_CycleLogs; typedef struct { IPAddr ip; uint32_t bits_specd; } REQ_SubnetsAccessed_Subnet; /* 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 EOR; } REQ_ManualList; typedef struct { int32_t index; int32_t EOR; } REQ_ManualDelete; typedef struct { int32_t EOR; } REQ_MakeStep; typedef struct { int32_t EOR; } REQ_Activity; typedef struct { int32_t EOR; } REQ_Reselect; typedef struct { Float distance; int32_t EOR; } REQ_ReselectDistance; /* ================================================== */ #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 */ #define PROTO_VERSION_NUMBER 5 /* The oldest protocol version that is compatible enough with the current version to report a version mismatch */ #define PROTO_VERSION_MISMATCH_COMPAT 4 /* ================================================== */ 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_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_Logon logon; REQ_Settime settime; REQ_Local local; REQ_Manual manual; REQ_N_Sources n_sources; REQ_Source_Data source_data; REQ_Rekey rekey; REQ_Allow_Deny allow_deny; REQ_Ac_Check ac_check; REQ_NTP_Source ntp_source; REQ_Del_Source del_source; REQ_WriteRtc writertc; REQ_Dfreq dfreq; REQ_Doffset doffset; REQ_Tracking tracking; REQ_Sourcestats sourcestats; REQ_RTCReport rtcreport; REQ_TrimRTC trimrtc; REQ_CycleLogs cyclelogs; REQ_ClientAccessesByIndex client_accesses_by_index; REQ_ManualList manual_list; REQ_ManualDelete manual_delete; REQ_MakeStep make_step; REQ_Activity activity; REQ_Reselect reselect; REQ_ReselectDistance reselect_distance; } data; /* Command 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_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 N_REPLY_TYPES 13 /* Status codes */ #define STT_SUCCESS 0 #define STT_FAILED 1 #define STT_UNAUTH 2 #define STT_INVALID 3 #define STT_NOSUCHSOURCE 4 #define STT_INVALIDTS 5 #define STT_NOTENABLED 6 #define STT_BADSUBNET 7 #define STT_ACCESSALLOWED 8 #define STT_ACCESSDENIED 9 #define STT_NOHOSTACCESS 10 #define STT_SOURCEALREADYKNOWN 11 #define STT_TOOMANYSOURCES 12 #define STT_NORTC 13 #define STT_BADRTCFILE 14 #define STT_INACTIVE 15 #define STT_BADSAMPLE 16 #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; uint16_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 bits_specd; uint32_t bitmap[8]; } RPY_SubnetsAccessed_Subnet; 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]; } RPY_ClientAccessesByIndex; #define MAX_MANUAL_LIST_SAMPLES 32 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]; } 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; typedef struct { uint8_t version; uint8_t pkt_type; uint8_t res1; uint8_t res2; uint16_t command; /* Which command is being replied to */ uint16_t reply; /* Which format of reply this is */ uint16_t status; /* Status of command processing */ uint16_t number; /* Which packet this is in reply sequence */ uint16_t total; /* Number of replies to expect in this sequence */ uint16_t pad1; /* Get up to 4 byte alignment */ uint32_t sequence; /* Echo of client's sequence number */ uint32_t utoken; /* Unique token per incarnation of daemon */ uint32_t token; /* New command token (only if command was successfully authenticated) */ 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; } 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-1.29/README0000644000076400007640000001704112200721757012711 0ustar mirosmirosThis is the README for chrony. What is chrony? =============== Chrony is a pair of programs for maintaining the accuracy of computer clocks. chronyd is a (background) daemon program that can be started at boot time. This does most of the work. chronyc is a command-line interface program which can be used to monitor chronyd's performance and to change various operating parameters whilst it is running. chronyd's main function is to obtain measurements of the true (UTC) time from one of several sources, and correct the system clock accordingly. It also works out the rate at which the system clock gains or loses time and uses this information to keep it accurate between measurements from the reference. The reference time can be derived from Network Time Protocol (NTP) servers, reference clocks, or wristwatch-and-keyboard (via chronyc). The main source of information about the Network Time Protocol is http://www.ntp.org. It is designed so that it can work on computers which only have intermittent access to reference sources, for example computers which use a dial-up account to access the Internet or laptops. Of course, it will work well on computers with permanent connections too. In addition, on Linux it can monitor the system's real time clock performance, so the system can maintain accurate time even across reboots. Typical accuracies available between 2 machines are On an ethernet LAN : 100-200 microseconds, often much better On a V32bis dial-up modem connection : 10's of milliseconds (from one session to the next) With a good reference clock the accuracy can reach one microsecond. chronyd can also operate as an RFC1305-compatible NTP server and peer. What will chrony run on? ======================== Chrony can be successfully built and run on 1. Linux 2.2.x, 2.3.x, 2.4.x, 2.6.x, 3.x 2. Solaris 2.5/2.5.1/2.6/2.7/2.8 (various platforms) 3. SunOS 4.1.4 (Sparc 2 and Sparc 20) 4. BSD/386 v1.1 has been reported to work using the SunOS 4.1 driver. 5. NetBSD. Any other system will require a porting exercise. You would need to start from one of the existing system-specific drivers and look into the quirks of certain system calls and the kernel on your target system. (This is described in the manual). How do I set it up? =================== The file INSTALL gives instructions. On supported systems the compilation process should be automatic. You will need an ANSI C compiler -- gcc is recommended. 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). New versions are announced also on Freshmeat (http://freshmeat.net/). 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. Author ====== Richard P. Curnow Maintainers =========== John Hasler 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 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 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 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-1.29/sched.c0000644000076400007640000004073512200721757013271 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= This file contains the scheduling loop and the timeout queue. */ #include "config.h" #include "sysincl.h" #include "sched.h" #include "memory.h" #include "util.h" #include "local.h" #include "logging.h" /* ================================================== */ /* Flag indicating that we are initialised */ static int initialised = 0; /* ================================================== */ /* Variables to handle the capability to dispatch on particular file handles becoming readable */ /* Each bit set in this fd set corresponds to a read descriptor that we are watching and with which we have a handler associated in the file_handlers array */ static fd_set read_fds; /* This is the number of bits that we have set in read_fds */ static unsigned int n_read_fds; /* One more than the highest file descriptor that is registered */ static unsigned int one_highest_fd; /* This assumes that fd_set is implemented as a fixed size array of bits, possibly embedded inside a record. It might therefore somewhat non-portable. */ #define FD_SET_SIZE (sizeof(fd_set) * 8) typedef struct { SCH_FileHandler handler; SCH_ArbitraryArgument arg; } FileHandlerEntry; static FileHandlerEntry file_handlers[FD_SET_SIZE]; /* 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, int is_step_change, void *anything); /* ================================================== */ void SCH_Initialise(void) { FD_ZERO(&read_fds); n_read_fds = 0; n_timer_queue_entries = 0; next_tqe_id = 0; timer_queue.next = &timer_queue; timer_queue.prev = &timer_queue; need_to_exit = 0; LCL_AddParameterChangeHandler(handle_slew, NULL); 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) { initialised = 0; } /* ================================================== */ void SCH_AddInputFileHandler (int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg) { assert(initialised); /* Don't want to allow the same fd to register a handler more than once without deleting a previous association - this suggests a bug somewhere else in the program. */ assert(!FD_ISSET(fd, &read_fds)); ++n_read_fds; file_handlers[fd].handler = handler; file_handlers[fd].arg = arg; FD_SET(fd, &read_fds); if ((fd + 1) > one_highest_fd) { one_highest_fd = fd + 1; } } /* ================================================== */ 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); 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) { int fh = 0; while (nfh > 0) { if (FD_ISSET(fh, fhs)) { /* This descriptor can be read from, dispatch its handler */ (file_handlers[fh].handler)(file_handlers[fh].arg); /* Decrement number of readable files still to find */ --nfh; } ++fh; } } /* ================================================== */ static void handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything) { TimerQueueEntry *ptr; double delta; int i; if (is_step_change) { /* 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); } /* ================================================== */ /* Try to handle unexpected backward time jump */ static void recover_backjump(struct timeval *raw, struct timeval *cooked, int timeout) { double diff, err; UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw); if (n_timer_queue_entries > 0) { UTI_DiffTimevalsToDouble(&err, &(timer_queue.next->tv), &last_select_ts_raw); } else { err = 0.0; } diff += err; if (timeout) { err = 1.0; } LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected! (correction %.1f +- %.1f seconds)", diff, err); LCL_NotifyExternalTimeStep(raw, cooked, diff, err); } /* ================================================== */ void SCH_MainLoop(void) { fd_set rd; int status, errsv; struct timeval tv, *ptv; struct timeval now, cooked; double err; assert(initialised); while (!need_to_exit) { /* Copy current set of read file descriptors */ memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set)); /* Dispatch timeouts and fill now with current raw time */ dispatch_timeouts(&now); /* 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); } else { ptv = NULL; } /* if there are no file descriptors being waited on and no timeout set, this is clearly ridiculous, so stop the run */ assert(ptv || n_read_fds); status = select(one_highest_fd, &rd, NULL, NULL, ptv); errsv = errno; LCL_ReadRawTime(&now); LCL_CookTime(&now, &cooked, &err); /* Check if time didn't jump backwards */ if (last_select_ts_raw.tv_sec > now.tv_sec + 1) { recover_backjump(&now, &cooked, status == 0); } 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-1.29/chrony.10000644000076400007640000000532412200721757013416 0ustar mirosmiros.TH CHRONY 1 "@MAN_DATE@" "chrony @VERSION@" "User's Manual" .SH NAME chrony \- programs for keeping computer clocks accurate .SH SYNOPSIS \fBchronyc\fR [\fIOPTIONS\fR] \fBchronyd\fR [\fIOPTIONS\fR] .SH DESCRIPTION \fBchrony\fR is a pair of programs for keeping computer clocks accurate. \fIchronyd\fR is a background (daemon) program and \fIchronyc\fR is a command-line interface to it. Time reference sources for chronyd can be RFC1305 NTP servers, human (via keyboard and \fIchronyc\fR), or the computer's real-time clock at boot time (Linux only). chronyd can determine the rate at which the computer gains or loses time and compensate for it while no external reference is present. Its use of NTP servers can be switched on and off (through \fIchronyc\fR) to support computers with dial-up/intermittent access to the Internet, and it can also act as an RFC1305-compatible NTP server. .SH USAGE \fIchronyc\fR is a command-line interface program which can be used to monitor \fIchronyd\fR's performance and to change various operating parameters whilst it is running. \fIchronyd\fR's main function is to obtain measurements of the true (UTC) time from one of several sources, and correct the system clock accordingly. It also works out the rate at which the system clock gains or loses time and uses this information to keep it accurate between measurements from the reference. The reference time can be derived from either Network Time Protocol (NTP) servers, reference clocks, or wristwatch-and-keyboard (via \fIchronyc\fR). The main source of information about the Network Time Protocol is \fIhttp://www.ntp.org\fR. It is designed so that it can work on computers which only have intermittent access to reference sources, for example computers which use a dial-up account to access the Internet or laptops. Of course, it will work well on computers with permanent connections too. In addition, on Linux it can monitor the system's real time clock performance, so the system can maintain accurate time even across reboots. Typical accuracies available between 2 machines are On an ethernet LAN : 100-200 microseconds, often much better On a V32bis dial-up modem connection : 10's of milliseconds (from one session to the next) With a good reference clock the accuracy can reach one microsecond. \fIchronyd\fR can also operate as an RFC1305-compatible NTP server and peer. .SH "SEE ALSO" .BR chronyc(1), .BR chrony(1) .I http://chrony.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-1.29/sysincl.h0000644000076400007640000000607212200721757013670 0ustar mirosmiros/* 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__) #if !defined(__NetBSD__) && !defined(__FreeBSD__) #include #endif #include #include #include #include #include #if !defined(__FreeBSD__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAS_STDINT_H #include #elif defined(HAS_INTTYPES_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 HAVE_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-1.29/local.c0000644000076400007640000004012712200721757013270 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= 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 "util.h" #include "logging.h" /* ================================================== */ /* 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; /* ================================================== */ /* 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; precision_log = 0; while (best_dusec < 500000) { precision_log--; best_dusec *= 2; } } /* ================================================== */ 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) { } /* ================================================== */ /* 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); } /* ================================================== */ 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; } /* ================================================== */ /* This involves both setting the absolute frequency with the system-specific driver, as well as calling all notify handlers */ void LCL_SetAbsoluteFrequency(double afreq_ppm) { ChangeListEntry *ptr; struct timeval raw, cooked; double dfreq; /* 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 */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(&raw, &cooked, dfreq, 0.0, 0, ptr->anything); } current_freq_ppm = afreq_ppm; } /* ================================================== */ void LCL_AccumulateDeltaFrequency(double dfreq) { ChangeListEntry *ptr; 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 = (1.0 + dfreq) * current_freq_ppm + 1.0e6 * dfreq; /* 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 */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(&raw, &cooked, dfreq, 0.0, 0, ptr->anything); } } /* ================================================== */ void LCL_AccumulateOffset(double offset, double corr_rate) { ChangeListEntry *ptr; 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); (*drv_accrue_offset)(offset, corr_rate); /* Dispatch to all handlers */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(&raw, &cooked, 0.0, offset, 0, ptr->anything); } } /* ================================================== */ void LCL_ApplyStepOffset(double offset) { ChangeListEntry *ptr; 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); (*drv_apply_step_offset)(offset); /* Dispatch to all handlers */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(&raw, &cooked, 0.0, offset, 1, ptr->anything); } } /* ================================================== */ void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, double offset, double dispersion) { ChangeListEntry *ptr; /* Dispatch to all handlers */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(raw, cooked, 0.0, offset, 1, ptr->anything); } lcl_InvokeDispersionNotifyHandlers(dispersion); } /* ================================================== */ void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) { ChangeListEntry *ptr; 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); old_freq_ppm = current_freq_ppm; /* Work out new absolute frequency. Note that absolute frequencies are handled in units of ppm, whereas the 'dfreq' argument is in terms of the gradient of the (offset) v (local time) function. */ current_freq_ppm = (1.0 + dfreq) * old_freq_ppm + 1.0e6 * dfreq; #ifdef TRACEON LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec", old_freq_ppm, current_freq_ppm, doffset); #endif /* Call the system-specific driver for setting the frequency */ 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 */ for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) { (ptr->handler)(&raw, &cooked, dfreq, doffset, 0, ptr->anything); } } /* ================================================== */ void lcl_InvokeDispersionNotifyHandlers(double dispersion) { DispersionNotifyListEntry *ptr; for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) { (ptr->handler)(dispersion, ptr->anything); } } /* ================================================== */ void lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq, lcl_SetFrequencyDriver set_freq, lcl_AccrueOffsetDriver accrue_offset, lcl_ApplyStepOffsetDriver apply_step_offset, lcl_OffsetCorrectionDriver offset_convert, lcl_SetLeapDriver set_leap) { 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; current_freq_ppm = (*drv_read_freq)(); #ifdef TRACEON LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm); #endif } /* ================================================== */ /* Look at the current difference between the system time and the NTP time, and make a step to cancel it if it's larger than the threshold. */ int LCL_MakeStep(double threshold) { struct timeval raw; double correction; LCL_ReadRawTime(&raw); LCL_GetOffsetCorrection(&raw, &correction, NULL); if (fabs(correction) <= threshold) return 0; /* Cancel remaining slew and make the step */ LCL_AccumulateOffset(correction, 0.0); LCL_ApplyStepOffset(-correction); LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.3f seconds", correction); return 1; } /* ================================================== */ void LCL_SetLeap(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; } /* ================================================== */ chrony-1.29/clientlog.h0000644000076400007640000000572212200721757014165 0ustar mirosmiros/* 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" /* Enough to hold flags for 256 hosts in a class C */ typedef uint32_t CLG_Bitmap[8]; extern void CLG_Initialise(void); extern void CLG_Finalise(void); extern void CLG_LogNTPClientAccess(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; /* For bits=0, 8, 16, flag which immediate subnets of that subnet are known. For bits=24, flag which hosts in that subnet are known. Other values, return 0 (failed) */ extern CLG_Status CLG_GetSubnetBitmap(IPAddr *subnet, int bits, CLG_Bitmap result); extern CLG_Status CLG_GetClientAccessReportByIP(IPAddr *ip, RPT_ClientAccess_Report *report, time_t now); CLG_Status CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, time_t now, unsigned long *n_indices); /* And an iterating function, to call 'fn' for each client or peer that has accessed us since 'since'. */ extern void CLG_IterateNTPClients (void (*fn)(IPAddr *client, void *arb), void *arb, time_t since); #endif /* GOT_CLIENTLOG_H */ chrony-1.29/INSTALL0000644000076400007640000000651412200721757013065 0ustar mirosmirosThe software is distributed as source code which has to be compiled. PARTS OF THE SOFTWARE ARE HIGHLY SYSTEM-SPECIFIC AND NON-PORTABLE. UNLESS YOU ARE RUNNING A SUPPORTED SYSTEM, BE PREPARED FOR SOME PROGRAMMING! After unpacking the source code, change directory into it, and type ./configure This is a shell script that automatically determines the system type. There is a single optional parameter, --prefix which indicates the directory tree where the software should be installed. For example, ./configure --prefix=/opt/free will install the chronyd daemon into /opt/free/sbin and the chronyc control program into /opt/free/bin. The default value for the prefix is /usr/local. The configure script assumes you want to use gcc as your compiler. If you want to use a different compiler, you can configure this way: CC=cc CFLAGS=-O ./configure --prefix=/opt/free for Bourne-family shells, or setenv CC cc setenv CFLAGS -O ./configure --prefix=/opt/free for C-family shells. If the software cannot (yet) be built on your system, an error message will be shown. Otherwise, `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 the chrony.txt file for more information. If a `timepps.h' header is available, 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 contents of this depend on the network environment in which the computer operates. Typical scenarios are described in the manual. The simplest case is for a computer with a permanent Internet connection - suppose you want to use public NTP servers from the pool.ntp.org project as your time reference. You would create an /etc/chrony.conf file containing server 0.pool.ntp.org server 1.pool.ntp.org server 2.pool.ntp.org driftfile /var/lib/chrony/drift and then run /usr/local/sbin/chronyd. chrony-1.29/sources.h0000644000076400007640000001563512200721757013674 0ustar mirosmiros/* 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 module that manages the collection of all sources that we are making measurements from. This include all NTP servers & peers, locally connected reference sources, eye/wristwatch drivers etc */ #ifndef GOT_SOURCES_H #define GOT_SOURCES_H #include "sysincl.h" #include "ntp.h" #include "reports.h" /* This datatype is used to hold information about sources. The instance must be passed when calling many of the interface functions */ typedef struct SRC_Instance_Record *SRC_Instance; /* Initialisation function */ extern void SRC_Initialise(void); /* Finalisation function */ extern void SRC_Finalise(void); 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); /* Function to get rid of a source when it is being unconfigured. This may cause the current reference source to be reselected, if this was the reference source or contributed significantly to a falseticker decision. */ extern void SRC_DestroyInstance(SRC_Instance instance); /* Function to get the range of frequencies, relative to the given source, that we believe the local clock lies within. The return values are in terms of the number of seconds fast (+ve) or slow (-ve) relative to the source that the local clock becomes after a given amount of local time has elapsed. Suppose the initial offset relative to the source is U (fast +ve, slow -ve) and a time interval T elapses measured in terms of the local clock. Then the error relative to the source at the end of the interval should lie in the interval [U+T*lo, U+T*hi]. */ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi); /* This function is called by one of the source drivers when it has a new sample that is to be accumulated. This function causes the frequency estimation to be re-run for the designated source, and the clock selection procedure to be re-run afterwards. sample_time is the local time at which the sample is to be considered to have been made, in terms of doing a regression fit of offset against local time. offset is the offset at the time, in seconds. Positive indicates that the local clock is SLOW relative to the source, negative indicates that the local clock is FAST relative to it. root_delay and root_dispersion are in seconds, and are as per RFC1305. root_dispersion only includes the peer's root dispersion + local sampling precision + skew dispersion accrued during the measurement. It is the job of the source statistics algorithms + track.c to add on the extra dispersion due to the residual standard deviation of the offsets from this source after regression, to form the root_dispersion field in the packets transmitted to clients or peers. stratum is the stratum of the source that supplied the sample. */ extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status); /* This routine indicates that packets with valid headers are being received from the designated source */ extern void SRC_SetSelectable(SRC_Instance instance); /* This routine indicates that we are no longer receiving packets with valid headers from the designated source */ extern void SRC_UnsetSelectable(SRC_Instance instance); /* 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 only made to the local reference if a new source is selected or match_addr is equal to the selected reference source address. (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(uint32_t match_refid); /* 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_ReadNumberOfSources(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); typedef enum { SRC_Skew_Decrease, SRC_Skew_Nochange, SRC_Skew_Increase } SRC_Skew_Direction; extern SRC_Skew_Direction SRC_LastSkewChange(SRC_Instance inst); extern int SRC_Samples(SRC_Instance inst); #endif /* GOT_SOURCES_H */ chrony-1.29/sys.h0000644000076400007640000000276612200721757013030 0ustar mirosmiros/* 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-1.29/logging.c0000644000076400007640000001533112200721757013623 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-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. * ********************************************************************** ======================================================================= 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" /* ================================================== */ /* Flag indicating we have initialised */ static int initialised = 0; static int system_log = 0; static int parent_fd = 0; static time_t last_limited = 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; } /* ================================================== */ void LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...) { char buf[2048]; va_list other_args; va_start(other_args, format); vsnprintf(buf, sizeof(buf), format, other_args); va_end(other_args); #ifdef WINNT if (logfile) { fprintf(logfile, "%s\n", buf); } #else if (system_log) { switch (severity) { case LOGS_INFO: syslog(LOG_INFO, "%s", buf); break; case LOGS_WARN: syslog(LOG_WARNING, "%s", buf); break; case LOGS_ERR: default: syslog(LOG_ERR, "%s", buf); break; } } else { fprintf(stderr, "%s\n", buf); } #endif } /* ================================================== */ void LOG_Fatal_Function(LOG_Facility facility, const char *format, ...) { char buf[2048]; va_list other_args; va_start(other_args, format); vsnprintf(buf, sizeof(buf), format, other_args); va_end(other_args); #ifdef WINNT if (logfile) { fprintf(logfile, "Fatal error : %s\n", buf); } #else if (system_log) { syslog(LOG_CRIT, "Fatal error : %s", buf); } else { fprintf(stderr, "Fatal error : %s\n", buf); } if (parent_fd) { if (write(parent_fd, buf, strlen(buf) + 1) < 0) ; /* Not much we can do here */ } #endif exit(1); } /* ================================================== */ void LOG_Position(const char *filename, int line_number, const char *function_name) { #ifdef WINNT #else time_t t; struct tm stm; char buf[64]; if (!system_log) { /* Don't clutter up syslog with internal debugging info */ time(&t); stm = *gmtime(&t); strftime(buf, sizeof(buf), "%d-%H:%M:%S", &stm); fprintf(stderr, "%s:%d:(%s)[%s] ", filename, line_number, function_name, buf); } #endif } /* ================================================== */ void LOG_OpenSystemLog(void) { #ifdef WINNT #else system_log = 1; openlog("chronyd", LOG_PID, LOG_DAEMON); #endif } /* ================================================== */ void LOG_SetParentFd(int fd) { parent_fd = fd; } /* ================================================== */ void LOG_CloseParentFd() { if (parent_fd > 0) close(parent_fd); } /* ================================================== */ int LOG_RateLimited(void) { time_t now; now = time(NULL); if (last_limited + 10 > now && last_limited <= now) return 1; last_limited = now; return 0; } /* ================================================== */ 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-1.29/reports.h0000644000076400007640000000650312200721757013701 0ustar mirosmiros/* 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 { unsigned long client_hits; unsigned long peer_hits; unsigned long cmd_hits_auth; unsigned long cmd_hits_normal; unsigned long cmd_hits_bad; unsigned long last_ntp_hit_ago; unsigned long last_cmd_hit_ago; } RPT_ClientAccess_Report; typedef struct { 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; #endif /* GOT_REPORTS_H */ chrony-1.29/tempcomp.c0000644000076400007640000000465312200721757014026 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= Routines implementing temperature compensation. */ #include "config.h" #include "conf.h" #include "local.h" #include "memory.h" #include "util.h" #include "logging.h" #include "sched.h" #include "tempcomp.h" static SCH_TimeoutID timeout_id; static LOG_FileID logfileid; static char *filename; static double update_interval; static double T0, k0, k1, k2; static void read_timeout(void *arg) { FILE *f; double temp, comp; f = fopen(filename, "r"); if (f && fscanf(f, "%lf", &temp) == 1) { comp = k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2; /* Don't allow corrections above 10 ppm */ if (fabs(comp) < 10.0) { comp = LCL_SetTempComp(comp); 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); } } } if (f) fclose(f); timeout_id = SCH_AddTimeoutByDelay(update_interval, read_timeout, NULL); } void TMC_Initialise(void) { CNF_GetTempComp(&filename, &update_interval, &T0, &k0, &k1, &k2); if (filename == NULL) return; if (update_interval <= 0.0) update_interval = 1.0; logfileid = CNF_GetLogTempComp() ? LOG_FileOpen("tempcomp", " Date (UTC) Time Temp. Comp.") : -1; read_timeout(NULL); } void TMC_Finalise(void) { if (filename == NULL) return; SCH_RemoveTimeout(timeout_id); Free(filename); } chrony-1.29/ntp_core.c0000644000076400007640000016534212200721757014016 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-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. * ********************************************************************** ======================================================================= Core NTP protocol engine */ #include "config.h" #include "sysincl.h" #include "ntp_core.h" #include "ntp_io.h" #include "memory.h" #include "sched.h" #include "reference.h" #include "local.h" #include "sources.h" #include "util.h" #include "conf.h" #include "logging.h" #include "keys.h" #include "addrfilt.h" #include "clientlog.h" /* ================================================== */ static LOG_FileID logfileid; /* ================================================== */ /* Day number of 1 Jan 1970 */ #define MJD_1970 40587 /* ================================================== */ /* Enumeration used for remembering the operating mode of one of the sources */ typedef enum { MD_OFFLINE, /* No sampling at all */ MD_ONLINE, /* Normal sampling based on sampling interval */ MD_BURST_WAS_OFFLINE, /* Burst sampling, return to offline afterwards */ MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */ } OperatingMode; /* ================================================== */ /* Structure used for holding a single peer/server's protocol machine */ struct NCR_Instance_Record { NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */ NTP_Mode mode; /* The source's NTP mode (client/server or symmetric active peer) */ OperatingMode opmode; /* Whether we are sampling this source or not and in what way */ int timer_running; /* Boolean indicating whether we have a timeout pending to transmit to the source */ SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are running on a timer. */ int auto_offline; /* If 1, automatically go offline if server/peer isn't responding */ int local_poll; /* Log2 of polling interval at our end */ int remote_poll; /* Log2 of server/peer's polling interval (recovered from received packets) */ int remote_stratum; /* Stratum of the server/peer (recovered from received packets) */ int presend_minpoll; /* If the current polling interval is at least this, an echo datagram will be send some time before every transmit. This ensures that both us and the server/peer have an ARP entry for each other ready, which means our measurement is not botched by an ARP round-trip on one side or the other. */ int presend_done; /* The presend packet has been sent */ int minpoll; /* Log2 of minimum defined polling interval */ int maxpoll; /* Log2 of maximum defined polling interval */ int min_stratum; /* Increase stratum in received packets to the minimum */ int poll_target; /* Target number of sourcestats samples */ 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 delta / 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. */ unsigned long auth_key_id; /* The ID of the authentication key to use. */ /* Count of how many packets we have transmitted since last successful receive from this peer */ int tx_count; /* Timestamp in tx field of last received packet. We have to reproduce this exactly as the orig field or our outgoing packet. */ NTP_int64 remote_orig; /* Local timestamp when the last packet was received from the source. We have to be prepared to tinker with this if the local clock has its frequency adjusted before we repond. The value we store here is what our own local time was when the same arrived. Before replying, we have to correct this to fit with the parameters for the current reference. (It must be stored relative to local time to permit frequency and offset adjustments to be made when we trim the local clock). */ struct timeval local_rx; /* Local timestamp when we last transmitted a packet to the source. We store two versions. The first is in NTP format, and is used to validate the next received packet from the source. Additionally, this is corrected to bring it into line with the current reference. The second is in timeval format, and is kept relative to the local clock. We modify this in accordance with local clock frequency/offset changes, and use this for computing statistics about the source when a return packet arrives. */ NTP_int64 local_ntp_tx; struct timeval local_tx; /* The instance record in the main source management module. This performs the statistical analysis on the samples we generate */ SRC_Instance source; int burst_good_samples_to_go; int burst_total_samples_to_go; }; /* ================================================== */ /* 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 8.0 /* Time to wait after sending echo to 'warm up' link */ #define WARM_UP_DELAY 4.0 /* The NTP protocol version that we support */ #define NTP_VERSION 3 /* Compatible NTP protocol versions */ #define NTP_MAX_COMPAT_VERSION 4 #define NTP_MIN_COMPAT_VERSION 1 /* Maximum allowed dispersion - as defined in RFC1305 (16 seconds) */ #define NTP_MAX_DISPERSION 16.0 /* Maximum allowed age of a reference to be supplied to a client (1 day) */ #define NTP_MAXAGE 86400 /* Maximum allowed stratum */ #define NTP_MAX_STRATUM 15 /* INVALID or Unkown stratum from external server as per the NTP 4 docs */ #define NTP_INVALID_STRATUM 0 /* ================================================== */ static ADF_AuthTable access_auth_table; /* ================================================== */ /* Forward prototypes */ static void transmit_timeout(void *arg); /* ================================================== */ void NCR_Initialise(void) { logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements", " Date (UTC) Time IP Address L St 1234 abc 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.") : -1; access_auth_table = ADF_CreateTable(); } /* ================================================== */ void NCR_Finalise(void) { ADF_DestroyTable(access_auth_table); } /* ================================================== */ static void start_initial_timeout(NCR_Instance inst) { /* Start timer for first transmission */ inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *)inst); inst->timer_running = 1; } /* ================================================== */ 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); } /* ================================================== */ 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; switch (type) { case NTP_SERVER: result->mode = MODE_CLIENT; break; case NTP_PEER: result->mode = MODE_ACTIVE; break; default: assert(0); } result->minpoll = params->minpoll; result->maxpoll = params->maxpoll; result->min_stratum = params->min_stratum; result->presend_minpoll = params->presend_minpoll; result->presend_done = 0; if (params->authkey == INACTIVE_AUTHKEY) { result->do_auth = 0; result->auth_key_id = 0UL; } else { result->do_auth = 1; result->auth_key_id = params->authkey; } result->max_delay = params->max_delay; result->max_delay_ratio = params->max_delay_ratio; result->max_delay_dev_ratio = params->max_delay_dev_ratio; result->tx_count = 0; result->remote_orig.hi = 0; result->remote_orig.lo = 0; result->poll_target = params->poll_target; result->poll_score = 0.0; if (params->online) { start_initial_timeout(result); result->opmode = MD_ONLINE; } else { result->timer_running = 0; result->timeout_id = 0; result->opmode = MD_OFFLINE; } if (params->iburst) { NCR_InitiateSampleBurst(result, 4, 8); } result->auto_offline = params->auto_offline; result->local_poll = params->minpoll; result->remote_poll = 0; result->remote_stratum = 0; /* 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); result->local_rx.tv_sec = 0; result->local_rx.tv_usec = 0; result->local_tx.tv_sec = 0; result->local_tx.tv_usec = 0; result->local_ntp_tx.hi = 0; result->local_ntp_tx.lo = 0; return result; } /* ================================================== */ /* Destroy an instance */ void NCR_DestroyInstance(NCR_Instance instance) { /* This will destroy the source instance inside the structure, which will cause reselection if this was the synchronising source etc. */ SRC_DestroyInstance(instance->source); /* Cancel any pending timeouts */ if (instance->timer_running) { SCH_RemoveTimeout(instance->timeout_id); instance->timer_running = 0; } /* Free the data structure */ Free(instance); } /* ================================================== */ static int check_packet_auth(NTP_Packet *pkt, unsigned long keyid, int auth_len) { return KEY_CheckAuth(keyid, (void *)pkt, offsetof(NTP_Packet, auth_keyid), (void *)&(pkt->auth_data), auth_len); } /* ================================================== */ 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: /* With burst, the timeout for new transmit after valid reply is shorter than the timeout without reply */ delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL; break; default: assert(0); break; } return delay_time; } /* ================================================== */ static void transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */ int my_poll, /* The log2 of the local poll interval */ int version, /* The NTP version to be set in the packet */ int do_auth, /* Boolean indicating whether to authenticate the packet or not */ unsigned long key_id, /* The authentication key ID */ NTP_int64 *orig_ts, /* Originate timestamp (from received packet) */ struct timeval *local_rx, /* Local time request packet was received */ struct timeval *local_tx, /* RESULT : Time this reply is sent as local time, or NULL if don't want to know */ NTP_int64 *local_ntp_tx, /* RESULT : Time reply sent as NTP timestamp (including adjustment to reference), ignored if NULL */ NTP_Remote_Address *where_to /* Where to address the reponse to */ ) { NTP_Packet message; int leap; struct timeval local_transmit; /* Parameters read from reference module */ int are_we_synchronised, our_stratum; NTP_Leap leap_status; uint32_t our_ref_id, ts_fuzz; struct timeval our_ref_time; double our_root_delay, our_root_dispersion; /* 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); if (are_we_synchronised) { leap = (int) leap_status; } else { leap = LEAP_Unsynchronised; } /* Generate transmit packet */ message.lvm = ((leap << 6) &0xc0) | ((version << 3) & 0x38) | (my_mode & 0x07); if (our_stratum <= NTP_MAX_STRATUM) { message.stratum = our_stratum; } else { /* (WGU) to handle NTP "Invalid" stratum as per the NTP V4 documents. */ 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((NTP_int32) 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_rx, &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); /* Authenticate */ if (do_auth) { int auth_len; /* 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); NIO_SendAuthenticatedPacket(&message, where_to, sizeof (message.auth_keyid) + auth_len); } } else { UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz); NIO_SendNormalPacket(&message, where_to); } if (local_tx) { *local_tx = local_transmit; } if (local_ntp_tx) { *local_ntp_tx = message.transmit_ts; } } /* ================================================== */ /* Timeout handler for transmitting to a source. */ static void transmit_timeout(void *arg) { NCR_Instance inst = (NCR_Instance) arg; double timeout_delay; int do_auth; 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; } if (inst->opmode == MD_OFFLINE) { return; } #ifdef TRACEON LOG(LOGS_INFO, LOGF_NtpCore, "Transmit timeout for [%s:%d]", UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); #endif /* Check whether we need to 'warm up' the link to the other end by sending an echo exchange to ensure both ends' ARP caches are primed. On loaded systems this might also help ensure that bits of the program are paged in properly before we start. */ if ((inst->presend_minpoll > 0) && (inst->presend_minpoll <= inst->local_poll) && !inst->presend_done) { /* Send */ NIO_SendEcho(&inst->remote_addr); inst->presend_done = 1; /* Requeue timeout */ inst->timer_running = 1; inst->timeout_id = SCH_AddTimeoutInClass(WARM_UP_DELAY, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *)inst); return; } inst->presend_done = 0; /* Reset for next time */ ++inst->tx_count; /* If the source loses connectivity, back off the sampling rate to reduce wasted sampling. If it's the source to which we are currently locked, back off slower. */ if (inst->tx_count >= 2) { /* Implies we have missed at least one transmission */ adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25); SRC_UpdateReachability(inst->source, 0); if (inst->auto_offline && inst->tx_count >= 3) { NCR_TakeSourceOffline(inst); } } if (inst->do_auth && KEY_KeyKnown(inst->auth_key_id)) { do_auth = 1; } else { do_auth = 0; } transmit_packet(inst->mode, inst->local_poll, NTP_VERSION, do_auth, inst->auth_key_id, &inst->remote_orig, &inst->local_rx, &inst->local_tx, &inst->local_ntp_tx, &inst->remote_addr); switch (inst->opmode) { case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_OFFLINE: --inst->burst_total_samples_to_go; break; default: break; } /* Restart timer for this message */ timeout_delay = get_transmit_delay(inst, 1, 0.0); inst->timer_running = 1; inst->timeout_id = SCH_AddTimeoutInClass(timeout_delay, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *)inst); } /* ================================================== */ static void receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, int auth_len) { int pkt_leap; int source_is_synchronized; double pkt_root_delay; double pkt_root_dispersion; unsigned long auth_key_id; /* The local time to which the (offset, delay, dispersion) triple will be taken to relate. For client/server operation this is practically the same as either the transmit or receive time. The difference comes in symmetric active mode, when the receive may come minutes after the transmit, and this time will be midway between the two */ struct timeval sample_time; /* The estimated offset (nomenclature from RFC1305 section 3.4.4). In seconds, a positive value indicates that the local clock is SLOW of the remote source and a negative value indicates that the local clock is FAST of the remote source. */ double theta; /* The estimated round delay in seconds */ double delta; /* The estimated peer dispersion in seconds */ double epsilon; /* The estimated peer distance in seconds */ double peer_distance; /* The total root delay */ double root_delay; /* The total root dispersion */ double root_dispersion; /* The skew relative to the remote source */ double skew; /* The estimated skew relative to the remote source. */ double source_freq_lo, source_freq_hi; /* These are the timeval equivalents of the remote epochs */ struct timeval remote_receive_tv, remote_transmit_tv; struct timeval remote_reference_tv; struct timeval local_average, remote_average; double local_interval, remote_interval; int test1, test2, test3, test4, test5, test6, test7, test7i, test7ii, test8; int test4a, test4b, test4c; /* In the words of section 3.4.4 of RFC1305, valid_data means that the NTP protocol association with the peer/server is properly synchronised. valid_header means that the measurement obtained from the packet is suitable for use in synchronising our local clock. Wierd choice of terminology. */ int valid_data, valid_header; int good_data, good_header; /* Kiss-of-Death packets */ int kod_rate = 0; int valid_kod; /* Variables used for doing logging */ static char sync_stats[4] = {'N', '+', '-', '?'}; /* The estimated offset predicted from previous samples. The convention here is that positive means local clock FAST of reference, i.e. backwards to the way that 'theta' is defined. */ double estimated_offset; /* The absolute difference between the offset estimate and measurement in seconds */ double error_in_estimate; double delay_time = 0; int requeue_transmit = 0; /* ==================== */ /* Save local receive timestamp */ inst->local_rx = *now; pkt_leap = (message->lvm >> 6) & 0x3; if (pkt_leap == 0x3) { source_is_synchronized = 0; } else { source_is_synchronized = 1; } pkt_root_delay = UTI_Int32ToDouble(message->root_delay); pkt_root_dispersion = UTI_Int32ToDouble(message->root_dispersion); /* Perform packet validity tests */ /* Test 1 requires that pkt.xmt != peer.org. This protects against receiving a duplicate packet */ if ((message->transmit_ts.hi == inst->remote_orig.hi) && (message->transmit_ts.lo == inst->remote_orig.lo)) { test1 = 0; /* Failed */ } else { test1 = 1; /* Success */ } /* Test 2 requires pkt.org == peer.xmt. This ensures the source is responding to the latest packet we sent to it. */ if ((message->originate_ts.hi != inst->local_ntp_tx.hi) || (message->originate_ts.lo != inst->local_ntp_tx.lo)) { test2 = 0; /* Failed */ } else { test2 = 1; /* Success */ } /* Regardless of any validity checks we apply, we are required to save this field from the packet into the ntp source instance record. See RFC1305 section 3.4.4, peer.org <- pkt.xmt & peer.peerpoll <- pkt.poll. Note we can't do this assignment before test1 has been carried out!! */ inst->remote_orig = message->transmit_ts; /* Test 3 requires that pkt.org != 0 and pkt.rec != 0. If either of these are true it means the association is not properly 'up'. */ if (((message->originate_ts.hi == 0) && (message->originate_ts.lo == 0)) || ((message->receive_ts.hi == 0) && (message->receive_ts.lo == 0))) { test3 = 0; /* Failed */ } else { test3 = 1; /* Success */ } SRC_GetFrequencyRange(inst->source, &source_freq_lo, &source_freq_hi); UTI_Int64ToTimeval(&message->receive_ts, &remote_receive_tv); UTI_Int64ToTimeval(&message->transmit_ts, &remote_transmit_tv); if (test3) { UTI_AverageDiffTimevals(&remote_receive_tv, &remote_transmit_tv, &remote_average, &remote_interval); UTI_AverageDiffTimevals(&inst->local_tx, now, &local_average, &local_interval); /* In our case, we work out 'delta' as the worst case delay, assuming worst case frequency error between us and the other source */ delta = local_interval - remote_interval * (1.0 + source_freq_lo); /* Calculate theta. Following the NTP definition, this is negative if we are fast of the remote source. */ UTI_DiffTimevalsToDouble(&theta, &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 */ epsilon = LCL_GetSysPrecisionAsQuantum() + now_err + skew * fabs(local_interval); } else { /* If test3 failed, we probably can't calculate these quantities properly (e.g. for the first sample received in a peering connection). */ theta = delta = epsilon = 0.0; sample_time = *now; } peer_distance = epsilon + 0.5 * fabs(delta); /* Test 4 requires that the round trip delay to the source and the source (RFC1305 'peer') dispersion are less than a cutoff value */ if ((fabs(delta) >= NTP_MAX_DISPERSION) || (epsilon >= NTP_MAX_DISPERSION)) { test4 = 0; /* Failed */ } else { test4 = 1; /* Success */ } /* Test 4a (additional to RFC1305) requires that the round trip delay is less than an administrator-defined value */ if (fabs(delta) > inst->max_delay) { test4a = 0; /* Failed */ } else { test4a = 1; /* Success */ } /* Test 4b (additional to RFC1305) requires that the ratio of the round trip delay to the minimum one currently in the stats data register is less than an administrator-defined value */ if (inst->max_delay_ratio > 1.0 && fabs(delta/SRC_MinRoundTripDelay(inst->source)) > inst->max_delay_ratio) { test4b = 0; /* Failed */ } else { test4b = 1; /* Success */ } /* Test 4c (additional to RFC1305) 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 */ if (!SRC_IsGoodSample(inst->source, -theta, fabs(delta), inst->max_delay_dev_ratio, LCL_GetMaxClockError(), &sample_time)) { test4c = 0; /* Failed */ } else { test4c = 1; /* Success */ } /* Test 5 relates to authentication. */ if (inst->do_auth) { if (auth_len > 0) { auth_key_id = ntohl(message->auth_keyid); if (!KEY_KeyKnown(auth_key_id)) { test5 = 0; } else { test5 = check_packet_auth(message, auth_key_id, auth_len); } } else { /* If we expect authenticated info from this peer/server and the packet doesn't have it, it's got to fail */ test5 = 0; } } else { /* If the peer or server sends us an authenticated frame, but we're not bothered about whether he authenticates or not, just ignore the test. */ test5 = 1; } /* Test 6 checks that (i) the remote clock is synchronised (ii) the transmit timestamp is not before the time it was synchronized (clearly bogus if it is), and (iii) that it was not synchronised too long ago */ UTI_Int64ToTimeval(&message->reference_ts, &remote_reference_tv); if ((!source_is_synchronized) || (UTI_CompareTimevals(&remote_reference_tv, &remote_transmit_tv) == 1) || ((remote_reference_tv.tv_sec + NTP_MAXAGE - remote_transmit_tv.tv_sec) < 0)) { test6 = 0; /* Failed */ } else { test6 = 1; /* Succeeded */ } /* (WGU) Set stratum to greater than any valid if incoming is 0 */ /* as per the NPT v4 documentation*/ if (message->stratum <= NTP_INVALID_STRATUM) { message->stratum = NTP_MAX_STRATUM + 1; } /* Increase stratum to the configured minimum */ if (message->stratum < inst->min_stratum) { message->stratum = inst->min_stratum; } /* Test 7i checks that the stratum in the packet is valid */ if (message->stratum > NTP_MAX_STRATUM) { test7i = 0; /* Failed */ } else { test7i = 1; } /* Test 7ii checks that the stratum in the packet is not higher than ours */ if (message->stratum > REF_GetOurStratum()) { test7ii = 0; /* Failed */ } else { test7ii = 1; } test7 = test7i && test7ii; /* Test 8 checks that the root delay and dispersion quoted in the packet are appropriate */ if ((pkt_root_delay >= NTP_MAX_DISPERSION) || (pkt_root_dispersion >= NTP_MAX_DISPERSION)) { test8 = 0; /* Failed */ } else { test8 = 1; } /* Check for Kiss-of-Death */ if (!test7i && !source_is_synchronized) { if (!memcmp(&message->reference_id, "RATE", 4)) kod_rate = 1; } valid_kod = test1 && test2 && test5; valid_data = test1 && test2 && test3 && test4 && test4a && test4b; good_data = valid_data && test4c; valid_header = test5 && test6 && test7i && test8; good_header = valid_header && test7ii; root_delay = pkt_root_delay + fabs(delta); root_dispersion = pkt_root_dispersion + epsilon; #ifdef TRACEON LOG(LOGS_INFO, LOGF_NtpCore, "lvm=%o stratum=%d poll=%d prec=%d", message->lvm, message->stratum, message->poll, message->precision); LOG(LOGS_INFO, LOGF_NtpCore, "Root delay=%08lx (%f), dispersion=%08lx (%f)", message->root_delay, pkt_root_delay, message->root_dispersion, pkt_root_dispersion); LOG(LOGS_INFO, LOGF_NtpCore, "Ref id=[%lx], ref_time=%08lx.%08lx [%s]", ntohl(message->reference_id), message->reference_ts.hi, message->reference_ts.lo, UTI_TimestampToString(&message->reference_ts)); LOG(LOGS_INFO, LOGF_NtpCore, "Originate=%08lx.%08lx [%s]", message->originate_ts.hi, message->originate_ts.lo, UTI_TimestampToString(&message->originate_ts)); LOG(LOGS_INFO, LOGF_NtpCore, "Message receive=%08lx.%08lx [%s]", message->receive_ts.hi, message->receive_ts.lo, UTI_TimestampToString(&message->receive_ts)); LOG(LOGS_INFO, LOGF_NtpCore, "Transmit=%08lx.%08lx [%s]", message->transmit_ts.hi, message->transmit_ts.lo, UTI_TimestampToString(&message->transmit_ts)); LOG(LOGS_INFO, LOGF_NtpCore, "theta=%f delta=%f epsilon=%f root_delay=%f root_dispersion=%f", theta, delta, epsilon, root_delay, root_dispersion); LOG(LOGS_INFO, LOGF_NtpCore, "test1=%d test2=%d test3=%d test4=%d valid_data=%d good_data=%d", test1, test2, test3, test4, valid_data, good_data); LOG(LOGS_INFO, LOGF_NtpCore, "test5=%d test6=%d test7=%d test8=%d valid_header=%d good_header=%d", test5, test6, test7, test8, valid_header, good_header); LOG(LOGS_INFO, LOGF_NtpCore, "kod_rate=%d valid_kod=%d", kod_rate, valid_kod); #endif /* Reduce polling rate if KoD RATE was received */ if (kod_rate && valid_kod) { if (message->poll > inst->minpoll) { inst->minpoll = message->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 from %s, minpoll set to %d", 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), inst->minpoll); } requeue_transmit = 1; } if (valid_header && valid_data) { inst->remote_poll = message->poll; inst->remote_stratum = message->stratum; inst->tx_count = 0; SRC_UpdateReachability(inst->source, 1); /* Mark the source as suitable for synchronisation when both header and data are good, unmark when header is not good (i.e. the stratum is higher than ours) */ if (good_header) { if (good_data) { SRC_SetSelectable(inst->source); } } else { SRC_UnsetSelectable(inst->source); } if (good_data) { /* 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, theta, fabs(delta), epsilon, root_delay, root_dispersion, message->stratum, (NTP_Leap) pkt_leap); /* Now examine the registers. First though, if the prediction is not even within +/- the peer distance of the peer, we are clearly not tracking the peer at all well, so we back off the sampling rate depending on just how bad the situation is. */ error_in_estimate = fabs(-theta - estimated_offset); /* Now update the polling interval */ adjust_poll(inst, get_poll_adj(inst, error_in_estimate, peer_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_data */ adjust_poll(inst, 0.1); } 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); SCH_RemoveTimeout(inst->timeout_id); inst->timeout_id = SCH_AddTimeoutInClass(delay_time, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *)inst); } /* Do measurement logging */ if (logfileid != -1) { LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d%1d %1d%1d%1d %1d%1d%1d%1d %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, test4, test4a, test4b, test4c, test5, test6, test7, test8, inst->local_poll, inst->remote_poll, inst->poll_score, theta, delta, epsilon, pkt_root_delay, pkt_root_dispersion); } } /* ================================================== */ /* From RFC1305, the standard handling of receive packets, depending on the mode of the packet and of the source, is : Source mode>>> Packet mode active passive client server bcast vvv active recv pkt recv xmit xmit passive recv error recv error error client xmit xmit error xmit xmit server recv error recv error error bcast recv error recv error error We ignore broadcasts in this implementation - they create too many problems. */ /* ================================================== */ /* This routine is called when a new packet arrives off the network, and it relates to a source we have an ongoing protocol exchange with */ void NCR_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 */ int length /* the length of the received packet */ ) { int pkt_mode; int version; int valid_auth, valid_key; int authenticate_reply, auth_len; unsigned long auth_key_id; unsigned long reply_auth_key_id; /* Ignore packets from offline sources */ if (inst->opmode == MD_OFFLINE) { return; } /* Check version */ version = (message->lvm >> 3) & 0x7; if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) { /* Ignore packet, but might want to log it */ return; } /* Perform tests mentioned in RFC1305 to validate packet contents */ pkt_mode = (message->lvm >> 0) & 0x7; /* Length of the authentication data, if any */ auth_len = length - (NTP_NORMAL_PACKET_SIZE + sizeof (message->auth_keyid)); if (auth_len < 0) { auth_len = 0; } /* Now, depending on the mode we decide what to do */ switch (pkt_mode) { case MODE_CLIENT: /* If message is client mode, we just respond with a server mode packet, regardless of what we think the remote machine is supposed to be. However, even though this is a configured peer or server, we still implement access restrictions on client mode operation. This is an extension to RFC1305, as we don't bother to check whether we are a client of the remote machine. This copes with the case for an isolated network where one machine is set by eye and is used as the master, with the other machines pointed at it. If the master goes down, we want to be able to reset its time at startup by relying on one of the secondaries to flywheel it. The behaviour coded here is required in the secondaries to make this possible. */ if (ADF_IsAllowed(access_auth_table, &inst->remote_addr.ip_addr)) { CLG_LogNTPClientAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec); if (auth_len > 0) { auth_key_id = ntohl(message->auth_keyid); valid_key = KEY_KeyKnown(auth_key_id); if (valid_key) { valid_auth = check_packet_auth(message, auth_key_id, auth_len); } else { valid_auth = 0; } if (valid_key && valid_auth) { authenticate_reply = 1; reply_auth_key_id = auth_key_id; } else { authenticate_reply = 0; reply_auth_key_id = 0UL; } } else { authenticate_reply = 0; reply_auth_key_id = 0UL; } transmit_packet(MODE_SERVER, inst->local_poll, version, authenticate_reply, reply_auth_key_id, &message->transmit_ts, now, &inst->local_tx, &inst->local_ntp_tx, &inst->remote_addr); } else if (!LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d", UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port); } break; case MODE_ACTIVE: switch(inst->mode) { case MODE_ACTIVE: /* Ordinary symmetric peering */ CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec); receive_packet(message, now, now_err, inst, auth_len); break; case MODE_PASSIVE: /* In this software this case should not arise, we don't support unconfigured peers */ break; case MODE_CLIENT: /* This is where we have the remote configured as a server and he has us configured as a peer - fair enough. */ CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec); receive_packet(message, now, now_err, inst, auth_len); break; case MODE_SERVER: /* Nonsense - we can't have a preconfigured server */ break; case MODE_BROADCAST: /* We don't handle broadcasts */ break; default: /* Obviously ignore */ break; } break; case MODE_SERVER: switch(inst->mode) { case MODE_ACTIVE: /* Slightly bizarre combination, but we can still process it */ CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec); receive_packet(message, now, now_err, inst, auth_len); break; case MODE_PASSIVE: /* We have no passive peers in this software */ break; case MODE_CLIENT: /* Standard case where he's a server and we're the client */ receive_packet(message, now, now_err, inst, auth_len); break; case MODE_SERVER: /* RFC1305 error condition. */ break; case MODE_BROADCAST: /* RFC1305 error condition */ break; default: /* Obviously ignore */ break; } break; case MODE_PASSIVE: switch(inst->mode) { case MODE_ACTIVE: /* This would arise if we have the remote configured as a peer and he does not have us configured */ CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec); receive_packet(message, now, now_err, inst, auth_len); break; case MODE_PASSIVE: /* Error condition in RFC1305. Also, we can't have any non-transient PASSIVE sources in this version, we only allow configured peers! */ break; case MODE_CLIENT: /* This is a wierd combination - how could it arise? */ receive_packet(message, now, now_err, inst, auth_len); break; case MODE_SERVER: /* Error condition in RFC1305 */ break; case MODE_BROADCAST: /* Error condition in RFC1305 */ break; default: /* Obviously ignore */ break; } break; case MODE_BROADCAST: /* Just ignore these, but might want to log them */ break; default: /* Obviously ignore */ break; } } /* ================================================== */ /* This routine is called when a new packet arrives off the network, and it relates to a source we have an ongoing protocol exchange with */ void NCR_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, int length /* the length of the received packet */ ) { NTP_Mode his_mode; NTP_Mode my_mode; int my_poll, version; int valid_key, valid_auth, auth_len; unsigned long key_id; /* Check version */ version = (message->lvm >> 3) & 0x7; if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) { /* Ignore packet, but might want to log it */ return; } if (ADF_IsAllowed(access_auth_table, &remote_addr->ip_addr)) { his_mode = message->lvm & 0x07; if (his_mode == MODE_CLIENT) { /* We are server */ my_mode = MODE_SERVER; CLG_LogNTPClientAccess(&remote_addr->ip_addr, (time_t) now->tv_sec); } else if (his_mode == MODE_ACTIVE) { /* We are symmetric passive, even though we don't ever lock to him */ my_mode = MODE_PASSIVE; CLG_LogNTPPeerAccess(&remote_addr->ip_addr, (time_t) now->tv_sec); } else { my_mode = MODE_UNDEFINED; } /* If we can't determine a sensible mode to reply with, it means he has supplied a wierd mode in his request, so ignore it. */ if (my_mode != MODE_UNDEFINED) { int do_auth = 0; auth_len = length - (NTP_NORMAL_PACKET_SIZE + sizeof (message->auth_keyid)); if (auth_len > 0) { /* Only reply if we know the key and the packet authenticates properly. */ key_id = ntohl(message->auth_keyid); valid_key = KEY_KeyKnown(key_id); do_auth = 1; if (valid_key) { valid_auth = check_packet_auth(message, key_id, auth_len); } else { valid_auth = 0; } } if (!do_auth || (valid_key && valid_auth)) { my_poll = message->poll; /* What should this be set to? Does the client actually care? */ transmit_packet(my_mode, my_poll, version, do_auth, do_auth ? key_id : 0, &message->transmit_ts, /* Originate (for us) is the transmit time for the client */ now, /* Time we received the packet */ NULL, /* Don't care when we send reply, we aren't maintaining state about this client */ NULL, /* Ditto */ remote_addr); } } } else if (!LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); } } /* ================================================== */ void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset) { struct timeval prev; double delta; prev = inst->local_rx; if (inst->local_rx.tv_sec || inst->local_rx.tv_usec) UTI_AdjustTimeval(&inst->local_rx, when, &inst->local_rx, &delta, dfreq, doffset); #ifdef TRACEON LOG(LOGS_INFO, LOGF_NtpCore, "rx prev=[%s] new=[%s]", UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_rx)); #endif prev = inst->local_tx; if (inst->local_tx.tv_sec || inst->local_tx.tv_usec) UTI_AdjustTimeval(&inst->local_tx, when, &inst->local_tx, &delta, dfreq, doffset); #ifdef TRACEON LOG(LOGS_INFO, LOGF_NtpCore, "tx prev=[%s] new=[%s]", UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_tx)); #else (void)prev; #endif } /* ================================================== */ void NCR_TakeSourceOnline(NCR_Instance inst) { switch (inst->opmode) { case MD_ONLINE: /* Nothing to do */ break; case MD_OFFLINE: if (!inst->timer_running) { /* We are not already actively polling it */ LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr)); inst->tx_count = 0; inst->local_poll = inst->minpoll; inst->poll_score = 0.5; inst->opmode = MD_ONLINE; start_initial_timeout(inst); } break; case MD_BURST_WAS_ONLINE: /* Will revert */ break; case MD_BURST_WAS_OFFLINE: inst->opmode = MD_BURST_WAS_ONLINE; 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: if (inst->timer_running) { 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) { inst->minpoll = new_minpoll; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll); } /* ================================================== */ void NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll) { inst->maxpoll = new_maxpoll; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll); } /* ================================================== */ void NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay) { inst->max_delay = new_max_delay; LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new max delay %f", UTI_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: if (inst->opmode == MD_ONLINE) inst->opmode = MD_BURST_WAS_ONLINE; else inst->opmode = MD_BURST_WAS_OFFLINE; inst->burst_good_samples_to_go = n_good_samples; inst->burst_total_samples_to_go = n_total_samples; if (inst->timer_running) { SCH_RemoveTimeout(inst->timeout_id); } inst->timer_running = 1; inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION, SAMPLING_RANDOMNESS, SCH_NtpSamplingClass, transmit_timeout, (void *) 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_BADSUBNET) { return 0; } else if (status == ADF_SUCCESS) { return 1; } else { return 0; } } /* ================================================== */ 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); } /* ================================================== */ chrony-1.29/ntp_sources.h0000644000076400007640000001013712200721757014545 0ustar mirosmiros/* 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 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 or peer source with currently unknown address. The name will be periodically resolved in exponentially increasing intervals until it succeeds or fails with a non-temporary error. */ extern void NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParameters *params); /* Procedure to try resolve unresolved sources immediately. */ extern void NSR_ResolveSources(void); /* Procedure to remove a source */ extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr); /* This routine is called by ntp_io when a new packet arrives off the network */ extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_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-1.29/sourcestats.c0000644000076400007640000007042212200721757014556 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-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. * ********************************************************************** ======================================================================= 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 /* User defined maximum and minimum number of samples */ int max_samples; int min_samples; /* 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; /* 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 direction the skew went in at the last sample */ SST_Skew_Direction skew_dirn; /* This is the estimated residual variance of the data points */ double variance; /* This array contains the sample epochs, in terms of the local clock. */ struct timeval sample_times[MAX_SAMPLES * 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; that part of the code is written to correspond with RFC1305 conventions. */ 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; max_samples = CNF_GetMaxSamples(); min_samples = CNF_GetMinSamples(); } /* ================================================== */ void SST_Finalise(void) { } /* ================================================== */ /* This function creates a new instance of the statistics handler */ SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr) { SST_Stats inst; inst = MallocNew(struct SST_Stats_Record); inst->refid = refid; inst->ip_addr = addr; 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->skew_dirn = SST_Skew_Nochange; inst->estimated_offset = 0.0; inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */ inst->offset_time.tv_sec = 0; inst->offset_time.tv_usec = 0; inst->variance = 16.0; inst->nruns = 0; return inst; } /* ================================================== */ /* This function deletes an instance of the statistics handler. */ void SST_DeleteInstance(SST_Stats inst) { Free(inst); } /* ================================================== */ /* 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 == 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)); prune_register(inst, inst->n_samples); } 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; #if 0 LOG(LOGS_INFO, LOGF_SourceStats, "n=%d best_index=%d", n, best_index); #endif } /* ================================================== */ 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, 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 (best_start > 0) { /* If we are throwing old data away, retain the current assumptions about the skew */ inst->skew_dirn = SST_Skew_Nochange; } else { if (inst->skew < old_skew) { inst->skew_dirn = SST_Skew_Decrease; } else { inst->skew_dirn = SST_Skew_Increase; } } if (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 { #if 0 LOG(LOGS_INFO, LOGF_SourceStats, "too few points (%d) for regression", inst->n_samples); #endif inst->estimated_frequency = 0.0; inst->skew = WORST_CASE_FREQ_BOUND; 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, int *select_ok) { double offset, sample_elapsed; int i, j; 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 *select_ok = inst->regression_ok; #ifdef TRACEON LOG(LOGS_INFO, LOGF_SourceStats, "n=%d off=%f dist=%f var=%f selok=%d", inst->n_samples, offset, *root_distance, *variance, *select_ok); #endif } /* ================================================== */ 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; 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; #ifdef TRACEON LOG(LOGS_INFO, 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); #endif } /* ================================================== */ 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); prev_offset = inst->offsets[i]; inst->offsets[i] += delta_time; #ifdef TRACEON LOG(LOGS_INFO, LOGF_SourceStats, "i=%d old_st=[%s] new_st=[%s] old_off=%f new_off=%f", i, UTI_TimevalToString(&prev), UTI_TimevalToString(sample), prev_offset, inst->offsets[i]); #else (void)prev_offset; #endif } /* Do a half-baked update to 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 -= dfreq; #ifdef TRACEON LOG(LOGS_INFO, LOGF_SourceStats, "old_off_time=[%s] new=[%s] old_off=%f new_off=%f old_freq=%.3fppm new_freq=%.3fppm", UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)), prev_offset, inst->estimated_offset, 1.0e6*prev_freq, 1.0e6*inst->estimated_frequency); #else (void)prev; (void)prev_freq; #endif } /* ================================================== */ 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; #if 0 LOG(LOGS_INFO, LOGF_SourceStats, "bad sample: offset=%f delay=%f incr_delay=%f allowed=%f", offset, delay, allowed_increase, delay_increase); #endif 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; if (fgets(line, sizeof(line), in) && (sscanf(line, "%u", &inst->n_samples) == 1) && 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; } } /* ================================================== */ SST_Skew_Direction SST_LastSkewChange(SST_Stats inst) { return inst->skew_dirn; } /* ================================================== */ 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-1.29/main.c0000644000076400007640000002435112200721757013123 0ustar mirosmiros/* 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 * * 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 "acquire.h" #include "manual.h" #include "rtc.h" #include "refclock.h" #include "clientlog.h" #include "broadcast.h" #include "nameserv.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 reload = 0; /* ================================================== */ static void delete_pidfile(void) { const char *pidfile = CNF_GetPidFile(); /* Don't care if this fails, there's not a lot we can do */ unlink(pidfile); } /* ================================================== */ void MAI_CleanupAndExit(void) { if (!initialised) exit(0); if (CNF_GetDumpOnExit()) { SRC_DumpSources(); } TMC_Finalise(); MNL_Finalise(); ACQ_Finalise(); CLG_Finalise(); NSR_Finalise(); NCR_Finalise(); BRD_Finalise(); SST_Finalise(); REF_Finalise(); KEY_Finalise(); RCL_Finalise(); SRC_Finalise(); RTC_Finalise(); CAM_Finalise(); NIO_Finalise(); SYS_Finalise(); SCH_Finalise(); LCL_Finalise(); delete_pidfile(); LOG_Finalise(); exit(0); } /* ================================================== */ static void signal_cleanup(int x) { if (!initialised) exit(0); SCH_QuitProgram(); } /* ================================================== */ static void post_acquire_hook(void *anything) { /* Close the pipe to the foreground process so it can exit */ LOG_CloseParentFd(); CNF_AddSources(); CNF_AddBroadcasts(); if (reload) { /* Note, we want reload to come well after the initialisation from the real time clock - this gives us a fighting chance that the system-clock scale for the reloaded samples still has a semblence of validity about it. */ SRC_ReloadSources(); } CNF_SetupAccessRestrictions(); RTC_StartMeasurements(); RCL_StartRefclocks(); } /* ================================================== */ static void post_init_rtc_hook(void *anything) { CNF_ProcessInitStepSlew(post_acquire_hook, NULL); } /* ================================================== */ /* Return 1 if the process exists on the system. */ static int does_process_exist(int pid) { int status; status = getsid(pid); if (status >= 0) { return 1; } else { return 0; } } /* ================================================== */ static int maybe_another_chronyd_running(int *other_pid) { const char *pidfile = CNF_GetPidFile(); FILE *in; int pid, count; *other_pid = 0; in = fopen(pidfile, "r"); if (!in) return 0; count = fscanf(in, "%d", &pid); fclose(in); if (count != 1) return 0; *other_pid = pid; return does_process_exist(pid); } /* ================================================== */ static void write_lockfile(void) { const char *pidfile = CNF_GetPidFile(); FILE *out; out = fopen(pidfile, "w"); if (!out) { LOG(LOGS_ERR, LOGF_Main, "could not open lockfile %s for writing", pidfile); } else { fprintf(out, "%d\n", getpid()); fclose(out); } } /* ================================================== */ 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(LOGS_ERR, LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno)); } /* Does this preserve existing signal handlers? */ pid = fork(); if (pid < 0) { LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); } else if (pid > 0) { /* 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(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno)); } else if (pid > 0) { exit(0); /* In the 'parent' */ } else { /* In the child we want to leave running as the daemon */ /* Change current directory to / */ if (chdir("/") < 0) { LOG(LOGS_ERR, 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; 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; 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\n", CHRONY_VERSION); exit(0); } else if (!strcmp("-n", *argv)) { nofork = 1; } else if (!strcmp("-d", *argv)) { debug = 1; nofork = 1; } else if (!strcmp("-4", *argv)) { address_family = IPADDR_INET4; } else if (!strcmp("-6", *argv)) { address_family = IPADDR_INET6; } else { LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv); } } if (getuid() != 0) { /* This write to the terminal is OK, it comes before we turn into a daemon */ fprintf(stderr,"Not superuser\n"); exit(1); } /* Turn into a daemon */ if (!nofork) { go_daemon(); } if (!debug) { LOG_OpenSystemLog(); } LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION); DNS_SetAddressFamily(address_family); CNF_SetRestarted(restarted); CNF_ReadFile(conf_file); /* 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(); if (do_init_rtc) { RTC_TimePreInit(); } LCL_Initialise(); SCH_Initialise(); SYS_Initialise(); NIO_Initialise(address_family); CAM_Initialise(address_family); RTC_Initialise(); 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) { SYS_DropRoot(user); } LOG_CreateLogFileDir(); REF_Initialise(); SST_Initialise(); BRD_Initialise(); NCR_Initialise(); NSR_Initialise(); CLG_Initialise(); ACQ_Initialise(); MNL_Initialise(); TMC_Initialise(); /* From now on, it is safe to do finalisation on exit */ initialised = 1; if (do_init_rtc) { RTC_TimeInit(post_init_rtc_hook, NULL); } else { post_init_rtc_hook(NULL); } signal(SIGINT, signal_cleanup); signal(SIGTERM, signal_cleanup); #if !defined(WINNT) signal(SIGQUIT, signal_cleanup); signal(SIGHUP, signal_cleanup); #endif /* WINNT */ /* The program normally runs under control of the main loop in the scheduler. */ SCH_MainLoop(); LOG(LOGS_INFO, LOGF_Main, "chronyd exiting"); MAI_CleanupAndExit(); return 0; } /* ================================================== */ chrony-1.29/nameserv.c0000644000076400007640000001077312200721757014022 0ustar mirosmiros/* 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 *addr) { #ifdef HAVE_IPV6 struct addrinfo hints, *res, *ai; int 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; !result && ai != NULL; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: addr->family = IPADDR_INET4; addr->addr.in4 = ntohl(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr); result = 1; break; #ifdef HAVE_IPV6 case AF_INET6: addr->family = IPADDR_INET6; memcpy(&addr->addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, sizeof (addr->addr.in6)); result = 1; break; #endif } if (result && address_family != IPADDR_UNSPEC && address_family != addr->family) result = 0; } freeaddrinfo(res); return result ? DNS_Success : DNS_Failure; #else struct hostent *host; host = gethostbyname(name); if (host == NULL) { if (h_errno == TRY_AGAIN) return DNS_TryAgain; } else { addr->family = IPADDR_INET4; addr->addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[0]); 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 HAVE_IPV6 struct sockaddr_in in4; struct sockaddr_in6 in6; char hbuf[NI_MAXHOST]; switch (ip_addr->family) { case IPADDR_INET4: memset(&in4, 0, sizeof (in4)); #ifdef SIN6_LEN in4.sin_len = sizeof (in4); #endif in4.sin_family = AF_INET; in4.sin_addr.s_addr = htonl(ip_addr->addr.in4); if (!getnameinfo((const struct sockaddr *)&in4, sizeof (in4), hbuf, sizeof (hbuf), NULL, 0, 0)) result = hbuf; break; case IPADDR_INET6: memset(&in6, 0, sizeof (in6)); #ifdef SIN6_LEN in6.sin6_len = sizeof (in6); #endif in6.sin6_family = AF_INET6; memcpy(&in6.sin6_addr.s6_addr, ip_addr->addr.in6, sizeof (in6.sin6_addr.s6_addr)); if (!getnameinfo((const struct sockaddr *)&in6, sizeof (in6), hbuf, sizeof (hbuf), NULL, 0, 0)) result = hbuf; break; } #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 HAVE_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-1.29/conf.h0000644000076400007640000000713712200721757013134 0ustar mirosmiros/* 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 for configuration module */ #ifndef GOT_CONF_H #define GOT_CONF_H #include "addressing.h" extern void CNF_SetRestarted(int); extern char *CNF_GetRtcDevice(void); extern void CNF_ReadFile(const char *filename); extern void CNF_AddSources(void); extern void CNF_AddBroadcasts(void); extern void CNF_AddRefclocks(void); extern void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything); extern unsigned short CNF_GetAcquisitionPort(void); extern unsigned short CNF_GetNTPPort(void); extern char *CNF_GetDriftFile(void); extern char *CNF_GetLogDir(void); extern char *CNF_GetDumpDir(void); extern int CNF_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 unsigned long 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_GetBindCommandAddress(int family, IPAddr *addr); extern char *CNF_GetPidFile(void); extern char *CNF_GetLeapSecTimezone(void); extern void CNF_GetLinuxHz(int *set, int *hz); extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale); /* Value returned in ppm, as read from file */ extern double CNF_GetMaxUpdateSkew(void); extern double CNF_GetMaxClockError(void); extern double CNF_GetCorrectionTimeRatio(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_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k1, double *k2); extern char *CNF_GetUser(void); extern int CNF_GetMaxSamples(void); extern int CNF_GetMinSamples(void); #endif /* GOT_CONF_H */ chrony-1.29/chrony.txt0000644000076400007640000051776412200726270014110 0ustar mirosmirosUser guide for the chrony suite ******************************* 1 Introduction ************** 1.1 Overview ============ Chrony is a software package for maintaining the accuracy of computer system clocks. It consists of a pair of programs : * 'chronyd'. This is a daemon which runs in background on the system. It obtains measurements (e.g. via the network) of the system's offset relative to other systems, and adjusts the system time accordingly. For isolated systems, the user can periodically enter the correct time by hand (using 'chronyc'). In either case, 'chronyd' determines the rate at which the computer gains or loses time, and compensates for this. 'chronyd' can also act as an NTP server, and provide a time-of-day service to other computers. A typical set-up is to run 'chronyd' on a gateway computer that has a dial-up link to the Internet, and use it to serve time to computers on a private LAN sitting behind the gateway. The IP addresses that can act as clients of 'chronyd' can be tightly controlled. The default is no client access. * 'chronyc'. This is a command-line driven control and monitoring program. An administrator can use this to fine-tune various parameters within the daemon, add or delete servers etc whilst the daemon is running. The IP addresses from which '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 RFC1305, written by David Mills. The 'ntp' suite's source code has been occasionally used to check details of the protocol that the RFC did not make absolutely clear. The core algorithms in 'chronyd' are all completely distinct from 'ntp', however. 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 in the following environments: * Linux 2.2 and newer * NetBSD * BSD/386 * Solaris 2.3/2.5/2.5.1/2.6/2.7/2.8 on Sparc (Sparc 20, Ultrasparc) and i386 * SunOS 4.1.4 on Sparc 2 and Sparc20. Closely related systems may work too, but they have not been tested. Porting the software to other system (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' fully supports NTP version 4 (RFC5905), including broadcast, multicast, manycast clients / servers and the orphan mode. It also supports extra authentication schemes based on public-key cryptography (RFC5906). 'chronyd' uses NTP version 3 (RFC1305), which is compatible with version 4. * '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. Note, the Linux driver has been reported as working on a range of different architectures (Alpha, Sparc, MIPS as well as x86 of course). 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 4. Automation of the trimrtc and writertc mechanisms Currently, the RTC trimming mechanism is a manual operation, because there has to be a reasonable guarantee that the system will stay up for a reasonable length of time afterwards. (If it is shut down too soon, a poor characterisation of the RTC drift rate will be stored on disc, giving a bad system clock error when the system is next booted.) To make chrony more automated for the non-expert user, it would be useful if this problem could be avoided so that trimrtc could be done automatically (e.g. in a crontab, or as part of the ip-up or ip-down scripts.) 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, 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 contents of this depend on the network environment in which the computer operates. Typical scenarios are described in the following section of the document. 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 have permanent connections to the internet (or to any network containing true NTP servers which ultimately derive their time from a reference clock). To operate in this mode, you will need to know the names of the NTP server machines you wish to use. You may be able to find names of suitable servers by one of the following methods: * 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 'a.b.c' and 'd.e.f', your 'chrony.conf' file could contain as a minimum server a.b.c server d.e.f server g.h.i However, you will probably want to include some of the other directives described later. The following directives will be particularly useful : 'driftfile', 'commandkey', 'keyfile'. The smallest useful configuration file would look something like server a.b.c server d.e.f server g.h.i keyfile /etc/chrony.keys commandkey 1 driftfile /var/lib/chrony/drift 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 'a.b.c' and 'd.e.f', your 'chrony.conf' file would need to contain something like server a.b.c server d.e.f server g.h.i 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 a.b.c offline server d.e.f offline server g.h.i 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. To set up an administrator password, you can create a file '/etc/chrony.keys' containing a single line 1 ALongAndRandomPassword and add the following line to '/etc/chrony.conf' (the order of the lines does not matter) commandkey 1 The smallest useful configuration file would look something like server a.b.c offline server d.e.f offline server g.h.i offline keyfile /etc/chrony.keys commandkey 1 driftfile /var/lib/chrony/drift 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 commandkey 25 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 commandkey 24 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 0.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline server 1.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline server 2.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline logdir /var/log/chrony log statistics measurements tracking driftfile /var/lib/chrony/drift keyfile /etc/chrony.keys commandkey 25 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. 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. '-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 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 systems where this is not the case, e.g. Solaris and SunOS the 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 conjuction 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/clock' program during the Linux boot sequence. Support for real-time clocks is limited at present - the criteria are described in the section on the 'rtcfile' directive (*note rtcfile directive::). If 'chronyd' cannot support the real time clock on your computer, this option cannot be used and a warning message will be logged to the syslog. If used in conjunction with the '-r' flag, 'chronyd' will attempt to preserve the old samples after setting the system clock from the real time clock. 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 real time clock and system clock last time the computer was on. '-u ' When this option is used, chronyd will drop root privileges to the specified user. So far, it works only on Linux when compiled with capabilities support. '-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. 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 --------------------- 'chronyd' uses a separate client-side port for the rapid-fire measurements requested with the 'initstepslew' directive (*note initstepslew directive::). Normally, that port is chosen arbitrarily by the operating system. However, you can use 'acquisitionport' to explicitly specify a port. This may be useful for getting through firewalls. Do not make acquisition and regular NTP service (*note port directive::) use the same port. An example of the 'acquisitionport' command is acquisitionport 1123 This would change the port used for rapid queries 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.bar.com 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 bindaddress ----------------- The bindaddress allows you to restrict the network interface to which chronyd will listen for NTP packets. This provides an additional level of access restriction above that available through the 'deny' mechanism. Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with a dial-up connection. The ethernet interface's IP address is 192.168.1.1. Suppose (for some reason) you want to block all access through the dialup connection (note, this will even block replies from servers on the dialup side, so you will not be able to synchronise to an external source). You could add the line bindaddress 192.168.1.1 to the configuration file. This directive affects NTP (UDP port 123) packets. If no 'bindcmdaddress' directive is present, the address supplied by 'bindaddress' will be used to control binding of the command socket (UDP port 323) as well. The 'bindaddress' directive has been found to cause problems when used on computers that need to pass NTP traffic over multiple network interfaces (e.g. firewalls). It is, therefore, not particularly useful. Use of the 'allow' and 'deny' directives together with a network firewall is more likely to be successful. For each of IPv4 and IPv6 protocols, only one 'bindaddress' directive can be specified. 4.2.5 bindcmdaddress -------------------- The bindcmdaddress allows you to restrict the network interface to which chronyd will listen for command packets (issued by chronyc). Suppose you have a local ethernet with addresses in the 192.168.1.0 subnet together with a dial-up connection. The ethernet interface's IP address is 192.168.1.1. Suppose you want to block all access through the dialup connection. You could add the line bindcmdaddress 192.168.1.1 to the configuration file. The 'bindcmdaddress' directive has been found to cause problems when used on computers that need to pass command traffic over multiple network interfaces. It is, therefore, not particularly useful. Use of the 'cmdallow' and 'cmddeny' directives together with a network firewall is more likely to be successful. For each of IPv4 and IPv6 protocols, only one 'bindcmdaddress' directive can be specified. 4.2.6 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.7 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). 4.2.8 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.9 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.10 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 foobar 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.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). 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 corrtimeratio -------------------- When 'chronyd' makes a time correction, it controls how quickly the system clock is slewed (so far only on Linux). This rate temporarily affects the frequency error of the system clock. The 'corrtimeratio' directive controls 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' makes the overall frequency error of the system clock smaller, but increases the overall time error as the corrections will take longer. By default, the ratio is 1, which means the duration of an average correction will be close to the update interval. The syntax is corrtimeratio 10 The current remaining correction is shown in the 'tracking' report (*note tracking command::) as the 'System time' value. 4.2.13 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.14 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.15 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.16 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.17 fallbackdrift -------------------- Fallback drifts are long-term averages of the system clock drift calculated over exponentially increasing intervals. They are used when the clock is unsynchronised to avoid quickly drifting away from true time if there was a short-term deviation in drift before the synchronisation was lost. The directive specifies the minimum and maximum interval for how long the system clock has to be unsynchronised 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 the synchronisation was lost, 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 will be used and the clock frequency will stay at the last value calculated before synchronisation was lost. 4.2.18 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.19 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.20 initstepslew ------------------- In normal operation, 'chronyd' always slews the time when it needs to adjust the system clock. For example, to correct a system clock which is 1 second slow, '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. A maximum of 8 will be used. Each of the servers is rapidly polled several times, and a majority voting mechanism used to find the most likely range of system clock error that is present. A step (or slew) is applied to the system clock to correct this error. 'chronyd' then enters its normal operating mode (where only slews are used). An example of use of the command is initstepslew 30 foo.bar.com baz.quz.com 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. 4.2.21 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. 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. 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.22 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.23 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.24 linux_hz --------------- (This option only applies to Linux). By default, chronyd will find the value of 'HZ' from a kernel header file at compile time. 'HZ' is the nominal number of timer interrupts per second. If you're running chronyd on the system where it was built, the value it has should be right, and you don't need to worry about this option. This option is provided for people who move a pre-built chronyd onto a system where the value of HZ in the kernel headers has been changed from the default value. An example of the command is linux_hz 100 4.2.25 linux_freq_scale ----------------------- (This option only applies to Linux). By default, chronyd will find the value of 'HZ' and 'SHIFT_HZ' from kernel header files at compile time. An internal value called 'freq_scale' is calculated from this. By default it is (1< 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.35 maxsamples ----------------- The 'maxsamples' directive sets the maximum number of samples 'chronyd' should keep for each source. The default is 0, which disables the configurable limit, and the useful range is 4 to 64. The syntax is maxsamples 4.2.36 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.37 minsamples ----------------- The 'minsamples' directive sets the minimum number of samples 'chronyd' should try to keep for each source. The default is 0 and the useful range is 4 to 64. The syntax is minsamples 4.2.38 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.39 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.40 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. 4.2.41 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.42 port ----------- This option allows you to configure the port used for the NTP service on your machine. The compiled in default is udp/123, the standard NTP port. It is unlikely that you would ever need to change this value. A possible exception would be if you wanted to operate strictly in client-only mode and never be available as a server to ntpd clients. If set to 0, the kernel will assign a random port. 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.43 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 three 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.1 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', 'shmpps' and 'radioclk'. '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 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 intervals specified by this option. This is defined as a power of 2. 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 are not controlled by external events and thus require polling. Again this is defined as a power of 2 and can be negative for sub-second intervals. 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 is used to specify how the refclock is assumed to be inaccurate (in seconds). Increasing the value is useful to avoid having no majority in the source selection algorithm or to make the algorithm prefer other refclocks. 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 and PPS refclocks. '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. 4.2.44 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.45 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.46 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.47 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/clock' program. 4.2.48 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.49 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.50 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, it is 1 second. This usually means that sources with lower stratum will be preferred to sources with higher stratum even when their distance is significantly worse. Setting 'stratumweight' to 0 makes 'chronyd' ignore stratum when selecting the source. 4.2.51 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.52 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 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 telnet or rlogin 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' * '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 a.b.c 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 identical to that for the 'peer' directive in the configuration file (*note peer directive::). An example of using this command is shown below. add peer foo.bar.com minpoll 6 maxpoll 10 authkey 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 identical to that for the 'server' directive in the configuration file (*note server directive::). An example of using this command is shown below. add server foo.bar.com minpoll 6 maxpoll 10 authkey 25 4.3.4.5 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.bar.com 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.6 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.7 authhash ................ This command sets the hash function used for authenticating user commands. For successful authentication the hash function has to be the same as the one set for the command key in the keys file on the server. It needs to be set 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.bar.com 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 a.b.c cmdaccheck 1.2.3.4 cmdaccheck 2001:db8::1 4.3.4.11 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.12 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.13 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.14 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.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.bar.com 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 ............. 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.bar.com 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.18 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.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. It cancels any remaining correction that was being slewed, and jumps the system clock by the equivalent amount, making it correct immediately. BE WARNED - certain software will be seriously affected by such jumps to the system time. (That is the reason why chronyd uses slewing normally.) The 'makestep' directive in the configuration file can be used to step the clock automatically when the adjustment is larger than a specified threshold, see *note makestep directive::. 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.bar.com 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.bar.com' 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 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.bar.com 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.bar.com' 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.28 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.bar.com 0.1 maxdelaydevratio 1.2.3.4 1.0 maxdelaydevratio 2001:db8::1 100.0 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.bar.com 10 which sets the maximum polling interval for the host 'foo.bar.com' 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.bar.com 5 which sets the minimum polling interval for the host 'foo.bar.com' 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.bar.com 5 which sets the minimum stratum for the host 'foo.bar.com' 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.bar.com 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. (Note that the no-echo mode is limited to 8 characters on SunOS 4.1 due to limitations in the system library. Other systems do not have this restriction.) The password 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::). 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.bar.com 12 which sets the poll target for the host 'foo.bar.com' 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 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 ^? a.b.c 2 6 377 23 -923us[ -924us] +/- 43ms ^+ d.e.f 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.44 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.45 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::). The default is 1000 milliseconds. 4.3.4.46 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 (a.b.c) 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. 'a.b.c' 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 such as Solaris and SunOS, 'chronyd' has no means to adjust the fundamental rate of the system clock, so keeps the system time correct by periodically making offsets to it as though an error had been measured. The build up of these offsets will be observed in this report. '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. In certain extreme situations, this value can be negative. (This can arise in a symmetric peer arrangement where the computers' frequencies are not tracking each other and the network delay is very short relative to the turn-around time at each computer.) '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.47 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. 4.3.4.48 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.49 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). Appendix A Porting guide ************************ This appendix discusses issues that have arisen in writing the system-specific parts of the existing ports. This will provide useful information for those attempting to write ports to other systems. A.1 System driver files ======================= The system specific parts of the software are contained in files with names like 'sys_linux.c'. The following functions are required in a system driver file: 1. A function to read the current frequency 2. A function to set the current frequency 3. A function to slew the system time by a specified delta 4. A function to step the system time by a specified delta 5. A function to work out the error at a particular time between the system's clock and 'chronyd's' estimate of real time. (This is required because some systems have to track real time by making the system time follow it in a 'sawtooth' fashion). The "frequency" is the rate at which the system gains or loses time, measured relative to the system when running uncompensated. A.2 Quirks of particular systems ================================ These sections describe quirks in each system type that needed to be investigated to port the software to each system type. A.2.1 Linux ----------- The following quirks have been found in developing the Linux port. 1. In order to avoid floating point arithmetic, the kernel uses shifting and adding to approximate a scaling of 100/128. This approximation implies that the frequency set via the 'adjtimex()' system call is not the frequency that is actually obtained. The method of approximation varies between kernel versions and must be determined by examining the kernel source. An inverse factor must be included in the driver to compensate. 2. In some kernel versions, an 'adjtimex()' system call with the flags bits all zeroed will return the amount of offset still to be corrected. In others (e.g. the 2.0 series beyond 2.0.32), the offset must be changed in order to get the old offset returned (similar to 'adjtime()' on other systems). A.2.2 Solaris 2.5 ----------------- The following quirks have been found in developing the Solaris port. 1. The 'adjtime()' system call with a zero argument does not cancel an adjustment that is in progress - it just reports the remaining adjustment. 2. The 'settimeofday()' system call only observes the seconds part of the argument - any fractional seconds part is lost. second. 3. The kernel variable 'dosynctodr' has to be set to zero, otherwise the system clock is periodically reset to the real-time clock. A.2.3 SunOS 4.1.4 ----------------- The following quirks have been found in developing the SunOS port. 1. The 'adjtime()' system call truncates its argument to a multiple of the system's 'tickadj' variable. ('chronyd' sets that to 100, giving a 1 part in 100 slewing capability for correcting offsets.) 2. The kernel variable 'dosynctodr' has to be set to zero, otherwise the system clock is periodically reset to the real-time clock. Appendix B 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 bindaddress 4.2.5 bindcmdaddress 4.2.6 broadcast 4.2.7 cmdallow 4.2.8 cmddeny 4.2.9 combinelimit 4.2.10 commandkey 4.2.11 cmdport 4.2.12 corrtimeratio 4.2.13 deny 4.2.14 driftfile 4.2.15 dumpdir 4.2.16 dumponexit 4.2.17 fallbackdrift 4.2.18 generatecommandkey 4.2.19 include 4.2.20 initstepslew 4.2.21 keyfile 4.2.22 leapsectz 4.2.23 local 4.2.24 linux_hz 4.2.25 linux_freq_scale 4.2.26 log 4.2.26.1 Measurements log file format 4.2.26.2 Statistics log file format 4.2.26.3 Tracking log file format 4.2.26.4 Real-time clock log file format 4.2.26.5 Refclocks log file format 4.2.26.6 Tempcomp log file format 4.2.27 logbanner 4.2.28 logchange 4.2.29 logdir 4.2.30 mailonchange 4.2.31 makestep 4.2.32 maxchange 4.2.33 manual 4.2.34 maxclockerror 4.2.35 maxsamples 4.2.36 maxupdateskew 4.2.37 minsamples 4.2.38 noclientlog 4.2.39 clientloglimit 4.2.40 peer 4.2.41 pidfile 4.2.42 port 4.2.43 refclock 4.2.44 reselectdist 4.2.45 rtcdevice 4.2.46 rtcfile 4.2.47 rtconutc 4.2.48 rtcsync 4.2.49 sched_priority 4.2.50 stratumweight 4.2.51 lock_all 4.2.52 server 4.2.53 tempcomp 4.2.54 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 4.3.4.6 allow all 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 4.3.4.12 cmdallow all 4.3.4.13 cmddeny 4.3.4.14 cmddeny all 4.3.4.15 cyclelogs 4.3.4.16 delete 4.3.4.17 deny 4.3.4.18 deny all 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 maxdelayratio 4.3.4.28 maxdelaydevratio 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 sources 4.3.4.44 sourcestats 4.3.4.45 timeout 4.3.4.46 tracking 4.3.4.47 trimrtc 4.3.4.48 waitsync 4.3.4.49 writertc Appendix A Porting guide A.1 System driver files A.2 Quirks of particular systems A.2.1 Linux A.2.2 Solaris 2.5 A.2.3 SunOS 4.1.4 Appendix B GNU General Public License chrony-1.29/ntp_io.h0000644000076400007640000000332112200721757013466 0ustar mirosmiros/* 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 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 transmit a packet */ extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr); /* Function to transmit an authenticated packet */ extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len); /* Function to send a datagram to a remote machine's UDP echo port. */ extern void NIO_SendEcho(NTP_Remote_Address *remote_addr); #endif /* GOT_NTP_IO_H */ chrony-1.29/rtc_linux.h0000644000076400007640000000305512200721757014211 0ustar mirosmiros/* 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 void RTC_Linux_TimePreInit(void); extern void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything); extern void RTC_Linux_StartMeasurements(void); /* 0=success, 1=no driver, 2=can't write file */ extern int RTC_Linux_WriteParameters(void); extern int RTC_Linux_GetReport(RPT_RTC_Report *report); extern int RTC_Linux_Trim(void); extern void RTC_Linux_CycleLogFile(void); #endif /* _GOT_RTC_LINUX_H */ chrony-1.29/config.h0000644000076400007640000000076612200726264013453 0ustar mirosmiros#define LINUX 1 #define HAS_STDINT_H 1 #define HAS_INTTYPES_H 1 #define HAVE_IPV6 1 #define _GNU_SOURCE 1 #define HAVE_IN6_PKTINFO 1 #define HAVE_SYS_TIMEPPS_H 1 #define HAVE_PPSAPI 1 #define FEAT_LINUXCAPS 1 #define FEAT_RTC 1 #define HAVE_SCHED_SETSCHEDULER 1 #define HAVE_MLOCKALL 1 #define FORCE_DNSRETRY 1 #define FEAT_READLINE 1 #define USE_EDITLINE 1 #define GENERATE_SHA1_KEY 1 #define DEFAULT_CONF_FILE "/etc/chrony.conf" #define MAIL_PROGRAM "/usr/lib/sendmail" #define CHRONY_VERSION "1.29" chrony-1.29/mkdirpp.h0000644000076400007640000000213112200721757013642 0ustar mirosmiros/* 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-1.29/chrony.spec0000644000076400007640000000310012200726264014174 0ustar mirosmiros%global chrony_version 1.29 %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/chrony.1.gz %{_mandir}/man1/chronyc.1.gz %{_mandir}/man5/chrony.conf.5.gz %{_mandir}/man8/chronyd.8.gz %doc README %doc chrony.txt %doc COPYING %doc examples/chrony.conf.example* %doc examples/chrony.keys.example chrony-1.29/main.h0000644000076400007640000000224212200721757013123 0ustar mirosmiros/* 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-1.29/chronyd.8.in0000644000076400007640000001157012200726264014174 0ustar mirosmiros.TH CHRONYD 8 "August 2013" "chrony 1.29" "System Administration" .SH NAME chronyd \- chrony background daemon .SH SYNOPSIS .B chronyd [\fIOPTIONS\fR] .SH DESCRIPTION \fIchrony\fR is a pair of programs for maintaining the accuracy of computer clocks. \fBchronyd\fR is a background daemon program that can be started at boot time. \fBchronyd\fR is a daemon which runs in background on the system. It obtains measurements (e.g. via the network) of the system's offset relative to other systems, and adjusts the system time accordingly. For isolated systems, the user can periodically enter the correct time by hand (using \fIchronyc\fR). In either case, \fBchronyd\fR determines the rate at which the computer gains or loses time, and compensates for this. .SH USAGE \fBchronyd\fR is usually started at boot-time and requires superuser 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. .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. .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 systems where this is not the case, e.g. Solaris and SunOS the 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 conjuction 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/clock\fR program during the Linux boot sequence. Support for real-time clocks is limited at present - the criteria are described in the section on the \fIrtcfile\fR directive in the documentation supplied with the distribution. If \fBchronyd\fR cannot support the real time clock on your computer, this option cannot be used and a warning message will be logged to the syslog. If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt to preserve the old samples after setting the system clock from the real time clock. This can be used to allow \fBchronyd\fR to perform long term averaging of the gain or loss rate across system reboots, and is useful for dial-up systems that are shut down when not in use. For this to work well, it relies on \fBchronyd\fR having been able to determine accurate statistics for the difference between the real time clock and system clock last time the computer was on. .TP \fB\-u\fR \fIuser\fR When this option is used, chronyd will drop root privileges to the specified user. So far, it works only on Linux when compiled with capabilities support. .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) and is also available from \fIhttp://go.to/chrony\fR .BR chrony(1), .BR chronyc(1), .BR chrony.conf(5), .BR hwclock(8), .BR ntpd(8) .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-1.29/version.txt0000644000076400007640000000000512200726264014245 0ustar mirosmiros1.29 chrony-1.29/ntp_io.c0000644000076400007640000003664712200721757013502 0ustar mirosmiros/* 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 * * 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" union sockaddr_in46 { struct sockaddr_in in4; #ifdef HAVE_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; /* The file descriptors for the IPv4 and IPv6 sockets */ static int sock_fd4; #ifdef HAVE_IPV6 static int sock_fd6; #endif /* Flag indicating that we have been initialised */ static int initialised=0; /* ================================================== */ /* Forward prototypes */ static int prepare_socket(int family); static void read_from_socket(void *anything); /* ================================================== */ static void do_size_checks(void) { /* Assertions to check the sizes of certain data types and the positions of certain record fields */ /* Check that certain invariants are true */ assert(sizeof(NTP_int32) == 4); assert(sizeof(NTP_int64) == 8); /* Check offsets of all fields in the NTP packet format */ assert(offsetof(NTP_Packet, lvm) == 0); assert(offsetof(NTP_Packet, stratum) == 1); assert(offsetof(NTP_Packet, poll) == 2); assert(offsetof(NTP_Packet, precision) == 3); assert(offsetof(NTP_Packet, root_delay) == 4); assert(offsetof(NTP_Packet, root_dispersion) == 8); assert(offsetof(NTP_Packet, reference_id) == 12); assert(offsetof(NTP_Packet, reference_ts) == 16); assert(offsetof(NTP_Packet, originate_ts) == 24); assert(offsetof(NTP_Packet, receive_ts) == 32); assert(offsetof(NTP_Packet, transmit_ts) == 40); } /* ================================================== */ static int prepare_socket(int family) { union sockaddr_in46 my_addr; socklen_t my_addr_len; int sock_fd; unsigned short port_number; IPAddr bind_address; int on_off = 1; port_number = CNF_GetNTPPort(); /* Open Internet domain UDP socket for NTP message transmissions */ #if 0 sock_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); #else sock_fd = socket(family, SOCK_DGRAM, 0); #endif if (sock_fd < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno)); return -1; } /* Close on exec */ UTI_FdSetCloexec(sock_fd); /* Make the socket capable of re-using an old address */ if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options"); /* Don't quit - we might survive anyway */ } /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */ if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options"); /* Don't quit - we might survive anyway */ } #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 if (family == AF_INET) { #ifdef IP_PKTINFO /* We want the local IP info too */ if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not request packet info using socket option"); /* Don't quit - we might survive anyway */ } #endif } #ifdef HAVE_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 request 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 request 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 request IPv6 packet info socket option"); } #endif } #endif /* Bind the port */ 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(port_number); CNF_GetBindAddress(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_ANY); break; #ifdef HAVE_IPV6 case AF_INET6: my_addr_len = sizeof (my_addr.in6); my_addr.in6.sin6_family = family; my_addr.in6.sin6_port = htons(port_number); CNF_GetBindAddress(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_any; break; #endif default: assert(0); } #if 0 LOG(LOGS_INFO, LOGF_NtpIO, "Initialising, socket fd=%d", sock_fd); #endif if (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 -1; } /* Register handler for read events on the socket */ SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd); #if 0 if (fcntl(sock_fd, F_SETFL, O_NONBLOCK | O_NDELAY) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not make socket non-blocking"); } if (ioctl(sock_fd, I_SETSIG, S_INPUT) < 0) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not enable signal"); } #endif return sock_fd; } /* ================================================== */ void NIO_Initialise(int family) { assert(!initialised); initialised = 1; do_size_checks(); if (family == IPADDR_UNSPEC || family == IPADDR_INET4) sock_fd4 = prepare_socket(AF_INET); else sock_fd4 = -1; #ifdef HAVE_IPV6 if (family == IPADDR_UNSPEC || family == IPADDR_INET6) sock_fd6 = prepare_socket(AF_INET6); else sock_fd6 = -1; #endif if (sock_fd4 < 0 #ifdef HAVE_IPV6 && sock_fd6 < 0 #endif ) { LOG_FATAL(LOGF_NtpIO, "Could not open any NTP socket"); } } /* ================================================== */ void NIO_Finalise(void) { if (sock_fd4 >= 0) { SCH_RemoveInputFileHandler(sock_fd4); close(sock_fd4); } sock_fd4 = -1; #ifdef HAVE_IPV6 if (sock_fd6 >= 0) { SCH_RemoveInputFileHandler(sock_fd6); close(sock_fd6); } sock_fd6 = -1; #endif initialised = 0; } /* ================================================== */ /* ================================================== */ 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; ReceiveBuffer message; union sockaddr_in46 where_from; unsigned int flags = 0; struct timeval now, now_raw; double now_err; NTP_Remote_Address remote_addr; char cmsgbuf[256]; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; assert(initialised); SCH_GetLastEventTime(&now, &now_err, &now_raw); iov.iov_base = message.arbitrary; 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) { memset(&remote_addr, 0, sizeof (remote_addr)); switch (where_from.u.sa_family) { case AF_INET: remote_addr.ip_addr.family = IPADDR_INET4; remote_addr.ip_addr.addr.in4 = ntohl(where_from.in4.sin_addr.s_addr); remote_addr.port = ntohs(where_from.in4.sin_port); break; #ifdef HAVE_IPV6 case AF_INET6: remote_addr.ip_addr.family = IPADDR_INET6; memcpy(&remote_addr.ip_addr.addr.in6, where_from.in6.sin6_addr.s6_addr, sizeof (remote_addr.ip_addr.addr.in6)); remote_addr.port = ntohs(where_from.in6.sin6_port); break; #endif default: assert(0); } 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)); remote_addr.local_ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr); remote_addr.local_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(&remote_addr.local_ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, sizeof (remote_addr.local_ip_addr.addr.in6)); remote_addr.local_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)); /* This should be more accurate than LCL_CookTime(&now_raw,...) */ UTI_AddDiffToTimeval(&now, &now_raw, &tv, &now); } #endif } if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) { NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr, status); } else { /* Just ignore the packet if it's not of a recognized length */ } } } /* ================================================== */ /* Send a packet to given address */ static void send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr) { union sockaddr_in46 remote; struct msghdr msg; struct iovec iov; char cmsgbuf[256]; int cmsglen; int sock_fd; socklen_t addrlen; assert(initialised); switch (remote_addr->ip_addr.family) { case IPADDR_INET4: memset(&remote.in4, 0, sizeof (remote.in4)); addrlen = sizeof (remote.in4); remote.in4.sin_family = AF_INET; remote.in4.sin_port = htons(remote_addr->port); remote.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4); sock_fd = sock_fd4; break; #ifdef HAVE_IPV6 case IPADDR_INET6: memset(&remote.in6, 0, sizeof (remote.in6)); addrlen = sizeof (remote.in6); remote.in6.sin6_family = AF_INET6; remote.in6.sin6_port = htons(remote_addr->port); memcpy(&remote.in6.sin6_addr.s6_addr, &remote_addr->ip_addr.addr.in6, sizeof (remote.in6.sin6_addr.s6_addr)); sock_fd = sock_fd6; break; #endif default: return; } if (sock_fd < 0) return; iov.iov_base = packet; iov.iov_len = packetlen; msg.msg_name = &remote.u; msg.msg_namelen = addrlen; 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 (remote_addr->local_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(remote_addr->local_ip_addr.addr.in4); } #endif #if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO) if (remote_addr->local_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, &remote_addr->local_ip_addr.addr.in6, sizeof(ipi->ipi6_addr.s6_addr)); } #endif #if 0 LOG(LOGS_INFO, LOGF_NtpIO, "sending to %s:%d from %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->local_ip_addr)); #endif msg.msg_controllen = cmsglen; /* This is apparently required on some systems */ if (!cmsglen) msg.msg_control = NULL; if (sendmsg(sock_fd, &msg, 0) < 0 && #ifdef ENETUNREACH errno != ENETUNREACH && #endif #ifdef ENETDOWN errno != ENETDOWN && #endif !LOG_RateLimited()) { LOG(LOGS_WARN, LOGF_NtpIO, "Could not send to %s:%d : %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno)); } } /* ================================================== */ /* Send an unauthenticated packet to a given address */ void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr) { send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr); } /* ================================================== */ /* Send an authenticated packet to a given address */ void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len) { send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr); } /* ================================================== */ /* We ought to use getservbyname, but I can't really see this changing */ #define ECHO_PORT 7 void NIO_SendEcho(NTP_Remote_Address *remote_addr) { unsigned long magic_message = 0xbe7ab1e7UL; NTP_Remote_Address addr; addr = *remote_addr; addr.port = ECHO_PORT; send_packet((void *) &magic_message, sizeof(unsigned long), &addr); } chrony-1.29/sys.c0000644000076400007640000000541512200721757013015 0ustar mirosmiros/* 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 /* ================================================== */ void SYS_Initialise(void) { #if defined(LINUX) SYS_Linux_Initialise(); #endif #if defined(SOLARIS) SYS_Solaris_Initialise(); #endif #if defined(SUNOS) SYS_SunOS_Initialise(); #endif #if defined(__NetBSD__) SYS_NetBSD_Initialise(); #endif } /* ================================================== */ void SYS_Finalise(void) { #if defined(LINUX) SYS_Linux_Finalise(); #endif #if defined(SOLARIS) SYS_Solaris_Finalise(); #endif #if defined(SUNOS) SYS_SunOS_Finalise(); #endif #if defined(__NetBSD__) SYS_NetBSD_Finalise(); #endif } /* ================================================== */ void SYS_DropRoot(char *user) { #if defined(LINUX) && defined (FEAT_LINUXCAPS) 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-1.29/hash_intmd5.c0000644000076400007640000000324312200721757014377 0ustar mirosmiros/* 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; } chrony-1.29/sys_sunos.c0000644000076400007640000002464212200721757014247 0ustar mirosmiros/* 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 void 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) { LOG_FATAL(LOGF_SysSunOS, "settimeofday() failed"); } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; start_adjust(); } /* ================================================== */ 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); #if 0 LOG(LOGS_INFO, LOGF_SysSunOS, "Set value of _dosynctodr to %d", on_off); #endif } /* ================================================== */ void SYS_SunOS_Initialise(void) { /* Need to do KVM stuff to turn off dosynctodr. */ clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */); /* 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-1.29/tempcomp.h0000644000076400007640000000213312200721757014022 0ustar mirosmiros/* 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-1.29/configure0000755000076400007640000004122412200721757013740 0ustar mirosmiros#!/bin/sh # ======================================================================= # # chronyd/chronyc - Programs for keeping computer clocks accurate. # # Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Miroslav Lichvar 2009, 2012 # # ======================================================================= rm -f config.h config.log # 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 } #}}} # ====================================================================== OPERATINGSYSTEM=`uname -s` VERSION=`uname -r` MACHINE=`uname -m` EXTRA_LIBS="" EXTRA_CLI_LIBS="" EXTRA_OBJECTS="" EXTRA_DEFS="" SYSDEFS="" # Support for readline (on by default) feat_readline=1 try_readline=1 try_editline=1 try_nss=1 try_tomcrypt=1 feat_rtc=1 try_rtc=0 feat_linuxcaps=1 try_linuxcaps=0 readline_lib="" readline_inc="" ncurses_lib="" feat_ipv6=1 feat_pps=1 try_setsched=0 try_lockmem=0 feat_forcednsretry=1 mail_program="/usr/lib/sendmail" for option do case "$option" in --enable-trace ) add_def TRACEON ;; --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-rtc) feat_rtc=0 ;; --disable-ipv6) feat_ipv6=0 ;; --disable-pps) feat_pps=0 ;; --disable-linuxcaps) feat_linuxcaps=0 ;; --disable-forcednsretry) feat_forcednsretry=0 ;; --with-sendmail=* ) mail_program=`echo $option | sed -e 's/^.*=//;'` ;; --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 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_linux.o wrap_adjtimex.o" try_linuxcaps=1 try_rtc=1 try_setsched=1 try_lockmem=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" ;; 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 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 HAS_STDINT_H fi if test_code '' 'inttypes.h' '' '' ''; then add_def HAS_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 HAVE_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 timepps_h="" if [ $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; pps_info_t i; struct timespec ts; return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);' then add_def HAVE_PPSAPI fi if [ $feat_linuxcaps = "1" ] && [ $try_linuxcaps = "1" ] && \ test_code \ linuxcaps \ '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_LINUXCAPS 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 [ $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 $ncurses_lib -lreadline" \ 'add_history(readline("prompt"));' then add_def FEAT_READLINE READLINE_COMPILE="$readline_inc" READLINE_LINK="$readline_lib $ncurses_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 fi HASH_OBJ="hash_intmd5.o" HASH_COMPILE="" HASH_LINK="" if [ $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" add_def GENERATE_SHA1_KEY fi fi if [ "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" add_def GENERATE_SHA1_KEY 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 MAIL_PROGRAM "\"$mail_program\"" 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@%${CPPFLAGS}%;\ s%@LIBS@%${LIBS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\ s%@READLINE_LINK@%${READLINE_LINK}%;\ s%@HASH_OBJ@%${HASH_OBJ}%;\ s%@HASH_LINK@%${HASH_LINK}%;\ 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}%;"\ < ${f}.in > $f done # ======================================================================= # vim:et:sw=2:ht=2:sts=2:fdm=marker:cms=#%s chrony-1.29/NEWS0000644000076400007640000004021112200721757012523 0ustar mirosmirosNew 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-1.29/addressing.h0000644000076400007640000000302712200721757014324 0ustar mirosmiros/* 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; IPAddr local_ip_addr; unsigned short port; } NTP_Remote_Address; #endif /* GOT_ADDRESSING_H */ chrony-1.29/refclock_pps.c0000644000076400007640000001004112200721757014640 0ustar mirosmiros/* 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 HAVE_PPSAPI #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) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "time_pps_fetch error"); #endif 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)) { 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-1.29/srcparams.h0000644000076400007640000000344512200721757014200 0ustar mirosmiros/* 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; unsigned long 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 16.0 #define SRC_DEFAULT_MAXDELAYRATIO 0.0 #define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0 #define SRC_DEFAULT_MINSTRATUM 0 #define SRC_DEFAULT_POLLTARGET 6 #define INACTIVE_AUTHKEY 0UL #endif /* GOT_SRCPARAMS_H */ chrony-1.29/client.c0000644000076400007640000023216312200721757013457 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-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. * ********************************************************************** ======================================================================= 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 "memory.h" #include "util.h" #ifdef FEAT_READLINE #ifdef USE_EDITLINE #include #else #include #include #endif #endif /* ================================================== */ union sockaddr_in46 { struct sockaddr_in in4; #ifdef HAVE_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; /* ================================================== */ /* Ought to extract some code from util.c to make a new set of utilities that can be linked into either the daemon or the client. */ static char * time_to_log_form(time_t t) { struct tm stm; static char buffer[64]; static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; stm = *gmtime(&t); snprintf(buffer, sizeof(buffer), "%2d%s%02d %02d:%02d:%02d", stm.tm_mday, months[stm.tm_mon], stm.tm_year % 100, stm.tm_hour, stm.tm_min, stm.tm_sec); return buffer; } /* ================================================== */ /* 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; /* Note, this call could block for a while */ if (DNS_Name2IPAddress(hostname, &ip) != DNS_Success) { fprintf(stderr, "Could not get IP address for %s\n", hostname); exit(1); } memset(&his_addr, 0, sizeof (his_addr)); switch (ip.family) { case IPADDR_INET4: sock_fd = socket(AF_INET, SOCK_DGRAM, 0); his_addr.in4.sin_family = AF_INET; his_addr.in4.sin_addr.s_addr = htonl(ip.addr.in4); his_addr.in4.sin_port = htons(port); his_addr_len = sizeof (his_addr.in4); break; #ifdef HAVE_IPV6 case IPADDR_INET6: sock_fd = socket(AF_INET6, SOCK_DGRAM, 0); his_addr.in6.sin6_family = AF_INET6; memcpy(his_addr.in6.sin6_addr.s6_addr, ip.addr.in6, sizeof (his_addr.in6.sin6_addr.s6_addr)); his_addr.in6.sin6_port = htons(port); his_addr_len = sizeof (his_addr.in6); break; #endif default: assert(0); } if (sock_fd < 0) { perror("Can't create socket"); exit(1); } } /* ================================================== */ 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) == 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) != 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) != 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 (!*p) { return 0; } else 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 (!*p) { return 0; } else 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 { return 0; } msg->command = htons(REQ_MANUAL); return 1; } /* ================================================== */ static int parse_allow_deny(CMD_Request *msg, char *line) { unsigned long a, b, c, d, n; 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) != 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) { int specified_subnet_bits, n; n = sscanf(slashpos+1, "%d", &specified_subnet_bits); if (n == 1) { msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits); } else { fprintf(stderr, "Warning: badly formatted subnet size, using %ld\n", (long) ntohl(msg->data.allow_deny.subnet_bits)); } } } } return 1; } /* ================================================== */ static int process_cmd_allow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_allowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_deny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_denyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddeny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddenyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int accheck_getaddr(char *line, 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) != 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; int result = 0; status = CPS_ParseNTPSourceAdd(line, &data); switch (status) { case CPS_Success: if (DNS_Name2IPAddress(data.name, &ip_addr) != DNS_Success) { Free(data.name); fprintf(stderr, "Invalid host/IP address\n"); break; } Free(data.name); 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; } 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; case CPS_BadOption: fprintf(stderr, "Unrecognized subcommand\n"); break; case CPS_BadHost: fprintf(stderr, "Invalid host/IP address\n"); break; case CPS_BadPort: fprintf(stderr, "Unreadable port number\n"); break; case CPS_BadMinpoll: fprintf(stderr, "Unreadable minpoll value\n"); break; case CPS_BadMaxpoll: fprintf(stderr, "Unreadable maxpoll value\n"); break; case CPS_BadPresend: fprintf(stderr, "Unreadable presend value\n"); break; case CPS_BadMaxdelaydevratio: fprintf(stderr, "Unreadable max delay dev ratio value\n"); break; case CPS_BadMaxdelayratio: fprintf(stderr, "Unreadable max delay ratio value\n"); break; case CPS_BadMaxdelay: fprintf(stderr, "Unreadable max delay value\n"); break; case CPS_BadKey: fprintf(stderr, "Unreadable key value\n"); break; case CPS_BadMinstratum: fprintf(stderr, "Unreadable minstratum value\n"); break; case CPS_BadPolltarget: fprintf(stderr, "Unreadable polltarget value\n"); break; } return result; } /* ================================================== */ static int process_cmd_add_server(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_SERVER); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_add_peer(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_PEER); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_delete(CMD_Request *msg, char *line) { char *hostname; 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) != 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 : Jump the time to remove any correction being slewed\n"); printf("manual off|on|reset : Disable/enable/reset settime command and statistics\n"); printf("manual list : Show previous settime entries\n"); printf("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("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; /* 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 auth_length; struct timeval tv; int timeout; int n_attempts; fd_set rdfd, wrfd, exfd; request->version = PROTO_VERSION_NUMBER; request->pkt_type = PKT_TYPE_CMD_REQUEST; request->res1 = 0; request->res2 = 0; tx_sequence = sequence++; request->sequence = htonl(tx_sequence); request->attempt = 0; request->utoken = htonl(utoken); request->token = htonl(token); timeout = initial_timeout; n_attempts = 0; do { /* 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; } command_length = PKL_CommandLength(request); assert(command_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) */ n_attempts++; if (n_attempts > max_retries) { return 0; } } else { read_length = recvfrom_status; expected_length = PKL_ReplyLength(reply); 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 HAVE_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_NUMBER && !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT && 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; } /* Good packet received, print out results */ #if 0 printf("Reply cmd=%d reply=%d stat=%d num=%d tot=%d seq=%d utok=%08lx tok=%d\n", ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status), ntohs(reply->number), ntohs(reply->total), ntohl(reply->sequence), ntohl(reply->utoken), ntohl(reply->token)); #endif if (password) { *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 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; uint16_t poll, stratum; uint16_t 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("|| / 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_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, time_to_log_form(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 void process_cmd_makestep(CMD_Request *msg, char *line) { msg->command = htons(REQ_MAKESTEP); } /* ================================================== */ 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")) { 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, "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; unsigned long key_id = 0, key_id2 = -1; int ret; FILE *in; in = fopen(filename, "r"); if (!in) { fprintf(stderr, "Could not open file %s\n", filename); 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")) { if (sscanf(arg, "%lu", &key_id) != 1) key_id = -1; } } fclose(in); if (!*keyfile || key_id == -1) { 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\n", filename); return 0; } 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 %lu 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] [-p ] [-n] [-4|-6] [-m] [-a] [-f ]] [command]\n", progname); exit(1); } else { break; /* And process remainder of line as a command */ } } if (isatty(0) && isatty(1) && isatty(2)) { on_terminal = 1; } if (on_terminal && (argc == 0)) { display_gpl(); } /* MD5 is the default authentication hash */ auth_hash_id = HSH_GetHashId("MD5"); if (auth_hash_id < 0) { fprintf(stderr, "Could not initialize MD5\n"); return 1; } open_io(hostname, port); if (auto_auth) { ret = authenticate_from_config(conf_file); } if (!ret) { ; } else if (argc > 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-1.29/refclock_sock.c0000644000076400007640000000622512200721757015006 0ustar mirosmiros/* 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) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "Error reading from SOCK socket : %s", strerror(errno)); #endif return; } if (s != sizeof (sample)) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %d", s, sizeof (sample)); #endif return; } if (sample.magic != SOCK_MAGIC) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x", sample.magic, SOCK_MAGIC); #endif 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-1.29/contrib/0000755000076400007640000000000012200721757013466 5ustar mirosmiroschrony-1.29/contrib/wolfgang_weisselberg10000644000076400007640000000764312200721757017703 0ustar mirosmiros > 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-1.29/contrib/andrew_bishop_20000644000076400007640000000447712200721757016472 0ustar mirosmirosFrom 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-1.29/contrib/erik_bryer_10000644000076400007640000000275712200721757016001 0ustar mirosmiros#!/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-1.29/contrib/DNSchrony/0000755000076400007640000000000012200721757015335 5ustar mirosmiroschrony-1.29/contrib/DNSchrony/ip-up.local0000644000076400007640000000134112200721757017402 0ustar mirosmiros#example file /etc/ppp/ip-up.local #originally from SuSE distribution #modified for chrony cat </dev/null 2>&1 & #other stuff who knows? # The following lines added for Linux-HA support # Heartbeat DEVFILE=`echo $DEVICE | sed -e 's!^/dev/!!' -e 's!/!.!g'` # Heartbeat OUTFILE=/var/run/ppp.d/$DEVFILE # Heartbeat ( # Heartbeat echo "$IPREMOTE" # Heartbeat echo "$IFNAME" # Heartbeat echo "$PPPD_PID" # Heartbeat echo "$IPLOCAL" # Heartbeat ) > $OUTFILE # Heartbeat chrony-1.29/contrib/DNSchrony/DNSchronyDELETE0000755000076400007640000000023212200721757020012 0ustar mirosmiros#!/usr/bin/bash # $1 is chrony password. # $2 host to be deleted if ip nn.n.n.n then no DNS used CHRONYPASSWORD=$1 \ /usr/local/bin/DNSchrony.pl -d $2 chrony-1.29/contrib/DNSchrony/COPYING0000644000076400007640000004310312200721757016371 0ustar mirosmiros 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-1.29/contrib/DNSchrony/DNSchrony.pl0000755000076400007640000003540212200721757017550 0ustar mirosmiros#!/usr/bin/perl # Copyright (C) Paul Elliott 2002 my($copyrighttext) = <<'EOF'; # Copyright (C) Paul Elliott 2002 # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # SEE COPYING FOR DETAILS EOF #modules we use. use Socket; use Getopt::Std; use Net::DNS; use Tie::Syslog; use File::Temp qw/ :mktemp /; use File::Copy; local($res) = new Net::DNS::Resolver; #dns lookup of IP address. #returns ip or errorstring. sub gethostaddr($) #get ip address from host { my($host) = shift; $query = $res->search($host); if ($query) { foreach $rr ($query->answer) { next unless $rr->type eq "A"; print $rr->address, "\n" if $pedebug; return $rr->address; } } else { print "query failed: ", $res->errorstring, "\n" if $pedebug; return $res->errorstring; } } #send messages to syslog sub Log($$) { if ($log) { my($level) = shift; my($mess) =shift; tie *MYLOG, 'Tie::Syslog',$level,$0,'pid','unix'; print MYLOG $mess; untie *MYLOG; } } #send message to output or syslog #and die. sub BadDie($) { my($myerr) =$!; my($mess)=shift; if($log){ tie *MYLOG, 'Tie::Syslog','local0.err',$0,'pid','unix'; print MYLOG $mess; print MYLOG $myerr; untie *MYLOG; } else { print "$mess\n$myerr\n"; } die $mess; } sub isIpAddr($) #return true if looks like ip address { my($ip) = shift; return 1 if ( $ip =~ m/$ipOnlyPAT/ ); return 0; } sub isHostname($) #return true if looks like ip address { my($ip) = shift; return 1 if ( $ip =~ m/$hostnameOnlyPAT/ ); return 0; } #send commands to chronyc by piping. sub chronyc($) #send commands to chronyc { my($command) = shift; my($err) = "/var/tmp/chronyc.log"; my($chronyP) = "/usr/local/bin/chronyc"; open(CHRONY, "| $chronyP 1>$err 2>&1"); print CHRONY "$passwd$command\n"; close(CHRONY); Log('local0.info',"chronyc command issued=$command"); #look at status lines till return bad. open( IN, "<$err"); my($status); while () { $status = $_; unless ( m/\A200 OK/ ) { last; } } $status ="" if ( $status =~ m/\A200 OK/ ); close(IN); unlink $err; Log('local0.info',"chronyc results=$status"); return $status; } #common patterns # an ip address patern local($ipPAT) = qr/\d{1,3}(?:\.\d{1,3}){3}/; # an hostname pattern local($hostnamePAT) = qr/\w+(?:\.\w+)*/; #line with hostname only local($hostnameOnlyPAT) = qr/\A$hostnamePAT\Z/; #line with ip address only local($ipOnlyPAT) =qr/\A$ipPAT\Z/; #options hash my(%opts); getopts('nuadslPSC', \%opts); local($log) = ( $opts{'l'} ) ? 1 : 0; my($offline) = !( $opts{'n'} ) ; my($offlineS) = ( $opts{'n'} ) ? " " : " offline" ; # paul elliotts secret debug var. no one will ever find out about it. local($pedebug)=( ($ENV{"PAULELLIOTTDEBUG"}) or ($opts{P}) ); if ($opts{C}) { print $copyrighttext; exit 0; } print <<"EOF" unless $opts{'S'}; $0, Copyright (C) 2002 Paul Elliott $0 comes with ABSOLUTELY NO WARRANTY; for details invoke $0 -C. This is free software, and you are welcome to redistribute it under certain conditions; invoke $0 -C for details. EOF local($passwd); # password to send to chronyc my($pl) = $ENV{"CHRONYPASSWORD"}; #password comand to send to chronyc if ( $pl ) { $passwd = "password $pl\n"; } else { $passwd = ""; } print "passwd=$passwd\n" if ($pedebug); my(%host2ip); # hash of arrays. host2ip{$host}[0] is ip address for this host # host2ip{$host}[1] is rest of paramenters for this host exc offline. #if debuging do chrony.conf in current directory. my($listfile) =( ($pedebug) ? "./chrony.conf" : "/etc/chrony.conf") ; # This section reads in the old data about # hostnames IP addresses and server parameters # data is stored as it would be in chrony.conf # file i.e.: #># HOSTNAME #>server IPADDR minpoll 5 maxpoll 10 maxdelay 0.4 offline # # the parameter offline is omitted if the -n switch is specified. # first parameter is the filename of the file usually # is /etc/DNSchrony.conf # this is where we store the list of DNS hosts. # hosts with static IP address shold be kept in chrony.conf # this is header that marks dnyamic host section my($noedithead)=<<'EOF'; ## DNSchrony dynamic dns server section. DO NOT EDIT ## per entry FORMAT: ## |--------------------------------------------| ## |#HOSTNAME | ## |server IP-ADDRESS extra-params [ offline ] | ## |--------------------------------------------| EOF #patern that recognizes above. my($noeditheadPAT) = qr/\#\#\s+DNSchrony\s+dynamic\s+dns\s+server\s+section\.\s+DO\s+NOT\s+EDIT\s*/; #end of header marker. my($noeditheadend)=<<'EOF'; ## END OF DNSchrony dynamic dns server section. EOF #pattern that matches above. my($noeditheadendPAT)= qr/\#\#\s+END\s+OF\s+DNSchrony\s+dynamic\s+dns\s+server\s+section.\s*/; #array to hold non dns portion of chrony.conf my(@chronyDconf); my($ip); my($rest); my($host); # for each entry in the list of hosts.... open(READIN, "<$listfile") or BadDie("Can not open $listfile"); # read till dynamic patern read save in @chronyDconf while ( ) { my($line) = $_; last if ( m/\A$noeditheadPAT\Z/ ); push(@chronyDconf,$line); } while ( ) { #end loop when end of header encountered last if ( m/\A$noeditheadendPAT/ ); # parse the line giving ip address, extra pamamters, and host #do host comment line first ($host) = m{ \A\#\s* ($hostnamePAT) \s*\z }xio; #no match skip this line. next unless ( $host ); # read next line $_ = ; # parse out ip address extra parameters. ($ip,$rest) = m{ \A \s* server #server comand \s+ ($ipPAT) #ip address (?ixo: \s ) \s* ( (?(?! (?iox: offline )? #skip to offline # \s* #or # \Z ).)* ) (?ixo: \s* (?ixo: offline )? #consume to # \s* \Z ) }xio ; #if failure again. next unless ( $ip ); $rest =~ s/\s*\z//; #remove trail blanks #from parameters # store the data in the list # key is host name value is # array [0] is ip address # [1] is other parameters $host2ip{$host} = [$ip,$rest] ; print "ip=$ip rest=$rest host=$host<\n" if $pedebug; } #read trailing line into @chronyDconf while ( ) { push(@chronyDconf,$_); } close(READIN) or BadDie("can not close $listfile"); #if the add command: # command can be HOST=IPADDRESS OTHER_PARAMETERS # means add the server trust the ip address geven with out a dns lookup # good for when dns is down but we know the ip addres # or # HOST OTHER_PARAMETERS #we lookup the ip address with dns. if ($opts{'a'}) { my($param)= shift; # parse the param is it hostname if ( ($host,$ip) = $param =~ m/\A($hostnamePAT)=($ipPAT)\Z/ ) { printf "ip=$ip host=$host\n" if ($pedebug); } else { $host = $param; # get the ip address $ip = gethostaddr($host); if ( ! isIpAddr($ip) or ! isHostname($host) ) { print "query failed: ", $ip, "host=$host\n" if $pedebug; exit 1; } } printf "ip=$ip host=$host\n" if ($pedebug); # add the server using chronyc my($status) = chronyc("add server $ip $rest"); if ($status) { #chronyc error print "chronyc failed, status=$status\n"; exit 1; } # get rest of arguements $rest = join( ' ', @ARGV); print "rest=$rest\n" if ($pedebug); #save node in hash $host2ip{$host} = [$ip,$rest] ; print "ip=$ip rest=$rest host=$host<\n" if $pedebug; } #delete command if arguement is ip address #just delete it #if a hostname look it up #then delete it. if ($opts{'d'}) { $host = shift; #get host name is it ap address if ( isIpAddr($host) ) { # if ip address my($hostIT); my($found) =0; foreach $hostIT (keys(%host2ip) ) { #search for match if ( $host2ip{$hostIT}[0] eq $host) { $found=1; #record match } } #end of search if ($found) { #if match found my($status) = chronyc("delete $host"); #chronyc if ($status) { #chronyc error print "chronyc failed, status=$status\n"; exit 1; } else { #reiterate foreach $hostIT (keys(%host2ip) ) { if ( $host2ip{$hostIT}[0] eq $host) { delete $host2ip{$hostIT}; #deleting match hosts } } } } } else { #else not ip address #must be hostname if ( ! $host2ip{$host} ) { print "No such host as $host listed\n"; exit 1; } #get ip address $ip=gethostaddr($host); if ( ! isIpAddr($ip) ) { #no ip address print "query failed: ", $ip, "\n" if $pedebug; exit 1; } printf "ip=$ip host=$host\n" if ($pedebug); my($listed_host_ip) = $host2ip{$host}[0]; # get the ip address saved if ( $ip ne $listed_host_ip) { print "Info: listed host ip=>$listed_host_ip". "< is different from DNS ip=>$ip<\n"; $ip = $listed_host_ip; } # delete the server my($status) = chronyc("delete $listed_host_ip\n"); if ($status) { print "chronyc failed, status=$status\n"; exit 1; } #delete table entry delete$host2ip{$host}; } } #update for each host who's dns ip address has changed #delete the old server and add the new. update the record. if ($opts{'u'}) { my($command); my(%prospective); # store new IP address we #are thinking of changing. Log('local0.info', "Now searching for modified DNS entries."); foreach $host (keys(%host2ip)) { #for each listed host my($old_ip) = $host2ip{$host}[0]; #get old ip $rest = $host2ip{$host}[1]; #extra params $ip = gethostaddr($host); #get new ip from dns #if error if ( ! isIpAddr($ip) or ! isHostname($host) ) { print "query failed: ", $ip, "host=$host\n"; Log('local0.err',"query failed: ". $ip . "host=$host"); exit 1; } next if($ip eq $old_ip); #if ip not changed, skip Log('local0.info',"Ip address for $host has changed. Old IP address=". "$old_ip, new IP address=$ip"); # add command to delete old host, add the new. $command = $command . "delete $old_ip\n" . "add server $ip $rest\n"; # we are now thinking about changing this host ip $prospective{$host} = [$ip,$rest]; } # submit all the accumulated chronyc commands if any. if ($command) { $status = chronyc($command); if ($status) { print "chronyc failed, status=$status\n"; Log('local0.err',"query failed: ". $ip . "host=$host"); exit 1; } } else { #if no commands exit exit 0; #because no rewrite of file needed } #copy prospective modifications back into main table. #we now know that all these mods were done with chronyc foreach $host (keys(%prospective)) { my($ip) = $prospective{$host}[0]; $rest = $prospective{$host}[1]; $host2ip{$host} = [$ip,$rest]; } } #starting for each entry we have read in from the old list # add the server in chronyc # this option is seldom used. if ($opts{'s'}) { my($command)=""; foreach $host (keys(%host2ip)) { $command = $command . "add server $host2ip{$host}[0] ". "$host2ip{$host}[1]\n"; } my($status) = chronyc($command); if ($status) { print "chronyc failed, status=$status\n"; exit 1; } } # write out the data file in format #># HOSTNAME #>server IPADDRESS extra parameters [offline] # offline is omitted if -n switch is specified. my(@value); my($such); { # to start out we write to temporary file. (my($writeout) , my($outname)) = mkstemp( "${listfile}.outXXXXXXX"); $outname or BadDie("can not open for $listfile"); # save the chrony.conf part! # and write the DYNAMIC header print $writeout @chronyDconf, $noedithead; # for each entry foreach $host (keys(%host2ip) ){ #write the record # write the comment that indicates the hostname # and the server command. print $writeout "\# $host\nserver $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\n" ; print "server $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\# $host\n" if $pedebug; } #WRITE THE end of dnyamic marker comment print $writeout $noeditheadend; # close the output file which was a temporary file. close($writeout) or BadDie("can not close $outname"); # we now begin a intracate dance to make the the temporary # the main chrony.conf # # if there is a chrony.conf.BAK save it to a temporary. # rename chrony.conf to chrony.conf.BAK # rename the temporary to chrony.conf # if there already was a chrony.conf.BAK, unlink the copy of this. my($backname) = "$listfile\.BAK"; my($backplain) = ( -f $backname ); my($saveback); #if chrony.conf.BAK exists rename to a temporary. if ($backplain ) { $saveback = mktemp("${backname}.bakXXXXXXX"); move($backname,$saveback) or BadDie "unable to move $backname to $savename"; } # rename old chrony.conf to chrony.conf.BAK move($listfile,$backname) or BadDie "unable to move $listfile to $backname"; # rename our output to chrony.conf move($outname,$listfile) or BadDie "unable to move $outname to $listfile"; #if there was a temporary chrony.conf.BAK that we saved to temp #unlink it unlink($saveback) or BadDie "unable to unlink $saveback" if($backplain); } chrony-1.29/contrib/DNSchrony/README0000644000076400007640000001457412200721757016230 0ustar mirosmiros Copyright (C) Paul Elliott 2002 DNSchrony.pl version -2.0 Problem: If you look at the list of secondary NTP servers: http://www.eecis.udel.edu/~mills/ntp/clock2.htm you will find statements like this: "Note: IP addresses are subject to change; please use DNS" These servers represent a problem for chrony. Chrony is a program designed to work on hosts with an intermittent connection to the internet. Often no DNS is available when chrony starts. As chrony is currently designed, chronyd never sees a DNS host name. If a user specifies one when using chronyc's "add server" command, the DNS lookup is done by chronyc and an IP address is passed to chronyd. One can imagine I suppose, a redesign to chrony in which chronyd keeps track of DNS changes. But this has problems, all the time chronyd is fooling around with DNS, it would not be keeping track of its prime function, what the clocks and NTP servers are saying. This could result in poorer performance. Or perhaps you say that chronyd should be multi threaded. One thread to fool with DNS and another to keep track of time. But this introduces a great deal of complexity, and complexity is the enemy of elegant robust code. Besides, Richard probably has better things to do. I have attempted to address this problem with a humble perl script, which I now release under the GPL: DNSchrony.pl PLEA FOR HELP FROM EXPERIENCED PERL HACKERS. Please go thru the code and find errors and improvements. I am not quite an polished perl hacker. Please fix bugs and make improvements. It needs better documentation. Someone who knows how, put in some POD. END OF PLEA Philosophy of DNSchrony.pl: keep a list of servers that use DNS. From time to time, hopefully when DNS is up, go thru the list lookup all the hostnames and see if any ip addresses have changed. If any have changed, update our list and do chronyc "delete" and "add server" commands so that chronyd now talks to the right NTP server. Additional nuance: keep the list in /etc/chrony.conf in the form of comments starting with "#" and "server" commands legal in a chrony.conf file. Format of a list entry: # hostname server IP-ADDRESS extra server parameters These entries are delimited by special comments that allow DNSchrony.pl to find them and also tell humans not to mess with them. Example of such a section of a chrony.conf file: dumpdir /var/log/chrony rtcfile /etc/chrony.rtc ## DNSchrony dynamic dns server section. DO NOT EDIT ## per entry FORMAT: ## |--------------------------------------------| ## |#HOSTNAME | ## |server IP-ADDRESS extra-params [ offline ] | ## |--------------------------------------------| # tock.greyware.com server 208.14.208.44 minpoll 5 maxpoll 10 maxdelay 0.4 offline # tick.greyware.com server 208.14.208.19 minpoll 5 maxpoll 10 maxdelay 0.4 offline # ntppub.tamu.edu server 128.194.254.9 minpoll 5 maxpoll 10 maxdelay 0.4 offline ## END OF DNSchrony dynamic dns server section. This allows the list of dynamic DNS servers to be preserved when chronyd is stoped/started. All servers that do not have ip addresses subject to change should be put in the regular part of chrony.conf as described in the chrony documentation. Security philosophy: DNSchrony does no security checking but relies on other security factors. Users without the privilege to modify /etc/chrony.conf and the directory /etc will be unable to use DNSchrony to do so, because of file protections. DNSchrony passes thru passwords to chronyc. Users that do not know the correct chronyc password will be unable to get chronyd do do anything. Thus, DNSchrony passes the buck to these other security features. INSTALLATION: copy the files: DNSchronyADD DNSchronyUPDATE DNSchronyDELETE DNSchrony.pl to /usr/local/bin. Backup the file /etc/chrony.conf leave hosts with static ip addresses in this file. DNSchrony uses the following perl modules. See that they are installed. Get them from CPAN if needed. Net::DNS, Tie::Syslog, Getopt::Std, Socket, File. Cause DNSchronyUPDATE bash script to run from time to time when DNS is working. If you have a dialup, one way to do this would be to modify your /etc/ppp/ip-up.local file as follows: cat </dev/null 2>&1 & Since this file contains the chronyc password you will want to set the file permissions so that just everybody will not be able to read it. But you already did that when you put in the chronyc command. Any other way to make DNSchronyUPDATE run perodicly when DNS is up will also work. To add a server with a varying IP address one could run: /usr/local/bin/DNSchronyADD mysecret tock.greyware.com or if you want to specify different server parameters you could say: /usr/local/bin/DNSchronyADD mysecret tock.greyware.com "minpoll 10 maxpoll 20 maxdelay 0.8" The DNSchronyADD's default for these parameters is: "minpoll 5 maxpoll 10 maxdelay 0.4" values that are often shown as examples in the chrony documentation. If DNS is not running now but you know the IP address, you can say: /usr/local/bin/DNSchronyADD mysecret tock.greyware.com=208.14.208.44 Of course, the IP address will be checked next time DNSchronyUPDATE runs. To delete dynamic DNS a server: /usr/local/bin/DNSchronyDELETE mysecret tock.greyware.com To change parameters delete and re-add. Of course, in all of the above "mysecret" is your chronyc password which SHOULD NOT BE "mysecret". ---------------------------------------------- DNSchrony.pl is covered by the GPL # Copyright (C) Paul Elliott 2002 # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # SEE COPYING FOR DETAILS chrony-1.29/contrib/DNSchrony/DNSchronyUPDATE0000755000076400007640000000014112200721757020031 0ustar mirosmiros#!/usr/bin/bash # $1 is chrony password. CHRONYPASSWORD=$1 \ /usr/local/bin/DNSchrony.pl -ulS chrony-1.29/contrib/DNSchrony/DNSchronyADD0000755000076400007640000000067512200721757017453 0ustar mirosmiros#!/usr/bin/bash # $1 is chrony password. # $2 is hostname to add or hostname=ipaddres # $3-$9 is rest of extra server parameters FIRST="$1" HOST="$2" shift 2 #remaining parameters a the other paramaters to server command #excluding "offline" ARGS="$*" #if none use default taken from chrony documentation. DEF="minpoll 5 maxpoll 10 maxdelay 0.4" DARGS=${ARGS:-$DEF} CHRONYPASSWORD=$FIRST \ /usr/local/bin/DNSchrony.pl -a "$HOST" "$DARGS" chrony-1.29/contrib/ken_gillett_10000644000076400007640000000363312200721757016137 0ustar mirosmiros#!/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-1.29/contrib/andrew_bishop_10000644000076400007640000001333012200721757016455 0ustar mirosmirosFrom 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-1.29/contrib/stephan_boettcher_10000644000076400007640000000720312200721757017334 0ustar mirosmirosFrom 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-1.29/rtc.c0000644000076400007640000001146412200721757012770 0ustar mirosmiros/* 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 "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); void (*time_pre_init)(void); void (*time_init)(void (*after_hook)(void*), void *anything); void (*start_measurements)(void); int (*write_parameters)(void); int (*get_report)(RPT_RTC_Report *report); int (*trim)(void); } 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 }; /* ================================================== */ void RTC_Initialise(void) { char *file_name; int ok; /* This is how we tell whether the user wants to load the RTC driver, if he is on a machine where it is an option. */ file_name = CNF_GetRtcFile(); if (file_name) { if (CNF_GetRTCSync()) { LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync"); } if (driver.init) { if ((driver.init)()) { ok = 1; } else { ok = 0; } } else { ok = 0; } if (ok) { driver_initialised = 1; } else { driver_initialised = 0; LOG(LOGS_ERR, LOGF_Rtc, "Real time clock not supported on this operating system"); } } else { driver_initialised = 0; } } /* ================================================== */ void RTC_Finalise(void) { if (driver.fini) { (driver.fini)(); } } /* ================================================== */ /* Start the processing to get a single measurement from the real time clock, and use it to trim the system time, based on knowing the drift rate of the RTC and the error the last time we set it. The TimePreInit routine has already run, so we can be sure that the trim required is not *too* large. We are called with a hook to a function to be called after the initialisation is complete. We also call this if we cannot do the initialisation. */ void RTC_TimeInit(void (*after_hook)(void *), void *anything) { if (driver_initialised) { (driver.time_init)(after_hook, anything); } else { LOG(LOGS_ERR, LOGF_Rtc, "Can't initialise from real time clock, driver not loaded"); (after_hook)(anything); } } /* ================================================== */ /* Do an initial read of the RTC and set the system time to it. This is analogous to what /sbin/clock -s -u would do on Linux. */ void RTC_TimePreInit(void) { if (driver.time_pre_init) { (driver.time_pre_init)(); } } /* ================================================== */ /* Start the RTC measurement process */ void RTC_StartMeasurements(void) { if (driver_initialised) { (driver.start_measurements)(); } /* Benign if driver not present */ } /* ================================================== */ /* Write RTC information out to RTC file. Return 0 for success, 1 if RTC driver not running, or 2 if the file cannot be written. */ int RTC_WriteParameters(void) { if (driver_initialised) { return (driver.write_parameters)(); } else { return RTC_ST_NODRV; } } /* ================================================== */ int RTC_GetReport(RPT_RTC_Report *report) { if (driver_initialised) { return (driver.get_report)(report); } else { return 0; } } /* ================================================== */ int RTC_Trim(void) { if (driver_initialised) { return (driver.trim)(); } else { return 0; } } /* ================================================== */ chrony-1.29/ntp_core.h0000644000076400007640000000756012200721757014020 0ustar mirosmiros/* 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); /* This routine is called when a new packet arrives off the network, and it relates to a source we have an ongoing protocol exchange with */ extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, 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, 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); #endif /* GOT_NTP_CORE_H */ chrony-1.29/md5.h0000644000076400007640000000546012200721757012671 0ustar mirosmiros/* *********************************************************************** ** 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-1.29/sys_linux.h0000644000076400007640000000251512200721757014237 0ustar mirosmiros/* 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-1.29/regress.h0000644000076400007640000001021312200721757013646 0ustar mirosmiros/* 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 /* 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-1.29/sched.h0000644000076400007640000000624312200721757013272 0ustar mirosmiros/* 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-1.29/faq.txt0000644000076400007640000003356412200721757013351 0ustar mirosmiros@@PROLOGUE Frequently asked questions

This is a set of questions and answers to common problems and issues.

As we receive more emails about the software, we will add new questions to this page.


The developers can be reached via the chrony-dev mailing list. See question 1.4. for details.



@@ENDPROLOGUE S: Administrative issues Q: Where can I get chrony source code? Tarballs are available via the Download link on the Chrony Web site. For the current development from the developers' version control system see the Git link on the Web site. Q: Are there any packaged versions of chrony? We are aware of packages for Debian, Fedora, Gentoo, Mandriva, Slackware, and Ubuntu. We are not involved with how these are built or distributed. Q: Where is the home page? It is currently at http://chrony.tuxfamily.org/. Q: Is there a mailing list? Yes, it's currently at chrony-users@chrony.tuxfamily.org. There is a low-volume list called chrony-announce which is just for announcements of new releases or similar matters of high importance. You can join the lists by sending a message with the subject subscribe to chrony-users-request@chrony.tuxfamily.org or chrony-announce-request@chrony.tuxfamily.org respectively. For those who want to contribute to the development of chrony, there is a developers' mailing list. You can subscribe by sending mail with the subject subscribe to chrony-dev-request@chrony.tuxfamily.org. Q: What licence is applied to chrony? Starting from version 1.15, chrony is licensed under the GNU General Public License, Version 2. Versions prior to 1.15 were licensed under a custom BSD-like license. S: Chrony compared to other programs Q: How does chrony compare to xntpd? If your computer is permenently connected, or connected for long periods (that is, for the several hours it takes xntpd to settle down), or you need to support hardware reference clocks to your computer, then xntpd will work fine. Apart from not supporting hardware clocks, chrony will work fine too. If your computer connects to the 'net for 5 minutes once a day (or something like that), or you turn your Linux computer off when you're not using it, or you want to use NTP on an isolated network with no hardware clocks in sight, chrony will work much better for you. The reason I wrote chrony was that I could not get xntpd to do anything sensible on my PC at home, which is connected to the 'net for about 5 minutes once or twice a day, mainly to upload/download email and news. Nowadays it is also turned off for 22-23 hours a day, when not in use. I wanted a program which would : - slew the time to correct it when I go online and NTP servers become visible - determine the rate at which the computer gains or loses time and use this information to keep it reasonably correct between connects to the 'net. This has to be done using a method that does not care about the intermittent availability of the references or the fact the computer is turned off between groups of measurements.. - maintain the time across reboots, by working out the error and drift rate of the computer's real-time clock and using this information to set the system clock correctly at boot up. (In the last few months, it became impossible for me to leave my computer powered permanently.) Also, when working with isolated networks with no true time references at all, I found xntpd gave me no help with managing the local clock's gain/loss rate on the NTP master node (which I set from my watch). I added some automated support in chrony to deal with this. S: Selection of NTP servers Q: I have several computers on a LAN. Should I make one the master, or make them all clients of an external server? I think the best configuration is to make one computer the master, with the others as clients of it. Add a 'local' directive to the master's chrony.conf file. This configuration will be better because * the load on the external connection is less * the load on the external NTP server(s) is less * if your external connection goes down, the computers on the LAN will maintain a common time with each other. S: Addressing issues Q: I get the following error message : "Could not get IP adress for localhost" Add a line like the following to your /etc/hosts file 127.0.0.1 localhost Q: I have problems if I put the names of my NTP servers in the chrony.conf file. If you have no connection to the Internet at boot time, chrony won't be able to turn the names into IP addresses when it starts. There seem to be 2 solutions: 1. Put the numeric IP addresses in the chrony.conf file or 2. Put the server->IP address mappings in your /etc/hosts file and ensure that /etc/host.conf reads 'order hosts,bind'. The problem is that chronyd (currently) isn't designed in a way that allows hostname->IP address lookups during normal operation. I hope to work on this problem very soon. S: My computer is not synchronising. This is the most common problem. There are a number of reasons, see the following questions. Q: Behind a firewall? If there is a firewall between you and the NTP server you're trying to use, the packets may be blocked. Try using a tool like etherfind or tcpdump to see if you're getting responses from the server. If you have an external modem, see if the receive light blinks straight after the transmit light (when the link is quiet apart from the NTP traffic.) Try adding 'log measurements' to the chrony.conf file and look in the measurements.log file after chrony has been running for a short period. See if any measurements appear. Most people run chronyd on the firewall itself, to avoid all issues of UDP packet forwarding and/or masquerading. Q: Do you have a non-permanant (i.e. intermittent) Internet connection? Check that you're using chronyc's 'online' and 'offline' commands appropriately. Again, check in measurements.log to see if you're getting any data back from the server. Q: In measurements.log, do the '7' and '8' flag columns always show zero? Do you have a 'local stratum X' directive in the chrony.conf file? If X is lower than the stratum of the server you're trying to use, this situation will arise. You should always make X quite high (e.g. 10) in this directive. S: Issues with chronyc Q: I keep getting the error '510 No command access from this host --- Reply not authenticated'. Make sure that the chrony.conf file (on the computer where chronyd is running) has a 'cmdallow' entry for the computer you are running chronyc on. This shouldn't be necessary for localhost, but some people still seem to need an entry like 'cmdallow 127.0.0.1'. (It would be good to understand why problem only affects some people). Q: I cannot log in from chronyc to carry out privileged tasks. This is the second most common problem. Perhaps your /etc/chrony.keys file is badly formatted. Make sure that the final line has a line feed at the end, otherwise the key on that line will work as though the last character is missing. (Note, this bug was fixed in version 1.16.) Q: When I enter a command and hit <Return>, chronyc hangs This probably means that chronyc cannot communicate with chronyd. Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, 'ps -auxw') to see if it's running. Or try 'netstat -a' and see if the ports 123/udp and 323/udp are listening. If chronyd is not running, you may have a problem with the way you are trying to start it (e.g. at boot time). Perhaps you have a firewall set up in a way that blocks packets on port 323/udp. You need to amend the firewall configuration in this case. Q: Is the chronyc<->chronyd protocol documented anywhere? Only by the source code :-) See cmdmon.c (chronyd side) and client.c (chronyc side). S: Real-time clock issues. Q: What is the real-time clock (RTC)? This is the clock which keeps the time even when your computer is turned off. It works with 1 second resolution. chronyd can monitor the rate at which the real-time clock gains or loses time, and compensate for it when you set the system time from it at the next reboot. See the documentation for details. Q: I want to use chronyd's real-time clock support. Must I disable hwclock? The hwclock program is often set-up by default in the boot and shutdown scripts with many Linux installations. If you want to use chronyd's real-time clock support, the important thing is to disable hwclock in the 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. Q: I just keep getting the '513 RTC driver not running' message For the real time clock support to work, you need the following three things: * a kernel that is supported (e.g. 2.2 onwards) * enhanced RTC support compiled into the kernel * an 'rtcfile' directive in your chrony.conf file. S: Microsoft Windows Q: Does chrony support Windows? No. The chronyc program (the command-line client used for configuring chronyd while it is running) has been successfully built and run under Cygwin in the past. chronyd is not portable, because part of it is very system-dependent. It needs adapting to work with Windows' equivalent of the adjtimex() call, and it needs to be made to work as an NT service. Q: Are there any plans to support Windows? We have no plans to do this. Anyone is welcome to pick this work up and contribute it back to the project. Q: What alternative NTP clients are there for Windows? Some of the names we've seen mentioned are - Automachron - NetTime (nettime.sourceforge.net) S: NTP-specific issues Q: Can chrony be driven from broadcast NTP servers? No. I remember looking at how they worked when I was first writing chrony. Since the 'target market' then was dial-up systems, broadcast packets were not relevant so I didn't bother working out how to deal with the complexities of doing the delay estimation. I no longer have root access to a LAN environment to develop and test broadcast server support. Neither have I the time to work on this. I would be very happy to accept a patch from anyone who can develop, test and debug the necessary changes! Q: Can chronyd transmit broadcast NTP packets (e.g. to synchronise other computers on a private LAN)? Yes. Starting from version 1.17, chrony has this capability. Q: Can chrony keep the system clock a fixed offset away from real time? I have not experimented much, but I don't believe this would be possible as the program currently stands. Q: What happens if the network connection is dropped without using chronyc's 'offline' command first? In this case chronyd will keep trying to access the server(s) that it thinks are online. Eventually it will decide that they are unreachable and no longer consider itself synchronised to them. If you have other computers on your LAN accessing the computer that is affected this way, they too will become 'unsynchronised', unless you have the 'local' directive set up on the master computer. The 'auto_offline' option to the 'server' entry in the chrony.conf file may be useful to avoid this situation. S: Development Q: Can I get the source via git from anywhere? Yes. See the Git link at http://chrony.tuxfamily.org for information. S: Linux-specific issues Q: Why does the source code include kernel header files? The program needs to see the definitions of structures used to interact with the real time clock (via /dev/rtc) and with the adjtimex() system call. Sadly this has led to a number of compilation problems with newer kernels which have been increasingly hard to fix in a way that makes the code compilable on all Linux kernel versions. Hopefully the situation will not deteriorate further with future kernel versions. Q: I get "Could not open /dev/rtc, Device or resource busy" in my syslog file. Check that you haven't accidentally got two copies of chronyd running (perhaps defined in different start-up scripts.) S: Solaris-specific issues Q: On Solaris 2.8, I get an error message about not being able to open kvm to change dosynctodr. (The dosynctodr variable controls whether Solaris couples the equivalent of its BIOS clock into its system clock at regular intervals). The Solaris port of chrony was developed in the Solaris 2.5 era. Some aspect of the Solaris kernel has changed which prevents the same technique working. I no longer have root access to any Solaris machines to work on this, and am reliant on somebody developing the patch and testing it. A good starting point would be to see if xntpd has been modified to work for Solaris 2.8. @@EPILOGUE
Back to the author's main page @@ENDEPILOGUE chrony-1.29/ntp.h0000644000076400007640000000467412200721757013013 0ustar mirosmiros/* 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; #define MAX_NTP_AUTH_DATA_LEN 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; NTP_int32 auth_keyid; uint8_t auth_data[MAX_NTP_AUTH_DATA_LEN]; } NTP_Packet; /* We have to declare a buffer type to hold a datagram read from the network. Even though we won't be using them (yet?!), this must be large enough to hold NTP control messages. */ /* Define the maximum number of bytes that can be read in a single message. (This is cribbed from ntp.h in the xntpd source code). */ #define MAX_NTP_MESSAGE_SIZE (468+12+16+4) typedef union { NTP_Packet ntp_pkt; uint8_t arbitrary[MAX_NTP_MESSAGE_SIZE]; } ReceiveBuffer; #define NTP_NORMAL_PACKET_SIZE offsetof(NTP_Packet, auth_keyid) #endif /* GOT_NTP_H */ chrony-1.29/pktlength.h0000644000076400007640000000243312200721757014201 0ustar mirosmiros/* 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_ReplyLength(CMD_Reply *r); #endif /* GOT_PKTLENGTH_H */ chrony-1.29/ntp_sources.c0000644000076400007640000004021412200721757014537 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2011-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. * ********************************************************************** ======================================================================= 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 "ntp_sources.h" #include "ntp_core.h" #include "util.h" #include "logging.h" #include "local.h" #include "memory.h" #include "nameserv.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 */ } SourceRecord; #define N_RECORDS 256 /* Fixed size table, because we use a hard coded hash algorithm. It is rather unlikely we would have anything approaching this number of sources. */ static SourceRecord records[N_RECORDS]; static int n_sources; /* The largest number of sources we want to have stored in the hash table */ #define MAX_SOURCES 64 /* Source with unknown address (which may be resolved later) */ struct UnresolvedSource { char *name; int port; NTP_Source_Type type; SourceParameters params; struct UnresolvedSource *next; }; static struct UnresolvedSource *unresolved_sources = NULL; static int resolving_interval = 0; static SCH_TimeoutID resolving_id; /* ================================================== */ /* Forward prototypes */ static void slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything); /* ================================================== */ /* Flag indicating whether module is initialised */ static int initialised = 0; /* ================================================== */ void NSR_Initialise(void) { int i; for (i=0; i Neither IP nor port matched, empty slot returned found = 1 => Only IP matched, port doesn't match found = 2 => Both IP and port matched. It is assumed that there can only ever be one record for a particular IP address. (If a different port comes up, it probably means someone is running ntpdate -d or something). Thus, if we match the IP address we stop the search regardless of whether the port number matches. */ static void find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found) { unsigned long hash; unsigned long ip; unsigned short port; uint8_t *ip6; assert(N_RECORDS == 256); switch (remote_addr->ip_addr.family) { case IPADDR_INET6: ip6 = remote_addr->ip_addr.addr.in6; ip = (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: ip = remote_addr->ip_addr.addr.in4; break; default: *found = *slot = 0; return; } port = remote_addr->port; /* Compute hash value just by xor'ing the 4 bytes of the address together */ hash = ip ^ (ip >> 16); hash = (hash ^ (hash >> 8)) & 0xff; while (records[hash].remote_addr && UTI_CompareIPs(&records[hash].remote_addr->ip_addr, &remote_addr->ip_addr, NULL)) { hash++; if (hash == 256) hash = 0; } if (records[hash].remote_addr) { if (records[hash].remote_addr->port == port) { *found = 2; } else { *found = 1; } *slot = hash; } else { *found = 0; *slot = hash; } } /* ================================================== */ /* Procedure to add a new source */ NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params) { int slot, found; assert(initialised); #if 0 LOG(LOGS_INFO, LOGF_NtpSources, "IP=%s port=%d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); #endif /* Find empty bin & check that we don't have the address already */ find_slot(remote_addr, &slot, &found); if (found) { return NSR_AlreadyInUse; } else { if (n_sources == MAX_SOURCES) { return NSR_TooManySources; } else if (remote_addr->ip_addr.family != IPADDR_INET4 && remote_addr->ip_addr.family != IPADDR_INET6) { return NSR_InvalidAF; } else { n_sources++; records[slot].data = NCR_GetInstance(remote_addr, type, params); /* Will need params passing through */ records[slot].remote_addr = NCR_GetRemoteAddress(records[slot].data); return NSR_Success; } } } /* ================================================== */ static void resolve_sources(void *arg) { NTP_Remote_Address address; struct UnresolvedSource *us, **i; DNS_Status s; memset(&address.local_ip_addr, 0, sizeof (address.local_ip_addr)); DNS_Reload(); for (i = &unresolved_sources; *i; ) { us = *i; s = DNS_Name2IPAddress(us->name, &address.ip_addr); if (s == DNS_TryAgain) { i = &(*i)->next; continue; } else if (s == DNS_Success) { address.port = us->port; NSR_AddSource(&address, us->type, &us->params); } else { LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name); } *i = us->next; Free(us->name); Free(us); } if (unresolved_sources) { /* Try again later */ if (resolving_interval < 9) resolving_interval++; resolving_id = SCH_AddTimeoutByDelay(7 * (1 << resolving_interval), resolve_sources, NULL); } else { resolving_interval = 0; } } /* ================================================== */ /* Procedure to add a new server or peer source, but instead of an IP address only a name is provided */ void NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParameters *params) { struct UnresolvedSource *us, **i; us = MallocNew(struct UnresolvedSource); us->name = name; us->port = port; us->type = type; us->params = *params; us->next = NULL; for (i = &unresolved_sources; *i; i = &(*i)->next) ; *i = us; if (!resolving_interval) { resolving_interval = 2; resolving_id = SCH_AddTimeoutByDelay(7 * (1 << resolving_interval), resolve_sources, NULL); } } /* ================================================== */ void NSR_ResolveSources(void) { /* Try to resolve unresolved sources now */ if (resolving_interval) { SCH_RemoveTimeout(resolving_id); resolving_interval--; resolve_sources(NULL); } } /* ================================================== */ /* 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 i, slot, found; SourceRecord temp_records[N_RECORDS]; assert(initialised); find_slot(remote_addr, &slot, &found); if (!found) { return NSR_NoSuchSource; } n_sources--; records[slot].remote_addr = NULL; NCR_DestroyInstance(records[slot].data); /* Rehash the table to make sure there are no broken probe sequences. This is costly, but it's not expected to happen frequently. */ memcpy(temp_records, records, sizeof (records)); for (i = 0; i < N_RECORDS; i++) { records[i].remote_addr = NULL; } for (i = 0; i < N_RECORDS; i++) { if (!temp_records[i].remote_addr) continue; find_slot(temp_records[i].remote_addr, &slot, &found); assert(!found); records[slot].remote_addr = temp_records[i].remote_addr; records[slot].data = temp_records[i].data; } return NSR_Success; } /* ================================================== */ /* 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, int length) { int slot, found; assert(initialised); #if 0 LOG(LOGS_INFO, LOGF_NtpSources, "from (%s,%d) at %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_TimevalToString(now)); #endif find_slot(remote_addr, &slot, &found); if (found == 2) { /* Must match IP address AND port number */ NCR_ProcessKnown(message, now, now_err, records[slot].data, length); } else { NCR_ProcessUnknown(message, now, now_err, remote_addr, length); } } /* ================================================== */ static void slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything) { int i; for (i=0; iip_addr), dfreq, doffset); #endif NCR_SlewTimes(records[i].data, cooked, dfreq, doffset); } } } /* ================================================== */ int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address) { int i; int any; NSR_ResolveSources(); any = 0; for (i=0; ifamily == IPADDR_UNSPEC || !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) { any = 1; NCR_TakeSourceOnline(records[i].data); } } } if (address->family == IPADDR_UNSPEC) { struct UnresolvedSource *us; for (us = unresolved_sources; us; us = us->next) { any = 1; us->params.online = 1; } } return any; } /* ================================================== */ int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address) { int i, any, syncpeer; any = 0; syncpeer = -1; for (i=0; ifamily == IPADDR_UNSPEC || !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) { any = 1; if (NCR_IsSyncPeer(records[i].data)) { syncpeer = i; continue; } NCR_TakeSourceOffline(records[i].data); } } } /* Take sync peer offline as last to avoid reference switching */ if (syncpeer >= 0) { NCR_TakeSourceOffline(records[syncpeer].data); } if (address->family == IPADDR_UNSPEC) { struct UnresolvedSource *us; for (us = unresolved_sources; us; us = us->next) { any = 1; us->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(records[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(records[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(records[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(records[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(records[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(records[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(records[slot].data, new_poll_target); return 1; } } /* ================================================== */ int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address) { int i; int any; any = 0; for (i=0; ifamily == IPADDR_UNSPEC || !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) { any = 1; NCR_InitiateSampleBurst(records[i].data, n_good_samples, n_total_samples); } } } return any; } /* ================================================== */ /* The ip address is assumed to be completed on input, that is how we identify the source record. */ void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now) { NTP_Remote_Address rem_addr; int slot, found; rem_addr.ip_addr = report->ip_addr; rem_addr.port = 0; find_slot(&rem_addr, &slot, &found); if (found) { NCR_ReportSource(records[slot].data, report, now); } else { report->poll = 0; report->latest_meas_ago = 0; } } /* ================================================== */ void NSR_GetActivityReport(RPT_ActivityReport *report) { int i; struct UnresolvedSource *us; report->online = 0; report->offline = 0; report->burst_online = 0; report->burst_offline = 0; for (i=0; ionline, &report->offline, &report->burst_online, &report->burst_offline); } } report->unresolved = 0; for (us = unresolved_sources; us; us = us->next) { report->unresolved++; } } /* ================================================== */ chrony-1.29/sys_linux.c0000644000076400007640000010513012200721757014227 0ustar mirosmiros/* 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 * * 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_LINUXCAPS #include #include #include #include #include #endif #include "localp.h" #include "sys_linux.h" #include "sched.h" #include "util.h" #include "conf.h" #include "logging.h" #include "wrap_adjtimex.h" static long current_tick; /* This is the value of tick, in seconds, including the current vernier frequency term */ static double current_total_tick; /* This is the uncompensated system tick value */ static int nominal_tick; /* This is the scaling required to go between absolute ppm and the scaled ppm used as an argument to adjtimex. Because chronyd is to an extent 'closed loop' maybe it doesn't matter if this is wrongly determined, UNLESS the system's ppm error is close to a multiple of HZ, in which case the relationship between changing the frequency and changing the value of 'tick' will be wrong. This would (I imagine) cause the system to thrash between two states. However..., if this effect was not corrected, and the system is left offline for a long period, a substantial error would build up. e.g. with HZ==100, the correction required is 128/128.125, giving a drift of about 84 seconds per day). */ static double freq_scale; /* The HZ value from the kernel header file (may be over-ridden from config file, e.g. if chronyd binary is moved to a box whose kernel was built with a different HZ value). */ static int hz; static double dhz; /* And dbl prec version of same for arithmetic */ /* ================================================== */ /* The operating system kernel version */ static int version_major; static int version_minor; static int version_patchlevel; /* Flag indicating whether adjtimex() returns the remaining time adjustment or not. If not we have to read the outstanding adjustment by setting it to zero, examining the return value and setting the outstanding adjustment back again. */ static int have_readonly_adjtime; /* Flag indicating whether kernel supports PLL in nanosecond resolution. If supported, it will be used instead of adjtime() for very small adjustments. */ static int have_nanopll; /* Flag indicating whether adjtimex() can step the clock */ static int have_setoffset; /* ================================================== */ static void handle_end_of_slew(void *anything); /* ================================================== */ inline static long our_round(double x) { long y; if (x > 0.0) y = x + 0.5; else y = x - 0.5; return y; } /* ================================================== */ /* Amount of outstanding offset to process */ static double offset_register; /* Flag set true if an adjtime slew was started and still may be running */ static int slow_slewing; /* Flag set true if a PLL nano slew was started and still may be running */ static int nano_slewing; /* Flag set true if a fast slew (one done by altering tick) is being run at the moment */ static int fast_slewing; /* The amount by which the fast slew was supposed to slew the clock */ static double fast_slew_wanted; /* The value programmed into the kernel's 'tick' variable whilst slewing a large offset */ static long slewing_tick; /* The timeval (raw) at which a fast slew was started. We need to know this for two reasons. First, if we want to change the frequency midway through, we will want to abort the slew and return the unprocessed portion to the offset register to start again later. Second, when the end of the slew expires, we need to know precisely how long we have been slewing for, so that we can negate the excess and slew it back the other way. */ static struct timeval slew_start_tv; /* This is the ID returned to use by the scheduler's timeout handler. We need this if we subsequently wish to abort a slew, because we will have to dequeue the timeout */ static SCH_TimeoutID slew_timeout_id; /* The adjustment that we apply to 'tick', in seconds, whilst applying a fast slew */ static double delta_total_tick; /* Maximum length of one fast slew */ #define MAX_FASTSLEW_TIMEOUT (3600 * 24 * 7) /* Max amount of time that we wish to slew by using adjtime (or its equivalent). If more than this is outstanding, we alter the value of tick instead, for a set period. Set this according to the amount of time that a dial-up clock might need to be shifted assuming it is resync'ed about once per day. (TBC) */ #define MAX_ADJUST_WITH_ADJTIME (0.2) /* Max amount of time that should be adjusted by kernel PLL */ #define MAX_ADJUST_WITH_NANOPLL (0.5) /* The amount by which we alter 'tick' when doing a large slew */ static int slew_delta_tick; /* The maximum amount by which 'tick' can be biased away from 'nominal_tick' (sys_adjtimex() in the kernel bounds this to 10%) */ static int max_tick_bias; /* The latest time at which system clock may still be slewed by previous adjtime() call and maximum offset correction error it can cause */ static struct timeval slow_slew_error_end; static int slow_slew_error; /* Timeval at which the latest nano PLL adjustment was started and maximum offset correction error it can cause */ static struct timeval nano_slew_error_start; static int nano_slew_error; /* The latest time at which 'tick' in kernel may be actually updated and maximum offset correction error it can cause */ static struct timeval fast_slew_error_end; static double fast_slew_error; /* The rate at which frequency and tick values are updated in kernel. */ static int tick_update_hz; #define MIN_PLL_TIME_CONSTANT 0 #define MAX_PLL_TIME_CONSTANT 10 /* PLL time constant used when adjusting offset by PLL */ static long pll_time_constant; /* Suggested offset correction rate (correction time * offset) */ static double correction_rate; /* Kernel time constant shift */ static int shift_pll; /* ================================================== */ /* These routines are used to estimate maximum error in offset correction */ static void update_slow_slew_error(int offset) { struct timeval now, newend; if (offset == 0 && slow_slew_error == 0) return; if (gettimeofday(&now, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } if (offset < 0) offset = -offset; /* assume 500ppm rate and one sec delay, plus 10 percent for fast slewing */ UTI_AddDoubleToTimeval(&now, (offset + 999) / 500 * 1.1, &newend); if (offset > 500) offset = 500; if (slow_slew_error > offset) { double previous_left; UTI_DiffTimevalsToDouble(&previous_left, &slow_slew_error_end, &now); if (previous_left > 0.0) { if (offset == 0) newend = slow_slew_error_end; offset = slow_slew_error; } } slow_slew_error = offset; slow_slew_error_end = newend; } static double get_slow_slew_error(struct timeval *now) { double left; if (slow_slew_error == 0) return 0.0; UTI_DiffTimevalsToDouble(&left, &slow_slew_error_end, now); return left > 0.0 ? slow_slew_error / 1e6 : 0.0; } static void update_nano_slew_error(long offset, int new) { struct timeval now; double ago; if (offset == 0 && nano_slew_error == 0) return; /* maximum error in offset reported by adjtimex */ offset /= (1 << (shift_pll + pll_time_constant)) - (new ? 0 : 1); if (offset < 0) offset = -offset; if (new || nano_slew_error_start.tv_sec > 0) { if (gettimeofday(&now, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } } /* When PLL offset is newly set, use the maximum of the old and new error. Otherwise use the minimum, but only when the last update is older than 1.1 seconds to be sure the previous adjustment is already gone. */ if (!new) { if (nano_slew_error > offset) { if (nano_slew_error_start.tv_sec == 0) { nano_slew_error = offset; } else { UTI_DiffTimevalsToDouble(&ago, &now, &nano_slew_error_start); if (ago > 1.1) { nano_slew_error_start.tv_sec = 0; nano_slew_error = offset; } } } } else { if (nano_slew_error < offset) nano_slew_error = offset; nano_slew_error_start = now; } } static double get_nano_slew_error(void) { if (nano_slew_error == 0) return 0.0; return nano_slew_error / 1e9; } static void update_fast_slew_error(struct timeval *now) { double max_tick; max_tick = current_total_tick + (delta_total_tick > 0.0 ? delta_total_tick : 0.0); UTI_AddDoubleToTimeval(now, 1e6 * max_tick / nominal_tick / tick_update_hz, &fast_slew_error_end); fast_slew_error = fabs(1e6 * delta_total_tick / nominal_tick / tick_update_hz); } static double get_fast_slew_error(struct timeval *now) { double left; if (fast_slew_error == 0.0) return 0.0; UTI_DiffTimevalsToDouble(&left, &fast_slew_error_end, now); if (left < -10.0) fast_slew_error = 0.0; return left > 0.0 ? fast_slew_error : 0.0; } /* ================================================== */ /* Select PLL time constant according to the suggested correction rate. */ static long get_pll_constant(double offset) { long c; double corr_time; if (offset < 1e-9) return MIN_PLL_TIME_CONSTANT; corr_time = correction_rate / offset; for (c = MIN_PLL_TIME_CONSTANT; c < MAX_PLL_TIME_CONSTANT; c++) if (corr_time < 1 << (c + 1 + shift_pll)) break; return c; } /* ================================================== */ /* This routine stops a fast slew, determines how long the slew has been running for, and consequently how much adjustment has actually been applied. It can be used both when a slew finishes naturally due to a timeout, and when a slew is being aborted. */ static void stop_fast_slew(void) { struct timeval T1; double fast_slew_done; double slew_duration; /* Should never get here unless this is true */ assert(fast_slewing); /* Now set the thing off */ if (gettimeofday(&T1, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } if (TMX_SetTick(current_tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } fast_slewing = 0; UTI_DiffTimevalsToDouble(&slew_duration, &T1, &slew_start_tv); /* Compute the dispersion we have introduced by changing tick this way. We handle this by adding dispersion to all statistics held at higher levels in the system. */ update_fast_slew_error(&T1); lcl_InvokeDispersionNotifyHandlers(fast_slew_error); fast_slew_done = delta_total_tick * slew_duration / (current_total_tick + delta_total_tick); offset_register += (fast_slew_wanted + fast_slew_done); } /* ================================================== */ /* This routine reschedules fast slew timeout according to the current frequency and offset */ static void adjust_fast_slew(double old_tick, double old_delta_tick) { struct timeval tv, end_of_slew; double fast_slew_done, slew_duration, dseconds; assert(fast_slewing); if (gettimeofday(&tv, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } UTI_DiffTimevalsToDouble(&slew_duration, &tv, &slew_start_tv); fast_slew_done = old_delta_tick * slew_duration / (old_tick + old_delta_tick); offset_register += fast_slew_wanted + fast_slew_done; dseconds = -offset_register * (current_total_tick + delta_total_tick) / delta_total_tick; if (dseconds > MAX_FASTSLEW_TIMEOUT) dseconds = MAX_FASTSLEW_TIMEOUT; UTI_AddDoubleToTimeval(&tv, dseconds, &end_of_slew); slew_start_tv = tv; fast_slew_wanted = offset_register; offset_register = 0.0; SCH_RemoveTimeout(slew_timeout_id); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); } /* ================================================== */ /* This routine is called to start a clock offset adjustment */ static void initiate_slew(void) { double dseconds; long tick_adjust; long offset; struct timeval T0; struct timeval end_of_slew; /* Don't want to get here if we already have an adjust on the go! */ assert(!fast_slewing); if (offset_register == 0.0) { return; } /* Cancel any slewing that is running */ if (slow_slewing) { offset = 0; if (TMX_ApplyOffset(&offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } offset_register -= (double) offset / 1.0e6; slow_slewing = 0; update_slow_slew_error(0); } else if (nano_slewing) { if (TMX_GetPLLOffsetLeft(&offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } offset_register -= (double) offset / 1.0e9; update_nano_slew_error(offset, 0); offset = 0; if (TMX_ApplyPLLOffset(offset, MIN_PLL_TIME_CONSTANT) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } nano_slewing = 0; update_nano_slew_error(offset, 1); } if (have_nanopll && fabs(offset_register) < MAX_ADJUST_WITH_NANOPLL) { /* Use the PLL with fixed frequency to do the shift. Until the kernel has a support for linear offset adjustments with programmable rate this is the best we can do. */ offset = 1.0e9 * -offset_register; /* First adjustment after accrue_offset() sets the PLL time constant */ if (pll_time_constant < 0) { pll_time_constant = get_pll_constant(fabs(offset_register)); } assert(pll_time_constant >= MIN_PLL_TIME_CONSTANT && pll_time_constant <= MAX_PLL_TIME_CONSTANT); if (TMX_ApplyPLLOffset(offset, pll_time_constant) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } offset_register = 0.0; /* Don't keep the sub-nanosecond leftover */ nano_slewing = 1; update_nano_slew_error(offset, 1); } else if (fabs(offset_register) < MAX_ADJUST_WITH_ADJTIME) { /* Use adjtime to do the shift */ offset = our_round(1.0e6 * -offset_register); offset_register += offset / 1.0e6; if (offset != 0) { if (TMX_ApplyOffset(&offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } slow_slewing = 1; update_slow_slew_error(offset); } } else { /* If the system clock has a high drift rate, the combination of current_tick + slew_delta_tick could be outside the range that adjtimex will accept. To prevent this, the tick adjustment that is used to slew an error off the clock is clamped according to what tick_adjust is. */ long min_allowed_tick, max_allowed_tick; min_allowed_tick = nominal_tick - max_tick_bias; max_allowed_tick = nominal_tick + max_tick_bias; if (offset_register > 0) { if (current_tick <= min_allowed_tick) { return; } slewing_tick = current_tick - slew_delta_tick; if (slewing_tick < min_allowed_tick) { slewing_tick = min_allowed_tick; } } else { if (current_tick >= max_allowed_tick) { return; } slewing_tick = current_tick + slew_delta_tick; if (slewing_tick > max_allowed_tick) { slewing_tick = max_allowed_tick; } } tick_adjust = slewing_tick - current_tick; delta_total_tick = (double) tick_adjust / 1.0e6; dseconds = - offset_register * (current_total_tick + delta_total_tick) / delta_total_tick; assert(dseconds > 0.0); /* Now set the thing off */ if (gettimeofday(&T0, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } if (TMX_SetTick(slewing_tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } /* Compute the dispersion we have introduced by changing tick this way. We handle this by adding dispersion to all statistics held at higher levels in the system. */ update_fast_slew_error(&T0); lcl_InvokeDispersionNotifyHandlers(fast_slew_error); fast_slewing = 1; slew_start_tv = T0; if (dseconds > MAX_FASTSLEW_TIMEOUT) dseconds = MAX_FASTSLEW_TIMEOUT; UTI_AddDoubleToTimeval(&T0, dseconds, &end_of_slew); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); fast_slew_wanted = offset_register; offset_register = 0.0; } } /* ================================================== */ /* This is the callback routine invoked by the scheduler at the end of a slew. */ static void handle_end_of_slew(void *anything) { stop_fast_slew(); initiate_slew(); /* To do any fine trimming required */ } /* ================================================== */ /* This routine is used to abort a slew that is in progress, if any */ static void abort_slew(void) { if (fast_slewing) { stop_fast_slew(); SCH_RemoveTimeout(slew_timeout_id); } } /* ================================================== */ /* This routine accrues an offset into the offset register, and starts a slew if required. The offset argument is measured in seconds. Positive means the clock needs to be slewed backwards (i.e. is currently fast of true time) */ static void accrue_offset(double offset, double corr_rate) { /* Add the new offset to the register */ offset_register += offset; correction_rate = corr_rate; /* Select a new time constant on the next adjustment */ pll_time_constant = -1; if (!fast_slewing) { initiate_slew(); } else { adjust_fast_slew(current_total_tick, delta_total_tick); } } /* ================================================== */ /* Positive means currently fast of true time, i.e. jump backwards */ static void apply_step_offset(double offset) { struct timeval old_time, new_time; double err; if (fast_slewing) { abort_slew(); } if (have_setoffset) { if (TMX_ApplyStepOffset(-offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } } else { if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } UTI_AddDoubleToTimeval(&old_time, -offset, &new_time); if (settimeofday(&new_time, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "settimeofday() failed"); } if (gettimeofday(&old_time, NULL) < 0) { LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed"); } UTI_DiffTimevalsToDouble(&err, &old_time, &new_time); lcl_InvokeDispersionNotifyHandlers(fabs(err)); } initiate_slew(); } /* ================================================== */ /* This call sets the Linux kernel frequency to a given value in parts per million relative to the nominal running frequency. Nominal is taken to be tick=10000, freq=0 (for a HZ==100 system, other values otherwise). The convention is that this is called with a positive argument if the local clock runs fast when uncompensated. */ static double set_frequency(double freq_ppm) { long required_tick; long min_allowed_tick, max_allowed_tick; double required_freq; /* what we use */ double scaled_freq; /* what adjtimex & the kernel use */ double old_total_tick; int required_delta_tick; required_delta_tick = our_round(freq_ppm / dhz); required_freq = -(freq_ppm - dhz * required_delta_tick); required_tick = nominal_tick - required_delta_tick; scaled_freq = freq_scale * required_freq; min_allowed_tick = nominal_tick - max_tick_bias; max_allowed_tick = nominal_tick + max_tick_bias; if (required_tick < min_allowed_tick || required_tick > max_allowed_tick) { LOG(LOGS_WARN, LOGF_SysLinux, "Required tick %ld outside allowed range (%ld .. %ld)", required_tick, min_allowed_tick, max_allowed_tick); if (required_tick < min_allowed_tick) { required_tick = min_allowed_tick; } else { required_tick = max_allowed_tick; } } current_tick = required_tick; old_total_tick = current_total_tick; current_total_tick = ((double)current_tick + required_freq/dhz) / 1.0e6 ; /* Don't change tick if we are fast slewing, just reschedule the timeout */ if (fast_slewing) { required_tick = slewing_tick; } if (TMX_SetFrequency(&scaled_freq, required_tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex failed for set_frequency, freq_ppm=%10.4e scaled_freq=%10.4e required_tick=%ld", freq_ppm, scaled_freq, required_tick); } if (fast_slewing) { double old_delta_tick; old_delta_tick = delta_total_tick; delta_total_tick = ((double)slewing_tick + required_freq/dhz) / 1.0e6 - current_total_tick; adjust_fast_slew(old_total_tick, old_delta_tick); } return dhz * (nominal_tick - current_tick) - scaled_freq / freq_scale; } /* ================================================== */ /* Read the ppm frequency from the kernel */ static double read_frequency(void) { double tick_term; double unscaled_freq; double freq_term; long tick; if (TMX_GetFrequency(&unscaled_freq, &tick) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } if (fast_slewing) { tick -= slewing_tick - current_tick; } tick_term = dhz * (double)(nominal_tick - tick); freq_term = unscaled_freq / freq_scale; #if 0 LOG(LOGS_INFO, LOGF_SysLinux, "txc.tick=%ld txc.freq=%ld tick_term=%f freq_term=%f", txc.tick, txc.freq, tick_term, freq_term); #endif return tick_term - freq_term; } /* ================================================== */ /* Given a raw time, determine the correction in seconds to generate the 'cooked' time. The correction has to be added to the raw time */ static void get_offset_correction(struct timeval *raw, double *corr, double *err) { /* Correction is given by these things : 1. Any value in offset register 2. Amount of fast slew remaining 3. Any amount of adjtime correction remaining 4. Any amount of nanopll correction remaining */ double fast_slew_duration; double fast_slew_achieved; double fast_slew_remaining; long offset, noffset, toffset; if (!slow_slewing) { offset = 0; } else { if (have_readonly_adjtime) { if (TMX_GetOffsetLeft(&offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } } else { toffset = 0; if (TMX_ApplyOffset(&toffset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } offset = toffset; if (TMX_ApplyOffset(&toffset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } } if (offset == 0) { /* adjtime slew has finished */ slow_slewing = 0; } } if (!nano_slewing) { noffset = 0; } else { if (TMX_GetPLLOffsetLeft(&noffset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } if (noffset == 0) { nano_slewing = 0; } } if (fast_slewing) { UTI_DiffTimevalsToDouble(&fast_slew_duration, raw, &slew_start_tv); fast_slew_achieved = delta_total_tick * fast_slew_duration / (current_total_tick + delta_total_tick); fast_slew_remaining = fast_slew_wanted + fast_slew_achieved; } else { fast_slew_remaining = 0.0; } *corr = - (offset_register + fast_slew_remaining) + offset / 1.0e6 + noffset / 1.0e9; if (err) { update_slow_slew_error(offset); update_nano_slew_error(noffset, 0); *err = get_slow_slew_error(raw) + get_fast_slew_error(raw) + get_nano_slew_error();; } } /* ================================================== */ static void set_leap(int leap) { 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"); } /* ================================================== */ /* Estimate the value of HZ given the value of txc.tick that chronyd finds when * it starts. The only credible values are 100 (Linux/x86) or powers of 2. * Also, the bounds checking inside the kernel's adjtimex system call enforces * a +/- 10% movement of tick away from the nominal value 1e6/HZ. */ static void guess_hz_and_shift_hz(int tick, int *hz, int *shift_hz) { int i, tick_lo, tick_hi, ihz; double tick_nominal; /* Pick off the hz=100 case first */ if (tick >= 9000 && tick <= 11000) { *hz = 100; *shift_hz = 7; return; } for (i=4; i<16; i++) { /* surely 16 .. 32768 is a wide enough range? */ ihz = 1 << i; tick_nominal = 1.0e6 / (double) ihz; tick_lo = (int)(0.5 + tick_nominal*2.0/3.0); tick_hi = (int)(0.5 + tick_nominal*4.0/3.0); if (tick_lo < tick && tick <= tick_hi) { *hz = ihz; *shift_hz = i; return; } } /* oh dear. doomed. */ *hz = 0; *shift_hz = 0; } /* ================================================== */ static int get_hz_and_shift_hz(int *hz, int *shift_hz) { #ifdef _SC_CLK_TCK if ((*hz = sysconf(_SC_CLK_TCK)) < 1) { return 0; } if (*hz == 100) { *shift_hz = 7; return 1; } for (*shift_hz = 1; (*hz >> *shift_hz) > 1; (*shift_hz)++) ; return 1; #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; int shift_hz; double dshift_hz; double basic_freq_scale; /* what to use if HZ!=100 */ int config_hz, set_config_hz; /* values of HZ from conf file */ int set_config_freq_scale; double config_freq_scale; struct tmx_params tmx_params; struct utsname uts; if (!get_hz_and_shift_hz(&hz, &shift_hz)) { TMX_ReadCurrentParams(&tmx_params); guess_hz_and_shift_hz(tmx_params.tick, &hz, &shift_hz); if (!shift_hz) { LOG_FATAL(LOGF_SysLinux, "Can't determine hz (txc.tick=%ld txc.freq=%ld (%.8f) txc.offset=%ld)", tmx_params.tick, tmx_params.freq, tmx_params.dfreq, tmx_params.offset); } else { #if 0 LOG(LOGS_INFO, LOGF_SysLinux, "Initial txc.tick=%ld txc.freq=%ld (%.8f) txc.offset=%ld => hz=%d shift_hz=%d", tmx_params.tick, tmx_params.freq, tmx_params.dfreq, tmx_params.offset, hz, shift_hz); #endif } } CNF_GetLinuxHz(&set_config_hz, &config_hz); if (set_config_hz) hz = config_hz; /* (If true, presumably freq_scale will be overridden anyway, making shift_hz redundant too.) */ dhz = (double) hz; dshift_hz = (double)(1UL << shift_hz); basic_freq_scale = dshift_hz / dhz; nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */ slew_delta_tick = nominal_tick / 12; max_tick_bias = nominal_tick / 10; tick_update_hz = hz; /* The basic_freq_scale comes from: * the kernel increments the usec counter HZ times per second (if the timer interrupt period were perfect) * the following code in the kernel time_adj (+/-)= ltemp >> (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE); causes the adjtimex 'freq' value to be divided down by 1< only 0.125% error (p. 14) * if (time_adj < 0) time_adj -= (-time_adj >> 2) + (-time_adj >> 5); else time_adj += (time_adj >> 2) + (time_adj >> 5); #endif Special case that later. */ if (uname(&uts) < 0) { LOG_FATAL(LOGF_SysLinux, "Cannot uname(2) to get kernel version, sorry."); } patch = 0; if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) < 2) { LOG_FATAL(LOGF_SysLinux, "Cannot read information from uname, sorry"); } LOG(LOGS_INFO, LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch); version_major = major; version_minor = minor; version_patchlevel = patch; 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) { freq_scale = (hz == 100) ? (128.0 / 128.125) : basic_freq_scale; } else { /* These don't seem to need scaling */ freq_scale = 1.0; if (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_OFFSET_SS_READ support. It's available since 2.6.24, but was buggy until 2.6.28. */ if (kernelvercmp(major, minor, patch, 2, 6, 28) < 0) { have_readonly_adjtime = 0; } else { have_readonly_adjtime = 1; } /* ADJ_NANO support */ if (kernelvercmp(major, minor, patch, 2, 6, 27) < 0) { have_nanopll = 0; } else { have_nanopll = 1; } /* ADJ_SETOFFSET support */ if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) { have_setoffset = 0; } else { have_setoffset = 1; } /* PLL time constant changed in 2.6.31 */ if (kernelvercmp(major, minor, patch, 2, 6, 31) < 0) { shift_pll = 4; } else { shift_pll = 2; } /* Override freq_scale if it appears in conf file */ CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale); if (set_config_freq_scale) { freq_scale = config_freq_scale; } LOG(LOGS_INFO, LOGF_SysLinux, "hz=%d shift_hz=%d freq_scale=%.8f nominal_tick=%d slew_delta_tick=%d max_tick_bias=%d shift_pll=%d", hz, shift_hz, freq_scale, nominal_tick, slew_delta_tick, max_tick_bias, shift_pll); } /* ================================================== */ /* Initialisation code for this module */ void SYS_Linux_Initialise(void) { long offset; double freq; offset_register = 0.0; fast_slewing = 0; get_version_specific_details(); offset = 0; if (TMX_ApplyOffset(&offset) < 0) { LOG_FATAL(LOGF_SysLinux, "adjtimex() failed"); } if (have_readonly_adjtime && (TMX_GetOffsetLeft(&offset) < 0 || offset)) { LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_OFFSET_SS_READ"); have_readonly_adjtime = 0; } if (have_nanopll && TMX_EnableNanoPLL() < 0) { LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support nanosecond PLL"); have_nanopll = 0; } if (have_setoffset && TMX_TestStepOffset() < 0) { LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET"); have_setoffset = 0; } TMX_SetSync(CNF_GetRTCSync()); /* Read current kernel frequency */ TMX_GetFrequency(&freq, ¤t_tick); current_total_tick = (current_tick + freq / freq_scale / dhz) / 1.0e6; lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, set_leap); } /* ================================================== */ /* Finalisation code for this module */ void SYS_Linux_Finalise(void) { /* Must *NOT* leave a fast slew running - clock would drift way off if the daemon is not restarted */ abort_slew(); } /* ================================================== */ #ifdef FEAT_LINUXCAPS 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, "prcap() 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_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); #if 0 LOG(LOGS_INFO, LOGF_SysLinux, "Privileges dropped to user %s", user); #endif } #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 { #if 0 LOG(LOGS_INFO, LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d", sched.sched_priority); #endif } } } #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 { #if 0 LOG(LOGS_INFO, LOGF_SysLinux, "Successfully locked into RAM"); #endif } } } } #endif /* HAVE_MLOCKALL */ chrony-1.29/sys_solaris.c0000644000076400007640000002734612200721757014560 0ustar mirosmiros/* 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 void 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) { LOG_FATAL(LOGF_SysSolaris, "settimeofday() failed"); } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; offset_register += rounding_error; start_adjust(); } /* ================================================== */ 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); #if 0 LOG(LOGS_INFO, LOGF_SysSolaris, "Set value of dosynctodr to %d", on_off); #endif } /* ================================================== */ void SYS_Solaris_Initialise(void) { check_need_dosynctodr(); /* Need to do KVM stuff to turn off dosynctodr. */ clock_initialise(); lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, apply_step_offset, get_offset_correction, NULL /* set_leap */); /* 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-1.29/md5.c0000644000076400007640000002727712200721757012676 0ustar mirosmiros/* *********************************************************************** ** 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-1.29/getdate.c0000644000076400007640000022277412200721757013625 0ustar mirosmiros /* A Bison parser, made by GNU Bison 2.4.1. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 189 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 189 of yacc.c */ #line 252 "getdate.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #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 214 of yacc.c */ #line 182 "getdate.y" int Number; enum _MERIDIAN Meridian; /* Line 214 of yacc.c */ #line 313 "getdate.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 325 "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 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # 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 _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (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 _STDLIB_H && (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) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* 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 /* 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 || YYTOKEN_TABLE /* 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", 0 }; #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 rule to reduce with in state STATE-NUM 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 zero, do what YYDEFACT says. 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 }; 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. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #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 { 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 YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ 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 yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* 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 = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #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; } } /* Prevent warnings from -Wmissing-prototypes. */ #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 */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #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 thru 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; /* 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; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; 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 (yyn == YYPACT_NINF) 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 (yyn == 0 || yyn == YYTABLE_NINF) 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; *++yyvsp = yylval; 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 1455 of yacc.c */ #line 202 "getdate.y" { yyHaveTime++; ;} break; case 5: /* Line 1455 of yacc.c */ #line 205 "getdate.y" { yyHaveZone++; ;} break; case 6: /* Line 1455 of yacc.c */ #line 208 "getdate.y" { yyHaveDate++; ;} break; case 7: /* Line 1455 of yacc.c */ #line 211 "getdate.y" { yyHaveDay++; ;} break; case 8: /* Line 1455 of yacc.c */ #line 214 "getdate.y" { yyHaveRel++; ;} break; case 10: /* Line 1455 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 1455 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 1455 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 1455 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 1455 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 1455 of yacc.c */ #line 259 "getdate.y" { yyTimezone = (yyvsp[(1) - (1)].Number); ;} break; case 16: /* Line 1455 of yacc.c */ #line 262 "getdate.y" { yyTimezone = (yyvsp[(1) - (1)].Number) - 60; ;} break; case 17: /* Line 1455 of yacc.c */ #line 266 "getdate.y" { yyTimezone = (yyvsp[(1) - (2)].Number) - 60; ;} break; case 18: /* Line 1455 of yacc.c */ #line 271 "getdate.y" { yyDayOrdinal = 1; yyDayNumber = (yyvsp[(1) - (1)].Number); ;} break; case 19: /* Line 1455 of yacc.c */ #line 275 "getdate.y" { yyDayOrdinal = 1; yyDayNumber = (yyvsp[(1) - (2)].Number); ;} break; case 20: /* Line 1455 of yacc.c */ #line 279 "getdate.y" { yyDayOrdinal = (yyvsp[(1) - (2)].Number); yyDayNumber = (yyvsp[(2) - (2)].Number); ;} break; case 21: /* Line 1455 of yacc.c */ #line 285 "getdate.y" { yyMonth = (yyvsp[(1) - (3)].Number); yyDay = (yyvsp[(3) - (3)].Number); ;} break; case 22: /* Line 1455 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 1455 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 1455 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 1455 of yacc.c */ #line 319 "getdate.y" { yyMonth = (yyvsp[(1) - (2)].Number); yyDay = (yyvsp[(2) - (2)].Number); ;} break; case 26: /* Line 1455 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 1455 of yacc.c */ #line 328 "getdate.y" { yyMonth = (yyvsp[(2) - (2)].Number); yyDay = (yyvsp[(1) - (2)].Number); ;} break; case 28: /* Line 1455 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 1455 of yacc.c */ #line 339 "getdate.y" { yyRelSeconds = -yyRelSeconds; yyRelMinutes = -yyRelMinutes; yyRelHour = -yyRelHour; yyRelDay = -yyRelDay; yyRelMonth = -yyRelMonth; yyRelYear = -yyRelYear; ;} break; case 31: /* Line 1455 of yacc.c */ #line 350 "getdate.y" { yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 32: /* Line 1455 of yacc.c */ #line 353 "getdate.y" { yyRelYear += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 33: /* Line 1455 of yacc.c */ #line 356 "getdate.y" { yyRelYear += (yyvsp[(1) - (1)].Number); ;} break; case 34: /* Line 1455 of yacc.c */ #line 359 "getdate.y" { yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 35: /* Line 1455 of yacc.c */ #line 362 "getdate.y" { yyRelMonth += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 36: /* Line 1455 of yacc.c */ #line 365 "getdate.y" { yyRelMonth += (yyvsp[(1) - (1)].Number); ;} break; case 37: /* Line 1455 of yacc.c */ #line 368 "getdate.y" { yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 38: /* Line 1455 of yacc.c */ #line 371 "getdate.y" { yyRelDay += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 39: /* Line 1455 of yacc.c */ #line 374 "getdate.y" { yyRelDay += (yyvsp[(1) - (1)].Number); ;} break; case 40: /* Line 1455 of yacc.c */ #line 377 "getdate.y" { yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 41: /* Line 1455 of yacc.c */ #line 380 "getdate.y" { yyRelHour += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 42: /* Line 1455 of yacc.c */ #line 383 "getdate.y" { yyRelHour += (yyvsp[(1) - (1)].Number); ;} break; case 43: /* Line 1455 of yacc.c */ #line 386 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 44: /* Line 1455 of yacc.c */ #line 389 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 45: /* Line 1455 of yacc.c */ #line 392 "getdate.y" { yyRelMinutes += (yyvsp[(1) - (1)].Number); ;} break; case 46: /* Line 1455 of yacc.c */ #line 395 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 47: /* Line 1455 of yacc.c */ #line 398 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (2)].Number) * (yyvsp[(2) - (2)].Number); ;} break; case 48: /* Line 1455 of yacc.c */ #line 401 "getdate.y" { yyRelSeconds += (yyvsp[(1) - (1)].Number); ;} break; case 49: /* Line 1455 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 1455 of yacc.c */ #line 440 "getdate.y" { (yyval.Meridian) = MER24; ;} break; case 51: /* Line 1455 of yacc.c */ #line 444 "getdate.y" { (yyval.Meridian) = (yyvsp[(1) - (1)].Meridian); ;} break; /* Line 1455 of yacc.c */ #line 2073 "getdate.c" default: break; } 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: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #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 (yyn != YYPACT_NINF) { 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); } *++yyvsp = yylval; /* 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) 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 1675 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 (*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-1.29/manual.h0000644000076400007640000000302612200721757013455 0ustar mirosmiros/* 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 void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n); extern int MNL_DeleteSample(int index); #endif /* GOT_MANUAL_H */ chrony-1.29/config.log0000644000076400007640000001000412200726264013767 0ustar mirosmirosdocheck.c: #include int main(int argc, char **argv) { return (int) pow(2.0, log(sqrt((double)argc))); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c /tmp/ccXvJsF5.o: In function `main': /home/miros/cvs/chrony/RELEASES/chrony-1.29/docheck.c:3: undefined reference to `log' /home/miros/cvs/chrony/RELEASES/chrony-1.29/docheck.c:3: undefined reference to `pow' /home/miros/cvs/chrony/RELEASES/chrony-1.29/docheck.c:3: undefined reference to `sqrt' collect2: error: ld returned 1 exit status docheck.c: #include int main(int argc, char **argv) { return (int) pow(2.0, log(sqrt((double)argc))); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c -lm docheck.c: #include int main(int argc, char **argv) { return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include int main(int argc, char **argv) { return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include #include #include int main(int argc, char **argv) { 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)); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include #include int main(int argc, char **argv) { return sizeof(struct in6_pktinfo); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: In function 'main': docheck.c:5:19: error: invalid application of 'sizeof' to incomplete type 'struct in6_pktinfo' return sizeof(struct in6_pktinfo); ^ docheck.c: #include #include int main(int argc, char **argv) { return sizeof(struct in6_pktinfo); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -D_GNU_SOURCE -o docheck docheck.c docheck.c: #include int main(int argc, char **argv) { return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include #include int main(int argc, char **argv) { pps_handle_t h; pps_info_t i; struct timespec ts; return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: In function 'main': docheck.c:8:5: warning: 'h' is used uninitialized in this function [-Wuninitialized] return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts); ^ docheck.c: #include #include #include #include #include int main(int argc, char **argv) { prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep")); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c -lcap docheck.c: #include #include int main(int argc, char **argv) { ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include int main(int argc, char **argv) { struct sched_param sched; sched_get_priority_max(SCHED_FIFO); sched_setscheduler(0, SCHED_FIFO, &sched); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include #include int main(int argc, char **argv) { struct rlimit rlim; setrlimit(RLIMIT_MEMLOCK, &rlim); mlockall(MCL_CURRENT|MCL_FUTURE); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c docheck.c: #include #include int main(int argc, char **argv) { add_history(readline("prompt")); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -o docheck docheck.c -ledit docheck.c: #include #include #include int main(int argc, char **argv) { NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512)); return 0; } gcc -O2 -g -Wmissing-prototypes -Wall -I/usr/include/nss3 -I/usr/include/nspr4 -o docheck docheck.c -lfreebl3 chrony-1.29/util.c0000644000076400007640000004214112200721757013151 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009, 2012-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. * ********************************************************************** ======================================================================= 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? */ /* This seems to be fairly benign, so don't bother logging it */ #if 0 LOG(LOGS_INFO, LOGF_Util, "Earlier=[%s] Later=[%s]", UTI_TimevalToString(earlier), UTI_TimevalToString(later)); #endif /* Assume the required behaviour is to treat it as zero */ *diff = 0.0; } tvhalf.tv_sec = tvdiff.tv_sec / 2; tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2) * 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 buffer[64], *result; struct tm stm; stm = *gmtime((time_t *) &(tv->tv_sec)); strftime(buffer, sizeof(buffer), "%a %x %X", &stm); result = NEXT_BUFFER; snprintf(result, BUFFER_LENGTH, "%s.%06ld", buffer, (unsigned long)(tv->tv_usec)); 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 buf[5], *result; for (i = j = 0; i < 4; i++) { c = (ref_id >> (24 - i * 8)) & 0xff; if (isprint(c)) buf[j++] = c; } buf[j] = '\0'; result = NEXT_BUFFER; snprintf(result, BUFFER_LENGTH, "%s", buf); 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 HAVE_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 HAVE_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; } /* ================================================== */ 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 RFC1305 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) { unsigned long usec = src->tv_usec; unsigned long sec = src->tv_sec; uint32_t lo; /* Recognize zero as a special case - it always signifies an 'unknown' value */ if (!usec && !sec) { dest->hi = dest->lo = 0; } else { dest->hi = htonl(src->tv_sec + JAN_1970); /* This formula gives an error of about 0.1us worst case */ 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) { /* As yet, there is no need to check for zero - all processing that has to detect that case is in the NTP layer */ dest->tv_sec = ntohl(src->hi) - JAN_1970; /* Until I invent a slick way to do this, just do it the obvious way */ dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296); } /* ================================================== */ void UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest) { uint32_t sec_low, sec_high; dest->tv_usec = ntohl(src->tv_nsec) / 1000; sec_high = ntohl(src->tv_sec_high); sec_low = ntohl(src->tv_sec_low); /* get the missing bits from current time when received timestamp is only 32-bit */ if (sizeof (time_t) > 4 && sec_high == TV_NOHIGHSEC) { struct timeval now; gettimeofday(&now, NULL); sec_high = now.tv_sec >> 16 >> 16; } dest->tv_sec = (time_t)sec_high << 16 << 16 | sec_low; } /* ================================================== */ void UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest) { dest->tv_nsec = htonl(src->tv_usec * 1000); if (sizeof (time_t) > 4) dest->tv_sec_high = htonl(src->tv_sec >> 16 >> 16); else dest->tv_sec_high = htonl(TV_NOHIGHSEC); 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 { 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; } /* ================================================== */ void UTI_FdSetCloexec(int fd) { int flags; flags = fcntl(fd, F_GETFD); if (flags != -1) { flags |= FD_CLOEXEC; fcntl(fd, F_SETFD, flags); } } /* ================================================== */ 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-1.29/manual.c0000644000076400007640000001737012200721757013457 0ustar mirosmiros/* 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 MAX_SAMPLES 16 static Sample samples[16]; static int n_samples; static int replace_margin; static int error; /* Eventually these constants need to be user-defined in conf file */ #define REPLACE_MARGIN 300 #define ERROR_MARGIN 0.2 /* ================================================== */ static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *not_used); /* ================================================== */ void MNL_Initialise(void) { if (CNF_GetManualEnabled()) { enabled = 1; } else { enabled = 0; } n_samples = 0; replace_margin = REPLACE_MARGIN; error = ERROR_MARGIN; LCL_AddParameterChangeHandler(slew_samples, NULL); } /* ================================================== */ 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-1.29/chrony_timex.h0000644000076400007640000000551612200721757014716 0ustar mirosmiros/* Taken from /usr/include/linux/timex.h. Avoids the need to * include kernel header files. */ #ifndef CHRONY_TIMEX_H #define CHRONY_TIMEX_H #include struct timex { unsigned int modes; /* mode selector */ long offset; /* time offset (usec) */ long freq; /* frequency offset (scaled ppm) */ long maxerror; /* maximum error (usec) */ long esterror; /* estimated error (usec) */ int status; /* clock command/status */ long constant; /* pll time constant */ long precision; /* clock precision (usec) (read only) */ long tolerance; /* clock frequency tolerance (ppm) * (read only) */ struct timeval time; /* (read only) */ long tick; /* (modified) usecs between clock ticks */ long ppsfreq; /* pps frequency (scaled ppm) (ro) */ long jitter; /* pps jitter (us) (ro) */ int shift; /* interval duration (s) (shift) (ro) */ long stabil; /* pps stability (scaled ppm) (ro) */ long jitcnt; /* jitter limit exceeded (ro) */ long calcnt; /* calibration intervals (ro) */ long errcnt; /* calibration errors (ro) */ long stbcnt; /* stability limit exceeded (ro) */ int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; int :32; }; #define ADJ_OFFSET 0x0001 /* time offset */ #define ADJ_FREQUENCY 0x0002 /* frequency offset */ #define ADJ_MAXERROR 0x0004 /* maximum time error */ #define ADJ_STATUS 0x0010 /* clock status */ #define ADJ_TIMECONST 0x0020 /* pll time constant */ #define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ #define ADJ_NANO 0x2000 /* select nanosecond resolution */ #define ADJ_TICK 0x4000 /* tick value */ #define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */ #define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ #define SHIFT_USEC 16 /* frequency offset scale (shift) */ #define STA_PLL 0x0001 /* enable PLL updates (rw) */ #define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */ #define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */ #define STA_FLL 0x0008 /* select frequency-lock mode (rw) */ #define STA_INS 0x0010 /* insert leap (rw) */ #define STA_DEL 0x0020 /* delete leap (rw) */ #define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */ #define STA_FREQHOLD 0x0080 /* hold frequency (rw) */ #define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */ #define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */ #define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */ #define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */ #define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */ #define STA_NANO 0x2000 /* resolution (0 = us, 1 = ns) (ro) */ /* This doesn't seem to be in any include files !! */ extern int adjtimex(struct timex *); #endif /* CHRONY_TIMEX_H */ chrony-1.29/keys.c0000644000076400007640000002113212200721757013144 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2012-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. * ********************************************************************** ======================================================================= Module for managing keys used for authenticating NTP packets and commands */ #include "config.h" #include "sysincl.h" #include "keys.h" #include "cmdparse.h" #include "conf.h" #include "memory.h" #include "util.h" #include "local.h" #include "logging.h" typedef struct { unsigned long id; char *val; int len; int hash_id; int auth_delay; } Key; #define MAX_KEYS 256 static int n_keys; static Key keys[MAX_KEYS]; static int command_key_valid; static int command_key_id; static int cache_valid; static unsigned long cache_key_id; static int cache_key_pos; /* ================================================== */ static int generate_key(unsigned long key_id) { #ifdef GENERATE_SHA1_KEY 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%lu %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 %lu", key_id); return 1; } /* ================================================== */ void KEY_Initialise(void) { n_keys = 0; 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) { } /* ================================================== */ static int determine_hash_delay(int 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_SIZE, (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; } } #if 0 LOG(LOGS_INFO, LOGF_Keys, "authentication delay for key %lu: %d useconds", key_id, min_usecs); #endif /* Add on a bit extra to allow for copying, conversions etc */ return min_usecs + (min_usecs >> 4); } /* ================================================== */ /* 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) { int i, line_number; FILE *in; unsigned long key_id; char line[2048], *keyval, *key_file; const char *hashname; for (i=0; i= 0) { /* Store key in cache, we will probably be using it in a minute... */ cache_valid = 1; cache_key_pos = position; cache_key_id = key_id; return 1; } else { return 0; } } } /* ================================================== */ int KEY_GetAuthDelay(unsigned long key_id) { int key_pos; key_pos = get_key_pos(key_id); if (key_pos < 0) { return 0; } return keys[key_pos].auth_delay; } /* ================================================== */ int KEY_GenerateAuth(unsigned long key_id, const unsigned char *data, int data_len, unsigned char *auth, int auth_len) { int key_pos; key_pos = get_key_pos(key_id); if (key_pos < 0) { return 0; } return UTI_GenerateNTPAuth(keys[key_pos].hash_id, (unsigned char *)keys[key_pos].val, keys[key_pos].len, data, data_len, auth, auth_len); } /* ================================================== */ int KEY_CheckAuth(unsigned long key_id, const unsigned char *data, int data_len, const unsigned char *auth, int auth_len) { int key_pos; key_pos = get_key_pos(key_id); if (key_pos < 0) { return 0; } return UTI_CheckNTPAuth(keys[key_pos].hash_id, (unsigned char *)keys[key_pos].val, keys[key_pos].len, data, data_len, auth, auth_len); } chrony-1.29/chronyc.1.in0000644000076400007640000000405012200726264014157 0ustar mirosmiros.TH CHRONYC 1 "August 2013" "chrony 1.29" "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 .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), .BR chrony(1) .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-1.29/nameserv.h0000644000076400007640000000273312200721757014024 0ustar mirosmiros/* 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 *addr); extern int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len); extern void DNS_Reload(void); #endif /* GOT_NAMESERV_H */ chrony-1.29/broadcast.c0000644000076400007640000001105412200721757014135 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= Deal with broadcast server functions. */ #include "config.h" #include "sysincl.h" #include "memory.h" #include "addressing.h" #include "broadcast.h" #include "sched.h" #include "ntp.h" #include "local.h" #include "reference.h" #include "util.h" #include "ntp_io.h" typedef struct { NTP_Remote_Address addr; int interval; } Destination; static Destination *destinations = 0; static int n_destinations = 0; static int max_destinations = 0; void BRD_Initialise(void) { } /* ================================================== */ void BRD_Finalise(void) { } /* ================================================== */ /* This is a cut-down version of what transmit_packet in ntp_core.c does */ static void timeout_handler(void *arbitrary) { Destination *d = (Destination *) arbitrary; NTP_Packet message; /* Parameters read from reference module */ int version; int leap; int are_we_synchronised, our_stratum; NTP_Leap leap_status; uint32_t our_ref_id, ts_fuzz; struct timeval our_ref_time; double our_root_delay, our_root_dispersion; struct timeval local_transmit; version = 3; LCL_ReadCookedTime(&local_transmit, NULL); REF_GetReferenceParams(&local_transmit, &are_we_synchronised, &leap_status, &our_stratum, &our_ref_id, &our_ref_time, &our_root_delay, &our_root_dispersion); if (are_we_synchronised) { leap = (int) leap_status; } else { leap = LEAP_Unsynchronised; } message.lvm = ((leap << 6) &0xc0) | ((version << 3) & 0x38) | (MODE_BROADCAST & 0x07); message.stratum = our_stratum; message.poll = 6; /* FIXME: what should this be? */ message.precision = LCL_GetSysPrecisionAsLog(); /* If we're sending a client mode packet and we aren't synchronized yet, we might have to set up artificial values for some of these parameters */ message.root_delay = UTI_DoubleToInt32(our_root_delay); message.root_dispersion = UTI_DoubleToInt32(our_root_dispersion); message.reference_id = htonl((NTP_int32) our_ref_id); /* Now fill in timestamps */ UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, 0); message.originate_ts.hi = 0UL; message.originate_ts.lo = 0UL; message.receive_ts.hi = 0UL; message.receive_ts.lo = 0UL; ts_fuzz = UTI_GetNTPTsFuzz(message.precision); LCL_ReadCookedTime(&local_transmit, NULL); UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz); NIO_SendNormalPacket(&message, &d->addr); /* Requeue timeout. Don't care if interval drifts gradually, so just do it * at the end. */ SCH_AddTimeoutInClass((double) d->interval, 1.0, 0.02, SCH_NtpBroadcastClass, timeout_handler, (void *) d); } /* ================================================== */ void BRD_AddDestination(IPAddr *addr, unsigned short port, int interval) { if (max_destinations == n_destinations) { /* Expand array */ max_destinations += 8; if (destinations) { destinations = ReallocArray(Destination, max_destinations, destinations); } else { destinations = MallocArray(Destination, max_destinations); } } destinations[n_destinations].addr.ip_addr = *addr; destinations[n_destinations].addr.local_ip_addr.family = IPADDR_UNSPEC; destinations[n_destinations].addr.port = port; destinations[n_destinations].interval = interval; SCH_AddTimeoutInClass((double) interval, 1.0, 0.0, SCH_NtpBroadcastClass, timeout_handler, (void *)(destinations + n_destinations)); ++n_destinations; } chrony-1.29/hash_tomcrypt.c0000644000076400007640000000545112200721757015063 0ustar mirosmiros/* 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; } chrony-1.29/hash.h0000644000076400007640000000256212200721757013127 0ustar mirosmiros/* 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); #endif chrony-1.29/hash_nss.c0000644000076400007640000000456612200721757014013 0ustar mirosmiros/* 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; } chrony-1.29/reference.c0000644000076400007640000007271412200721757014143 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-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. * ********************************************************************** ======================================================================= 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; /* Stored relative to reference, NOT local 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; /* 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; /* Filename of the drift file. */ static char *drift_file=NULL; static double drift_file_age; static void update_drift_file(double, double); /* 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; /* ================================================== */ /* Reference ID supplied when we are locally referenced */ #define LOCAL_REFERENCE_ID 0x7f7f0101UL /* ================================================== */ /* 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 handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything) { if (is_step_change) { UTI_AddDoubleToTimeval(&last_ref_update, -doffset, &last_ref_update); } } /* ================================================== */ void REF_Initialise(void) { FILE *in; double file_freq_ppm, file_skew_ppm; double our_frequency_ppm; 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_tzname = CNF_GetLeapSecTimezone(); if (leap_tzname) { /* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */ if (get_tz_leap(1214784000) == LEAP_Normal && get_tz_leap(1230681600) == LEAP_InsertSecond) { 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) { if (our_leap_sec) { LCL_SetLeap(0); } if (drift_file && drift_file_age > 0.0) { update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); } Free(fb_drifts); initialised = 0; } /* ================================================== */ static double Sqr(double x) { return x*x; } /* ================================================== */ #if 0 static double Cube(double x) { return x*x*x; } #endif /* ================================================== */ /* Update the drift coefficients to the file. */ static void update_drift_file(double freq_ppm, double skew) { struct stat buf; char *temp_drift_file; FILE *out; /* Create a temporary file with a '.tmp' extension. */ temp_drift_file = (char*) Malloc(strlen(drift_file)+8); if(!temp_drift_file) { return; } strcpy(temp_drift_file,drift_file); strcat(temp_drift_file,".tmp"); out = fopen(temp_drift_file, "w"); if (!out) { Free(temp_drift_file); LOG(LOGS_WARN, LOGF_Reference, "Could not open temporary driftfile %s.tmp for writing", drift_file); return; } /* Write the frequency and skew parameters in ppm */ if ((fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew) < 0) | fclose(out)) { 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)) { LOG(LOGS_WARN, LOGF_Reference, "Could not change ownership of temporary driftfile %s.tmp", drift_file); } chmod(temp_drift_file,buf.st_mode&0777); } /* Rename the temporary file to the correct location (see rename(2) for details). */ if (rename(temp_drift_file,drift_file)) { unlink(temp_drift_file); Free(temp_drift_file); LOG(LOGS_WARN, LOGF_Reference, "Could not replace old driftfile %s with new one %s.tmp", 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 < 0.0 || update_interval > last_ref_update_interval * 4.0) return; for (i = 0; i < fb_drift_max - fb_drift_min + 1; i++) { /* Don't allow differences larger than 10 ppm */ if (fabs(freq_ppm - fb_drifts[i].freq) > 10.0) fb_drifts[i].secs = 0.0; 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); } #if 0 LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds", i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs); #endif } } /* ================================================== */ static void fb_drift_timeout(void *arg) { assert(are_we_synchronised == 0); assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max); fb_drift_timeout_id = -1; 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; #if 0 LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d set", c); #endif } 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); #if 0 LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d scheduled", i); #endif } } /* ================================================== */ #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 void maybe_make_step() { if (make_step_limit == 0) { return; } else if (make_step_limit > 0) { make_step_limit--; } LCL_MakeStep(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) SCH_QuitProgram(); else if (max_offset_ignore > 0) max_offset_ignore--; return 0; } return 1; } /* ================================================== */ 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); /* Check for leap second only in the latter half of June and December */ if (stm.tm_mon == 5 && stm.tm_mday > 14) stm.tm_mday = 30; else if (stm.tm_mon == 11 && stm.tm_mday > 14) stm.tm_mday = 31; else 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 update_leap_status(NTP_Leap leap, time_t now) { struct tm stm; 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) { /* Insert/delete leap second only on June 30 or December 31 and in other months ignore the leap status completely */ stm = *gmtime(&now); if (stm.tm_mon != 5 && stm.tm_mon != 11) { leap = LEAP_Normal; } else if ((stm.tm_mon == 5 && stm.tm_mday == 30) || (stm.tm_mon == 11 && stm.tm_mday == 31)) { if (leap == LEAP_InsertSecond) { leap_sec = 1; } else { leap_sec = -1; } } } if (leap_sec != our_leap_sec) { LCL_SetLeap(leap_sec); our_leap_sec = leap_sec; } 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); } } /* ================================================== */ 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; struct timeval now, raw_now, ev_now, ev_raw_now; assert(initialised); /* 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); /* This is cheaper than calling LCL_CookTime */ SCH_GetLastEventTime(&ev_now, NULL, &ev_raw_now); UTI_DiffTimevalsToDouble(&uncorrected_offset, &ev_now, &ev_raw_now); 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; /* 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, our_offset, correction_rate); } else { #if 0 LOG(LOGS_INFO, LOGF_Reference, "Skew %f too large to track, offset=%f", skew, our_offset); #endif LCL_AccumulateOffset(our_offset, correction_rate); our_residual_freq = frequency; } update_leap_status(leap, raw_now.tv_sec); maybe_log_offset(our_offset, raw_now.tv_sec); maybe_make_step(); 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); } 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 ) { uint32_t manual_refid = 0x4D414E55; /* MANU */ /* We are not synchronised to an external source, as such. This is only supposed to be used with the local source option, really ... */ REF_SetReference(0, LEAP_Unsynchronised, 1, manual_refid, 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); /* This is cheaper than calling LCL_CookTime */ SCH_GetLastEventTime(&now, NULL, &now_raw); UTI_DiffTimevalsToDouble(&uncorrected_offset, &now, &now_raw); if (fb_drifts) { schedule_fb_drift(&now); } update_leap_status(LEAP_Unsynchronised, 0); are_we_synchronised = 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 = our_leap_status; *ref_id = our_ref_id; *ref_time = our_ref_time; *root_delay = our_root_delay; *root_dispersion = our_root_dispersion + extra_dispersion; } else if (enable_local_stratum) { *is_synchronised = 1; *stratum = local_stratum; *ref_id = LOCAL_REFERENCE_ID; /* Make the reference time be now less a second - this will scarcely affect the client, but will ensure that the transmit timestamp cannot come before this (which would cause test 6 to fail in the client's read routine) if the local system clock's read routine is broken in any way. */ *ref_time = *local_time; --ref_time->tv_sec; /* Not much else we can do for leap second bits - maybe need to have a way for the administrator to feed leap bits in */ *leap_status = LEAP_Normal; *root_delay = 0.0; *root_dispersion = LCL_GetSysPrecisionAsQuantum(); } else { *is_synchronised = 0; *leap_status = LEAP_Unsynchronised; *stratum = 0; *ref_id = 0; ref_time->tv_sec = ref_time->tv_usec = 0; /* These values seem to be standard for a client, and any peer or client of ours will ignore them anyway because we don't claim to be synchronised */ *root_dispersion = 1.0; *root_delay = 1.0; } } /* ================================================== */ int REF_GetOurStratum(void) { if (are_we_synchronised) { return our_stratum; } else if (enable_local_stratum) { return local_stratum; } else { return 16; } } /* ================================================== */ void REF_ModifyMaxupdateskew(double new_max_update_skew) { max_update_skew = new_max_update_skew * 1.0e-6; #if 0 LOG(LOGS_INFO, LOGF_Reference, "New max update skew = %.3fppm", new_max_update_skew); #endif } /* ================================================== */ void REF_EnableLocal(int stratum) { enable_local_stratum = 1; local_stratum = stratum; } /* ================================================== */ void REF_DisableLocal(void) { enable_local_stratum = 0; } /* ================================================== */ int REF_IsLocalActive(void) { return !are_we_synchronised && enable_local_stratum; } /* ================================================== */ 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 = 0; 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 = LOCAL_REFERENCE_ID; rep->ip_addr.family = IPADDR_UNSPEC; rep->stratum = local_stratum; rep->ref_time = now_cooked; rep->root_dispersion = LCL_GetSysPrecisionAsQuantum(); } } /* ================================================== */ chrony-1.29/addrfilt.c0000644000076400007640000002552012200721757013767 0ustar mirosmiros/* 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 * * 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); 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; } /* ================================================== */ #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-1.29/rtc_linux.c0000644000076400007640000006717112200721757014215 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * 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. * ********************************************************************** ======================================================================= 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 "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 /* Try to avoid doing regression after _every_ sample we accumulate */ #define N_SAMPLES_PER_REGRESSION 4 static int measurement_period = LOWEST_MEASUREMENT_PERIOD; static int timeout_running = 0; static SCH_TimeoutID timeout_id; 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[MAX_SAMPLES]; static double rtc_trim[MAX_SAMPLES]; /* Reference time, against which delta times on the RTC scale are measured */ static time_t rtc_ref; /* System clock (gettimeofday) samples associated with the above samples. */ static struct timeval system_times[MAX_SAMPLES]; /* Number of samples currently stored. */ static int n_samples; /* Number of new samples since last regression */ static int n_samples_since_regression; /* Number of runs of residuals in last regression (for logging) */ static int n_runs; /* Coefficients */ /* Whether they are valid */ static int coefs_valid; /* Reference time */ static time_t coef_ref_time; /* Number of seconds by which RTC was fast of the system time at coef_ref_time */ static double coef_seconds_fast; /* Estimated number of seconds that RTC gains relative to system time for each second of ITS OWN time */ static double coef_gain_rate; /* Gain rate saved just before we step the RTC to correct it to the nearest second, so that we can write a useful set of coefs to the RTC data file once we have reacquired its offset after the step */ static double saved_coef_gain_rate; /* Filename supplied by config file where RTC coefficients are stored. */ static char *coefs_file_name; /* ================================================== */ /* Coefficients read from file at start of run. */ /* Whether we have tried to load the coefficients */ static int tried_to_load_coefs = 0; /* Whether valid coefficients were read */ static int valid_coefs_from_file = 0; /* Coefs read in */ static time_t file_ref_time; static double file_ref_offset, file_rate_ppm; /* ================================================== */ /* Flag to remember whether to assume the RTC is running on UTC */ static int rtc_on_utc = 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, int is_step_change, void *anything) { int i; double delta_time; double old_seconds_fast, old_gain_rate; for (i=0; i= 0) { SCH_RemoveInputFileHandler(fd); close(fd); /* Save the RTC data */ (void) RTC_Linux_WriteParameters(); } } /* ================================================== */ 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; 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; } /* ================================================== */ /* Day number of 1 Jan 1970 */ #define MJD_1970 40587 static void process_reading(time_t rtc_time, struct timeval *system_time) { double rtc_fast; accumulate_sample(rtc_time, system_time); switch (operating_mode) { case OM_NORMAL: if (n_samples_since_regression >= /* 4 */ 1 ) { run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); n_samples_since_regression = 0; } break; case OM_INITIAL: if (n_samples_since_regression >= 8) { handle_initial_trim(); } break; case OM_AFTERTRIM: if (n_samples_since_regression >= 8) { handle_relock_after_trim(); } break; default: 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)); error = 1; 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/clock -s -u would do. We're not as picky about OS version etc in this case, since we have fewer requirements regarding the RTC behaviour than we do for the rest of the module. */ void RTC_Linux_TimePreInit(void) { int fd, status; struct rtc_time rtc_raw; struct tm rtc_tm; time_t rtc_t, estimated_correct_rtc_t; long interval; double accumulated_error = 0.0; struct timeval new_sys_time, old_sys_time; coefs_file_name = CNF_GetRtcFile(); setup_config(); read_coefs_from_file(); fd = open(CNF_GetRtcDevice(), O_RDONLY); if (fd < 0) { return; /* Can't open it, and won't be able to later */ } status = ioctl(fd, RTC_RD_TIME, &rtc_raw); if (status >= 0) { /* Convert to seconds since 1970 */ rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_mday = rtc_raw.tm_mday; rtc_tm.tm_mon = rtc_raw.tm_mon; rtc_tm.tm_year = rtc_raw.tm_year; rtc_t = t_from_rtc(&rtc_tm); if (rtc_t != (time_t)(-1)) { /* Work out approximatation to correct time (to about the nearest second) */ if (valid_coefs_from_file) { interval = rtc_t - file_ref_time; accumulated_error = file_ref_offset + (double)(interval) * 1.0e-6 * file_rate_ppm; /* Correct time */ estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error); } else { estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error); } new_sys_time.tv_sec = estimated_correct_rtc_t; new_sys_time.tv_usec = 0; /* Set system time only if the step is larger than 1 second */ if (!(gettimeofday(&old_sys_time, NULL) < 0) && (old_sys_time.tv_sec - new_sys_time.tv_sec > 1 || old_sys_time.tv_sec - new_sys_time.tv_sec < -1)) { LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f", accumulated_error); /* Tough luck if this fails */ if (settimeofday(&new_sys_time, NULL) < 0) { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not settimeofday"); } } } else { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970"); } } close(fd); } /* ================================================== */ int RTC_Linux_GetReport(RPT_RTC_Report *report) { report->ref_time.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-1.29/acquire.c0000644000076400007640000005106512200721757013632 0ustar mirosmiros/* 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. * ********************************************************************** ======================================================================= Processing to perform the equivalent of what ntpdate does. That is, make a rapid-fire set of measurements to a designated set of sources, and step or slew the local clock to bring it into line with the result. This is kept completely separate of the main chronyd processing, by using a separate socket for sending/receiving the measurement packets. That way, ntp_core.c can be kept completely independent of this functionality. A few of the finer points of how to construct valid RFC1305 packets and validate responses for this case have been cribbed from the ntpdate source. */ #include "config.h" #include "sysincl.h" #include "acquire.h" #include "memory.h" #include "sched.h" #include "local.h" #include "logging.h" #include "ntp.h" #include "util.h" #include "main.h" #include "conf.h" /* ================================================== */ /* Interval between firing off the first sample to successive sources */ #define INTER_SOURCE_START (0.2) #define MAX_SAMPLES 8 #define MAX_DEAD_PROBES 4 #define N_GOOD_SAMPLES 4 #define RETRANSMISSION_TIMEOUT (1.0) #define NTP_VERSION 3 #define NTP_MAX_COMPAT_VERSION 4 #define NTP_MIN_COMPAT_VERSION 2 typedef struct { IPAddr ip_addr; /* Address of the server */ int sanity; /* Flag indicating whether source looks sane or not */ int n_dead_probes; /* Number of probes sent to the server since a good one */ int n_samples; /* Number of samples accumulated */ int n_total_samples; /* Total number of samples received including useless ones */ double offsets[MAX_SAMPLES]; /* In seconds, positive means local clock is fast of reference */ double root_distances[MAX_SAMPLES]; /* in seconds */ double inter_lo; /* Low end of estimated range of offset */ double inter_hi; /* High end of estimated range of offset */ NTP_int64 last_tx; /* Transmit timestamp in last packet transmitted to source. */ int timer_running; SCH_TimeoutID timeout_id; } SourceRecord; static SourceRecord *sources; static int n_sources; static int n_started_sources; static int n_completed_sources; static double init_slew_threshold; union sockaddr_in46 { struct sockaddr_in in4; #ifdef HAVE_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr u; }; static int sock_fd4 = -1; #ifdef HAVE_IPV6 static int sock_fd6 = -1; #endif /* ================================================== */ static void (*saved_after_hook)(void *) = NULL; static void *saved_after_hook_anything = NULL; /* ================================================== */ typedef struct { double offset; enum {LO, HIGH} type; int index; } Endpoint; typedef struct { double lo; double hi; } Interval; /* ================================================== */ static void read_from_socket(void *anything); static void transmit_timeout(void *x); static void wind_up_acquisition(void); static void start_source_timeout_handler(void *not_used); /* ================================================== */ static SCH_TimeoutID source_start_timeout_id; /* ================================================== */ void ACQ_Initialise(void) { } /* ================================================== */ void ACQ_Finalise(void) { } /* ================================================== */ static int prepare_socket(int family) { unsigned short port_number = CNF_GetAcquisitionPort(); int sock_fd; socklen_t addrlen; sock_fd = socket(family, SOCK_DGRAM, 0); if (sock_fd < 0) { LOG_FATAL(LOGF_Acquire, "Could not open socket : %s", strerror(errno)); } /* Close on exec */ UTI_FdSetCloexec(sock_fd); if (port_number == 0) { /* Don't bother binding this socket - we're not fussed what port number it gets */ } else { union sockaddr_in46 my_addr; memset(&my_addr, 0, sizeof (my_addr)); switch (family) { case AF_INET: my_addr.in4.sin_family = family; my_addr.in4.sin_port = htons(port_number); my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); addrlen = sizeof (my_addr.in4); break; #ifdef HAVE_IPV6 case AF_INET6: my_addr.in6.sin6_family = family; my_addr.in6.sin6_port = htons(port_number); my_addr.in6.sin6_addr = in6addr_any; addrlen = sizeof (my_addr.in6); break; #endif default: assert(0); } if (bind(sock_fd, &my_addr.u, addrlen) < 0) { LOG(LOGS_ERR, LOGF_Acquire, "Could not bind socket : %s", strerror(errno)); /* but keep running */ } } SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd); return sock_fd; } /* ================================================== */ static void initialise_io(int family) { if (family == IPADDR_INET4 || family == IPADDR_UNSPEC) sock_fd4 = prepare_socket(AF_INET); #ifdef HAVE_IPV6 if (family == IPADDR_INET6 || family == IPADDR_UNSPEC) sock_fd6 = prepare_socket(AF_INET6); #endif } /* ================================================== */ static void finalise_io(void) { if (sock_fd4 >= 0) { SCH_RemoveInputFileHandler(sock_fd4); close(sock_fd4); } sock_fd4 = -1; #ifdef HAVE_IPV6 if (sock_fd6 >= 0) { SCH_RemoveInputFileHandler(sock_fd6); close(sock_fd6); } sock_fd6 = -1; #endif } /* ================================================== */ static void probe_source(SourceRecord *src) { NTP_Packet pkt; int version = NTP_VERSION; NTP_Mode my_mode = MODE_CLIENT; struct timeval cooked; union sockaddr_in46 his_addr; int sock_fd; socklen_t addrlen; uint32_t ts_fuzz; #if 0 printf("Sending probe to %s sent=%d samples=%d\n", UTI_IPToString(&src->ip_addr), src->n_probes_sent, src->n_samples); #endif pkt.lvm = (((LEAP_Unsynchronised << 6) & 0xc0) | ((version << 3) & 0x38) | ((my_mode) & 0x7)); pkt.stratum = 0; pkt.poll = 4; pkt.precision = -6; /* as ntpdate */ pkt.root_delay = UTI_DoubleToInt32(1.0); /* 1 second */ pkt.root_dispersion = UTI_DoubleToInt32(1.0); /* likewise */ pkt.reference_id = 0; pkt.reference_ts.hi = 0; /* Set to 0 */ pkt.reference_ts.lo = 0; /* Set to 0 */ pkt.originate_ts.hi = 0; /* Set to 0 */ pkt.originate_ts.lo = 0; /* Set to 0 */ pkt.receive_ts.hi = 0; /* Set to 0 */ pkt.receive_ts.lo = 0; /* Set to 0 */ /* And do transmission */ memset(&his_addr, 0, sizeof (his_addr)); switch (src->ip_addr.family) { case IPADDR_INET4: his_addr.in4.sin_addr.s_addr = htonl(src->ip_addr.addr.in4); his_addr.in4.sin_port = htons(123); /* Fixed for now */ his_addr.in4.sin_family = AF_INET; addrlen = sizeof (his_addr.in4); sock_fd = sock_fd4; break; #ifdef HAVE_IPV6 case IPADDR_INET6: memcpy(&his_addr.in6.sin6_addr.s6_addr, &src->ip_addr.addr.in6, sizeof (his_addr.in6.sin6_addr.s6_addr)); his_addr.in6.sin6_port = htons(123); /* Fixed for now */ his_addr.in6.sin6_family = AF_INET6; addrlen = sizeof (his_addr.in6); sock_fd = sock_fd6; break; #endif default: assert(0); } ts_fuzz = UTI_GetNTPTsFuzz(LCL_GetSysPrecisionAsLog()); LCL_ReadCookedTime(&cooked, NULL); UTI_TimevalToInt64(&cooked, &pkt.transmit_ts, ts_fuzz); if (sendto(sock_fd, (void *) &pkt, NTP_NORMAL_PACKET_SIZE, 0, &his_addr.u, addrlen) < 0) { LOG(LOGS_WARN, LOGF_Acquire, "Could not send to %s : %s", UTI_IPToString(&src->ip_addr), strerror(errno)); } src->last_tx = pkt.transmit_ts; ++(src->n_dead_probes); src->timer_running = 1; src->timeout_id = SCH_AddTimeoutByDelay(RETRANSMISSION_TIMEOUT, transmit_timeout, (void *) src); } /* ================================================== */ static void transmit_timeout(void *x) { SourceRecord *src = (SourceRecord *) x; src->timer_running = 0; #if 0 printf("Timeout expired for server %s\n", UTI_IPToString(&src->ip_addr)); #endif if (src->n_dead_probes < MAX_DEAD_PROBES) { probe_source(src); } else { /* Source has croaked or is taking too long to respond */ ++n_completed_sources; if (n_completed_sources == n_sources) { wind_up_acquisition(); } } } /* ================================================== */ #define MAX_STRATUM 15 static void process_receive(NTP_Packet *msg, SourceRecord *src, struct timeval *now) { unsigned long lvm; int leap, version, mode; double root_delay, root_dispersion; double total_root_delay, total_root_dispersion, total_root_distance; struct timeval local_orig, local_average, remote_rx, remote_tx, remote_average; double remote_interval, local_interval; double delta, theta, epsilon; int n; /* Most of the checks are from ntpdate */ /* Need to do something about authentication */ lvm = msg->lvm; leap = (lvm >> 6) & 0x3; version = (lvm >> 3) & 0x7; mode = lvm & 0x7; if ((leap == LEAP_Unsynchronised) || (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) || (mode != MODE_SERVER && mode != MODE_PASSIVE)) { return; } if (msg->stratum > MAX_STRATUM) { return; } /* Check whether server is responding to our last request */ if ((msg->originate_ts.hi != src->last_tx.hi) || (msg->originate_ts.lo != src->last_tx.lo)) { return; } /* Check that the server is sane */ if (((msg->originate_ts.hi == 0) && (msg->originate_ts.lo == 0)) || ((msg->receive_ts.hi == 0) && (msg->receive_ts.lo) == 0)) { return; } root_delay = UTI_Int32ToDouble(msg->root_delay); root_dispersion = UTI_Int32ToDouble(msg->root_dispersion); UTI_Int64ToTimeval(&src->last_tx, &local_orig); UTI_Int64ToTimeval(&msg->receive_ts, &remote_rx); UTI_Int64ToTimeval(&msg->transmit_ts, &remote_tx); UTI_AverageDiffTimevals(&remote_rx, &remote_tx, &remote_average, &remote_interval); UTI_AverageDiffTimevals(&local_orig, now, &local_average, &local_interval); delta = local_interval - remote_interval; /* Defined as positive if we are fast. Note this sign convention is opposite to that used in ntp_core.c */ UTI_DiffTimevalsToDouble(&theta, &local_average, &remote_average); /* Could work out epsilon - leave till later */ epsilon = 0.0; total_root_delay = fabs(delta) + root_delay; total_root_dispersion = epsilon + root_dispersion; total_root_distance = 0.5 * fabs(total_root_delay) + total_root_dispersion; n = src->n_samples; #if 0 printf("Sample %d theta=%.6f delta=%.6f root_del=%.6f root_disp=%.6f root_dist=%.6f\n", n, theta, delta, total_root_delay, total_root_dispersion, total_root_distance); #endif src->offsets[n] = theta; src->root_distances[n] = total_root_distance; ++(src->n_samples); } /* ================================================== */ static void read_from_socket(void *anything) { int status; ReceiveBuffer msg; union sockaddr_in46 his_addr; int sock_fd; socklen_t his_addr_len; int flags; int message_length; IPAddr remote_ip; int i, ok; struct timeval now; SourceRecord *src; flags = 0; message_length = sizeof(msg); his_addr_len = sizeof(his_addr); /* Get timestamp */ SCH_GetLastEventTime(&now, NULL, NULL); sock_fd = (long)anything; status = recvfrom (sock_fd, (char *)&msg, message_length, flags, &his_addr.u, &his_addr_len); if (status < 0) { LOG(LOGS_WARN, LOGF_Acquire, "Error reading from socket, %s", strerror(errno)); return; } switch (his_addr.u.sa_family) { case AF_INET: remote_ip.family = IPADDR_INET4; remote_ip.addr.in4 = ntohl(his_addr.in4.sin_addr.s_addr); break; #ifdef HAVE_IPV6 case AF_INET6: remote_ip.family = IPADDR_INET6; memcpy(&remote_ip.addr.in6, his_addr.in6.sin6_addr.s6_addr, sizeof (remote_ip.addr.in6)); break; #endif default: assert(0); } #if 0 printf("Got message from %s\n", UTI_IPToString(&remote_ip)); #endif /* Find matching host */ ok = 0; for (i=0; in_total_samples; src->n_dead_probes = 0; /* reset this when we actually receive something */ /* If we got into this function, we know the retransmission timeout has not expired for the source */ if (src->timer_running) { SCH_RemoveTimeout(src->timeout_id); src->timer_running = 0; } process_receive(&msg.ntp_pkt, src, &now); /* Check if server done and requeue timeout */ if ((src->n_samples >= N_GOOD_SAMPLES) || (src->n_total_samples >= MAX_SAMPLES)) { ++n_completed_sources; #if 0 printf("Source %s completed\n", UTI_IPToString(&src->ip_addr)); #endif if (n_completed_sources == n_sources) { wind_up_acquisition(); } } else { /* Send the next probe */ probe_source(src); } } } /* ================================================== */ static void start_next_source(void) { probe_source(sources + n_started_sources); #if 0 printf("Trying to start source %s\n", UTI_IPToString(&sources[n_started_sources].ip_addr)); #endif n_started_sources++; if (n_started_sources < n_sources) { source_start_timeout_id = SCH_AddTimeoutByDelay(INTER_SOURCE_START, start_source_timeout_handler, NULL); } } /* ================================================== */ static int endpoint_compare(const void *a, const void *b) { const Endpoint *aa = (const Endpoint *) a; const Endpoint *bb = (const Endpoint *) b; if (aa->offset < bb->offset) { return -1; } else if (aa->offset > bb->offset) { return +1; } else { return 0; } } /* ================================================== */ static void process_measurements(void) { SourceRecord *s; Endpoint *eps; int i, j; int n_sane_sources; double lo, hi; double inter_lo, inter_hi; int depth; int best_depth; int n_at_best_depth; Interval *intervals; double estimated_offset; int index1, index2; n_sane_sources = 0; /* First, get a consistent interval for each source. Those for which this is not possible are considered to be insane. */ for (i=0; in_samples == 0) { s->sanity = 0; } else { s->sanity = 1; /* so far ... */ lo = s->offsets[0] - s->root_distances[0]; hi = s->offsets[0] + s->root_distances[0]; inter_lo = lo; inter_hi = hi; for (j=1; jn_samples; j++) { lo = s->offsets[j] - s->root_distances[j]; hi = s->offsets[j] + s->root_distances[j]; if ((inter_hi <= lo) || (inter_lo >= hi)) { /* Oh dear, we won't get an interval for this source */ s->sanity = 0; break; } else { inter_lo = (lo < inter_lo) ? inter_lo : lo; inter_hi = (hi > inter_hi) ? inter_hi : hi; } } if (s->sanity) { s->inter_lo = inter_lo; s->inter_hi = inter_hi; } } if (s->sanity) { ++n_sane_sources; } } /* Now build the endpoint list, similar to the RFC1305 clock selection algorithm. */ eps = MallocArray(Endpoint, 2*n_sane_sources); intervals = MallocArray(Interval, n_sane_sources); j = 0; for (i=0; isanity) { eps[j].offset = s->inter_lo; eps[j].type = LO; eps[j].index = i; eps[j+1].offset = s->inter_hi; eps[j+1].type = HIGH; eps[j+1].index = i; j += 2; } } qsort(eps, 2*n_sane_sources, sizeof(Endpoint), endpoint_compare); /* Now do depth searching algorithm */ n_at_best_depth = best_depth = depth = 0; for (i=0; i<2*n_sane_sources; i++) { #if 0 fprintf(stderr, "Endpoint type %s source index %d [ip=%s] offset=%.6f\n", (eps[i].type == LO) ? "LO" : "HIGH", eps[i].index, UTI_IPToString(&sources[eps[i].index].ip_addr), eps[i].offset); #endif switch (eps[i].type) { case LO: depth++; if (depth > best_depth) { best_depth = depth; n_at_best_depth = 0; intervals[0].lo = eps[i].offset; } else if (depth == best_depth) { intervals[n_at_best_depth].lo = eps[i].offset; } else { /* Nothing to do */ } break; case HIGH: if (depth == best_depth) { intervals[n_at_best_depth].hi = eps[i].offset; n_at_best_depth++; } depth--; break; } } if (best_depth > 0) { if ((n_at_best_depth % 2) == 1) { index1 = (n_at_best_depth - 1) / 2; estimated_offset = 0.5 * (intervals[index1].lo + intervals[index1].hi); } else { index2 = (n_at_best_depth / 2); index1 = index2 - 1; estimated_offset = 0.5 * (intervals[index1].lo + intervals[index2].hi); } /* Apply a step change to the system clock. As per sign convention in local.c and its children, a positive offset means the system clock is fast of the reference, i.e. it needs to be stepped backwards. */ if (fabs(estimated_offset) > init_slew_threshold) { LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (step)", fabs(estimated_offset), (estimated_offset >= 0) ? "fast" : "slow"); LCL_ApplyStepOffset(estimated_offset); } else { LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (slew)", fabs(estimated_offset), (estimated_offset >= 0) ? "fast" : "slow"); LCL_AccumulateOffset(estimated_offset, 0.0); } } else { LOG(LOGS_WARN, LOGF_Acquire, "No intersecting endpoints found"); } Free(intervals); Free(eps); } /* ================================================== */ static void wind_up_acquisition(void) { /* Now process measurements */ process_measurements(); Free(sources); finalise_io(); if (saved_after_hook) { (saved_after_hook)(saved_after_hook_anything); } } /* ================================================== */ static void start_source_timeout_handler(void *not_used) { start_next_source(); } /* ================================================== */ void ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double threshold, void (*after_hook)(void *), void *anything) { int i, ip4, ip6; int k, duplicate_ip; saved_after_hook = after_hook; saved_after_hook_anything = anything; init_slew_threshold = threshold; n_started_sources = 0; n_completed_sources = 0; n_sources = 0; sources = MallocArray(SourceRecord, n); for (i = ip4 = ip6 = 0; i < n; i++) { /* check for duplicate IP addresses and ignore them */ duplicate_ip = 0; for (k = 0; k < i; k++) { duplicate_ip |= UTI_CompareIPs(&(sources[k].ip_addr), &ip_addrs[i], NULL) == 0; } if (!duplicate_ip) { sources[n_sources].ip_addr = ip_addrs[i]; sources[n_sources].n_samples = 0; sources[n_sources].n_total_samples = 0; sources[n_sources].n_dead_probes = 0; if (ip_addrs[i].family == IPADDR_INET4) ip4++; else if (ip_addrs[i].family == IPADDR_INET6) ip6++; n_sources++; } else { LOG(LOGS_WARN, LOGF_Acquire, "Ignoring duplicate source: %s", UTI_IPToString(&ip_addrs[i])); } } initialise_io((ip4 && ip6) ? IPADDR_UNSPEC : (ip4 ? IPADDR_INET4 : IPADDR_INET6)); /* Start sampling first source */ start_next_source(); } /* ================================================== */ chrony-1.29/cmdparse.h0000644000076400007640000000367412200721757014007 0ustar mirosmiros/* 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_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); /* 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, unsigned long *id, const char **hash, char **key); #endif /* GOT_CMDPARSE_H */ chrony-1.29/refclock.h0000644000076400007640000000450212200721757013770 0ustar mirosmiros/* 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; uint32_t ref_id; uint32_t lock_ref_id; double offset; double delay; double precision; 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_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-1.29/memory.h0000644000076400007640000000254212200721757013512 0ustar mirosmiros/* 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 #define Malloc(x) malloc(x) #define MallocNew(T) ((T *) malloc(sizeof(T))) #define MallocArray(T, n) ((T *) malloc((n) * sizeof(T))) #define Realloc(x,y) realloc(x,y) #define ReallocArray(T,n,x) ((T *) realloc((void *)(x), (n)*sizeof(T))) #define Free(x) free(x) #endif /* GOT_MEMORY_H */ chrony-1.29/conf.c0000644000076400007640000012634412200721757013131 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Miroslav Lichvar 2009-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. * ********************************************************************** ======================================================================= Module that reads and processes the configuration file. */ #include "config.h" #include "sysincl.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 "acquire.h" #include "cmdparse.h" #include "broadcast.h" #include "util.h" /* ================================================== */ /* Forward prototypes */ static void parse_acquisitionport(char *); static void parse_allow(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_cmdport(char *); static void parse_combinelimit(char *); static void parse_commandkey(char *); static void parse_corrtimeratio(char *); static void parse_deny(char *); static void parse_driftfile(char *); static void parse_dumpdir(char *); static void parse_dumponexit(char *); static void parse_fallbackdrift(char *); static void parse_generatecommandkey(char *); static void parse_include(char *); static void parse_initstepslew(char *); static void parse_keyfile(char *); static void parse_leapsectz(char *); static void parse_linux_freq_scale(char *); static void parse_linux_hz(char *); static void parse_local(char *); static void parse_lockall(char *); static void parse_log(char *); static void parse_logbanner(char *); static void parse_logchange(char *); static void parse_logdir(char *); static void parse_mailonchange(char *); static void parse_makestep(char *); static void parse_manual(char *); static void parse_maxchange(char *); static void parse_maxclockerror(char *); static void parse_maxsamples(char *line); static void parse_maxupdateskew(char *); static void parse_minsamples(char *line); static void parse_noclientlog(char *); static void parse_peer(char *); static void parse_pidfile(char *); static void parse_port(char *); static void parse_refclock(char *); static void parse_reselectdist(char *); static void parse_rtcdevice(char *); static void parse_rtcfile(char *); static void parse_rtconutc(char *); static void parse_rtcsync(char *); static void parse_sched_priority(char *); static void parse_server(char *); static void parse_stratumweight(char *); static void parse_tempcomp(char *); static void parse_user(char *); /* ================================================== */ /* Configuration variables */ static int restarted = 0; static int generate_command_key = 0; static char *rtc_device = "/dev/rtc"; static int acquisition_port = 0; /* 0 means let kernel choose port */ static int ntp_port = 123; static char *keys_file = NULL; static char *drift_file = NULL; static char *rtc_file = NULL; static unsigned long command_key_id; static double max_update_skew = 1000.0; static double correction_time_ratio = 1.0; static double max_clock_error = 1.0; /* in ppm */ static double reselect_distance = 1e-4; static double stratum_weight = 1.0; 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; static int do_init_stepslew = 0; static int n_init_srcs; /* Threshold (in seconds) - if absolute value of initial error is less than this, slew instead of stepping */ static double init_slew_threshold; #define MAX_INIT_SRCS 8 static IPAddr init_srcs_ip[MAX_INIT_SRCS]; static int enable_manual=0; /* Flag set if the RTC runs UTC (default is it runs local time incl. daylight saving). */ static int rtc_on_utc = 0; /* Flag set if 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; /* 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 socket to. UNSPEC family means INADDR_ANY will be used */ static IPAddr bind_address4, bind_address6; /* IP addresses for binding the command socket to. UNSPEC family means use the value of bind_address */ 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 = "/var/run/chronyd.pid"; /* Temperature sensor, update interval and compensation coefficients */ static char *tempcomp_file = NULL; static double tempcomp_interval; static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2; /* Boolean for whether the Linux HZ value has been overridden, and the * new value. */ static int set_linux_hz = 0; static int linux_hz; /* Boolean for whether the Linux frequency scaling value (i.e. the one that's * approx (1< 0 ? "Missing" : "Too many", processed_command, line_number, processed_file); } } /* ================================================== */ void CNF_SetRestarted(int r) { restarted = r; } /* ================================================== */ /* Read the configuration file */ void CNF_ReadFile(const char *filename) { FILE *in; char line[2048]; char *p, *command; const char *prev_processed_file; int prev_line_number; in = fopen(filename, "r"); if (!in) { LOG_FATAL(LOGF_Configure, "Could not open configuration file %s", filename); } else { /* Save current line number in case this is an included file */ prev_line_number = line_number; prev_processed_file = processed_file; line_number = 0; processed_file = filename; /* Success */ while (fgets(line, sizeof(line), in)) { line_number++; /* Remove extra white-space and comments */ CPS_NormalizeLine(line); /* Skip blank lines */ if (!*line) continue; /* We have a real line, now try to match commands */ processed_command = command = line; p = CPS_SplitWord(line); if (!strcasecmp(command, "acquisitionport")) { parse_acquisitionport(p); } else if (!strcasecmp(command, "allow")) { parse_allow(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_cmdport(p); } else if (!strcasecmp(command, "combinelimit")) { parse_combinelimit(p); } else if (!strcasecmp(command, "commandkey")) { parse_commandkey(p); } else if (!strcasecmp(command, "corrtimeratio")) { parse_corrtimeratio(p); } else if (!strcasecmp(command, "deny")) { parse_deny(p); } else if (!strcasecmp(command, "driftfile")) { parse_driftfile(p); } else if (!strcasecmp(command, "dumpdir")) { parse_dumpdir(p); } else if (!strcasecmp(command, "dumponexit")) { parse_dumponexit(p); } else if (!strcasecmp(command, "fallbackdrift")) { parse_fallbackdrift(p); } else if (!strcasecmp(command, "generatecommandkey")) { parse_generatecommandkey(p); } else if (!strcasecmp(command, "include")) { parse_include(p); } else if (!strcasecmp(command, "initstepslew")) { parse_initstepslew(p); } else if (!strcasecmp(command, "keyfile")) { parse_keyfile(p); } else if (!strcasecmp(command, "leapsectz")) { parse_leapsectz(p); } else if (!strcasecmp(command, "linux_freq_scale")) { parse_linux_freq_scale(p); } else if (!strcasecmp(command, "linux_hz")) { parse_linux_hz(p); } else if (!strcasecmp(command, "local")) { parse_local(p); } else if (!strcasecmp(command, "lock_all")) { parse_lockall(p); } else if (!strcasecmp(command, "log")) { parse_log(p); } else if (!strcasecmp(command, "logbanner")) { parse_logbanner(p); } else if (!strcasecmp(command, "logchange")) { parse_logchange(p); } else if (!strcasecmp(command, "logdir")) { parse_logdir(p); } else if (!strcasecmp(command, "mailonchange")) { parse_mailonchange(p); } else if (!strcasecmp(command, "makestep")) { parse_makestep(p); } else if (!strcasecmp(command, "manual")) { parse_manual(p); } else if (!strcasecmp(command, "maxchange")) { parse_maxchange(p); } else if (!strcasecmp(command, "maxclockerror")) { parse_maxclockerror(p); } else if (!strcasecmp(command, "maxsamples")) { parse_maxsamples(p); } else if (!strcasecmp(command, "maxupdateskew")) { parse_maxupdateskew(p); } else if (!strcasecmp(command, "minsamples")) { parse_minsamples(p); } else if (!strcasecmp(command, "noclientlog")) { parse_noclientlog(p); } else if (!strcasecmp(command, "peer")) { parse_peer(p); } else if (!strcasecmp(command, "pidfile")) { parse_pidfile(p); } else if (!strcasecmp(command, "port")) { parse_port(p); } else if (!strcasecmp(command, "refclock")) { parse_refclock(p); } else if (!strcasecmp(command, "reselectdist")) { parse_reselectdist(p); } else if (!strcasecmp(command, "rtcdevice")) { parse_rtcdevice(p); } else if (!strcasecmp(command, "rtcfile")) { parse_rtcfile(p); } else if (!strcasecmp(command, "rtconutc")) { parse_rtconutc(p); } else if (!strcasecmp(command, "rtcsync")) { parse_rtcsync(p); } else if (!strcasecmp(command, "sched_priority")) { parse_sched_priority(p); } else if (!strcasecmp(command, "server")) { parse_server(p); } else if (!strcasecmp(command, "stratumweight")) { parse_stratumweight(p); } else if (!strcasecmp(command, "tempcomp")) { parse_tempcomp(p); } else if (!strcasecmp(command, "user")) { parse_user(p); } else { other_parse_error("Invalid command"); } } line_number = prev_line_number; processed_file = prev_processed_file; fclose(in); } } /* ================================================== */ static void parse_source(char *line, NTP_Source_Type type) { CPS_Status status; if (n_ntp_sources >= MAX_NTP_SOURCES) return; ntp_sources[n_ntp_sources].type = type; status = CPS_ParseNTPSourceAdd(line, &ntp_sources[n_ntp_sources].params); switch (status) { case CPS_Success: n_ntp_sources++; break; case CPS_BadOption: other_parse_error("Invalid server/peer parameter"); break; case CPS_BadHost: other_parse_error("Invalid host/IP address"); break; case CPS_BadPort: other_parse_error("Unreadable port"); break; case CPS_BadMinpoll: other_parse_error("Unreadable minpoll"); break; case CPS_BadMaxpoll: other_parse_error("Unreadable maxpoll"); break; case CPS_BadPresend: other_parse_error("Unreadable presend"); break; case CPS_BadMaxdelaydevratio: other_parse_error("Unreadable maxdelaydevratio"); break; case CPS_BadMaxdelayratio: other_parse_error("Unreadable maxdelayratio"); break; case CPS_BadMaxdelay: other_parse_error("Unreadable maxdelay"); break; case CPS_BadKey: other_parse_error("Unreadable key"); break; case CPS_BadMinstratum: other_parse_error("Unreadable minstratum"); break; case CPS_BadPolltarget: other_parse_error("Unreadable polltarget"); break; } } /* ================================================== */ static void parse_sched_priority(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%d", &sched_priority) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_lockall(char *line) { check_number_of_args(line, 0); lock_memory = 1; } /* ================================================== */ static void parse_server(char *line) { parse_source(line, NTP_SERVER); } /* ================================================== */ static void parse_peer(char *line) { parse_source(line, NTP_PEER); } /* ================================================== */ static void parse_refclock(char *line) { int i, n, poll, dpoll, filter_length, pps_rate; uint32_t ref_id, lock_ref_id; double offset, delay, precision; char *p, *cmd, *name, *param; unsigned char ref[5]; SRC_SelectOption sel_option; i = n_refclock_sources; if (i >= MAX_RCL_SOURCES) return; poll = 4; dpoll = 0; filter_length = 64; pps_rate = 0; offset = 0.0; delay = 1e-9; precision = 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, "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, "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_sources[i].driver_name = name; refclock_sources[i].driver_parameter = param; refclock_sources[i].driver_poll = dpoll; refclock_sources[i].poll = poll; refclock_sources[i].filter_length = filter_length; refclock_sources[i].pps_rate = pps_rate; refclock_sources[i].offset = offset; refclock_sources[i].delay = delay; refclock_sources[i].precision = precision; refclock_sources[i].sel_option = sel_option; refclock_sources[i].ref_id = ref_id; refclock_sources[i].lock_ref_id = lock_ref_id; n_refclock_sources++; } /* ================================================== */ static void parse_some_port(char *line, int *portvar) { check_number_of_args(line, 1); if (sscanf(line, "%d", portvar) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_acquisitionport(char *line) { parse_some_port(line, &acquisition_port); } /* ================================================== */ static void parse_port(char *line) { parse_some_port(line, &ntp_port); } /* ================================================== */ static void parse_maxupdateskew(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &max_update_skew) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_maxclockerror(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &max_clock_error) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_corrtimeratio(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &correction_time_ratio) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_reselectdist(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &reselect_distance) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_stratumweight(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &stratum_weight) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_combinelimit(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &combine_limit) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_driftfile(char *line) { check_number_of_args(line, 1); drift_file = strdup(line); } /* ================================================== */ static void parse_keyfile(char *line) { check_number_of_args(line, 1); keys_file = strdup(line); } /* ================================================== */ static void parse_rtcfile(char *line) { check_number_of_args(line, 1); rtc_file = strdup(line); } /* ================================================== */ static void parse_rtcdevice(char *line) { check_number_of_args(line, 1); rtc_device = strdup(line); } /* ================================================== */ static void parse_logbanner(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%d", &log_banner) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_logdir(char *line) { check_number_of_args(line, 1); logdir = strdup(line); } /* ================================================== */ static void parse_maxsamples(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%d", &max_samples) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_minsamples(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%d", &min_samples) != 1) { command_parse_error(); } } /* ================================================== */ static void parse_dumpdir(char *line) { check_number_of_args(line, 1); dumpdir = strdup(line); } /* ================================================== */ static void parse_dumponexit(char *line) { check_number_of_args(line, 0); do_dump_on_exit = 1; } /* ================================================== */ 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_commandkey(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lu", &command_key_id) != 1) { command_parse_error(); } } /* ================================================== */ 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_cmdport(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%d", &cmd_port) != 1) { 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; } n_init_srcs = 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) == DNS_Success) { init_srcs_ip[n_init_srcs] = ip_addr; ++n_init_srcs; } else { LOG(LOGS_WARN, LOGF_Configure, "Could not resolve address of initstepslew server %s", hostname); } if (n_init_srcs >= MAX_INIT_SRCS) { other_parse_error("Too many initstepslew servers"); } } } if (n_init_srcs > 0) { do_init_stepslew = 1; } } /* ================================================== */ static void parse_manual(char *line) { check_number_of_args(line, 0); enable_manual = 1; } /* ================================================== */ static void parse_rtconutc(char *line) { check_number_of_args(line, 0); rtc_on_utc = 1; } /* ================================================== */ static void parse_rtcsync(char *line) { check_number_of_args(line, 0); rtc_sync = 1; } /* ================================================== */ static void parse_noclientlog(char *line) { check_number_of_args(line, 0); no_client_log = 1; } /* ================================================== */ 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_generatecommandkey(char *line) { check_number_of_args(line, 0); generate_command_key = 1; } /* ================================================== */ 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_logchange(char *line) { check_number_of_args(line, 1); if (sscanf(line, "%lf", &log_change_threshold) == 1) { do_log_change = 1; } else { do_log_change = 0; command_parse_error(); } } /* ================================================== */ static void parse_mailonchange(char *line) { char *address; check_number_of_args(line, 2); address = line; line = CPS_SplitWord(line); 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, AllowDeny *list, 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 = MallocNew(AllowDeny); 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 = MallocNew(AllowDeny); 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) == DNS_Success) { new_node = MallocNew(AllowDeny); 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(); } } } if (new_node) { new_node->prev = list->prev; new_node->next = list; list->prev->next = new_node; list->prev = new_node; } } /* ================================================== */ static void parse_allow(char *line) { parse_allow_deny(line, &ntp_auth_list, 1); } /* ================================================== */ static void parse_deny(char *line) { parse_allow_deny(line, &ntp_auth_list, 0); } /* ================================================== */ static void parse_cmdallow(char *line) { parse_allow_deny(line, &cmd_auth_list, 1); } /* ================================================== */ static void parse_cmddeny(char *line) { parse_allow_deny(line, &cmd_auth_list, 0); } /* ================================================== */ 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_pidfile(char *line) { check_number_of_args(line, 1); pidfile = strdup(line); } /* ================================================== */ static void parse_broadcast(char *line) { /* Syntax : broadcast [] */ 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; } if (max_broadcasts == n_broadcasts) { /* Expand array */ max_broadcasts += 8; if (broadcasts) { broadcasts = ReallocArray(NTP_Broadcast_Destination, max_broadcasts, broadcasts); } else { broadcasts = MallocArray(NTP_Broadcast_Destination, max_broadcasts); } } broadcasts[n_broadcasts].addr = ip; broadcasts[n_broadcasts].port = port; broadcasts[n_broadcasts].interval = interval; ++n_broadcasts; } /* ================================================== */ static void parse_tempcomp(char *line) { char *p; check_number_of_args(line, 6); p = line; line = CPS_SplitWord(line); if (!*p) { command_parse_error(); return; } if (sscanf(line, "%lf %lf %lf %lf %lf", &tempcomp_interval, &tempcomp_T0, &tempcomp_k0, &tempcomp_k1, &tempcomp_k2) != 5) { command_parse_error(); return; } tempcomp_file = strdup(p); } /* ================================================== */ static void parse_include(char *line) { check_number_of_args(line, 1); CNF_ReadFile(line); } /* ================================================== */ static void parse_leapsectz(char *line) { check_number_of_args(line, 1); leapsec_tz = strdup(line); } /* ================================================== */ static void parse_linux_hz(char *line) { check_number_of_args(line, 1); if (1 == sscanf(line, "%d", &linux_hz)) { set_linux_hz = 1; } else { command_parse_error(); } } /* ================================================== */ static void parse_linux_freq_scale(char *line) { check_number_of_args(line, 1); if (1 == sscanf(line, "%lf", &linux_freq_scale)) { set_linux_freq_scale = 1; } else { command_parse_error(); } } /* ================================================== */ static void parse_user(char *line) { check_number_of_args(line, 1); user = strdup(line); } /* ================================================== */ void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything) { if (do_init_stepslew) { ACQ_StartAcquisition(n_init_srcs, init_srcs_ip, init_slew_threshold, after_hook, anything); } else { (after_hook)(anything); } } /* ================================================== */ void CNF_AddSources(void) { int i; for (i=0; inext) { status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); if (!status) { LOG_FATAL(LOGF_Configure, "Bad subnet for %08lx", node->ip); } } for (node = cmd_auth_list.next; node != &cmd_auth_list; node = node->next) { status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all); if (!status) { LOG_FATAL(LOGF_Configure, "Bad subnet for %08lx", node->ip); } } } /* ================================================== */ 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_GetBindCommandAddress(int family, IPAddr *addr) { if (family == IPADDR_INET4) *addr = bind_cmd_address4.family != IPADDR_UNSPEC ? bind_cmd_address4 : bind_address4; else if (family == IPADDR_INET6) *addr = bind_cmd_address6.family != IPADDR_UNSPEC ? bind_cmd_address6 : bind_address6; else addr->family = IPADDR_UNSPEC; } /* ================================================== */ char * CNF_GetPidFile(void) { return pidfile; } /* ================================================== */ char * CNF_GetLeapSecTimezone(void) { return leapsec_tz; } /* ================================================== */ void CNF_GetLinuxHz(int *set, int *hz) { *set = set_linux_hz; *hz = linux_hz; } /* ================================================== */ void CNF_GetLinuxFreqScale(int *set, double *freq_scale) { *set = set_linux_freq_scale; *freq_scale = linux_freq_scale ; } /* ================================================== */ int CNF_GetSchedPriority(void) { return sched_priority; } /* ================================================== */ int CNF_GetLockMemory(void) { return lock_memory; } /* ================================================== */ void CNF_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k1, double *k2) { *file = tempcomp_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; } chrony-1.29/sys_sunos.h0000644000076400007640000000221512200721757014244 0ustar mirosmiros/* 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-1.29/refclock.c0000644000076400007640000005642312200721757013774 0ustar mirosmiros/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * 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. * ********************************************************************** ======================================================================= Routines implementing reference clocks. */ #include "config.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; 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; 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; struct MedianFilter filter; uint32_t ref_id; uint32_t lock_ref; double offset; double delay; double precision; SCH_TimeoutID timeout_id; SRC_Instance source; }; #define MAX_RCL_SOURCES 8 static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES]; static int n_sources = 0; 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, int is_step_change, 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); 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); void RCL_Initialise(void) { CNF_AddRefclocks(); if (n_sources > 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) { int i; for (i = 0; i < n_sources; i++) { RCL_Instance inst = (RCL_Instance)&refclocks[i]; if (inst->driver->fini) inst->driver->fini(inst); filter_fini(&inst->filter); Free(inst->driver_parameter); } if (n_sources > 0) { LCL_RemoveParameterChangeHandler(slew_samples, NULL); LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL); } } int RCL_AddRefclock(RefclockParameters *params) { int pps_source = 0; RCL_Instance inst = &refclocks[n_sources]; if (n_sources == MAX_RCL_SOURCES) return 0; 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 { 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->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 }; snprintf((char *)ref, 5, "%3.3s%d", params->driver_name, n_sources % 10); 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); inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL); #if 0 LOG(LOGS_INFO, LOGF_Refclock, "refclock added poll=%d dpoll=%d filter=%d", inst->poll, inst->driver_poll, params->filter_length); #endif n_sources++; Free(params->driver_name); return 1; } void RCL_StartRefclocks(void) { int i, j; for (i = 0; i < n_sources; i++) { RCL_Instance inst = &refclocks[i]; SRC_SetSelectable(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_sources && refclocks[j].ref_id != inst->lock_ref; j++) ; inst->lock_ref = (j < n_sources) ? j : -1; } else inst->lock_ref = -1; } } void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) { int i; uint32_t ref_id; assert(report->ip_addr.family == IPADDR_INET4); ref_id = report->ip_addr.addr.in4; for (i = 0; i < n_sources; i++) { RCL_Instance inst = &refclocks[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 + filter_get_avg_sample_dispersion(&instance->filter); if (!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; } 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; LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion); UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time); dispersion += instance->precision + filter_get_avg_sample_dispersion(&instance->filter); if (!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) { struct timeval ref_sample_time; double sample_diff, ref_offset, ref_dispersion, shift; if (!filter_get_last_sample(&refclocks[instance->lock_ref].filter, &ref_sample_time, &ref_offset, &ref_dispersion)) return 0; UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time); if (fabs(sample_diff) >= 2.0 / rate) 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) return 0; #if 0 LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f", second, offset, ref_offset - offset, sample_diff); #endif } else { struct timeval ref_time; int is_synchronised, stratum; double root_delay, root_dispersion, distance; NTP_Leap leap; 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) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse dropped second=%.9f sync=%d dist=%.9f", second, is_synchronised, distance); #endif /* Drop also all stored samples */ filter_reset(&instance->filter); return 0; } } filter_add_sample(&instance->filter, &cooked_time, offset, dispersion); instance->leap_status = LEAP_Normal; 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 > 1 << (instance->poll + 1)) return 0; return 1; } static int pps_stratum(RCL_Instance instance, struct timeval *tv) { struct timeval ref_time; int is_synchronised, stratum, i; double root_delay, root_dispersion; NTP_Leap leap; uint32_t ref_id; 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 < n_sources; i++) { if (refclocks[i].ref_id == ref_id && refclocks[i].pps_rate && refclocks[i].lock_ref == -1) return stratum - 1; } return 0; } static void poll_timeout(void *arg) { double next; 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_rate && 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); log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion); } else { SRC_UpdateReachability(inst->source, 0); } } if (poll >= 0) next = 1 << poll; else next = 1.0 / (1 << -poll); inst->timeout_id = SCH_AddTimeoutByDelay(next, poll_timeout, arg); } static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double doffset, int is_step_change, void *anything) { int i; for (i = 0; i < n_sources; i++) filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset); } static void add_dispersion(double dispersion, void *anything) { int i; for (i = 0; i < n_sources; i++) filter_add_dispersion(&refclocks[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) { 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->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; } 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; e -= sqrt(filter->avg_var); 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); } 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, prev_offset; 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); prev_offset = filter->samples[i].offset; filter->samples[i].offset -= delta_time; #if 0 LOG(LOGS_INFO, LOGF_Refclock, "i=%d old_off=%.9f new_off=%.9f", i, prev_offset, filter->samples[i].offset); #else (void)prev_offset; #endif } } 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-1.29/sys_netbsd.c0000644000076400007640000001707612200721757014362 0ustar mirosmiros/* 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 void 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) { LOG_FATAL(LOGF_SysNetBSD, "settimeofday() failed"); } UTI_AddDoubleToTimeval(&T0, offset, &T1); T0 = T1; start_adjust(); } /* ================================================== */ 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; FILE *fp; 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 */); } /* ================================================== */ void SYS_NetBSD_Finalise(void) { clock_finalise(); } /* ================================================== */ #endif /* __NetBSD__ */ chrony-1.29/getdate.y0000644000076400007640000006226412200721757013647 0ustar mirosmiros%{ /* ** 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 (*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-1.29/sys_netbsd.h0000644000076400007640000000227112200721757014356 0ustar mirosmiros/* 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-1.29/sourcestats.h0000644000076400007640000001326412200721757014564 0ustar mirosmiros/* 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); /* This function deletes an instance of the statistics handler. */ extern void SST_DeleteInstance(SST_Stats inst); /* This function accumulates a single sample into the statistics handler sample_time is the epoch at which the sample is to be considered to have been made. offset is the offset of the local clock relative to the source in seconds. Positive indicates that the local clock if FAST (contrary to the NTP parts of the software) root_distance is the Lambda+Delta/2 term in RFC1305, but excluding the extra dispersion due to the residual standard deviation after we have done the regression fit. stratum is the stratum of the source from which the sample came. */ extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum); /* This function runs the linear regression operation on the data. It finds the set of most recent samples that give the tightest confidence interval for the frequency, and truncates the register down to that number of samples. */ extern void SST_DoNewRegression(SST_Stats inst); /* This function does a simple regression on what is in the register, without trying to optimise the error bounds on the frequency by deleting old samples */ extern void SST_DoUpdateRegression(SST_Stats inst); /* Return the assumed worst case range of values that this source's frequency lies within. Frequency is defined as the amount of time the local clock gains relative to the source per unit local clock time. */ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi); /* Get data needed for selection */ extern void SST_GetSelectionData(SST_Stats inst, struct timeval *now, int *stratum, double *offset_lo_limit, double *offset_hi_limit, double *root_distance, double *variance, 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); typedef enum { SST_Skew_Decrease, SST_Skew_Nochange, SST_Skew_Increase } SST_Skew_Direction; extern SST_Skew_Direction SST_LastSkewChange(SST_Stats inst); extern int SST_Samples(SST_Stats inst); #endif /* GOT_SOURCESTATS_H */ chrony-1.29/addrfilt.h0000644000076400007640000000511512200721757013772 0ustar mirosmiros/* 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); #endif /* GOT_ADDRFILT_H */ chrony-1.29/mkdirpp.c0000644000076400007640000000542212200721757013643 0ustar mirosmiros/* 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 "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-1.29/getdate.h0000644000076400007640000000175612200721757013625 0ustar mirosmiros/* 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-1.29/strerror.c0000644000076400007640000000231312200721757014053 0ustar mirosmiros/* 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-1.29/regress.c0000644000076400007640000004272612200721757013657 0ustar mirosmiros/* 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 */ #define MIN_SAMPLES_FOR_REGRESS 3 int RGR_FindBestRegression (double *x, /* independent variable */ double *y, /* measured data */ double *w, /* weightings (large => data less reliable) */ int n, /* number of data points */ 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); 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 (x[l] < piv && l < v) 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-1.29/refclock_shm.c0000644000076400007640000000660712200721757014642 0ustar mirosmiros/* 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) { #if 0 LOG(LOGS_INFO, LOGF_Refclock, "sample ignored mode: %d count: %d valid: %d", t.mode, t.count, t.valid); #endif 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 };