mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 17:45:07 -05:00
Compare commits
290 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4bedce1f4 | ||
|
|
79eacdb7e6 | ||
|
|
cf19042ecb | ||
|
|
d856bd34c4 | ||
|
|
ebab36e859 | ||
|
|
3988a1e9a8 | ||
|
|
949ef3e1dc | ||
|
|
dd12303276 | ||
|
|
f1379a6574 | ||
|
|
ad58384760 | ||
|
|
0e786f5907 | ||
|
|
e1accce498 | ||
|
|
28db0fdde9 | ||
|
|
584bf9382b | ||
|
|
0168b405a3 | ||
|
|
b5e0d76337 | ||
|
|
c924fba4fa | ||
|
|
8ec43a39af | ||
|
|
9f16445464 | ||
|
|
1a795b04ee | ||
|
|
b862f3e64d | ||
|
|
4e66b5ce8a | ||
|
|
d446950c6a | ||
|
|
e3c77f9b4b | ||
|
|
090ec985f3 | ||
|
|
e63bd490b0 | ||
|
|
badf97d4ba | ||
|
|
ba283e6b6e | ||
|
|
0bdac2c7b3 | ||
|
|
58b211d707 | ||
|
|
068ce237af | ||
|
|
a5e9e5d0df | ||
|
|
e0af8069c1 | ||
|
|
696b05d6e6 | ||
|
|
7e1a699616 | ||
|
|
716d73d982 | ||
|
|
38ac081114 | ||
|
|
5fce101f85 | ||
|
|
c6e064200d | ||
|
|
c52e9085d1 | ||
|
|
d0fb17d70c | ||
|
|
713153b610 | ||
|
|
09d039fba6 | ||
|
|
07f7f28058 | ||
|
|
a2b40f527d | ||
|
|
6d8ffeefd6 | ||
|
|
9ce25bab04 | ||
|
|
cd5105b1db | ||
|
|
ff4abc69c3 | ||
|
|
192f74f0a1 | ||
|
|
be203d9af0 | ||
|
|
f8af299321 | ||
|
|
474b2af1a6 | ||
|
|
cb88cea3c4 | ||
|
|
fc2892fbb0 | ||
|
|
36b25cbd2b | ||
|
|
d18c071849 | ||
|
|
61b629fdad | ||
|
|
29647c8280 | ||
|
|
97b15cb3ae | ||
|
|
f725921dfb | ||
|
|
a4b4d0c0d8 | ||
|
|
f59ade7f80 | ||
|
|
a9b9e7befe | ||
|
|
ead9394a31 | ||
|
|
80129fa9ab | ||
|
|
18796a3c18 | ||
|
|
f632b6d4cb | ||
|
|
7799e14770 | ||
|
|
20aab86e12 | ||
|
|
b7766478a6 | ||
|
|
3d57b7a44d | ||
|
|
51a2b436f4 | ||
|
|
88015081f2 | ||
|
|
20cc1f6550 | ||
|
|
43cca04c33 | ||
|
|
17d944c333 | ||
|
|
6789b5165c | ||
|
|
d631d7e81f | ||
|
|
c6245dc616 | ||
|
|
4b36799ce1 | ||
|
|
d26bb9b4eb | ||
|
|
698404b02f | ||
|
|
d46d7ad947 | ||
|
|
7c6630905d | ||
|
|
129aa587c6 | ||
|
|
cc1c6c94e3 | ||
|
|
41266cbaa0 | ||
|
|
fbfd261da6 | ||
|
|
71602b8ee6 | ||
|
|
14cae239f6 | ||
|
|
2e9e309a0d | ||
|
|
3fba33d5f5 | ||
|
|
77a7162361 | ||
|
|
75efa5174c | ||
|
|
c62afbe77b | ||
|
|
a6f0688f46 | ||
|
|
5762d33e38 | ||
|
|
9c6d1c214f | ||
|
|
a5c865937f | ||
|
|
f48fd84d76 | ||
|
|
a8693a21f8 | ||
|
|
9b630a0664 | ||
|
|
79ac20c161 | ||
|
|
cb74f3e7ad | ||
|
|
c4865e2cb6 | ||
|
|
20d2363fb7 | ||
|
|
64ba5a5b65 | ||
|
|
9913851413 | ||
|
|
e9a8503c6b | ||
|
|
a02d7555c2 | ||
|
|
779b341b61 | ||
|
|
a646cf7923 | ||
|
|
0dea8d97f4 | ||
|
|
97ba9e4d47 | ||
|
|
3f3ebd3b3b | ||
|
|
571669ad6c | ||
|
|
855eb09d58 | ||
|
|
ead3ca14a0 | ||
|
|
34f12c0864 | ||
|
|
28876d6afa | ||
|
|
cea68ebc6f | ||
|
|
a33a955163 | ||
|
|
a3e60c93da | ||
|
|
44c9744d69 | ||
|
|
b69b648d18 | ||
|
|
3ebebac695 | ||
|
|
13d734c8d2 | ||
|
|
c903c5f72b | ||
|
|
b03c7581f2 | ||
|
|
2ed9853bcc | ||
|
|
26e00ffbeb | ||
|
|
b745b6d546 | ||
|
|
e147f2f11e | ||
|
|
14687d003d | ||
|
|
e8bb95ba55 | ||
|
|
a43810533f | ||
|
|
98bbfdf73c | ||
|
|
ee53d816ce | ||
|
|
e176587e96 | ||
|
|
8210be0f17 | ||
|
|
f88a712d01 | ||
|
|
9cf78b974a | ||
|
|
3e1dc801b0 | ||
|
|
cf3c7b3bd6 | ||
|
|
0b7f64cb33 | ||
|
|
ec4542bbe4 | ||
|
|
fc235a3f16 | ||
|
|
4cf8395470 | ||
|
|
fd03d823f2 | ||
|
|
e65fa1aa7b | ||
|
|
3de72917c3 | ||
|
|
b3b2f67d2f | ||
|
|
c2dc25e062 | ||
|
|
ad9c360845 | ||
|
|
a65686e83f | ||
|
|
fe35de6931 | ||
|
|
3f1aea2f53 | ||
|
|
0c542dcd3d | ||
|
|
5483567190 | ||
|
|
d243f1f8fe | ||
|
|
9b137b2e37 | ||
|
|
6ee357d230 | ||
|
|
779e40ed66 | ||
|
|
be3439fef1 | ||
|
|
ed96b4d49d | ||
|
|
5ca8aa7840 | ||
|
|
1eede1bc08 | ||
|
|
cc86461d9b | ||
|
|
29c5ca9091 | ||
|
|
86fbcdc62b | ||
|
|
e82220974e | ||
|
|
46951b8598 | ||
|
|
08faca03b7 | ||
|
|
3217421797 | ||
|
|
7fa22d4c25 | ||
|
|
8671002bd7 | ||
|
|
bc6b40568d | ||
|
|
3888b9dcec | ||
|
|
ae104b5c28 | ||
|
|
5cb7e6c9c3 | ||
|
|
ff31702f74 | ||
|
|
3edd3fe5a4 | ||
|
|
4f3fb95981 | ||
|
|
7c7ab95e2e | ||
|
|
70feea48f8 | ||
|
|
c546c48d0d | ||
|
|
0baf00e1c0 | ||
|
|
788e7fcd89 | ||
|
|
7c45b1d2a3 | ||
|
|
93b66ac141 | ||
|
|
610284dcc3 | ||
|
|
60d8586b6d | ||
|
|
70928dba52 | ||
|
|
7fda9c6723 | ||
|
|
4932f9d077 | ||
|
|
0094128ca6 | ||
|
|
de5178575f | ||
|
|
9eac078c18 | ||
|
|
05c5445fe2 | ||
|
|
f9d8b6f99e | ||
|
|
597a37d66e | ||
|
|
73e4986866 | ||
|
|
91e74c704b | ||
|
|
727bf195d1 | ||
|
|
b13836e9cc | ||
|
|
cf12d72f21 | ||
|
|
5c2bbaca3b | ||
|
|
b717904f9e | ||
|
|
f2c4ab09a8 | ||
|
|
9a657cd4a3 | ||
|
|
308de81221 | ||
|
|
6823109cfb | ||
|
|
a02149cf65 | ||
|
|
7aa4bbf621 | ||
|
|
5afddad0d2 | ||
|
|
0380cf0c76 | ||
|
|
6c2a1e62e0 | ||
|
|
6560628209 | ||
|
|
3cc81376a6 | ||
|
|
8d02e5f680 | ||
|
|
f9e2213afd | ||
|
|
8b362ba3e7 | ||
|
|
eecec8fffa | ||
|
|
a26058d425 | ||
|
|
c14b81f3a9 | ||
|
|
0059a43254 | ||
|
|
7dd3cc354d | ||
|
|
ce34aa0763 | ||
|
|
7a512ad9c3 | ||
|
|
0a56c0e8c1 | ||
|
|
0b71504ee9 | ||
|
|
9479c6451e | ||
|
|
115e83f3aa | ||
|
|
ea526b96dd | ||
|
|
726cf84e19 | ||
|
|
dc8a46363f | ||
|
|
916ca7ab86 | ||
|
|
be036ed58a | ||
|
|
3f507b782c | ||
|
|
2fc3525fdf | ||
|
|
0f3e464202 | ||
|
|
925d7119ec | ||
|
|
f456cd57b9 | ||
|
|
ea58500cef | ||
|
|
4048b200ed | ||
|
|
54211f0f6e | ||
|
|
4b5f465026 | ||
|
|
7efd1151cb | ||
|
|
19dbe52930 | ||
|
|
2a981b7d39 | ||
|
|
d34ebdb431 | ||
|
|
60d0fa2993 | ||
|
|
8545ba733a | ||
|
|
04c8a8c75d | ||
|
|
b7ed44f113 | ||
|
|
46a39716b6 | ||
|
|
e77b0070af | ||
|
|
3c5cf81e32 | ||
|
|
be14dbffef | ||
|
|
b4f6a0f94a | ||
|
|
c15acff39a | ||
|
|
ed0ac6e3f6 | ||
|
|
6cc9c4940c | ||
|
|
0e8b27556d | ||
|
|
162c6a49b5 | ||
|
|
61dbdd80b9 | ||
|
|
8df1bedb1b | ||
|
|
b55e914273 | ||
|
|
1c3aff37de | ||
|
|
4bbc5520b8 | ||
|
|
0731cd6950 | ||
|
|
f88e96599c | ||
|
|
cd7bfa2510 | ||
|
|
ea418b2e18 | ||
|
|
c00d93a897 | ||
|
|
bc361c48a0 | ||
|
|
1f39169be3 | ||
|
|
030833087d | ||
|
|
c38dbcc6b5 | ||
|
|
ba1d548cc8 | ||
|
|
7261d11bb0 | ||
|
|
f38872eab3 | ||
|
|
1939aae70e | ||
|
|
3f85d1dcc1 | ||
|
|
922e2fe23b | ||
|
|
d5a9c1535e | ||
|
|
169eee6792 | ||
|
|
9c398051bb | ||
|
|
1d289787b6 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,3 +17,5 @@ config.h
|
||||
config.log
|
||||
tags
|
||||
version.h
|
||||
/test/simulation/clknetsim
|
||||
/test/simulation/tmp
|
||||
|
||||
95
INSTALL
95
INSTALL
@@ -1,95 +0,0 @@
|
||||
The software is distributed as source code which has to be compiled.
|
||||
|
||||
PARTS OF THE SOFTWARE ARE HIGHLY SYSTEM-SPECIFIC AND NON-PORTABLE.
|
||||
UNLESS YOU ARE RUNNING A SUPPORTED SYSTEM, BE PREPARED FOR SOME
|
||||
PROGRAMMING!
|
||||
|
||||
After unpacking the source code, change directory into it, and type
|
||||
|
||||
./configure
|
||||
|
||||
This is a shell script that automatically determines the system type.
|
||||
There is a single optional parameter, --prefix which indicates the
|
||||
directory tree where the software should be installed. For example,
|
||||
|
||||
./configure --prefix=/opt/free
|
||||
|
||||
will install the chronyd daemon into /opt/free/sbin and the chronyc
|
||||
control program into /opt/free/bin. The default value for the prefix
|
||||
is /usr/local.
|
||||
|
||||
The configure script assumes you want to use gcc as your compiler.
|
||||
If you want to use a different compiler, you can configure this way:
|
||||
|
||||
CC=cc CFLAGS=-O ./configure --prefix=/opt/free
|
||||
|
||||
for Bourne-family shells, or
|
||||
|
||||
setenv CC cc
|
||||
setenv CFLAGS -O
|
||||
./configure --prefix=/opt/free
|
||||
|
||||
for C-family shells.
|
||||
|
||||
If the software cannot (yet) be built on your system, an error message
|
||||
will be shown. Otherwise, `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.
|
||||
|
||||
|
||||
24
Makefile.in
24
Makefile.in
@@ -42,10 +42,10 @@ 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 \
|
||||
nameserv.o nameserv_async.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)
|
||||
broadcast.o refclock.o refclock_phc.o refclock_pps.o \
|
||||
refclock_shm.o refclock_sock.o tempcomp.o $(HASH_OBJ)
|
||||
|
||||
EXTRA_OBJS=@EXTRA_OBJECTS@
|
||||
|
||||
@@ -66,10 +66,10 @@ EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
|
||||
all : chronyd chronyc
|
||||
|
||||
chronyd : $(OBJS) $(EXTRA_OBJS)
|
||||
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) @HASH_LINK@ $(LIBS) $(EXTRA_LIBS)
|
||||
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
|
||||
|
||||
chronyc : $(CLI_OBJS)
|
||||
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) @READLINE_LINK@ @HASH_LINK@ $(LIBS) $(EXTRA_CLI_LIBS)
|
||||
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
|
||||
|
||||
client.o : client.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
|
||||
@@ -85,14 +85,17 @@ clean :
|
||||
-rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt
|
||||
-rm -rf .deps
|
||||
|
||||
getdate.c : ;
|
||||
getdate.c :
|
||||
bison -o getdate.c getdate.y
|
||||
|
||||
# This can be used to force regeneration of getdate.c
|
||||
getdate :
|
||||
bison -o getdate.c getdate.y
|
||||
|
||||
# For install, don't use the install command, because its switches
|
||||
# seem to vary between systems.
|
||||
|
||||
install: chronyd chronyc
|
||||
install: chronyd chronyc chrony.txt
|
||||
[ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR)
|
||||
[ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR)
|
||||
[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
|
||||
@@ -129,6 +132,9 @@ install: chronyd chronyc
|
||||
%.s : %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
|
||||
|
||||
check : chronyd chronyc
|
||||
cd test/simulation && ./run
|
||||
|
||||
install-docs : docs
|
||||
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
|
||||
cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
|
||||
@@ -151,10 +157,6 @@ 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
|
||||
|
||||
|
||||
85
NEWS
85
NEWS
@@ -1,3 +1,88 @@
|
||||
New in version 1.31.1
|
||||
=====================
|
||||
|
||||
Security fixes
|
||||
--------------
|
||||
* Protect authenticated symmetric NTP associations against DoS attacks
|
||||
(CVE-2015-1799)
|
||||
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
|
||||
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822)
|
||||
|
||||
New in version 1.31
|
||||
===================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Support operation in other NTP eras (next era begins in 2036),
|
||||
NTP time is mapped to [-50, +86] years around build date by default
|
||||
* Restore time from driftfile with -s when RTC is missing/unsupported
|
||||
* Close connected client sockets when not waiting for reply
|
||||
* Use one client socket with random port when acquisitionport is 0
|
||||
* Use NTP packets instead of UDP echo for presend
|
||||
* Don't adjust polling interval when sending fails
|
||||
* Allow binding to addresses that don't exist yet
|
||||
* Ignore measurements around leap second
|
||||
* Improve detection of unexpected time jumps
|
||||
* Include example of logrotate configuration, systemd services and
|
||||
NetworkManager dispatcher script
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Reconnect client sockets for each request to follow changes
|
||||
in network configuration automatically
|
||||
* Restart timer when polling interval is changed on reset
|
||||
|
||||
New in version 1.30
|
||||
===================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add asynchronous name resolving with POSIX threads
|
||||
* Add PTP hardware clock (PHC) refclock driver
|
||||
* Add new generic clock driver to slew by adjusting frequency only
|
||||
(without kernel PLL or adjtime) and use it on Linux
|
||||
* Add rtcautotrim directive to trim RTC automatically
|
||||
* Add hwclockfile directive to share RTC LOCAL/UTC setting with hwclock
|
||||
* Add maxslewrate directive to set maximum allowed slew rate
|
||||
* Add maxdispersion option for refclocks
|
||||
* Add -q/-Q options to set clock/print offset once and exit
|
||||
* Allow directives to be specified on chronyd command line
|
||||
* Replace frequency scaling in Linux driver with retaining of tick
|
||||
* Try to detect unexpected forward time jumps and reset state
|
||||
* Exit with non-zero code when maxchange limit is reached
|
||||
* Improve makestep to not start and stop slew unnecessarily
|
||||
* Change default corrtimeratio to 3.0 to improve frequency accuracy
|
||||
* Announce leap second only on last day of June and December
|
||||
* Use separate connected client sockets for each NTP server
|
||||
* Remove separate NTP implementation used for initstepslew
|
||||
* Limit maximum minpoll set by KoD RATE to default maxpoll
|
||||
* Don't send NTP requests with unknown key
|
||||
* Print warning when source is added with unknown key
|
||||
* Take leap second in PPS refclock from locked source
|
||||
* Make reading of RTC for initial trim more reliable
|
||||
* Don't create cmdmon sockets when cmdport is 0
|
||||
* Add configure option to set default user to drop root privileges
|
||||
* Add configure option to compile with debug messages
|
||||
* Print debug messages when -d is used more than once
|
||||
* Change format of messages written to terminal with -d
|
||||
* Write fatal messages also to stderr with -n
|
||||
* Use IP_RECVERR socket option in chronyc to not wait unnecessarily
|
||||
* Shorten default chronyc timeout for localhost
|
||||
* Change default hostname in chronyc from localhost to 127.0.0.1
|
||||
* Print error message on invalid syntax with all chronyc commands
|
||||
* Include simulation test suite using clknetsim
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix crash when selecting with multiple preferred sources
|
||||
* Fix frequency calculation with large frequency offsets
|
||||
* Fix code writing drift and RTC files to compile correctly
|
||||
* Fix -4/-6 options in chronyc to not reset hostname set by -h
|
||||
* Fix refclock sample validation with sub-second polling interval
|
||||
* Set stratum correctly with non-PPS SOCK refclock and local stratum
|
||||
* Modify dispersion accounting in refclocks to prevent PPS getting
|
||||
stuck with large dispersion and not accepting new samples
|
||||
|
||||
New in version 1.29.1
|
||||
=====================
|
||||
|
||||
|
||||
13
README
13
README
@@ -2,6 +2,7 @@ This is the README for chrony.
|
||||
|
||||
What is chrony?
|
||||
===============
|
||||
|
||||
Chrony is a pair of programs for maintaining the accuracy of computer
|
||||
clocks.
|
||||
|
||||
@@ -61,7 +62,7 @@ Chrony can be successfully built and run on
|
||||
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).
|
||||
system.
|
||||
|
||||
How do I set it up?
|
||||
===================
|
||||
@@ -94,8 +95,7 @@ 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/).
|
||||
These messages will be copied to chrony-users (see below).
|
||||
|
||||
How can I get support for chrony?
|
||||
and where can I discuss new features, possible bugs etc?
|
||||
@@ -125,7 +125,6 @@ Richard P. Curnow <rc@rc0.org.uk>
|
||||
Maintainers
|
||||
===========
|
||||
|
||||
John Hasler <john@dhh.gt.org>
|
||||
Miroslav Lichvar <mlichvar@redhat.com>
|
||||
|
||||
|
||||
@@ -179,6 +178,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
|
||||
Port to NetBSD
|
||||
|
||||
John Hasler <john@dhh.gt.org>
|
||||
Project and website at tuxfamily.org
|
||||
Changes to support 64 bit machines (i.e. those where
|
||||
sizeof(unsigned long) > 4)
|
||||
Bug fix to initstepslew directive
|
||||
@@ -186,6 +186,11 @@ John Hasler <john@dhh.gt.org>
|
||||
Memory locking and real-time scheduler support
|
||||
Fix fault where chronyd enters an endless loop
|
||||
|
||||
Tjalling Hattink <t.hattink@fugro.nl>
|
||||
Fix scheduler to allow stepping clock from timeout handler
|
||||
Patch to take leap second in PPS refclock from locked source
|
||||
Patch to make reading of RTC for initial trim more reliable
|
||||
|
||||
Liam Hatton <me@liamhatton.com>
|
||||
Advice on configuring for Linux on PPC
|
||||
|
||||
|
||||
796
acquire.c
796
acquire.c
@@ -1,796 +0,0 @@
|
||||
/*
|
||||
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; i<n_sources; i++) {
|
||||
if (UTI_CompareIPs(&remote_ip, &sources[i].ip_addr, NULL) == 0) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
|
||||
src = sources + i;
|
||||
++src->n_total_samples;
|
||||
|
||||
src->n_dead_probes = 0; /* reset this when we actually receive something */
|
||||
|
||||
/* If we got into this function, we know the retransmission timeout has not
|
||||
expired for the source */
|
||||
if (src->timer_running) {
|
||||
SCH_RemoveTimeout(src->timeout_id);
|
||||
src->timer_running = 0;
|
||||
}
|
||||
|
||||
process_receive(&msg.ntp_pkt, src, &now);
|
||||
|
||||
/* Check if server done and requeue timeout */
|
||||
if ((src->n_samples >= N_GOOD_SAMPLES) ||
|
||||
(src->n_total_samples >= MAX_SAMPLES)) {
|
||||
++n_completed_sources;
|
||||
#if 0
|
||||
printf("Source %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; i<n_sources; i++) {
|
||||
s = sources + i;
|
||||
/* If we got no measurements, the source is insane */
|
||||
if (s->n_samples == 0) {
|
||||
s->sanity = 0;
|
||||
} else {
|
||||
s->sanity = 1; /* so far ... */
|
||||
lo = s->offsets[0] - s->root_distances[0];
|
||||
hi = s->offsets[0] + s->root_distances[0];
|
||||
inter_lo = lo;
|
||||
inter_hi = hi;
|
||||
for (j=1; j<s->n_samples; j++) {
|
||||
lo = s->offsets[j] - s->root_distances[j];
|
||||
hi = s->offsets[j] + s->root_distances[j];
|
||||
if ((inter_hi <= lo) || (inter_lo >= hi)) {
|
||||
/* Oh dear, we won't get an interval for this source */
|
||||
s->sanity = 0;
|
||||
break;
|
||||
} else {
|
||||
inter_lo = (lo < inter_lo) ? inter_lo : lo;
|
||||
inter_hi = (hi > inter_hi) ? inter_hi : hi;
|
||||
}
|
||||
}
|
||||
if (s->sanity) {
|
||||
s->inter_lo = inter_lo;
|
||||
s->inter_hi = inter_hi;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->sanity) {
|
||||
++n_sane_sources;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Now build the endpoint list, similar to the RFC1305 clock
|
||||
selection algorithm. */
|
||||
eps = MallocArray(Endpoint, 2*n_sane_sources);
|
||||
intervals = MallocArray(Interval, n_sane_sources);
|
||||
|
||||
j = 0;
|
||||
for (i=0; i<n_sources; i++) {
|
||||
s = sources + i;
|
||||
if (s->sanity) {
|
||||
eps[j].offset = s->inter_lo;
|
||||
eps[j].type = LO;
|
||||
eps[j].index = i;
|
||||
eps[j+1].offset = s->inter_hi;
|
||||
eps[j+1].type = HIGH;
|
||||
eps[j+1].index = i;
|
||||
j += 2;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(eps, 2*n_sane_sources, sizeof(Endpoint), endpoint_compare);
|
||||
|
||||
/* Now do depth searching algorithm */
|
||||
n_at_best_depth = best_depth = depth = 0;
|
||||
for (i=0; i<2*n_sane_sources; i++) {
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "Endpoint type %s source index %d [ip=%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();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -46,9 +46,13 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
IPAddr ip_addr;
|
||||
IPAddr local_ip_addr;
|
||||
unsigned short port;
|
||||
} NTP_Remote_Address;
|
||||
|
||||
typedef struct {
|
||||
IPAddr ip_addr;
|
||||
int sock_fd;
|
||||
} NTP_Local_Address;
|
||||
|
||||
#endif /* GOT_ADDRESSING_H */
|
||||
|
||||
|
||||
@@ -199,7 +199,10 @@ set_subnet(TableNode *start_node,
|
||||
|
||||
/* How many subnet entries to set : 1->8, 2->4, 3->2 */
|
||||
N = 1 << (NBITS-bits_to_go);
|
||||
subnet = get_subnet(ip, bits_consumed);
|
||||
|
||||
subnet = get_subnet(ip, bits_consumed) & ~(N - 1);
|
||||
assert(subnet + N <= TABLE_SIZE);
|
||||
|
||||
if (!(node->extended)) {
|
||||
open_node(node);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
typedef struct {
|
||||
NTP_Remote_Address addr;
|
||||
NTP_Local_Address local_addr;
|
||||
int interval;
|
||||
} Destination;
|
||||
static Destination *destinations = 0;
|
||||
@@ -114,7 +115,7 @@ timeout_handler(void *arbitrary)
|
||||
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);
|
||||
NIO_SendNormalPacket(&message, &d->addr, &d->local_addr);
|
||||
|
||||
/* Requeue timeout. Don't care if interval drifts gradually, so just do it
|
||||
* at the end. */
|
||||
@@ -141,8 +142,10 @@ BRD_AddDestination(IPAddr *addr, unsigned short port, int interval)
|
||||
}
|
||||
|
||||
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].local_addr.ip_addr.family = IPADDR_UNSPEC;
|
||||
destinations[n_destinations].local_addr.sock_fd =
|
||||
NIO_GetServerSocket(&destinations[n_destinations].addr);
|
||||
destinations[n_destinations].interval = interval;
|
||||
|
||||
SCH_AddTimeoutInClass((double) interval, 1.0, 0.0,
|
||||
|
||||
8
candm.h
8
candm.h
@@ -518,7 +518,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
IPAddr ip_addr;
|
||||
uint16_t poll;
|
||||
int16_t poll;
|
||||
uint16_t stratum;
|
||||
uint16_t state;
|
||||
uint16_t mode;
|
||||
@@ -637,9 +637,9 @@ typedef struct {
|
||||
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 */
|
||||
uint16_t pad1; /* Padding for compatibility and 4 byte alignment */
|
||||
uint16_t pad2;
|
||||
uint16_t pad3;
|
||||
uint32_t sequence; /* Echo of client's sequence number */
|
||||
uint32_t utoken; /* Unique token per incarnation of daemon */
|
||||
uint32_t token; /* New command token (only if command was successfully
|
||||
|
||||
3
chrony.1
3
chrony.1
@@ -55,7 +55,8 @@ With a good reference clock the accuracy can reach one microsecond.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR chronyc(1),
|
||||
.BR chrony(1)
|
||||
.BR chrony.conf(5),
|
||||
.BR chronyd(8)
|
||||
|
||||
.I http://chrony.tuxfamily.org/
|
||||
|
||||
|
||||
@@ -22,16 +22,19 @@ as a minimum
|
||||
|
||||
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
|
||||
(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives may be
|
||||
particularly useful : `driftfile', `generatecommandkey', `keyfile', `makestep'.
|
||||
Also, the `iburst' server option is useful to speed up the initial
|
||||
synchronization. The smallest useful configuration file would look something
|
||||
like
|
||||
|
||||
server a.b.c
|
||||
server d.e.f
|
||||
server g.h.i
|
||||
server a.b.c iburst
|
||||
server d.e.f iburst
|
||||
server g.h.i iburst
|
||||
keyfile @SYSCONFDIR@/chrony.keys
|
||||
commandkey 1
|
||||
generatecommandkey
|
||||
driftfile @CHRONYVARDIR@/drift
|
||||
makestep 10 3
|
||||
|
||||
|
||||
.SH "SEE ALSO"
|
||||
|
||||
1126
chrony.texi.in
1126
chrony.texi.in
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ A summary of the options supported by \fBchronyc\fR is included below.
|
||||
|
||||
.TP
|
||||
\fB\-h\fR \fIhostname\fR
|
||||
specify hostname
|
||||
specify hostname (default 127.0.0.1)
|
||||
.TP
|
||||
\fB\-p\fR \fIport-number\fR
|
||||
specify port-number
|
||||
|
||||
45
chronyd.8.in
45
chronyd.8.in
@@ -4,7 +4,7 @@ chronyd \- chrony background daemon
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B chronyd
|
||||
[\fIOPTIONS\fR]
|
||||
[\fIOPTIONS\fR] [\fIconfiguration commands\fR]
|
||||
|
||||
.SH DESCRIPTION
|
||||
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
|
||||
@@ -31,6 +31,9 @@ command:
|
||||
|
||||
Information messages and warnings will be logged to syslog.
|
||||
|
||||
If no configuration commands are specified on the command line,
|
||||
\fBchronyd\fR will read the commands from the configuration file
|
||||
(default \fI@SYSCONFDIR@/chrony.conf\fR).
|
||||
|
||||
.SH OPTIONS
|
||||
A summary of the options supported by \fBchronyd\fR is included below.
|
||||
@@ -52,7 +55,8 @@ terminal.
|
||||
.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.
|
||||
to syslog. When \fBchronyd\fR was compiled with debugging support,
|
||||
this option can be used twice to print also debugging messages.
|
||||
.TP
|
||||
\fB\-f\fR \fIconf-file\fR
|
||||
This option can be used to specify an alternate location for the
|
||||
@@ -72,35 +76,45 @@ should not be used.
|
||||
.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
|
||||
option is useful when restarting \fBchronyd\fR and can be used in conjunction
|
||||
with the \fB-r\fR option.
|
||||
.TP
|
||||
.B \-s
|
||||
This option will set the system clock from the computer's real-time
|
||||
clock. This is analogous to supplying the \fI-s\fR flag to the
|
||||
\fI/sbin/clock\fR program during the Linux boot sequence.
|
||||
\fI/sbin/hwclock\fR program during the Linux boot sequence.
|
||||
|
||||
Support for real-time clocks is limited at present - the criteria
|
||||
are described in the section on the \fIrtcfile\fR directive in the
|
||||
documentation supplied with the distribution.
|
||||
|
||||
If \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
|
||||
the real time clock (RTC). This can be used to allow \fBchronyd\fR to
|
||||
perform long term averaging of the gain or loss rate across system
|
||||
reboots, and is useful for dial-up systems that are shut down when
|
||||
not in use. For this to work well, it relies on \fBchronyd\fR having
|
||||
been able to determine accurate statistics for the difference
|
||||
between the real time clock and system clock last time the
|
||||
computer was on.
|
||||
between the RTC and system clock last time the computer was on.
|
||||
|
||||
If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC
|
||||
installed, the system clock will be set with this option forward to the time of
|
||||
the last modification of the drift file (specified by the \fIdriftfile\fR
|
||||
directive) to restore the system time at which \fBchronyd\fR was previously
|
||||
stopped.
|
||||
.TP
|
||||
\fB\-u\fR \fIuser\fR
|
||||
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.
|
||||
This option sets the name of the user to which will \fBchronyd\fR switch to
|
||||
drop root privileges if compiled with Linux capabilities support (default
|
||||
\fB@DEFAULT_USER@\fR).
|
||||
.TP
|
||||
.B \-q
|
||||
When run in this mode, chronyd will set the system clock once
|
||||
and exit. It will not detach from the terminal.
|
||||
.TP
|
||||
.B \-Q
|
||||
This option is similar to \fB\-q\fR, but it will only print the offset and
|
||||
not correct the clock.
|
||||
.TP
|
||||
.B \-v
|
||||
This option displays \fBchronyd\fR's version number to the terminal and exits
|
||||
@@ -119,8 +133,7 @@ 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
|
||||
distribution (\fIchrony.txt\fR and \fIchrony.texi\fR).
|
||||
|
||||
.BR chrony(1),
|
||||
.BR chronyc(1),
|
||||
@@ -128,6 +141,8 @@ from \fIhttp://go.to/chrony\fR
|
||||
.BR hwclock(8),
|
||||
.BR ntpd(8)
|
||||
|
||||
.I http://chrony.tuxfamily.org/
|
||||
|
||||
.SH AUTHOR
|
||||
Richard Curnow <rc@rc0.org.uk>
|
||||
|
||||
|
||||
97
client.c
97
client.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2013
|
||||
* Copyright (C) Miroslav Lichvar 2009-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -66,6 +66,8 @@ static int on_terminal = 0;
|
||||
|
||||
static int no_dns = 0;
|
||||
|
||||
static int recv_errqueue = 0;
|
||||
|
||||
/* ================================================== */
|
||||
/* Ought to extract some code from util.c to make
|
||||
a new set of utilities that can be linked into either
|
||||
@@ -139,6 +141,7 @@ static void
|
||||
open_io(const char *hostname, int port)
|
||||
{
|
||||
IPAddr ip;
|
||||
int on_off = 1;
|
||||
|
||||
/* Note, this call could block for a while */
|
||||
if (DNS_Name2IPAddress(hostname, &ip) != DNS_Success) {
|
||||
@@ -176,6 +179,22 @@ open_io(const char *hostname, int port)
|
||||
perror("Can't create socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Enable extended error reporting (e.g. ECONNREFUSED on ICMP unreachable) */
|
||||
#ifdef IP_RECVERR
|
||||
if (ip.family == IPADDR_INET4 &&
|
||||
!setsockopt(sock_fd, IPPROTO_IP, IP_RECVERR, &on_off, sizeof(on_off))) {
|
||||
recv_errqueue = 1;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_IPV6
|
||||
#ifdef IPV6_RECVERR
|
||||
if (ip.family == IPADDR_INET6 &&
|
||||
!setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVERR, &on_off, sizeof(on_off))) {
|
||||
recv_errqueue = 1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -613,9 +632,7 @@ process_cmd_local(CMD_Request *msg, const char *line)
|
||||
|
||||
p = line;
|
||||
|
||||
if (!*p) {
|
||||
return 0;
|
||||
} else if (!strcmp(p, "off")) {
|
||||
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) {
|
||||
@@ -639,15 +656,14 @@ process_cmd_manual(CMD_Request *msg, const char *line)
|
||||
|
||||
p = line;
|
||||
|
||||
if (!*p) {
|
||||
return 0;
|
||||
} else if (!strcmp(p, "off")) {
|
||||
if (!strcmp(p, "off")) {
|
||||
msg->data.manual.option = htonl(0);
|
||||
} else if (!strcmp(p, "on")) {
|
||||
msg->data.manual.option = htonl(1);
|
||||
} else if (!strcmp(p, "reset")) {
|
||||
msg->data.manual.option = htonl(2);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid syntax for manual command\n");
|
||||
return 0;
|
||||
}
|
||||
msg->command = htons(REQ_MANUAL);
|
||||
@@ -1368,6 +1384,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
|
||||
/* If we get connrefused here, it suggests the sendto is
|
||||
going to a dead port - but only if the daemon machine is
|
||||
running Linux (Solaris doesn't return anything) */
|
||||
|
||||
#ifdef IP_RECVERR
|
||||
/* Fetch the message from the error queue */
|
||||
if (recv_errqueue &&
|
||||
recvfrom(sock_fd, (void *)reply, sizeof(CMD_Reply), MSG_ERRQUEUE,
|
||||
&where_from.u, &where_from_len) < 0)
|
||||
;
|
||||
#endif
|
||||
|
||||
n_attempts++;
|
||||
if (n_attempts > max_retries) {
|
||||
return 0;
|
||||
@@ -1375,7 +1400,11 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
|
||||
} else {
|
||||
|
||||
read_length = recvfrom_status;
|
||||
expected_length = PKL_ReplyLength(reply);
|
||||
if (read_length >= offsetof(CMD_Reply, data)) {
|
||||
expected_length = PKL_ReplyLength(reply);
|
||||
} else {
|
||||
expected_length = 0;
|
||||
}
|
||||
|
||||
bad_length = (read_length < expected_length ||
|
||||
expected_length < offsetof(CMD_Reply, data));
|
||||
@@ -1436,10 +1465,9 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
|
||||
|
||||
/* 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",
|
||||
printf("Reply cmd=%d reply=%d stat=%d seq=%d utok=%08lx tok=%d\n",
|
||||
ntohs(reply->command), ntohs(reply->reply),
|
||||
ntohs(reply->status), ntohs(reply->number),
|
||||
ntohs(reply->total),
|
||||
ntohs(reply->status),
|
||||
ntohl(reply->sequence),
|
||||
ntohl(reply->utoken),
|
||||
ntohl(reply->token));
|
||||
@@ -1669,6 +1697,18 @@ print_freq_ppm(double f)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_signed_freq_ppm(double f)
|
||||
{
|
||||
if (fabs(f) < 99999.5) {
|
||||
printf("%+10.3f", f);
|
||||
} else {
|
||||
printf("%+10.0f", f);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_for_verbose_flag(char *line)
|
||||
{
|
||||
@@ -1693,8 +1733,8 @@ process_cmd_sources(char *line)
|
||||
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;
|
||||
int16_t poll;
|
||||
uint16_t stratum, state, mode, flags, reachability;
|
||||
char hostname_buf[50];
|
||||
|
||||
/* Check whether to output verbose headers */
|
||||
@@ -1863,7 +1903,7 @@ process_cmd_sourcestats(char *line)
|
||||
printf("%-25s %3lu %3lu ", hostname_buf, n_samples, n_runs);
|
||||
print_seconds(span_seconds);
|
||||
printf(" ");
|
||||
print_freq_ppm(resid_freq_ppm);
|
||||
print_signed_freq_ppm(resid_freq_ppm);
|
||||
printf(" ");
|
||||
print_freq_ppm(skew_ppm);
|
||||
printf(" ");
|
||||
@@ -1952,7 +1992,7 @@ process_cmd_tracking(char *line)
|
||||
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("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);
|
||||
@@ -1961,7 +2001,7 @@ process_cmd_tracking(char *line)
|
||||
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("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);
|
||||
@@ -2576,7 +2616,7 @@ authenticate_from_config(const char *filename)
|
||||
|
||||
in = fopen(filename, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "Could not open file %s\n", filename);
|
||||
fprintf(stderr, "Could not open file %s : %s\n", filename, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2601,7 +2641,7 @@ authenticate_from_config(const char *filename)
|
||||
|
||||
in = fopen(keyfile, "r");
|
||||
if (!in) {
|
||||
fprintf(stderr, "Could not open keyfile %s\n", filename);
|
||||
fprintf(stderr, "Could not open keyfile %s : %s\n", keyfile, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2675,7 +2715,7 @@ static void
|
||||
display_gpl(void)
|
||||
{
|
||||
printf("chrony version %s\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2013 Richard P. Curnow and others\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2014 Richard P. Curnow and others\n"
|
||||
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
|
||||
"you are welcome to redistribute it under certain conditions. See the\n"
|
||||
"GNU General Public License version 2 for details.\n\n",
|
||||
@@ -2689,9 +2729,9 @@ main(int argc, char **argv)
|
||||
{
|
||||
char *line;
|
||||
const char *progname = argv[0];
|
||||
const char *hostname = "localhost";
|
||||
const char *hostname = NULL;
|
||||
const char *conf_file = DEFAULT_CONF_FILE;
|
||||
int quit = 0, ret = 1, multi = 0, auto_auth = 0;
|
||||
int quit = 0, ret = 1, multi = 0, auto_auth = 0, family = IPADDR_UNSPEC;
|
||||
int port = DEFAULT_CANDM_PORT;
|
||||
|
||||
/* Parse command line options */
|
||||
@@ -2718,11 +2758,9 @@ main(int argc, char **argv)
|
||||
} else if (!strcmp(*argv, "-n")) {
|
||||
no_dns = 1;
|
||||
} else if (!strcmp(*argv, "-4")) {
|
||||
DNS_SetAddressFamily(IPADDR_INET4);
|
||||
hostname = "127.0.0.1";
|
||||
family = IPADDR_INET4;
|
||||
} else if (!strcmp(*argv, "-6")) {
|
||||
DNS_SetAddressFamily(IPADDR_INET6);
|
||||
hostname = "::1";
|
||||
family = IPADDR_INET6;
|
||||
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
|
||||
printf("chronyc (chrony) version %s\n", CHRONY_VERSION);
|
||||
exit(0);
|
||||
@@ -2749,6 +2787,15 @@ main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
DNS_SetAddressFamily(family);
|
||||
|
||||
if (!hostname) {
|
||||
hostname = family == IPADDR_INET6 ? "::1" : "127.0.0.1";
|
||||
#ifdef FEAT_ASYNCDNS
|
||||
initial_timeout /= 10;
|
||||
#endif
|
||||
}
|
||||
|
||||
open_io(hostname, port);
|
||||
|
||||
if (auto_auth) {
|
||||
|
||||
153
cmdmon.c
153
cmdmon.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012
|
||||
* Copyright (C) Miroslav Lichvar 2009-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -171,22 +171,19 @@ 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)
|
||||
prepare_socket(int family, int port_number)
|
||||
{
|
||||
int port_number, sock_fd;
|
||||
int 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",
|
||||
@@ -202,6 +199,14 @@ prepare_socket(int family)
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
|
||||
#ifdef IP_FREEBIND
|
||||
/* Allow binding to address that doesn't exist yet */
|
||||
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (family == AF_INET6) {
|
||||
#ifdef IPV6_V6ONLY
|
||||
@@ -265,7 +270,7 @@ prepare_socket(int family)
|
||||
void
|
||||
CAM_Initialise(int family)
|
||||
{
|
||||
int i;
|
||||
int i, port_number;
|
||||
|
||||
assert(!initialised);
|
||||
initialised = 1;
|
||||
@@ -293,18 +298,20 @@ CAM_Initialise(int family)
|
||||
free_replies = NULL;
|
||||
kept_replies.next = NULL;
|
||||
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
|
||||
sock_fd4 = prepare_socket(AF_INET);
|
||||
port_number = CNF_GetCommandPort();
|
||||
|
||||
if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4))
|
||||
sock_fd4 = prepare_socket(AF_INET, port_number);
|
||||
else
|
||||
sock_fd4 = -1;
|
||||
#ifdef HAVE_IPV6
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
|
||||
sock_fd6 = prepare_socket(AF_INET6);
|
||||
if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET6))
|
||||
sock_fd6 = prepare_socket(AF_INET6, port_number);
|
||||
else
|
||||
sock_fd6 = -1;
|
||||
#endif
|
||||
|
||||
if (sock_fd4 < 0
|
||||
if (port_number && sock_fd4 < 0
|
||||
#ifdef HAVE_IPV6
|
||||
&& sock_fd6 < 0
|
||||
#endif
|
||||
@@ -559,6 +566,7 @@ get_more_replies(void)
|
||||
for (i=1; i<REPLY_EXTEND_QUANTUM; i++) {
|
||||
new_replies[i-1].next = new_replies + i;
|
||||
}
|
||||
new_replies[REPLY_EXTEND_QUANTUM - 1].next = NULL;
|
||||
free_replies = new_replies;
|
||||
}
|
||||
}
|
||||
@@ -667,46 +675,6 @@ token_acknowledged(unsigned long token, struct timeval *now)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if 0
|
||||
/* These two routines are not legal if the program is operating as a daemon, since
|
||||
stderr is no longer open */
|
||||
|
||||
static void
|
||||
print_command_packet(CMD_Request *pkt, int length)
|
||||
{
|
||||
unsigned char *x;
|
||||
int i;
|
||||
x = (unsigned char *) pkt;
|
||||
for (i=0; i<length; i++) {
|
||||
fprintf(stderr, "%02x ", x[i]);
|
||||
if (i%16 == 15) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_reply_packet(CMD_Reply *pkt)
|
||||
{
|
||||
unsigned char *x;
|
||||
int i;
|
||||
x = (unsigned char *) pkt;
|
||||
for (i=0; i<sizeof(CMD_Reply); i++) {
|
||||
fprintf(stderr, "%02x ", x[i]);
|
||||
if (i%16 == 15) {
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
|
||||
{
|
||||
@@ -734,7 +702,7 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
|
||||
status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
|
||||
&where_to->u, addrlen);
|
||||
|
||||
if (status < 0 && !LOG_RateLimited()) {
|
||||
if (status < 0) {
|
||||
unsigned short port;
|
||||
IPAddr ip;
|
||||
|
||||
@@ -755,7 +723,7 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port);
|
||||
DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1276,7 +1244,6 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
|
||||
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);
|
||||
@@ -1324,7 +1291,6 @@ handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
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);
|
||||
@@ -1380,8 +1346,8 @@ 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));
|
||||
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
|
||||
usec = (int32_t)ntohl(rx_message->data.doffset.usec);
|
||||
doffset = (double) sec + 1.0e-6 * (double) usec;
|
||||
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
|
||||
LCL_AccumulateOffset(doffset, 0.0);
|
||||
@@ -1591,7 +1557,7 @@ handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
static void
|
||||
handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
{
|
||||
LCL_MakeStep(0.0);
|
||||
LCL_MakeStep();
|
||||
tx_message->status = htons(STT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1631,20 +1597,6 @@ handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
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 */
|
||||
|
||||
@@ -1693,6 +1645,9 @@ read_from_cmd_socket(void *anything)
|
||||
return;
|
||||
}
|
||||
|
||||
if (from_length > sizeof (where_from))
|
||||
LOG_FATAL(LOGF_CmdMon, "Truncated source address");
|
||||
|
||||
read_length = status;
|
||||
|
||||
LCL_ReadRawTime(&now);
|
||||
@@ -1758,9 +1713,9 @@ read_from_cmd_socket(void *anything)
|
||||
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.pad2 = 0;
|
||||
tx_message.pad3 = 0;
|
||||
tx_message.utoken = htonl(utoken);
|
||||
/* Set this to a default (invalid) value. This protects against the
|
||||
token field being set to an arbitrary value if we reject the
|
||||
@@ -1769,10 +1724,7 @@ read_from_cmd_socket(void *anything)
|
||||
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);
|
||||
}
|
||||
DEBUG_LOG(LOGF_CmdMon, "Read command packet with protocol version %d (expected %d) from %s:%hu", rx_message.version, PROTO_VERSION_NUMBER, UTI_IPToString(&remote_ip), remote_port);
|
||||
|
||||
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
|
||||
|
||||
@@ -1784,9 +1736,7 @@ read_from_cmd_socket(void *anything)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
DEBUG_LOG(LOGF_CmdMon, "Read command packet with invalid command %d from %s:%hu", rx_command, UTI_IPToString(&remote_ip), remote_port);
|
||||
|
||||
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
|
||||
|
||||
@@ -1796,9 +1746,7 @@ read_from_cmd_socket(void *anything)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
DEBUG_LOG(LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port);
|
||||
|
||||
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
|
||||
|
||||
@@ -1881,8 +1829,8 @@ read_from_cmd_socket(void *anything)
|
||||
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);
|
||||
if (status < 0) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&remote_ip), remote_port);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1896,20 +1844,22 @@ read_from_cmd_socket(void *anything)
|
||||
}
|
||||
|
||||
valid_ts = 0;
|
||||
issue_token = 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))))
|
||||
if (utoken_ok && token_ok) {
|
||||
issue_token = 1;
|
||||
else
|
||||
issue_token = 0;
|
||||
} else {
|
||||
issue_token = 0;
|
||||
} else if (rx_command == REQ_LOGON &&
|
||||
ntohl(rx_message.utoken) == SPECIAL_UTOKEN) {
|
||||
struct timeval ts;
|
||||
|
||||
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
|
||||
valid_ts = ts_is_unique_and_not_stale(&ts, &now);
|
||||
|
||||
if (valid_ts) {
|
||||
issue_token = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authenticated = auth_ok & utoken_ok & token_ok;
|
||||
@@ -1936,8 +1886,6 @@ read_from_cmd_socket(void *anything)
|
||||
/* 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:
|
||||
@@ -1959,6 +1907,7 @@ read_from_cmd_socket(void *anything)
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
allowed = 0;
|
||||
}
|
||||
|
||||
if (allowed) {
|
||||
@@ -2010,8 +1959,8 @@ read_from_cmd_socket(void *anything)
|
||||
|
||||
case REQ_LOGON:
|
||||
/* If the log-on fails, record the reason why */
|
||||
if (!issue_token && !LOG_RateLimited()) {
|
||||
LOG(LOGS_WARN, LOGF_CmdMon,
|
||||
if (!issue_token) {
|
||||
DEBUG_LOG(LOGF_CmdMon,
|
||||
"Bad command logon from %s port %d (auth_ok=%d valid_ts=%d)",
|
||||
UTI_IPToString(&remote_ip),
|
||||
remote_port,
|
||||
|
||||
11
cmdparse.c
11
cmdparse.c
@@ -134,7 +134,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
line += n;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "key")) {
|
||||
if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1) {
|
||||
if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1 ||
|
||||
src->params.authkey == INACTIVE_AUTHKEY) {
|
||||
result = CPS_BadKey;
|
||||
ok = 0;
|
||||
done = 1;
|
||||
@@ -203,7 +204,7 @@ CPS_NormalizeLine(char *line)
|
||||
|
||||
/* Remove white-space at beginning and replace white-spaces with space char */
|
||||
for (p = q = line; *p; p++) {
|
||||
if (isspace(*p)) {
|
||||
if (isspace((unsigned char)*p)) {
|
||||
if (!space)
|
||||
*q++ = ' ';
|
||||
space = 1;
|
||||
@@ -233,15 +234,15 @@ CPS_SplitWord(char *line)
|
||||
char *p = line, *q = line;
|
||||
|
||||
/* Skip white-space before the word */
|
||||
while (*q && isspace(*q))
|
||||
while (*q && isspace((unsigned char)*q))
|
||||
q++;
|
||||
|
||||
/* Move the word to the beginning */
|
||||
while (*q && !isspace(*q))
|
||||
while (*q && !isspace((unsigned char)*q))
|
||||
*p++ = *q++;
|
||||
|
||||
/* Find the next word */
|
||||
while (*q && isspace(*q))
|
||||
while (*q && isspace((unsigned char)*q))
|
||||
q++;
|
||||
|
||||
*p = '\0';
|
||||
|
||||
23
conf.h
23
conf.h
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -34,15 +35,15 @@ extern void CNF_SetRestarted(int);
|
||||
extern char *CNF_GetRtcDevice(void);
|
||||
|
||||
extern void CNF_ReadFile(const char *filename);
|
||||
extern void CNF_ParseLine(const char *filename, int number, char *line);
|
||||
|
||||
extern void CNF_AddInitSources(void);
|
||||
extern void CNF_AddSources(void);
|
||||
extern void CNF_AddBroadcasts(void);
|
||||
extern void CNF_AddRefclocks(void);
|
||||
|
||||
extern void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything);
|
||||
|
||||
extern unsigned short CNF_GetAcquisitionPort(void);
|
||||
extern unsigned short CNF_GetNTPPort(void);
|
||||
extern int CNF_GetAcquisitionPort(void);
|
||||
extern int CNF_GetNTPPort(void);
|
||||
extern char *CNF_GetDriftFile(void);
|
||||
extern char *CNF_GetLogDir(void);
|
||||
extern char *CNF_GetDumpDir(void);
|
||||
@@ -60,8 +61,8 @@ 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 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);
|
||||
@@ -70,16 +71,16 @@ extern int CNF_GetNoClientLog(void);
|
||||
extern unsigned long CNF_GetClientLogLimit(void);
|
||||
extern void CNF_GetFallbackDrifts(int *min, int *max);
|
||||
extern void CNF_GetBindAddress(int family, IPAddr *addr);
|
||||
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
|
||||
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
|
||||
extern char *CNF_GetPidFile(void);
|
||||
extern 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_GetMaxSlewRate(void);
|
||||
|
||||
extern double CNF_GetReselectDistance(void);
|
||||
extern double CNF_GetStratumWeight(void);
|
||||
@@ -99,4 +100,10 @@ extern char *CNF_GetUser(void);
|
||||
extern int CNF_GetMaxSamples(void);
|
||||
extern int CNF_GetMinSamples(void);
|
||||
|
||||
extern double CNF_GetRtcAutotrim(void);
|
||||
extern char *CNF_GetHwclockFile(void);
|
||||
|
||||
extern int CNF_GetInitSources(void);
|
||||
extern double CNF_GetInitStepThreshold(void);
|
||||
|
||||
#endif /* GOT_CONF_H */
|
||||
|
||||
132
configure
vendored
132
configure
vendored
@@ -4,7 +4,7 @@
|
||||
# chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
#
|
||||
# Copyright (C) Richard P. Curnow 1997-2003
|
||||
# Copyright (C) Miroslav Lichvar 2009, 2012
|
||||
# Copyright (C) Miroslav Lichvar 2009, 2012-2014
|
||||
#
|
||||
# =======================================================================
|
||||
|
||||
@@ -108,12 +108,17 @@ For better control, use the options below.
|
||||
--without-nss Don't use NSS even if it is available
|
||||
--without-tomcrypt Don't use libtomcrypt even if it is available
|
||||
--disable-ipv6 Disable IPv6 support
|
||||
--disable-phc Disable PHC support
|
||||
--disable-pps Disable PPS API support
|
||||
--disable-rtc Don't include RTC even on Linux
|
||||
--disable-linuxcaps Disable Linux capabilities support
|
||||
--disable-asyncdns Disable asynchronous name resolving
|
||||
--disable-forcednsretry Don't retry on permanent DNS error
|
||||
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
|
||||
since 1970-01-01 [50*365 days ago]
|
||||
--with-user=USER Specify default chronyd user [root]
|
||||
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
|
||||
--enable-trace Enable tracing
|
||||
--enable-debug Enable debugging support
|
||||
|
||||
Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR chrony.conf location [/etc]
|
||||
@@ -155,6 +160,13 @@ add_def () {
|
||||
fi
|
||||
}
|
||||
#}}}
|
||||
#{{{ pkg_config
|
||||
pkg_config () {
|
||||
type pkg-config > /dev/null 2> /dev/null || return 1
|
||||
|
||||
pkg-config $@ 2> /dev/null
|
||||
}
|
||||
#}}}
|
||||
|
||||
# ======================================================================
|
||||
|
||||
@@ -170,7 +182,7 @@ EXTRA_OBJECTS=""
|
||||
EXTRA_DEFS=""
|
||||
SYSDEFS=""
|
||||
|
||||
# Support for readline (on by default)
|
||||
debug=0
|
||||
feat_readline=1
|
||||
try_readline=1
|
||||
try_editline=1
|
||||
@@ -184,17 +196,22 @@ readline_lib=""
|
||||
readline_inc=""
|
||||
ncurses_lib=""
|
||||
feat_ipv6=1
|
||||
feat_phc=1
|
||||
try_phc=0
|
||||
feat_pps=1
|
||||
try_setsched=0
|
||||
try_lockmem=0
|
||||
feat_asyncdns=1
|
||||
feat_forcednsretry=1
|
||||
ntp_era_split=""
|
||||
default_user="root"
|
||||
mail_program="/usr/lib/sendmail"
|
||||
|
||||
for option
|
||||
do
|
||||
case "$option" in
|
||||
--enable-trace )
|
||||
add_def TRACEON
|
||||
--enable-debug )
|
||||
debug=1
|
||||
;;
|
||||
--disable-readline )
|
||||
feat_readline=0
|
||||
@@ -253,15 +270,27 @@ do
|
||||
--disable-ipv6)
|
||||
feat_ipv6=0
|
||||
;;
|
||||
--disable-phc)
|
||||
feat_phc=0
|
||||
;;
|
||||
--disable-pps)
|
||||
feat_pps=0
|
||||
;;
|
||||
--disable-linuxcaps)
|
||||
feat_linuxcaps=0
|
||||
;;
|
||||
--disable-asyncdns)
|
||||
feat_asyncdns=0
|
||||
;;
|
||||
--disable-forcednsretry)
|
||||
feat_forcednsretry=0
|
||||
;;
|
||||
--with-ntp-era=* )
|
||||
ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
--with-user=* )
|
||||
default_user=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
--with-sendmail=* )
|
||||
mail_program=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
@@ -310,11 +339,12 @@ case $SYSTEM in
|
||||
esac
|
||||
;;
|
||||
Linux* )
|
||||
EXTRA_OBJECTS="sys_linux.o wrap_adjtimex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_linux.o wrap_adjtimex.o"
|
||||
try_linuxcaps=1
|
||||
try_rtc=1
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
try_phc=1
|
||||
add_def LINUX
|
||||
echo "Configuring for " $SYSTEM
|
||||
if [ "${MACHINE}" = "alpha" ]; then
|
||||
@@ -359,6 +389,40 @@ case $SYSTEM in
|
||||
;;
|
||||
esac
|
||||
|
||||
if test_code '64-bit time_t' 'time.h' '' '' '
|
||||
char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
|
||||
return x[0];'
|
||||
then
|
||||
add_def HAVE_LONG_TIME_T 1
|
||||
|
||||
if [ "x$ntp_era_split" != "x" ]; then
|
||||
split_seconds=$ntp_era_split
|
||||
split_days=0
|
||||
else
|
||||
split_seconds=`date '+%s'`
|
||||
if [ "x$split_seconds" = "" ]; then
|
||||
echo "Could not get current time, --with-ntp-era option is needed"
|
||||
exit 1
|
||||
fi
|
||||
split_days=$((50 * 365))
|
||||
fi
|
||||
|
||||
add_def NTP_ERA_SPLIT "(${split_seconds}LL - $split_days * 24 * 3600)"
|
||||
|
||||
date_format='+%Y-%m-%dT%H:%M:%SZ'
|
||||
|
||||
# Print the full NTP interval if a suitable date is found
|
||||
if [ "x`date -u -d '1970-01-01 UTC 9 days ago 5 seconds 3 seconds' \
|
||||
$date_format 2> /dev/null`" = "x1969-12-23T00:00:08Z" ]
|
||||
then
|
||||
time1="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds" \
|
||||
$date_format`"
|
||||
time2="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds 4294967296 seconds" \
|
||||
$date_format`"
|
||||
echo "NTP time mapped to $time1/$time2"
|
||||
fi
|
||||
fi
|
||||
|
||||
MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
|
||||
if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
|
||||
LIBS=""
|
||||
@@ -401,6 +465,21 @@ then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \
|
||||
'return getaddrinfo(0, 0, 0, 0);'
|
||||
then
|
||||
add_def HAVE_GETADDRINFO
|
||||
fi
|
||||
|
||||
if [ $feat_asyncdns = "1" ] && \
|
||||
test_code 'pthread' 'pthread.h' '-pthread' '' \
|
||||
'return pthread_create((void *)1, NULL, (void *)1, NULL);'
|
||||
then
|
||||
add_def FEAT_ASYNCDNS
|
||||
add_def USE_PTHREAD_ASYNCDNS
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
fi
|
||||
|
||||
timepps_h=""
|
||||
if [ $feat_pps = "1" ]; then
|
||||
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
|
||||
@@ -416,7 +495,7 @@ fi
|
||||
|
||||
if [ "x$timepps_h" != "x" ] && \
|
||||
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
|
||||
pps_handle_t h;
|
||||
pps_handle_t h = 0;
|
||||
pps_info_t i;
|
||||
struct timespec ts;
|
||||
return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);'
|
||||
@@ -432,7 +511,7 @@ if [ $feat_linuxcaps = "1" ] && [ $try_linuxcaps = "1" ] && \
|
||||
'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));'
|
||||
then
|
||||
add_def FEAT_LINUXCAPS
|
||||
EXTRA_LIBS="-lcap"
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lcap"
|
||||
fi
|
||||
|
||||
if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \
|
||||
@@ -443,6 +522,22 @@ then
|
||||
add_def FEAT_RTC
|
||||
fi
|
||||
|
||||
if [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
|
||||
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
|
||||
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
|
||||
then
|
||||
if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then
|
||||
add_def FEAT_PHC
|
||||
else
|
||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
||||
'clock_gettime(0, NULL);'
|
||||
then
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
||||
add_def FEAT_PHC
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $try_setsched = "1" ] && \
|
||||
test_code \
|
||||
'sched_setscheduler()' \
|
||||
@@ -487,12 +582,12 @@ if [ $feat_readline = "1" ]; then
|
||||
|
||||
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" \
|
||||
"$readline_inc" "$readline_lib -lreadline" \
|
||||
'add_history(readline("prompt"));'
|
||||
then
|
||||
add_def FEAT_READLINE
|
||||
READLINE_COMPILE="$readline_inc"
|
||||
READLINE_LINK="$readline_lib $ncurses_lib -lreadline"
|
||||
READLINE_LINK="$readline_lib -lreadline"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -507,6 +602,8 @@ if [ $feat_readline = "1" ]; then
|
||||
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
|
||||
fi
|
||||
fi
|
||||
|
||||
EXTRA_CLI_LIBS="$EXTRA_CLI_LIBS $READLINE_LINK"
|
||||
fi
|
||||
|
||||
HASH_OBJ="hash_intmd5.o"
|
||||
@@ -514,8 +611,8 @@ HASH_COMPILE=""
|
||||
HASH_LINK=""
|
||||
|
||||
if [ $try_nss = "1" ]; then
|
||||
test_cflags="`pkg-config --cflags nss`"
|
||||
test_link="`pkg-config --libs-only-L nss` -lfreebl3"
|
||||
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));'
|
||||
@@ -523,6 +620,7 @@ if [ $try_nss = "1" ]; then
|
||||
HASH_OBJ="hash_nss.o"
|
||||
HASH_COMPILE="$test_cflags"
|
||||
HASH_LINK="$test_link"
|
||||
LIBS="$LIBS $HASH_LINK"
|
||||
add_def GENERATE_SHA1_KEY
|
||||
fi
|
||||
fi
|
||||
@@ -534,6 +632,7 @@ if [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
||||
HASH_OBJ="hash_tomcrypt.o"
|
||||
HASH_COMPILE="-I/usr/include/tomcrypt"
|
||||
HASH_LINK="-ltomcrypt"
|
||||
LIBS="$LIBS $HASH_LINK"
|
||||
add_def GENERATE_SHA1_KEY
|
||||
fi
|
||||
fi
|
||||
@@ -593,7 +692,9 @@ if [ "x$SETCHRONYVARDIR" != "x" ]; then
|
||||
CHRONYVARDIR=$SETCHRONYVARDIR
|
||||
fi
|
||||
|
||||
add_def DEBUG $debug
|
||||
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
|
||||
add_def DEFAULT_USER "\"$default_user\""
|
||||
add_def MAIL_PROGRAM "\"$mail_program\""
|
||||
|
||||
if [ -f version.txt ]; then
|
||||
@@ -608,15 +709,13 @@ do
|
||||
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
|
||||
s%@CC@%${MYCC}%;\
|
||||
s%@CFLAGS@%${MYCFLAGS}%;\
|
||||
s%@CPPFLAGS@%${CPPFLAGS}%;\
|
||||
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
|
||||
s%@LIBS@%${LIBS}%;\
|
||||
s%@LDFLAGS@%${MYLDFLAGS}%;\
|
||||
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
|
||||
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
|
||||
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
|
||||
s%@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}%;\
|
||||
@@ -625,7 +724,8 @@ do
|
||||
s%@MANDIR@%${MANDIR}%;\
|
||||
s%@INFODIR@%${INFODIR}%;\
|
||||
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
|
||||
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;"\
|
||||
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
|
||||
s%@DEFAULT_USER@%${default_user}%;"\
|
||||
< ${f}.in > $f
|
||||
done
|
||||
|
||||
|
||||
17
examples/chrony-wait.service
Normal file
17
examples/chrony-wait.service
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Wait for chrony to synchronize system clock
|
||||
After=chronyd.service
|
||||
Requires=chronyd.service
|
||||
Before=time-sync.target
|
||||
Wants=time-sync.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
|
||||
# clock correction to be less than 0.1 seconds
|
||||
ExecStart=/usr/bin/chronyc waitsync 60 0.1
|
||||
RemainAfterExit=yes
|
||||
StandardOutput=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -106,6 +106,9 @@ keyfile /etc/chrony.keys
|
||||
|
||||
commandkey 1
|
||||
|
||||
# With this directive a random password will be generated automatically.
|
||||
generatecommandkey
|
||||
|
||||
# chronyd can save the measurement history for the servers to files when
|
||||
# it it exits. This is useful in 2 situations:
|
||||
#
|
||||
|
||||
8
examples/chrony.logrotate
Normal file
8
examples/chrony.logrotate
Normal file
@@ -0,0 +1,8 @@
|
||||
/var/log/chrony/*.log {
|
||||
missingok
|
||||
nocreate
|
||||
sharedscripts
|
||||
postrotate
|
||||
/usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
17
examples/chrony.nm-dispatcher
Normal file
17
examples/chrony.nm-dispatcher
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
|
||||
# online/offline when a default route is configured/removed on the system.
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$2" = "up" ]; then
|
||||
/sbin/ip route list dev "$1" | grep -q '^default' &&
|
||||
/usr/bin/chronyc -a online > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ "$2" = "down" ]; then
|
||||
/sbin/ip route list | grep -q '^default' ||
|
||||
/usr/bin/chronyc -a offline > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
13
examples/chronyd.service
Normal file
13
examples/chronyd.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=NTP client/server
|
||||
After=ntpdate.service sntp.service ntpd.service
|
||||
Conflicts=ntpd.service systemd-timesyncd.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/chronyd.pid
|
||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||
ExecStart=/usr/sbin/chronyd $OPTIONS
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
286
faq.txt
286
faq.txt
@@ -1,286 +0,0 @@
|
||||
@@PROLOGUE
|
||||
<html>
|
||||
<head>
|
||||
<title>Frequently asked questions</title>
|
||||
<meta name="description" content="Chrony FAQ (frequently asked questions)">
|
||||
<meta name="keywords" content="chrony,network time protocol,NTP,RFC 1305,dial-up connection,real time clock,RTC,Linux,FAQ,frequently asked questns">
|
||||
<?php
|
||||
$root = ".";
|
||||
include "$root/styles.php";
|
||||
?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php
|
||||
include 'main_banner.php';
|
||||
include 'header.php';
|
||||
?>
|
||||
<?php pretty_h1("Introduction") ?>
|
||||
<p>
|
||||
This is a set of questions and answers to common problems and issues.
|
||||
<p>
|
||||
As we receive more emails about the software, we will add new questions
|
||||
to this page.
|
||||
<hr>
|
||||
<p>
|
||||
The developers can be reached via the chrony-dev mailing list. See
|
||||
<a href="#question_1.4">question 1.4.</a> for details.
|
||||
<hr>
|
||||
|
||||
<br clear=all>
|
||||
@@ENDPROLOGUE
|
||||
S: Administrative issues
|
||||
|
||||
Q: Where can I get chrony source code?
|
||||
Tarballs are available via the <b>Download</b> link on the Chrony
|
||||
Web site. For the current development from the developers' version control
|
||||
system see the <b>Git</b> 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 <a href="http://chrony.tuxfamily.org/">http://chrony.tuxfamily.org/</a>.
|
||||
|
||||
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 <a href="mailto:chrony-users-request@chrony.tuxfamily.org">chrony-users-request@chrony.tuxfamily.org</a> or
|
||||
<a href="mailto:chrony-announce-request@chrony.tuxfamily.org">chrony-announce-request@chrony.tuxfamily.org</a> respectively.
|
||||
|
||||
For those who want to contribute to the development of chrony, there is a
|
||||
developers' mailing list. You can subscribe by sending mail with the
|
||||
subject subscribe to
|
||||
<a href="mailto:chrony-dev-request@chrony.tuxfamily.org">chrony-dev-request@chrony.tuxfamily.org</a>.
|
||||
|
||||
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 '506 Cannot talk to daemon'.
|
||||
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
|
||||
isn't necessary for localhost.
|
||||
|
||||
Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, 'ps
|
||||
-auxw') to see if it's running. Or try 'netstat -a' and see if the ports
|
||||
123/udp and 323/udp are listening. If chronyd is not running, you may have a
|
||||
problem with the way you are trying to start it (e.g. at boot time).
|
||||
|
||||
Perhaps you have a firewall set up in a way that blocks packets on port
|
||||
323/udp. You need to amend the firewall configuration in this case.
|
||||
|
||||
Q: Is the chronyc<->chronyd protocol documented anywhere?
|
||||
Only by the source code :-) See cmdmon.c (chronyd side) and client.c (chronyc
|
||||
side).
|
||||
|
||||
S: Real-time clock issues.
|
||||
Q: What is the real-time clock (RTC)?
|
||||
This is the clock which keeps the time even when your computer is turned off.
|
||||
It works with 1 second resolution. chronyd can monitor the rate at which the
|
||||
real-time clock gains or loses time, and compensate for it when you set the
|
||||
system time from it at the next reboot. See the documentation for details.
|
||||
|
||||
Q: I want to use chronyd's real-time clock support. Must I disable hwclock?
|
||||
The hwclock program is often set-up by default in the boot and shutdown scripts
|
||||
with many Linux installations. If you want to use chronyd's real-time clock
|
||||
support, the important thing is to disable hwclock in the <b>shutdown</b>
|
||||
procedure. If you don't, it will over-write the RTC with a new value, unknown
|
||||
to chronyd. At the next reboot, chronyd will compensate this (wrong) time with
|
||||
its estimate of how far the RTC has drifted whilst the power was off, giving a
|
||||
meaningless initial system time.
|
||||
|
||||
There is no need to remove hwclock from the boot process, as long as chronyd is
|
||||
started after it has run.
|
||||
|
||||
Q: I just keep getting the '513 RTC driver not running' message
|
||||
For the real time clock support to work, you need the following three things:
|
||||
|
||||
* a kernel that is supported (e.g. 2.2 onwards)
|
||||
* enhanced RTC support compiled into the kernel
|
||||
* an 'rtcfile' directive in your chrony.conf file.
|
||||
|
||||
S: 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 <a
|
||||
href="http://chrony.tuxfamily.org/">http://chrony.tuxfamily.org</a> 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
|
||||
<hr>
|
||||
|
||||
Back to
|
||||
<a href="mailto:rc@rc0.org.uk?subject=chrony">the author</a>'s
|
||||
<a href="http://www.rc0.org.uk/">main page</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ENDEPILOGUE
|
||||
140
faqgen.pl
140
faqgen.pl
@@ -1,140 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# $Header
|
||||
|
||||
# Copyright 2001 Richard P. Curnow
|
||||
# LICENCE
|
||||
|
||||
# A script to generate an HTML FAQ page from a text input file. The input is assumed to consist of the following:
|
||||
# Lines starting with 'S:'. These introduce sections.
|
||||
# Lines starting with 'Q:'. These are the topics of questions.
|
||||
# Body text (either as an introduction to the sections, or as answers to the questions.
|
||||
# The body text is set as pre-formatted.
|
||||
|
||||
$| = 1;
|
||||
|
||||
@prologue = ();
|
||||
@epilogue = ();
|
||||
|
||||
@sections=(); # section titles
|
||||
@sect_text=(); # introductory text in sections
|
||||
|
||||
@questions=(); # questions in sections
|
||||
@answers=(); # answers to questions
|
||||
|
||||
$sn = -1;
|
||||
$had_q = 0;
|
||||
|
||||
#{{{ Parse input
|
||||
while (<>) {
|
||||
if (m/\@\@PROLOG/o) {
|
||||
while (<>) {
|
||||
last if (m/^\@\@ENDPROLOG/);
|
||||
push (@prologue, $_);
|
||||
}
|
||||
} elsif (m/\@\@EPILOG/o) {
|
||||
while (<>) {
|
||||
last if (m/^\@\@ENDEPILOG/);
|
||||
push (@epilogue, $_);
|
||||
}
|
||||
} elsif (m/^[sS]:[ \t]*(.*)$/) {
|
||||
chomp;
|
||||
$qn = -1;
|
||||
++$sn;
|
||||
$sections[$sn] = &guard($1);
|
||||
$sect_text[$sn] = "";
|
||||
$questions[$sn] = [ ];
|
||||
$answers[$sn] = [ ];
|
||||
$had_q = 0;
|
||||
} elsif (/^[qQ]:[ \t]*(.*)$/) {
|
||||
chomp;
|
||||
die unless ($sn >= 0);
|
||||
++$qn;
|
||||
$questions[$sn]->[$qn] = &guard($1);
|
||||
$had_q = 1;
|
||||
} else {
|
||||
if ($had_q) {
|
||||
if ($qn >= 0) {
|
||||
$answers[$sn]->[$qn] .= $_;
|
||||
}
|
||||
} else {
|
||||
if ($sect_text[$sn] ne "" || $_ !~ /^\s*$/) {
|
||||
$sect_text[$sn] .= $_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#}}}
|
||||
|
||||
# Emit file header
|
||||
if ($#prologue >= 0) {
|
||||
print @prologue;
|
||||
} else {
|
||||
print <<EOF;
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Chrony Frequently Asked Questions
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<font face=\"arial,helvetica\" size=+4><b>Table of contents</b></font>
|
||||
EOF
|
||||
}
|
||||
|
||||
# Emit table of contents
|
||||
print "<ul>\n";
|
||||
for $sn (0 .. $#sections) {
|
||||
print "<b><li> <a href=\"#section_".($sn+1)."\">".($sn+1).".</a> ".$sections[$sn]."</b>\n";
|
||||
print " <ul>\n";
|
||||
for $qn (0 .. $#{$questions[$sn]}) {
|
||||
$sq = ($sn+1).".".($qn+1);
|
||||
print " <li> <a href=\"#question_".$sq."\">".$sq.".</a> ".$questions[$sn]->[$qn]."\n";
|
||||
#print " <li> ".$sq.". ".$questions[$sn]->[$qn]."\n";
|
||||
}
|
||||
print " </ul>\n";
|
||||
}
|
||||
print "</ul>\n";
|
||||
|
||||
# Emit main sections
|
||||
for $sn (0 .. $#sections) {
|
||||
print "<hr>\n";
|
||||
print "<a name=section_".($sn+1).">\n";
|
||||
#print "<b><font size=+2 face=\"arial,helvetica\">".($sn+1).". ".$sections[$sn]."</font></b>\n";
|
||||
print "<?php pretty_h2(\"".($sn+1).". ".$sections[$sn]."\"); ?>\n";
|
||||
if ($sect_text[$sn] ne "") {
|
||||
print "<pre>\n";
|
||||
print $sect_text[$sn];
|
||||
print "</pre>\n";
|
||||
}
|
||||
for $qn (0 .. $#{$questions[$sn]}) {
|
||||
$sq = ($sn+1).".".($qn+1);
|
||||
print "<p>\n";
|
||||
print "<a name=question_".$sq.">\n";
|
||||
print "<font size=+1 face=\"arial,helvetica\">".$sq.". ".$questions[$sn]->[$qn]."</font>\n";
|
||||
print "<pre>\n";
|
||||
print $answers[$sn]->[$qn];
|
||||
print "</pre>\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Print footer
|
||||
if ($#epilogue >= 0) {
|
||||
print @epilogue;
|
||||
} else {
|
||||
print <<EOF;
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
#{{{ sub guard {
|
||||
sub guard {
|
||||
# Hide wierd tags etc
|
||||
my ($x) = @_;
|
||||
return $x;
|
||||
}
|
||||
#}}}
|
||||
|
||||
|
||||
@@ -711,7 +711,7 @@ LookupWord (buff)
|
||||
/* Make it lowercase. */
|
||||
for (p = buff; *p; p++)
|
||||
if (ISUPPER ((unsigned char) *p))
|
||||
*p = tolower (*p);
|
||||
*p = tolower ((unsigned char) *p);
|
||||
|
||||
if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
|
||||
{
|
||||
|
||||
49
keys.c
49
keys.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2012-2013
|
||||
* Copyright (C) Miroslav Lichvar 2012-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -142,7 +142,7 @@ KEY_Finalise(void)
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
determine_hash_delay(int key_id)
|
||||
determine_hash_delay(unsigned long key_id)
|
||||
{
|
||||
NTP_Packet pkt;
|
||||
struct timeval before, after;
|
||||
@@ -162,12 +162,12 @@ determine_hash_delay(int key_id)
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
||||
min_usecs += min_usecs >> 4;
|
||||
|
||||
DEBUG_LOG(LOGF_Keys, "authentication delay for key %lu: %ld useconds", key_id, min_usecs);
|
||||
|
||||
return min_usecs;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -260,7 +260,7 @@ KEY_Reload(void)
|
||||
/* Check for duplicates */
|
||||
for (i = 1; i < n_keys; i++) {
|
||||
if (keys[i - 1].id == keys[i].id) {
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %lu", key_id);
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %lu", keys[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,13 +295,20 @@ lookup_key(unsigned long id)
|
||||
static int
|
||||
get_key_pos(unsigned long key_id)
|
||||
{
|
||||
if (!cache_valid || key_id != cache_key_id) {
|
||||
int position;
|
||||
|
||||
if (cache_valid && key_id == cache_key_id)
|
||||
return cache_key_pos;
|
||||
|
||||
position = lookup_key(key_id);
|
||||
|
||||
if (position >= 0) {
|
||||
cache_valid = 1;
|
||||
cache_key_pos = lookup_key(key_id);
|
||||
cache_key_pos = position;
|
||||
cache_key_id = key_id;
|
||||
}
|
||||
|
||||
return cache_key_pos;
|
||||
return position;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -321,25 +328,7 @@ KEY_GetCommandKey(void)
|
||||
int
|
||||
KEY_KeyKnown(unsigned long key_id)
|
||||
{
|
||||
int position;
|
||||
|
||||
if (cache_valid && (key_id == cache_key_id)) {
|
||||
return 1;
|
||||
} else {
|
||||
|
||||
position = lookup_key(key_id);
|
||||
|
||||
if (position >= 0) {
|
||||
/* Store key in cache, we will probably be using it in a
|
||||
minute... */
|
||||
cache_valid = 1;
|
||||
cache_key_pos = position;
|
||||
cache_key_id = key_id;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return get_key_pos(key_id) >= 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
93
local.c
93
local.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -126,11 +126,15 @@ calculate_sys_precision(void)
|
||||
assert(best_dusec > 0);
|
||||
|
||||
precision_quantum = best_dusec * 1.0e-6;
|
||||
|
||||
/* Get rounded log2 value of the measured precision */
|
||||
precision_log = 0;
|
||||
while (best_dusec < 500000) {
|
||||
while (best_dusec < 707107) {
|
||||
precision_log--;
|
||||
best_dusec *= 2;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -248,6 +252,28 @@ void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
|
||||
{
|
||||
return change_list.next->handler == handler;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
|
||||
double dfreq, double doffset,
|
||||
LCL_ChangeType change_type)
|
||||
{
|
||||
ChangeListEntry *ptr;
|
||||
|
||||
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
||||
(ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
|
||||
{
|
||||
@@ -369,7 +395,6 @@ LCL_ReadAbsoluteFrequency(void)
|
||||
void
|
||||
LCL_SetAbsoluteFrequency(double afreq_ppm)
|
||||
{
|
||||
ChangeListEntry *ptr;
|
||||
struct timeval raw, cooked;
|
||||
double dfreq;
|
||||
|
||||
@@ -382,15 +407,13 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
|
||||
|
||||
afreq_ppm = (*drv_set_freq)(afreq_ppm);
|
||||
|
||||
dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 + current_freq_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);
|
||||
}
|
||||
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
|
||||
|
||||
current_freq_ppm = afreq_ppm;
|
||||
|
||||
@@ -401,7 +424,6 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
|
||||
void
|
||||
LCL_AccumulateDeltaFrequency(double dfreq)
|
||||
{
|
||||
ChangeListEntry *ptr;
|
||||
struct timeval raw, cooked;
|
||||
double old_freq_ppm;
|
||||
|
||||
@@ -411,20 +433,17 @@ LCL_AccumulateDeltaFrequency(double dfreq)
|
||||
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;
|
||||
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
|
||||
|
||||
/* Call the system-specific driver for setting the frequency */
|
||||
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
|
||||
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 + old_freq_ppm);
|
||||
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);
|
||||
}
|
||||
|
||||
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -432,7 +451,6 @@ LCL_AccumulateDeltaFrequency(double dfreq)
|
||||
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
|
||||
@@ -444,10 +462,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
|
||||
(*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);
|
||||
}
|
||||
|
||||
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -455,7 +470,6 @@ LCL_AccumulateOffset(double offset, double corr_rate)
|
||||
void
|
||||
LCL_ApplyStepOffset(double offset)
|
||||
{
|
||||
ChangeListEntry *ptr;
|
||||
struct timeval raw, cooked;
|
||||
|
||||
/* In this case, the cooked time to be passed to the notify clients
|
||||
@@ -467,10 +481,7 @@ LCL_ApplyStepOffset(double offset)
|
||||
(*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);
|
||||
}
|
||||
|
||||
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -479,12 +490,8 @@ 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);
|
||||
}
|
||||
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
||||
|
||||
lcl_InvokeDispersionNotifyHandlers(dispersion);
|
||||
}
|
||||
@@ -494,7 +501,6 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
|
||||
void
|
||||
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
||||
{
|
||||
ChangeListEntry *ptr;
|
||||
struct timeval raw, cooked;
|
||||
double old_freq_ppm;
|
||||
|
||||
@@ -508,25 +514,19 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
||||
/* 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;
|
||||
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
|
||||
DEBUG_LOG(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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -561,17 +561,15 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
||||
|
||||
current_freq_ppm = (*drv_read_freq)();
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
|
||||
#endif
|
||||
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Look at the current difference between the system time and the NTP
|
||||
time, and make a step to cancel it if it's larger than the threshold. */
|
||||
time, and make a step to cancel it. */
|
||||
|
||||
int
|
||||
LCL_MakeStep(double threshold)
|
||||
LCL_MakeStep(void)
|
||||
{
|
||||
struct timeval raw;
|
||||
double correction;
|
||||
@@ -579,14 +577,11 @@ LCL_MakeStep(double threshold)
|
||||
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);
|
||||
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
17
local.h
17
local.h
@@ -67,16 +67,22 @@ extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, dou
|
||||
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)
|
||||
change_type : what type of change is being applied
|
||||
|
||||
anything : Passthrough argument from call to registration routine */
|
||||
|
||||
|
||||
typedef enum {
|
||||
LCL_ChangeAdjust,
|
||||
LCL_ChangeStep,
|
||||
LCL_ChangeUnknownStep
|
||||
} LCL_ChangeType;
|
||||
|
||||
typedef void (*LCL_ParameterChangeHandler)
|
||||
(struct timeval *raw, struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset, int is_step_change,
|
||||
double doffset,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything
|
||||
);
|
||||
|
||||
@@ -86,6 +92,9 @@ extern void LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, vo
|
||||
/* Remove a handler */
|
||||
extern void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler, void *anything);
|
||||
|
||||
/* Check if a handler is invoked first when dispatching */
|
||||
extern int LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler);
|
||||
|
||||
/* Function type for handlers to be called back when an indeterminate
|
||||
offset is introduced into the local time. This situation occurs
|
||||
when the frequency must be adjusted to effect a clock slew and
|
||||
@@ -183,7 +192,7 @@ 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);
|
||||
extern int LCL_MakeStep(void);
|
||||
|
||||
/* Routine to schedule a leap second. Leap second will be inserted
|
||||
at the end of the day if argument is positive, deleted if negative,
|
||||
|
||||
146
logging.c
146
logging.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2012
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -34,6 +34,9 @@
|
||||
#include "mkdirpp.h"
|
||||
#include "util.h"
|
||||
|
||||
/* This is used by DEBUG_LOG macro */
|
||||
int log_debug_enabled = 0;
|
||||
|
||||
/* ================================================== */
|
||||
/* Flag indicating we have initialised */
|
||||
static int initialised = 0;
|
||||
@@ -42,7 +45,9 @@ static int system_log = 0;
|
||||
|
||||
static int parent_fd = 0;
|
||||
|
||||
static time_t last_limited = 0;
|
||||
#define DEBUG_LEVEL_PRINT_FUNCTION 2
|
||||
#define DEBUG_LEVEL_PRINT_DEBUG 2
|
||||
static int debug_level = 0;
|
||||
|
||||
#ifdef WINNT
|
||||
static FILE *logfile;
|
||||
@@ -98,86 +103,94 @@ LOG_Finalise(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...)
|
||||
static void log_message(int fatal, LOG_Severity severity, const char *message)
|
||||
{
|
||||
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);
|
||||
fprintf(logfile, fatal ? "Fatal error : %s\n" : "%s\n", message);
|
||||
}
|
||||
#else
|
||||
if (system_log) {
|
||||
int priority;
|
||||
switch (severity) {
|
||||
case LOGS_DEBUG:
|
||||
priority = LOG_DEBUG;
|
||||
break;
|
||||
case LOGS_INFO:
|
||||
syslog(LOG_INFO, "%s", buf);
|
||||
priority = LOG_INFO;
|
||||
break;
|
||||
case LOGS_WARN:
|
||||
syslog(LOG_WARNING, "%s", buf);
|
||||
priority = LOG_WARNING;
|
||||
break;
|
||||
case LOGS_ERR:
|
||||
default:
|
||||
syslog(LOG_ERR, "%s", buf);
|
||||
priority = LOG_ERR;
|
||||
break;
|
||||
case LOGS_FATAL:
|
||||
priority = LOG_CRIT;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_Fatal_Function(LOG_Facility facility, const char *format, ...)
|
||||
void LOG_Message(LOG_Severity severity, LOG_Facility facility,
|
||||
int line_number, const char *filename,
|
||||
const char *function_name, const char *format, ...)
|
||||
{
|
||||
char buf[2048];
|
||||
va_list other_args;
|
||||
time_t t;
|
||||
struct tm stm;
|
||||
|
||||
#ifdef WINNT
|
||||
#else
|
||||
if (!system_log) {
|
||||
/* Don't clutter up syslog with timestamps and internal debugging info */
|
||||
time(&t);
|
||||
stm = *gmtime(&t);
|
||||
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
|
||||
fprintf(stderr, "%s ", buf);
|
||||
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
|
||||
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
va_start(other_args, format);
|
||||
vsnprintf(buf, sizeof(buf), format, other_args);
|
||||
va_end(other_args);
|
||||
|
||||
#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
|
||||
switch (severity) {
|
||||
case LOGS_DEBUG:
|
||||
case LOGS_INFO:
|
||||
case LOGS_WARN:
|
||||
case LOGS_ERR:
|
||||
log_message(0, severity, buf);
|
||||
break;
|
||||
case LOGS_FATAL:
|
||||
log_message(1, severity, buf);
|
||||
|
||||
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);
|
||||
/* With syslog, send the message also to the grandparent
|
||||
process or write it to stderr if not detached */
|
||||
if (system_log) {
|
||||
if (parent_fd > 0) {
|
||||
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
|
||||
; /* Not much we can do here */
|
||||
} else if (parent_fd == 0) {
|
||||
system_log = 0;
|
||||
log_message(1, severity, buf);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -194,6 +207,18 @@ LOG_OpenSystemLog(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void LOG_SetDebugLevel(int level)
|
||||
{
|
||||
debug_level = level;
|
||||
if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
|
||||
if (!DEBUG)
|
||||
LOG(LOGS_WARN, LOGF_Logging, "Not compiled with full debugging support");
|
||||
log_debug_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_SetParentFd(int fd)
|
||||
{
|
||||
@@ -207,22 +232,7 @@ 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;
|
||||
parent_fd = -1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
67
logging.h
67
logging.h
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -28,11 +29,41 @@
|
||||
#ifndef GOT_LOGGING_H
|
||||
#define GOT_LOGGING_H
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
/* Flag indicating whether debug messages are logged */
|
||||
extern int log_debug_enabled;
|
||||
|
||||
/* Line logging macros. If the compiler is GNU C, we take advantage of
|
||||
being able to get the function name also. */
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define FUNCTION_NAME __FUNCTION__
|
||||
#define FORMAT_ATTRIBUTE_PRINTF(str, first) __attribute__ ((format (printf, str, first)))
|
||||
#else
|
||||
#define FUNCTION_NAME ""
|
||||
#define FORMAT_ATTRIBUTE_PRINTF(str, first)
|
||||
#endif
|
||||
|
||||
#define DEBUG_LOG(facility, ...) \
|
||||
do { \
|
||||
if (DEBUG && log_debug_enabled) \
|
||||
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
|
||||
#define LOG_FATAL(facility, ...) \
|
||||
do { \
|
||||
LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
/* Definition of severity */
|
||||
typedef enum {
|
||||
LOGS_INFO,
|
||||
LOGS_WARN,
|
||||
LOGS_ERR
|
||||
LOGS_ERR,
|
||||
LOGS_FATAL,
|
||||
LOGS_DEBUG
|
||||
} LOG_Severity;
|
||||
|
||||
/* Definition of facility. Each message is tagged with who generated
|
||||
@@ -56,14 +87,17 @@ typedef enum {
|
||||
LOGF_Manual,
|
||||
LOGF_Keys,
|
||||
LOGF_Logging,
|
||||
LOGF_Nameserv,
|
||||
LOGF_Rtc,
|
||||
LOGF_Regress,
|
||||
LOGF_Sys,
|
||||
LOGF_SysGeneric,
|
||||
LOGF_SysLinux,
|
||||
LOGF_SysNetBSD,
|
||||
LOGF_SysSolaris,
|
||||
LOGF_SysSunOS,
|
||||
LOGF_SysWinnt,
|
||||
LOGF_TempComp,
|
||||
LOGF_RtcLinux,
|
||||
LOGF_Refclock
|
||||
} LOG_Facility;
|
||||
@@ -75,13 +109,17 @@ extern void LOG_Initialise(void);
|
||||
extern void LOG_Finalise(void);
|
||||
|
||||
/* Line logging function */
|
||||
extern void LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...);
|
||||
FORMAT_ATTRIBUTE_PRINTF(6, 7)
|
||||
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
|
||||
int line_number, const char *filename,
|
||||
const char *function_name, const char *format, ...);
|
||||
|
||||
/* 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);
|
||||
/* Set debug level:
|
||||
0, 1 - only non-debug messages are logged
|
||||
2 - debug messages are logged too, all messages are prefixed with
|
||||
filename, line, and function name
|
||||
*/
|
||||
extern void LOG_SetDebugLevel(int level);
|
||||
|
||||
/* Log messages to syslog instead of stderr */
|
||||
extern void LOG_OpenSystemLog(void);
|
||||
@@ -92,24 +130,13 @@ 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);
|
||||
|
||||
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||
extern void LOG_FileWrite(LOG_FileID id, const char *format, ...);
|
||||
|
||||
extern void LOG_CreateLogFileDir(void);
|
||||
|
||||
143
main.c
143
main.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2012
|
||||
* Copyright (C) Miroslav Lichvar 2012-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -44,7 +44,6 @@
|
||||
#include "conf.h"
|
||||
#include "cmdmon.h"
|
||||
#include "keys.h"
|
||||
#include "acquire.h"
|
||||
#include "manual.h"
|
||||
#include "rtc.h"
|
||||
#include "refclock.h"
|
||||
@@ -60,10 +59,12 @@
|
||||
|
||||
static int initialised = 0;
|
||||
|
||||
/* ================================================== */
|
||||
static int exit_status = 0;
|
||||
|
||||
static int reload = 0;
|
||||
|
||||
static REF_Mode ref_mode = REF_ModeNormal;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
@@ -79,7 +80,7 @@ delete_pidfile(void)
|
||||
void
|
||||
MAI_CleanupAndExit(void)
|
||||
{
|
||||
if (!initialised) exit(0);
|
||||
if (!initialised) exit(exit_status);
|
||||
|
||||
if (CNF_GetDumpOnExit()) {
|
||||
SRC_DumpSources();
|
||||
@@ -87,7 +88,6 @@ MAI_CleanupAndExit(void)
|
||||
|
||||
TMC_Finalise();
|
||||
MNL_Finalise();
|
||||
ACQ_Finalise();
|
||||
CLG_Finalise();
|
||||
NSR_Finalise();
|
||||
NCR_Finalise();
|
||||
@@ -108,7 +108,7 @@ MAI_CleanupAndExit(void)
|
||||
|
||||
LOG_Finalise();
|
||||
|
||||
exit(0);
|
||||
exit(exit_status);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -123,13 +123,10 @@ signal_cleanup(int x)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
post_acquire_hook(void *anything)
|
||||
ntp_source_resolving_end(void)
|
||||
{
|
||||
/* Close the pipe to the foreground process so it can exit */
|
||||
LOG_CloseParentFd();
|
||||
NSR_SetSourceResolvingEndHandler(NULL);
|
||||
|
||||
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
|
||||
@@ -137,10 +134,62 @@ post_acquire_hook(void *anything)
|
||||
semblence of validity about it. */
|
||||
SRC_ReloadSources();
|
||||
}
|
||||
CNF_SetupAccessRestrictions();
|
||||
|
||||
RTC_StartMeasurements();
|
||||
RCL_StartRefclocks();
|
||||
NSR_StartSources();
|
||||
NSR_AutoStartSources();
|
||||
|
||||
/* Special modes can end only when sources update their reachability.
|
||||
Give up immediatelly if there are no active sources. */
|
||||
if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
|
||||
REF_SetUnsynchronised();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
post_init_ntp_hook(void *anything)
|
||||
{
|
||||
if (ref_mode == REF_ModeInitStepSlew) {
|
||||
/* Remove the initstepslew sources and set normal mode */
|
||||
NSR_RemoveAllSources();
|
||||
ref_mode = REF_ModeNormal;
|
||||
REF_SetMode(ref_mode);
|
||||
}
|
||||
|
||||
/* Close the pipe to the foreground process so it can exit */
|
||||
LOG_CloseParentFd();
|
||||
|
||||
CNF_AddSources();
|
||||
CNF_AddBroadcasts();
|
||||
|
||||
NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end);
|
||||
NSR_ResolveSources();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
reference_mode_end(int result)
|
||||
{
|
||||
switch (ref_mode) {
|
||||
case REF_ModeNormal:
|
||||
case REF_ModeUpdateOnce:
|
||||
case REF_ModePrintOnce:
|
||||
exit_status = !result;
|
||||
SCH_QuitProgram();
|
||||
break;
|
||||
case REF_ModeInitStepSlew:
|
||||
/* Switch to the normal mode, the delay is used to prevent polling
|
||||
interval shorter than the burst interval if some configured servers
|
||||
were used also for initstepslew */
|
||||
SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -148,7 +197,14 @@ post_acquire_hook(void *anything)
|
||||
static void
|
||||
post_init_rtc_hook(void *anything)
|
||||
{
|
||||
CNF_ProcessInitStepSlew(post_acquire_hook, NULL);
|
||||
if (CNF_GetInitSources() > 0) {
|
||||
CNF_AddInitSources();
|
||||
NSR_StartSources();
|
||||
assert(REF_GetMode() != REF_ModeNormal);
|
||||
/* Wait for mode end notification */
|
||||
} else {
|
||||
(post_init_ntp_hook)(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -200,7 +256,7 @@ write_lockfile(void)
|
||||
|
||||
out = fopen(pidfile, "w");
|
||||
if (!out) {
|
||||
LOG(LOGS_ERR, LOGF_Main, "could not open lockfile %s for writing", pidfile);
|
||||
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
|
||||
} else {
|
||||
fprintf(out, "%d\n", getpid());
|
||||
fclose(out);
|
||||
@@ -222,14 +278,14 @@ go_daemon(void)
|
||||
/* 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));
|
||||
LOG_FATAL(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));
|
||||
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
|
||||
} else if (pid > 0) {
|
||||
/* In the 'grandparent' */
|
||||
char message[1024];
|
||||
@@ -254,7 +310,7 @@ go_daemon(void)
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
|
||||
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
|
||||
} else if (pid > 0) {
|
||||
exit(0); /* In the 'parent' */
|
||||
} else {
|
||||
@@ -262,7 +318,7 @@ go_daemon(void)
|
||||
|
||||
/* Change current directory to / */
|
||||
if (chdir("/") < 0) {
|
||||
LOG(LOGS_ERR, LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
|
||||
LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
|
||||
}
|
||||
|
||||
/* Don't keep stdin/out/err from before. But don't close
|
||||
@@ -290,6 +346,8 @@ int main
|
||||
int do_init_rtc = 0, restarted = 0;
|
||||
int other_pid;
|
||||
int lock_memory = 0, sched_priority = 0;
|
||||
int system_log = 1;
|
||||
int config_args = 0;
|
||||
|
||||
LOG_Initialise();
|
||||
|
||||
@@ -326,14 +384,27 @@ int main
|
||||
} else if (!strcmp("-n", *argv)) {
|
||||
nofork = 1;
|
||||
} else if (!strcmp("-d", *argv)) {
|
||||
debug = 1;
|
||||
debug++;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-q", *argv)) {
|
||||
ref_mode = REF_ModeUpdateOnce;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-Q", *argv)) {
|
||||
ref_mode = REF_ModePrintOnce;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-4", *argv)) {
|
||||
address_family = IPADDR_INET4;
|
||||
} else if (!strcmp("-6", *argv)) {
|
||||
address_family = IPADDR_INET6;
|
||||
} else {
|
||||
} else if (*argv[0] == '-') {
|
||||
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
|
||||
} else {
|
||||
/* Process remaining arguments and configuration lines */
|
||||
config_args = argc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,16 +419,26 @@ int main
|
||||
go_daemon();
|
||||
}
|
||||
|
||||
if (!debug) {
|
||||
if (system_log) {
|
||||
LOG_OpenSystemLog();
|
||||
}
|
||||
|
||||
LOG_SetDebugLevel(debug);
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION);
|
||||
|
||||
DNS_SetAddressFamily(address_family);
|
||||
|
||||
CNF_SetRestarted(restarted);
|
||||
CNF_ReadFile(conf_file);
|
||||
|
||||
/* Parse the config file or the remaining command line arguments */
|
||||
if (!config_args) {
|
||||
CNF_ReadFile(conf_file);
|
||||
} else {
|
||||
do {
|
||||
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
|
||||
} while (++argv, --argc);
|
||||
}
|
||||
|
||||
/* Check whether another chronyd may already be running. Do this after
|
||||
* forking, so that message logging goes to the right place (i.e. syslog), in
|
||||
@@ -371,16 +452,12 @@ int main
|
||||
* 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();
|
||||
RTC_Initialise(do_init_rtc);
|
||||
SRC_Initialise();
|
||||
RCL_Initialise();
|
||||
KEY_Initialise();
|
||||
@@ -400,7 +477,7 @@ int main
|
||||
if (!user) {
|
||||
user = CNF_GetUser();
|
||||
}
|
||||
if (user) {
|
||||
if (user && strcmp(user, "root")) {
|
||||
SYS_DropRoot(user);
|
||||
}
|
||||
|
||||
@@ -412,13 +489,21 @@ int main
|
||||
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;
|
||||
|
||||
CNF_SetupAccessRestrictions();
|
||||
|
||||
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
|
||||
ref_mode = REF_ModeInitStepSlew;
|
||||
}
|
||||
|
||||
REF_SetModeEndHandler(reference_mode_end);
|
||||
REF_SetMode(ref_mode);
|
||||
|
||||
if (do_init_rtc) {
|
||||
RTC_TimeInit(post_init_rtc_hook, NULL);
|
||||
} else {
|
||||
|
||||
42
make_release
42
make_release
@@ -9,6 +9,7 @@ if [ $# -ne 1 ]; then
|
||||
fi
|
||||
|
||||
version=$1
|
||||
tag=$version
|
||||
subdir=chrony-${version}
|
||||
mandate=$(date +'%B %Y')
|
||||
|
||||
@@ -21,20 +22,24 @@ fi
|
||||
|
||||
[ -d RELEASES ] || mkdir RELEASES
|
||||
|
||||
git tag -s $version || exit 1
|
||||
|
||||
rm -rf RELEASES/$subdir
|
||||
|
||||
git archive --format=tar --prefix=RELEASES/${subdir}/ $version | \
|
||||
if [ $version != test ]; then
|
||||
git tag -s $tag || exit 1
|
||||
else
|
||||
tag=HEAD
|
||||
fi
|
||||
|
||||
git archive --format=tar --prefix=RELEASES/${subdir}/ $tag | \
|
||||
tar xf - || exit 1
|
||||
|
||||
cd RELEASES/$subdir || exit 1
|
||||
|
||||
echo $version > version.txt
|
||||
|
||||
sed -e "s%@@VERSION@@%${version}%" < chrony.spec.sample > chrony.spec
|
||||
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
|
||||
|
||||
for m in chrony.1.in chronyc.1.in chrony.conf.5.in chronyd.8.in; do
|
||||
for m in chrony.1 chronyc.1.in chrony.conf.5.in chronyd.8.in; do
|
||||
sed -e "s%@VERSION@%${version}%;s%@MAN_DATE@%${mandate}%" \
|
||||
< $m > ${m}_
|
||||
mv -f ${m}_ $m
|
||||
@@ -45,10 +50,29 @@ mv chrony.txt chrony.txt_
|
||||
make distclean
|
||||
mv chrony.txt_ chrony.txt
|
||||
|
||||
rm -f faqgen.pl make_release chrony.spec.sample .gitignore
|
||||
awk '/^[1-9] Installation$/{p=1}
|
||||
/^[1-9]\.. Support for line editing/{exit}; p' chrony.txt | \
|
||||
tail -n +4 > INSTALL
|
||||
|
||||
if [ $(wc -l < INSTALL) -gt 100 -o $(wc -l < INSTALL) -lt 85 ]; then
|
||||
echo "INSTALL generated incorrectly?"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
awk '/^[1-9] Frequently asked questions$/{p=1}
|
||||
/^Appendix A GNU General Public License$/{exit}; p' chrony.txt | \
|
||||
tail -n +4 | sed 's/^[1-9]\.\([1-9]\)/\1/' | sed 's/^----/--/' | \
|
||||
sed 's/^====/==/' > FAQ
|
||||
|
||||
if [ $(wc -l < FAQ) -gt 400 -o $(wc -l < FAQ) -lt 200 ]; then
|
||||
echo "FAQ generated incorrectly?"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
rm -f config.h config.log make_release .gitignore
|
||||
|
||||
cd ..
|
||||
tar cvf - $subdir | gzip -9 > ${subdir}.tar.gz
|
||||
gpg -b -a -o ${subdir}-tar-gz-asc.txt ${subdir}.tar.gz
|
||||
|
||||
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz
|
||||
|
||||
[ $version != test ] && \
|
||||
gpg -b -a -o ${subdir}-tar-gz-asc.txt ${subdir}.tar.gz
|
||||
|
||||
19
manual.c
19
manual.c
@@ -59,13 +59,6 @@ typedef struct {
|
||||
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
|
||||
@@ -73,7 +66,7 @@ slew_samples(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *not_used);
|
||||
|
||||
/* ================================================== */
|
||||
@@ -89,9 +82,6 @@ MNL_Initialise(void)
|
||||
|
||||
n_samples = 0;
|
||||
|
||||
replace_margin = REPLACE_MARGIN;
|
||||
error = ERROR_MARGIN;
|
||||
|
||||
LCL_AddParameterChangeHandler(slew_samples, NULL);
|
||||
}
|
||||
|
||||
@@ -226,11 +216,16 @@ slew_samples(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *not_used)
|
||||
{
|
||||
double delta_time;
|
||||
int i;
|
||||
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
MNL_Reset();
|
||||
}
|
||||
|
||||
for (i=0; i<n_samples; i++) {
|
||||
UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
|
||||
dfreq, doffset);
|
||||
|
||||
@@ -46,7 +46,7 @@ DNS_SetAddressFamily(int family)
|
||||
DNS_Status
|
||||
DNS_Name2IPAddress(const char *name, IPAddr *addr)
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
#ifdef HAVE_GETADDRINFO
|
||||
struct addrinfo hints, *res, *ai;
|
||||
int result;
|
||||
|
||||
|
||||
144
nameserv_async.c
Normal file
144
nameserv_async.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Functions to asynchronously convert name to IP address
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "nameserv_async.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "sched.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef FEAT_ASYNCDNS
|
||||
|
||||
#ifdef USE_PTHREAD_ASYNCDNS
|
||||
#include <pthread.h>
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
struct DNS_Async_Instance {
|
||||
const char *name;
|
||||
DNS_Status status;
|
||||
IPAddr addr;
|
||||
DNS_NameResolveHandler handler;
|
||||
void *arg;
|
||||
|
||||
pthread_t thread;
|
||||
int pipe[2];
|
||||
};
|
||||
|
||||
static int resolving_threads = 0;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void *
|
||||
start_resolving(void *anything)
|
||||
{
|
||||
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
|
||||
|
||||
inst->status = DNS_Name2IPAddress(inst->name, &inst->addr);
|
||||
|
||||
/* Notify the main thread that the result is ready */
|
||||
if (write(inst->pipe[1], "", 1) < 0)
|
||||
;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
end_resolving(void *anything)
|
||||
{
|
||||
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
|
||||
|
||||
if (pthread_join(inst->thread, NULL)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
|
||||
}
|
||||
|
||||
resolving_threads--;
|
||||
|
||||
SCH_RemoveInputFileHandler(inst->pipe[0]);
|
||||
close(inst->pipe[0]);
|
||||
close(inst->pipe[1]);
|
||||
|
||||
(inst->handler)(inst->status, &inst->addr, inst->arg);
|
||||
|
||||
Free(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
|
||||
{
|
||||
struct DNS_Async_Instance *inst;
|
||||
|
||||
inst = MallocNew(struct DNS_Async_Instance);
|
||||
inst->name = name;
|
||||
inst->handler = handler;
|
||||
inst->arg = anything;
|
||||
inst->status = DNS_Failure;
|
||||
|
||||
if (pipe(inst->pipe)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
|
||||
}
|
||||
|
||||
resolving_threads++;
|
||||
assert(resolving_threads <= 1);
|
||||
|
||||
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
|
||||
}
|
||||
|
||||
SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/* This is a blocking implementation used when nothing else is available */
|
||||
|
||||
void
|
||||
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
|
||||
{
|
||||
IPAddr addr;
|
||||
DNS_Status status;
|
||||
|
||||
status = DNS_Name2IPAddress(name, &addr);
|
||||
(handler)(status, &addr, anything);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -21,25 +21,21 @@
|
||||
|
||||
=======================================================================
|
||||
|
||||
Header file for acquisition module
|
||||
Header for asynchronous nameserver functions
|
||||
*/
|
||||
|
||||
#ifndef GOT_ACQUIRE_H
|
||||
#define GOT_ACQUIRE_H
|
||||
|
||||
#include "addressing.h"
|
||||
#ifndef GOT_NAMESERV_ASYNC_H
|
||||
#define GOT_NAMESERV_ASYNC_H
|
||||
|
||||
typedef struct ACQ_SourceRecord *ACQ_Source;
|
||||
#include "nameserv.h"
|
||||
|
||||
extern void ACQ_Initialise(void);
|
||||
/* Function type for callback to process the result */
|
||||
typedef void (*DNS_NameResolveHandler)(DNS_Status status, IPAddr *ip_addr, void *anything);
|
||||
|
||||
extern void ACQ_Finalise(void);
|
||||
/* Request resolving of a name to IP address. The handler will be
|
||||
called when the result is available, but it may be also called
|
||||
directly from this function call. */
|
||||
extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything);
|
||||
|
||||
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 */
|
||||
#endif
|
||||
576
ntp_core.c
576
ntp_core.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2013
|
||||
* Copyright (C) Miroslav Lichvar 2009-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -47,11 +47,6 @@
|
||||
|
||||
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 */
|
||||
@@ -69,6 +64,7 @@ typedef enum {
|
||||
|
||||
struct NCR_Instance_Record {
|
||||
NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */
|
||||
NTP_Local_Address local_addr; /* Local address/socket used to send packets */
|
||||
NTP_Mode mode; /* The source's NTP mode
|
||||
(client/server or symmetric active peer) */
|
||||
OperatingMode opmode; /* Whether we are sampling this source
|
||||
@@ -77,6 +73,7 @@ struct NCR_Instance_Record {
|
||||
pending to transmit to the source */
|
||||
SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are
|
||||
running on a timer. */
|
||||
int tx_suspended; /* Boolean indicating we can't transmit yet */
|
||||
|
||||
int auto_offline; /* If 1, automatically go offline if server/peer
|
||||
isn't responding */
|
||||
@@ -88,8 +85,8 @@ struct NCR_Instance_Record {
|
||||
received packets) */
|
||||
|
||||
int presend_minpoll; /* If the current polling interval is
|
||||
at least this, an echo datagram
|
||||
will be send some time before every
|
||||
at least this, an extra client packet
|
||||
will be send some time before normal
|
||||
transmit. This ensures that both
|
||||
us and the server/peer have an ARP
|
||||
entry for each other ready, which
|
||||
@@ -188,9 +185,13 @@ struct NCR_Instance_Record {
|
||||
|
||||
/* Time to wait before retransmitting in burst mode, if we did not get
|
||||
a reply to the previous probe */
|
||||
#define BURST_TIMEOUT 8.0
|
||||
#define BURST_TIMEOUT 2.0
|
||||
|
||||
/* Time to wait after sending echo to 'warm up' link */
|
||||
/* Number of samples in initial burst */
|
||||
#define IBURST_GOOD_SAMPLES 4
|
||||
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
|
||||
|
||||
/* Time to wait after sending packet to 'warm up' link */
|
||||
#define WARM_UP_DELAY 4.0
|
||||
|
||||
/* The NTP protocol version that we support */
|
||||
@@ -212,6 +213,14 @@ struct NCR_Instance_Record {
|
||||
/* INVALID or Unkown stratum from external server as per the NTP 4 docs */
|
||||
#define NTP_INVALID_STRATUM 0
|
||||
|
||||
/* Minimum allowed poll interval */
|
||||
#define MIN_POLL 0
|
||||
|
||||
/* Maximum poll interval set by KoD RATE */
|
||||
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
|
||||
|
||||
#define INVALID_SOCK_FD -1
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static ADF_AuthTable access_auth_table;
|
||||
@@ -220,12 +229,79 @@ static ADF_AuthTable access_auth_table;
|
||||
/* Forward prototypes */
|
||||
|
||||
static void transmit_timeout(void *arg);
|
||||
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
do_size_checks(void)
|
||||
{
|
||||
/* Assertions to check the sizes of certain data types
|
||||
and the positions of certain record fields */
|
||||
|
||||
/* Check that certain invariants are true */
|
||||
assert(sizeof(NTP_int32) == 4);
|
||||
assert(sizeof(NTP_int64) == 8);
|
||||
|
||||
/* Check offsets of all fields in the NTP packet format */
|
||||
assert(offsetof(NTP_Packet, lvm) == 0);
|
||||
assert(offsetof(NTP_Packet, stratum) == 1);
|
||||
assert(offsetof(NTP_Packet, poll) == 2);
|
||||
assert(offsetof(NTP_Packet, precision) == 3);
|
||||
assert(offsetof(NTP_Packet, root_delay) == 4);
|
||||
assert(offsetof(NTP_Packet, root_dispersion) == 8);
|
||||
assert(offsetof(NTP_Packet, reference_id) == 12);
|
||||
assert(offsetof(NTP_Packet, reference_ts) == 16);
|
||||
assert(offsetof(NTP_Packet, originate_ts) == 24);
|
||||
assert(offsetof(NTP_Packet, receive_ts) == 32);
|
||||
assert(offsetof(NTP_Packet, transmit_ts) == 40);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
do_time_checks(void)
|
||||
{
|
||||
struct timeval now;
|
||||
time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */
|
||||
|
||||
#ifdef HAVE_LONG_TIME_T
|
||||
/* Check that time before NTP_ERA_SPLIT underflows correctly */
|
||||
|
||||
struct timeval tv1 = {NTP_ERA_SPLIT, 1}, tv2 = {NTP_ERA_SPLIT - 1, 1};
|
||||
NTP_int64 ntv1, ntv2;
|
||||
int r;
|
||||
|
||||
UTI_TimevalToInt64(&tv1, &ntv1, 0);
|
||||
UTI_TimevalToInt64(&tv2, &ntv2, 0);
|
||||
UTI_Int64ToTimeval(&ntv1, &tv1);
|
||||
UTI_Int64ToTimeval(&ntv2, &tv2);
|
||||
|
||||
r = tv1.tv_sec == NTP_ERA_SPLIT &&
|
||||
tv1.tv_sec + (1ULL << 32) - 1 == tv2.tv_sec;
|
||||
|
||||
assert(r);
|
||||
|
||||
LCL_ReadRawTime(&now);
|
||||
if (tv2.tv_sec - now.tv_sec < warning_advance)
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!",
|
||||
UTI_TimeToLogForm(tv2.tv_sec));
|
||||
#else
|
||||
LCL_ReadRawTime(&now);
|
||||
if (now.tv_sec > 0x7fffffff - warning_advance)
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "System time ends at %s!",
|
||||
UTI_TimeToLogForm(0x7fffffff));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NCR_Initialise(void)
|
||||
{
|
||||
do_size_checks();
|
||||
do_time_checks();
|
||||
|
||||
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements",
|
||||
" Date (UTC) Time IP Address L St 1234 abc 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
|
||||
: -1;
|
||||
@@ -245,11 +321,20 @@ NCR_Finalise(void)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
start_initial_timeout(NCR_Instance inst)
|
||||
restart_timeout(NCR_Instance inst, double delay)
|
||||
{
|
||||
/* Check if we can transmit */
|
||||
if (inst->tx_suspended) {
|
||||
assert(!inst->timer_running);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start timer for first transmission */
|
||||
inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
|
||||
/* Stop old timer if running */
|
||||
if (inst->timer_running)
|
||||
SCH_RemoveTimeout(inst->timeout_id);
|
||||
|
||||
/* Start new timer for transmission */
|
||||
inst->timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
|
||||
SAMPLING_RANDOMNESS,
|
||||
SCH_NtpSamplingClass,
|
||||
transmit_timeout, (void *)inst);
|
||||
@@ -258,6 +343,32 @@ start_initial_timeout(NCR_Instance inst)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
start_initial_timeout(NCR_Instance inst)
|
||||
{
|
||||
if (!inst->timer_running) {
|
||||
/* This will be the first transmission after mode change */
|
||||
|
||||
/* Mark source active */
|
||||
SRC_SetActive(inst->source);
|
||||
}
|
||||
|
||||
restart_timeout(inst, INITIAL_DELAY);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
close_client_socket(NCR_Instance inst)
|
||||
{
|
||||
if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) {
|
||||
NIO_CloseClientSocket(inst->local_addr.sock_fd);
|
||||
inst->local_addr.sock_fd = INVALID_SOCK_FD;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
take_offline(NCR_Instance inst)
|
||||
{
|
||||
@@ -269,6 +380,13 @@ take_offline(NCR_Instance inst)
|
||||
|
||||
/* Mark source unreachable */
|
||||
SRC_ResetReachability(inst->source);
|
||||
|
||||
/* And inactive */
|
||||
SRC_UnsetActive(inst->source);
|
||||
|
||||
close_client_socket(inst);
|
||||
|
||||
NCR_ResetInstance(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -281,11 +399,16 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
|
||||
result = MallocNew(struct NCR_Instance_Record);
|
||||
|
||||
result->remote_addr = *remote_addr;
|
||||
result->local_addr.ip_addr.family = IPADDR_UNSPEC;
|
||||
|
||||
switch (type) {
|
||||
case NTP_SERVER:
|
||||
/* Client socket will be obtained when sending request */
|
||||
result->local_addr.sock_fd = INVALID_SOCK_FD;
|
||||
result->mode = MODE_CLIENT;
|
||||
break;
|
||||
case NTP_PEER:
|
||||
result->local_addr.sock_fd = NIO_GetServerSocket(remote_addr);
|
||||
result->mode = MODE_ACTIVE;
|
||||
break;
|
||||
default:
|
||||
@@ -293,11 +416,22 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
|
||||
}
|
||||
|
||||
result->minpoll = params->minpoll;
|
||||
if (result->minpoll < MIN_POLL)
|
||||
result->minpoll = SRC_DEFAULT_MINPOLL;
|
||||
result->maxpoll = params->maxpoll;
|
||||
result->min_stratum = params->min_stratum;
|
||||
if (result->maxpoll < MIN_POLL)
|
||||
result->maxpoll = SRC_DEFAULT_MAXPOLL;
|
||||
if (result->maxpoll < result->minpoll)
|
||||
result->maxpoll = result->minpoll;
|
||||
|
||||
result->min_stratum = params->min_stratum;
|
||||
result->presend_minpoll = params->presend_minpoll;
|
||||
result->presend_done = 0;
|
||||
|
||||
result->max_delay = params->max_delay;
|
||||
result->max_delay_ratio = params->max_delay_ratio;
|
||||
result->max_delay_dev_ratio = params->max_delay_dev_ratio;
|
||||
result->auto_offline = params->auto_offline;
|
||||
result->poll_target = params->poll_target;
|
||||
|
||||
if (params->authkey == INACTIVE_AUTHKEY) {
|
||||
result->do_auth = 0;
|
||||
@@ -305,51 +439,28 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
|
||||
} else {
|
||||
result->do_auth = 1;
|
||||
result->auth_key_id = params->authkey;
|
||||
if (!KEY_KeyKnown(result->auth_key_id)) {
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "Source %s added with unknown key %lu",
|
||||
UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
result->timer_running = 0;
|
||||
result->timeout_id = 0;
|
||||
result->tx_suspended = 1;
|
||||
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
|
||||
result->local_poll = result->minpoll;
|
||||
|
||||
NCR_ResetInstance(result);
|
||||
|
||||
if (params->iburst) {
|
||||
NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -358,23 +469,60 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
|
||||
void
|
||||
NCR_DestroyInstance(NCR_Instance instance)
|
||||
{
|
||||
if (instance->opmode != MD_OFFLINE)
|
||||
take_offline(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);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NCR_StartInstance(NCR_Instance instance)
|
||||
{
|
||||
instance->tx_suspended = 0;
|
||||
if (instance->opmode != MD_OFFLINE)
|
||||
start_initial_timeout(instance);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NCR_ResetInstance(NCR_Instance instance)
|
||||
{
|
||||
instance->tx_count = 0;
|
||||
instance->presend_done = 0;
|
||||
|
||||
instance->poll_score = 0.0;
|
||||
instance->remote_poll = 0;
|
||||
instance->remote_stratum = 0;
|
||||
|
||||
instance->remote_orig.hi = 0;
|
||||
instance->remote_orig.lo = 0;
|
||||
instance->local_rx.tv_sec = 0;
|
||||
instance->local_rx.tv_usec = 0;
|
||||
instance->local_tx.tv_sec = 0;
|
||||
instance->local_tx.tv_usec = 0;
|
||||
instance->local_ntp_tx.hi = 0;
|
||||
instance->local_ntp_tx.lo = 0;
|
||||
|
||||
if (instance->local_poll != instance->minpoll) {
|
||||
instance->local_poll = instance->minpoll;
|
||||
|
||||
/* The timer was set with a longer poll interval, restart it */
|
||||
if (instance->timer_running)
|
||||
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_packet_auth(NTP_Packet *pkt, unsigned long keyid, int auth_len)
|
||||
{
|
||||
@@ -520,8 +668,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
|
||||
|
||||
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 */
|
||||
/* Burst modes */
|
||||
delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL;
|
||||
break;
|
||||
default:
|
||||
@@ -534,7 +681,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
static int
|
||||
transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
|
||||
int my_poll, /* The log2 of the local poll interval */
|
||||
int version, /* The NTP version to be set in the packet */
|
||||
@@ -551,11 +698,12 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
|
||||
(including adjustment to
|
||||
reference), ignored if
|
||||
NULL */
|
||||
NTP_Remote_Address *where_to /* Where to address the reponse to */
|
||||
NTP_Remote_Address *where_to, /* Where to address the reponse to */
|
||||
NTP_Local_Address *from /* From what address to send it */
|
||||
)
|
||||
{
|
||||
NTP_Packet message;
|
||||
int leap;
|
||||
int leap, ret;
|
||||
struct timeval local_transmit;
|
||||
|
||||
/* Parameters read from reference module */
|
||||
@@ -639,12 +787,17 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
|
||||
(unsigned char *)&message.auth_data, sizeof (message.auth_data));
|
||||
if (auth_len > 0) {
|
||||
message.auth_keyid = htonl(key_id);
|
||||
NIO_SendAuthenticatedPacket(&message, where_to,
|
||||
ret = NIO_SendAuthenticatedPacket(&message, where_to, from,
|
||||
sizeof (message.auth_keyid) + auth_len);
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_NtpCore,
|
||||
"Could not generate auth data with key %lu to send packet",
|
||||
key_id);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
|
||||
NIO_SendNormalPacket(&message, where_to);
|
||||
ret = NIO_SendNormalPacket(&message, where_to, from);
|
||||
}
|
||||
|
||||
if (local_tx) {
|
||||
@@ -655,6 +808,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
|
||||
*local_ntp_tx = message.transmit_ts;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -664,8 +818,7 @@ static void
|
||||
transmit_timeout(void *arg)
|
||||
{
|
||||
NCR_Instance inst = (NCR_Instance) arg;
|
||||
double timeout_delay;
|
||||
int do_auth;
|
||||
int sent;
|
||||
|
||||
inst->timer_running = 0;
|
||||
|
||||
@@ -686,13 +839,18 @@ transmit_timeout(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Transmit timeout for [%s:%d]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Transmit timeout for [%s:%d]",
|
||||
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
|
||||
#endif
|
||||
|
||||
/* Open new client socket */
|
||||
if (inst->mode == MODE_CLIENT) {
|
||||
close_client_socket(inst);
|
||||
assert(inst->local_addr.sock_fd == INVALID_SOCK_FD);
|
||||
inst->local_addr.sock_fd = NIO_GetClientSocket(&inst->remote_addr);
|
||||
}
|
||||
|
||||
/* 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
|
||||
sending an NTP exchange to ensure both ends' ARP caches are
|
||||
primed. On loaded systems this might also help ensure that bits
|
||||
of the program are paged in properly before we start. */
|
||||
|
||||
@@ -700,33 +858,42 @@ transmit_timeout(void *arg)
|
||||
(inst->presend_minpoll <= inst->local_poll) &&
|
||||
!inst->presend_done) {
|
||||
|
||||
/* Send */
|
||||
NIO_SendEcho(&inst->remote_addr);
|
||||
/* Send a client packet, don't store the local tx values
|
||||
as the reply will be ignored */
|
||||
transmit_packet(MODE_CLIENT, inst->local_poll, NTP_VERSION, 0, 0,
|
||||
&inst->remote_orig, &inst->local_rx, NULL, NULL,
|
||||
&inst->remote_addr, &inst->local_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);
|
||||
restart_timeout(inst, WARM_UP_DELAY);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
inst->presend_done = 0; /* Reset for next time */
|
||||
|
||||
sent = transmit_packet(inst->mode, inst->local_poll,
|
||||
NTP_VERSION,
|
||||
inst->do_auth, inst->auth_key_id,
|
||||
&inst->remote_orig,
|
||||
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
|
||||
&inst->remote_addr,
|
||||
&inst->local_addr);
|
||||
|
||||
++inst->tx_count;
|
||||
|
||||
/* If the source loses connectivity, back off the sampling rate to reduce
|
||||
wasted sampling. If it's the source to which we are currently locked,
|
||||
back off slower. */
|
||||
/* If the source loses connectivity and our packets are still being sent,
|
||||
back off the sampling rate to reduce the network traffic. If it's the
|
||||
source to which we are currently locked, back off slowly. */
|
||||
|
||||
if (inst->tx_count >= 2) {
|
||||
/* Implies we have missed at least one transmission */
|
||||
|
||||
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
|
||||
if (sent) {
|
||||
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
|
||||
}
|
||||
|
||||
SRC_UpdateReachability(inst->source, 0);
|
||||
|
||||
@@ -735,21 +902,12 @@ transmit_timeout(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
/* When not reachable, don't stop online burst until sending succeeds */
|
||||
if (!sent && !SRC_IsReachable(inst->source))
|
||||
break;
|
||||
/* Fall through */
|
||||
case MD_BURST_WAS_OFFLINE:
|
||||
--inst->burst_total_samples_to_go;
|
||||
break;
|
||||
@@ -758,12 +916,7 @@ transmit_timeout(void *arg)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
|
||||
}
|
||||
|
||||
|
||||
@@ -852,9 +1005,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
|
||||
/* ==================== */
|
||||
|
||||
/* Save local receive timestamp */
|
||||
inst->local_rx = *now;
|
||||
|
||||
pkt_leap = (message->lvm >> 6) & 0x3;
|
||||
if (pkt_leap == 0x3) {
|
||||
source_is_synchronized = 0;
|
||||
@@ -886,14 +1036,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
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'. */
|
||||
@@ -998,11 +1140,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
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);
|
||||
}
|
||||
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 */
|
||||
@@ -1070,6 +1208,14 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
kod_rate = 1;
|
||||
}
|
||||
|
||||
/* The transmit timestamp and local receive timestamp must not be saved when
|
||||
the authentication test failed to prevent denial-of-service attacks on
|
||||
symmetric associations using authentication */
|
||||
if (test5) {
|
||||
inst->remote_orig = message->transmit_ts;
|
||||
inst->local_rx = *now;
|
||||
}
|
||||
|
||||
valid_kod = test1 && test2 && test5;
|
||||
|
||||
valid_data = test1 && test2 && test3 && test4 && test4a && test4b;
|
||||
@@ -1080,55 +1226,61 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
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",
|
||||
DEBUG_LOG(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)",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Root delay=%08x (%f), dispersion=%08x (%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]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Ref id=[%x], ref_time=%08x.%08x [%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]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Originate=%08x.%08x [%s]",
|
||||
message->originate_ts.hi, message->originate_ts.lo,
|
||||
UTI_TimestampToString(&message->originate_ts));
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Message receive=%08lx.%08lx [%s]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Message receive=%08x.%08x [%s]",
|
||||
message->receive_ts.hi, message->receive_ts.lo,
|
||||
UTI_TimestampToString(&message->receive_ts));
|
||||
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Transmit=%08lx.%08lx [%s]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "Transmit=%08x.%08x [%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",
|
||||
DEBUG_LOG(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",
|
||||
DEBUG_LOG(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",
|
||||
DEBUG_LOG(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
|
||||
DEBUG_LOG(LOGF_NtpCore, "kod_rate=%d valid_kod=%d", kod_rate, valid_kod);
|
||||
|
||||
/* Reduce polling rate if KoD RATE was received */
|
||||
if (kod_rate && valid_kod) {
|
||||
if (message->poll > inst->minpoll) {
|
||||
inst->minpoll = message->poll;
|
||||
/* Set our minpoll to message poll, but use a reasonable maximum */
|
||||
if (message->poll <= MAX_KOD_RATE_POLL)
|
||||
inst->minpoll = message->poll;
|
||||
else if (inst->minpoll < MAX_KOD_RATE_POLL)
|
||||
inst->minpoll = MAX_KOD_RATE_POLL;
|
||||
|
||||
if (inst->minpoll > inst->maxpoll)
|
||||
inst->maxpoll = inst->minpoll;
|
||||
if (inst->minpoll > inst->local_poll)
|
||||
inst->local_poll = inst->minpoll;
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, minpoll set to %d",
|
||||
UTI_IPToString(&inst->remote_addr.ip_addr), inst->minpoll);
|
||||
|
||||
LOG(LOGS_WARN, LOGF_NtpCore,
|
||||
"Received KoD RATE with poll %d from %s, minpoll set to %d",
|
||||
message->poll, UTI_IPToString(&inst->remote_addr.ip_addr),
|
||||
inst->minpoll);
|
||||
}
|
||||
|
||||
/* Stop ongoing burst */
|
||||
if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
|
||||
inst->burst_good_samples_to_go = 0;
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped",
|
||||
UTI_IPToString(&inst->remote_addr.ip_addr), inst->minpoll);
|
||||
UTI_IPToString(&inst->remote_addr.ip_addr));
|
||||
}
|
||||
|
||||
requeue_transmit = 1;
|
||||
@@ -1161,6 +1313,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
root_delay, root_dispersion,
|
||||
message->stratum, (NTP_Leap) pkt_leap);
|
||||
|
||||
SRC_SelectSource(inst->source);
|
||||
|
||||
/* Now examine the registers. First though, if the prediction is
|
||||
not even within +/- the peer distance of the peer, we are clearly
|
||||
not tracking the peer at all well, so we back off the sampling
|
||||
@@ -1191,6 +1345,10 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
adjust_poll(inst, 0.1);
|
||||
}
|
||||
|
||||
/* If in client mode, no more packets are expected to be coming from the
|
||||
server and the socket can be closed */
|
||||
close_client_socket(inst);
|
||||
|
||||
requeue_transmit = 1;
|
||||
}
|
||||
|
||||
@@ -1205,11 +1363,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
|
||||
|
||||
/* 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);
|
||||
restart_timeout(inst, delay_time);
|
||||
}
|
||||
|
||||
/* Do measurement logging */
|
||||
@@ -1259,18 +1413,24 @@ NCR_ProcessKnown
|
||||
struct timeval *now, /* timestamp at time of receipt */
|
||||
double now_err,
|
||||
NCR_Instance inst, /* the instance record for this peer/server */
|
||||
int sock_fd, /* the receiving socket */
|
||||
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;
|
||||
int auth_len;
|
||||
|
||||
/* Make sure the packet was received by the sending socket */
|
||||
if (sock_fd != inst->local_addr.sock_fd) {
|
||||
DEBUG_LOG(LOGF_NtpCore,
|
||||
"Packet received by wrong socket %d (expected %d)",
|
||||
sock_fd, inst->local_addr.sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore packets from offline sources */
|
||||
if (inst->opmode == MD_OFFLINE) {
|
||||
if (inst->opmode == MD_OFFLINE || inst->tx_suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1309,45 +1469,8 @@ NCR_ProcessKnown
|
||||
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);
|
||||
}
|
||||
NCR_ProcessUnknown(message, now, now_err,
|
||||
&inst->remote_addr, &inst->local_addr, length);
|
||||
|
||||
break;
|
||||
|
||||
@@ -1383,6 +1506,9 @@ NCR_ProcessKnown
|
||||
break;
|
||||
|
||||
case MODE_SERVER:
|
||||
/* Ignore presend reply */
|
||||
if (inst->presend_done)
|
||||
break;
|
||||
|
||||
switch(inst->mode) {
|
||||
case MODE_ACTIVE:
|
||||
@@ -1452,7 +1578,7 @@ NCR_ProcessKnown
|
||||
|
||||
/* ================================================== */
|
||||
/* 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 */
|
||||
and it relates to a source we don't know (not our server or peer) */
|
||||
|
||||
void
|
||||
NCR_ProcessUnknown
|
||||
@@ -1460,15 +1586,23 @@ NCR_ProcessUnknown
|
||||
struct timeval *now, /* timestamp at time of receipt */
|
||||
double now_err, /* assumed error in the timestamp */
|
||||
NTP_Remote_Address *remote_addr,
|
||||
NTP_Local_Address *local_addr,
|
||||
int length /* the length of the received packet */
|
||||
)
|
||||
{
|
||||
NTP_Mode his_mode;
|
||||
NTP_Mode my_mode;
|
||||
int my_poll, version;
|
||||
int valid_key, valid_auth, auth_len;
|
||||
int valid_auth, auth_len;
|
||||
unsigned long key_id;
|
||||
|
||||
/* Ignore the packet if it wasn't received by server socket */
|
||||
if (!NIO_IsServerSocket(local_addr->sock_fd)) {
|
||||
DEBUG_LOG(LOGF_NtpCore, "NTP request packet received by client socket %d",
|
||||
local_addr->sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check version */
|
||||
version = (message->lvm >> 3) & 0x7;
|
||||
if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) {
|
||||
@@ -1505,18 +1639,13 @@ NCR_ProcessUnknown
|
||||
/* 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;
|
||||
}
|
||||
valid_auth = check_packet_auth(message, key_id, auth_len);
|
||||
}
|
||||
|
||||
if (!do_auth || (valid_key && valid_auth)) {
|
||||
my_poll = message->poll; /* What should this be set to? Does the client actually care? */
|
||||
if (!do_auth || valid_auth) {
|
||||
/* Reply with the same poll, the client may use it to control its poll */
|
||||
my_poll = message->poll;
|
||||
|
||||
transmit_packet(my_mode, my_poll,
|
||||
version,
|
||||
@@ -1525,11 +1654,12 @@ NCR_ProcessUnknown
|
||||
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);
|
||||
remote_addr,
|
||||
local_addr);
|
||||
}
|
||||
}
|
||||
} else if (!LOG_RateLimited()) {
|
||||
LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
|
||||
UTI_IPToString(&remote_addr->ip_addr),
|
||||
remote_addr->port);
|
||||
}
|
||||
@@ -1545,19 +1675,13 @@ NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doff
|
||||
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]",
|
||||
DEBUG_LOG(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]",
|
||||
DEBUG_LOG(LOGF_NtpCore, "tx prev=[%s] new=[%s]",
|
||||
UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_tx));
|
||||
#else
|
||||
(void)prev;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1570,15 +1694,10 @@ NCR_TakeSourceOnline(NCR_Instance inst)
|
||||
/* 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);
|
||||
}
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
|
||||
inst->opmode = MD_ONLINE;
|
||||
NCR_ResetInstance(inst);
|
||||
start_initial_timeout(inst);
|
||||
break;
|
||||
case MD_BURST_WAS_ONLINE:
|
||||
/* Will revert */
|
||||
@@ -1597,10 +1716,8 @@ 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);
|
||||
}
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
|
||||
take_offline(inst);
|
||||
break;
|
||||
case MD_OFFLINE:
|
||||
break;
|
||||
@@ -1619,8 +1736,12 @@ NCR_TakeSourceOffline(NCR_Instance inst)
|
||||
void
|
||||
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
|
||||
{
|
||||
if (new_minpoll < MIN_POLL)
|
||||
return;
|
||||
inst->minpoll = new_minpoll;
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
|
||||
if (inst->maxpoll < inst->minpoll)
|
||||
NCR_ModifyMaxpoll(inst, inst->minpoll);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1628,8 +1749,12 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
|
||||
void
|
||||
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
|
||||
{
|
||||
if (new_maxpoll < MIN_POLL)
|
||||
return;
|
||||
inst->maxpoll = new_maxpoll;
|
||||
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
|
||||
if (inst->minpoll > inst->maxpoll)
|
||||
NCR_ModifyMinpoll(inst, inst->maxpoll);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1704,20 +1829,11 @@ NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_sampl
|
||||
|
||||
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->opmode = inst->opmode == MD_ONLINE ?
|
||||
MD_BURST_WAS_ONLINE : MD_BURST_WAS_OFFLINE;
|
||||
inst->burst_good_samples_to_go = n_good_samples;
|
||||
inst->burst_total_samples_to_go = n_total_samples;
|
||||
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);
|
||||
start_initial_timeout(inst);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
|
||||
10
ntp_core.h
10
ntp_core.h
@@ -52,13 +52,19 @@ extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_
|
||||
/* Destroy an instance */
|
||||
extern void NCR_DestroyInstance(NCR_Instance instance);
|
||||
|
||||
/* Start an instance */
|
||||
extern void NCR_StartInstance(NCR_Instance instance);
|
||||
|
||||
/* Reset an instance */
|
||||
extern void NCR_ResetInstance(NCR_Instance inst);
|
||||
|
||||
/* 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);
|
||||
extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int sock_fd, 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);
|
||||
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
|
||||
|
||||
/* Slew receive and transmit times in instance records */
|
||||
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
|
||||
|
||||
528
ntp_io.c
528
ntp_io.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Timo Teras 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -39,6 +39,8 @@
|
||||
#include "conf.h"
|
||||
#include "util.h"
|
||||
|
||||
#define INVALID_SOCK_FD -1
|
||||
|
||||
union sockaddr_in46 {
|
||||
struct sockaddr_in in4;
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -47,87 +49,108 @@ union sockaddr_in46 {
|
||||
struct sockaddr u;
|
||||
};
|
||||
|
||||
/* The file descriptors for the IPv4 and IPv6 sockets */
|
||||
static int sock_fd4;
|
||||
/* The server/peer and client sockets for IPv4 and IPv6 */
|
||||
static int server_sock_fd4;
|
||||
static int client_sock_fd4;
|
||||
#ifdef HAVE_IPV6
|
||||
static int sock_fd6;
|
||||
static int server_sock_fd6;
|
||||
static int client_sock_fd6;
|
||||
#endif
|
||||
|
||||
/* Flag indicating we create a new connected client socket for each
|
||||
server instead of sharing client_sock_fd4 and client_sock_fd6 */
|
||||
static int separate_client_sockets;
|
||||
|
||||
/* Flag indicating 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)
|
||||
prepare_socket(int family, int port_number, int client_only)
|
||||
{
|
||||
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;
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
/* Prepare local address */
|
||||
memset(&my_addr, 0, sizeof (my_addr));
|
||||
my_addr_len = 0;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
if (!client_only)
|
||||
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
|
||||
else
|
||||
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
|
||||
|
||||
if (bind_address.family == IPADDR_INET4)
|
||||
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
|
||||
else if (port_number)
|
||||
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
else
|
||||
break;
|
||||
|
||||
my_addr.in4.sin_family = family;
|
||||
my_addr.in4.sin_port = htons(port_number);
|
||||
my_addr_len = sizeof (my_addr.in4);
|
||||
|
||||
break;
|
||||
#ifdef HAVE_IPV6
|
||||
case AF_INET6:
|
||||
if (!client_only)
|
||||
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
|
||||
else
|
||||
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
|
||||
|
||||
if (bind_address.family == IPADDR_INET6)
|
||||
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
|
||||
sizeof (my_addr.in6.sin6_addr.s6_addr));
|
||||
else if (port_number)
|
||||
my_addr.in6.sin6_addr = in6addr_any;
|
||||
else
|
||||
break;
|
||||
|
||||
my_addr.in6.sin6_family = family;
|
||||
my_addr.in6.sin6_port = htons(port_number);
|
||||
my_addr_len = sizeof (my_addr.in6);
|
||||
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Make the socket capable of re-using an old address if binding to a specific port */
|
||||
if (port_number &&
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
|
||||
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
if (!client_only &&
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
@@ -140,11 +163,20 @@ prepare_socket(int family)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef IP_FREEBIND
|
||||
/* Allow binding to address that doesn't exist yet */
|
||||
if (my_addr_len > 0 &&
|
||||
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (family == AF_INET) {
|
||||
#ifdef IP_PKTINFO
|
||||
/* We want the local IP info 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");
|
||||
/* We want the local IP info on server sockets */
|
||||
if (!client_only &&
|
||||
setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set packet info socket option");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
#endif
|
||||
@@ -154,110 +186,164 @@ prepare_socket(int family)
|
||||
#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");
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!client_only) {
|
||||
#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");
|
||||
}
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
|
||||
}
|
||||
#elif defined(IPV6_PKTINFO)
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
|
||||
}
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Bind the port */
|
||||
memset(&my_addr, 0, sizeof (my_addr));
|
||||
/* Bind the socket if a port or address was specified */
|
||||
if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
|
||||
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
|
||||
close(sock_fd);
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
|
||||
/* Register handler for read events on the socket */
|
||||
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
|
||||
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
prepare_separate_client_socket(int family)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
my_addr_len = sizeof (my_addr.in4);
|
||||
my_addr.in4.sin_family = family;
|
||||
my_addr.in4.sin_port = htons(port_number);
|
||||
case IPADDR_INET4:
|
||||
return prepare_socket(AF_INET, 0, 1);
|
||||
#ifdef HAVE_IPV6
|
||||
case IPADDR_INET6:
|
||||
return prepare_socket(AF_INET6, 0, 1);
|
||||
#endif
|
||||
default:
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
static int
|
||||
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
|
||||
{
|
||||
union sockaddr_in46 addr;
|
||||
socklen_t addr_len;
|
||||
|
||||
memset(&addr, 0, sizeof (addr));
|
||||
|
||||
switch (remote_addr->ip_addr.family) {
|
||||
case IPADDR_INET4:
|
||||
addr_len = sizeof (addr.in4);
|
||||
addr.in4.sin_family = AF_INET;
|
||||
addr.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
|
||||
addr.in4.sin_port = htons(remote_addr->port);
|
||||
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;
|
||||
case IPADDR_INET6:
|
||||
addr_len = sizeof (addr.in6);
|
||||
addr.in6.sin6_family = AF_INET6;
|
||||
memcpy(addr.in6.sin6_addr.s6_addr, remote_addr->ip_addr.addr.in6,
|
||||
sizeof (addr.in6.sin6_addr.s6_addr));
|
||||
addr.in6.sin6_port = htons(remote_addr->port);
|
||||
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;
|
||||
if (connect(sock_fd, &addr.u, addr_len) < 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
close_socket(int sock_fd)
|
||||
{
|
||||
if (sock_fd == INVALID_SOCK_FD)
|
||||
return;
|
||||
|
||||
SCH_RemoveInputFileHandler(sock_fd);
|
||||
close(sock_fd);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
void
|
||||
NIO_Initialise(int family)
|
||||
{
|
||||
int server_port, client_port;
|
||||
|
||||
assert(!initialised);
|
||||
initialised = 1;
|
||||
|
||||
do_size_checks();
|
||||
server_port = CNF_GetNTPPort();
|
||||
client_port = CNF_GetAcquisitionPort();
|
||||
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
|
||||
sock_fd4 = prepare_socket(AF_INET);
|
||||
else
|
||||
sock_fd4 = -1;
|
||||
/* Use separate connected sockets if client port is negative */
|
||||
separate_client_sockets = client_port < 0;
|
||||
if (client_port < 0)
|
||||
client_port = 0;
|
||||
|
||||
server_sock_fd4 = INVALID_SOCK_FD;
|
||||
client_sock_fd4 = INVALID_SOCK_FD;
|
||||
#ifdef HAVE_IPV6
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
|
||||
sock_fd6 = prepare_socket(AF_INET6);
|
||||
else
|
||||
sock_fd6 = -1;
|
||||
server_sock_fd6 = INVALID_SOCK_FD;
|
||||
client_sock_fd6 = INVALID_SOCK_FD;
|
||||
#endif
|
||||
|
||||
if (sock_fd4 < 0
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
|
||||
if (server_port)
|
||||
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
|
||||
if (!separate_client_sockets) {
|
||||
if (client_port != server_port || !server_port)
|
||||
client_sock_fd4 = prepare_socket(AF_INET, client_port, 1);
|
||||
else
|
||||
client_sock_fd4 = server_sock_fd4;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
&& sock_fd6 < 0
|
||||
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
|
||||
if (server_port)
|
||||
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
|
||||
if (!separate_client_sockets) {
|
||||
if (client_port != server_port || !server_port)
|
||||
client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1);
|
||||
else
|
||||
client_sock_fd6 = server_sock_fd6;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
) {
|
||||
LOG_FATAL(LOGF_NtpIO, "Could not open any NTP socket");
|
||||
|
||||
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD
|
||||
#ifdef HAVE_IPV6
|
||||
&& server_sock_fd6 == INVALID_SOCK_FD
|
||||
#endif
|
||||
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
|
||||
#ifdef HAVE_IPV6
|
||||
&& client_sock_fd6 == INVALID_SOCK_FD
|
||||
#endif
|
||||
)) {
|
||||
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,23 +352,88 @@ NIO_Initialise(int family)
|
||||
void
|
||||
NIO_Finalise(void)
|
||||
{
|
||||
if (sock_fd4 >= 0) {
|
||||
SCH_RemoveInputFileHandler(sock_fd4);
|
||||
close(sock_fd4);
|
||||
}
|
||||
sock_fd4 = -1;
|
||||
if (server_sock_fd4 != client_sock_fd4)
|
||||
close_socket(client_sock_fd4);
|
||||
close_socket(server_sock_fd4);
|
||||
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
|
||||
#ifdef HAVE_IPV6
|
||||
if (sock_fd6 >= 0) {
|
||||
SCH_RemoveInputFileHandler(sock_fd6);
|
||||
close(sock_fd6);
|
||||
}
|
||||
sock_fd6 = -1;
|
||||
if (server_sock_fd6 != client_sock_fd6)
|
||||
close_socket(client_sock_fd6);
|
||||
close_socket(server_sock_fd6);
|
||||
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
|
||||
#endif
|
||||
initialised = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_GetClientSocket(NTP_Remote_Address *remote_addr)
|
||||
{
|
||||
if (separate_client_sockets) {
|
||||
int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
|
||||
|
||||
if (sock_fd == INVALID_SOCK_FD)
|
||||
return INVALID_SOCK_FD;
|
||||
|
||||
if (!connect_socket(sock_fd, remote_addr)) {
|
||||
close_socket(sock_fd);
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
|
||||
return sock_fd;
|
||||
} else {
|
||||
switch (remote_addr->ip_addr.family) {
|
||||
case IPADDR_INET4:
|
||||
return client_sock_fd4;
|
||||
#ifdef HAVE_IPV6
|
||||
case IPADDR_INET6:
|
||||
return client_sock_fd6;
|
||||
#endif
|
||||
default:
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_GetServerSocket(NTP_Remote_Address *remote_addr)
|
||||
{
|
||||
switch (remote_addr->ip_addr.family) {
|
||||
case IPADDR_INET4:
|
||||
return server_sock_fd4;
|
||||
#ifdef HAVE_IPV6
|
||||
case IPADDR_INET6:
|
||||
return server_sock_fd6;
|
||||
#endif
|
||||
default:
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NIO_CloseClientSocket(int sock_fd)
|
||||
{
|
||||
if (separate_client_sockets)
|
||||
close_socket(sock_fd);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_IsServerSocket(int sock_fd)
|
||||
{
|
||||
return sock_fd != INVALID_SOCK_FD &&
|
||||
(sock_fd == server_sock_fd4
|
||||
#ifdef HAVE_IPV6
|
||||
|| sock_fd == server_sock_fd6
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -296,9 +447,10 @@ read_from_socket(void *anything)
|
||||
ReceiveBuffer message;
|
||||
union sockaddr_in46 where_from;
|
||||
unsigned int flags = 0;
|
||||
struct timeval now, now_raw;
|
||||
struct timeval now;
|
||||
double now_err;
|
||||
NTP_Remote_Address remote_addr;
|
||||
NTP_Local_Address local_addr;
|
||||
char cmsgbuf[256];
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
@@ -306,7 +458,7 @@ read_from_socket(void *anything)
|
||||
|
||||
assert(initialised);
|
||||
|
||||
SCH_GetLastEventTime(&now, &now_err, &now_raw);
|
||||
SCH_GetLastEventTime(&now, &now_err, NULL);
|
||||
|
||||
iov.iov_base = message.arbitrary;
|
||||
iov.iov_len = sizeof(message);
|
||||
@@ -329,7 +481,8 @@ read_from_socket(void *anything)
|
||||
reponse on a subsequent recvfrom). */
|
||||
|
||||
if (status > 0) {
|
||||
memset(&remote_addr, 0, sizeof (remote_addr));
|
||||
if (msg.msg_namelen > sizeof (where_from))
|
||||
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
|
||||
|
||||
switch (where_from.u.sa_family) {
|
||||
case AF_INET:
|
||||
@@ -349,14 +502,17 @@ read_from_socket(void *anything)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
local_addr.ip_addr.family = IPADDR_UNSPEC;
|
||||
local_addr.sock_fd = sock_fd;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
#ifdef IP_PKTINFO
|
||||
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
|
||||
struct in_pktinfo ipi;
|
||||
|
||||
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
|
||||
remote_addr.local_ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
|
||||
remote_addr.local_ip_addr.family = IPADDR_INET4;
|
||||
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
|
||||
local_addr.ip_addr.family = IPADDR_INET4;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -365,9 +521,9 @@ read_from_socket(void *anything)
|
||||
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;
|
||||
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
|
||||
sizeof (local_addr.ip_addr.addr.in6));
|
||||
local_addr.ip_addr.family = IPADDR_INET6;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -376,16 +532,22 @@ read_from_socket(void *anything)
|
||||
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);
|
||||
LCL_CookTime(&tv, &now, &now_err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (status > 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
|
||||
status,
|
||||
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
|
||||
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
|
||||
}
|
||||
|
||||
if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
|
||||
|
||||
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr, status);
|
||||
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
|
||||
&remote_addr, &local_addr, status);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -398,50 +560,62 @@ read_from_socket(void *anything)
|
||||
/* ================================================== */
|
||||
/* Send a packet to given address */
|
||||
|
||||
static void
|
||||
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
|
||||
static int
|
||||
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
|
||||
{
|
||||
union sockaddr_in46 remote;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char cmsgbuf[256];
|
||||
int cmsglen;
|
||||
int sock_fd;
|
||||
socklen_t addrlen;
|
||||
socklen_t addrlen = 0;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
if (local_addr->sock_fd == INVALID_SOCK_FD) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (remote_addr->ip_addr.family) {
|
||||
case IPADDR_INET4:
|
||||
/* Don't set address with connected socket */
|
||||
if (local_addr->sock_fd != server_sock_fd4 && separate_client_sockets)
|
||||
break;
|
||||
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:
|
||||
/* Don't set address with connected socket */
|
||||
if (local_addr->sock_fd != server_sock_fd6 && separate_client_sockets)
|
||||
break;
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sock_fd < 0)
|
||||
return;
|
||||
if (addrlen) {
|
||||
msg.msg_name = &remote.u;
|
||||
msg.msg_namelen = addrlen;
|
||||
} else {
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
}
|
||||
|
||||
iov.iov_base = packet;
|
||||
iov.iov_len = packetlen;
|
||||
msg.msg_name = &remote.u;
|
||||
msg.msg_namelen = addrlen;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsgbuf;
|
||||
@@ -450,7 +624,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
|
||||
cmsglen = 0;
|
||||
|
||||
#ifdef IP_PKTINFO
|
||||
if (remote_addr->local_ip_addr.family == IPADDR_INET4) {
|
||||
if (local_addr->ip_addr.family == IPADDR_INET4) {
|
||||
struct cmsghdr *cmsg;
|
||||
struct in_pktinfo *ipi;
|
||||
|
||||
@@ -463,12 +637,12 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
|
||||
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);
|
||||
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
|
||||
if (remote_addr->local_ip_addr.family == IPADDR_INET6) {
|
||||
if (local_addr->ip_addr.family == IPADDR_INET6) {
|
||||
struct cmsghdr *cmsg;
|
||||
struct in6_pktinfo *ipi;
|
||||
|
||||
@@ -481,65 +655,45 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
|
||||
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,
|
||||
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->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));
|
||||
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_NtpIO, "Sent to %s:%d from %s fd %d",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Send an unauthenticated packet to a given address */
|
||||
|
||||
void
|
||||
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
|
||||
int
|
||||
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
|
||||
{
|
||||
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr);
|
||||
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr, local_addr);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Send an authenticated packet to a given address */
|
||||
|
||||
void
|
||||
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len)
|
||||
int
|
||||
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_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);
|
||||
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr, local_addr);
|
||||
}
|
||||
|
||||
20
ntp_io.h
20
ntp_io.h
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -37,13 +38,22 @@ extern void NIO_Initialise(int family);
|
||||
/* Function to finalise the module */
|
||||
extern void NIO_Finalise(void);
|
||||
|
||||
/* Function to obtain a socket for sending client packets */
|
||||
extern int NIO_GetClientSocket(NTP_Remote_Address *remote_addr);
|
||||
|
||||
/* Function to obtain a socket for sending server/peer packets */
|
||||
extern int NIO_GetServerSocket(NTP_Remote_Address *remote_addr);
|
||||
|
||||
/* Function to close a socket returned by NIO_GetClientSocket() */
|
||||
extern void NIO_CloseClientSocket(int sock_fd);
|
||||
|
||||
/* Function to check if socket is a server socket */
|
||||
extern int NIO_IsServerSocket(int sock_fd);
|
||||
|
||||
/* Function to transmit a packet */
|
||||
extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
|
||||
extern int NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_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);
|
||||
extern int NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len);
|
||||
|
||||
#endif /* GOT_NTP_IO_H */
|
||||
|
||||
220
ntp_sources.c
220
ntp_sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2012
|
||||
* Copyright (C) Miroslav Lichvar 2011-2012, 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "logging.h"
|
||||
#include "local.h"
|
||||
#include "memory.h"
|
||||
#include "nameserv.h"
|
||||
#include "nameserv_async.h"
|
||||
#include "sched.h"
|
||||
|
||||
/* ================================================== */
|
||||
@@ -62,6 +62,9 @@ static int n_sources;
|
||||
/* The largest number of sources we want to have stored in the hash table */
|
||||
#define MAX_SOURCES 64
|
||||
|
||||
/* Flag indicating new sources will be started automatically when added */
|
||||
static int auto_start_sources = 0;
|
||||
|
||||
/* Source with unknown address (which may be resolved later) */
|
||||
struct UnresolvedSource {
|
||||
char *name;
|
||||
@@ -71,18 +74,27 @@ struct UnresolvedSource {
|
||||
struct UnresolvedSource *next;
|
||||
};
|
||||
|
||||
#define RESOLVE_INTERVAL_UNIT 7
|
||||
#define MIN_RESOLVE_INTERVAL 2
|
||||
#define MAX_RESOLVE_INTERVAL 9
|
||||
|
||||
static struct UnresolvedSource *unresolved_sources = NULL;
|
||||
static int resolving_interval = 0;
|
||||
static SCH_TimeoutID resolving_id;
|
||||
static struct UnresolvedSource *resolving_source = NULL;
|
||||
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
|
||||
|
||||
/* ================================================== */
|
||||
/* Forward prototypes */
|
||||
|
||||
static void resolve_sources(void *arg);
|
||||
|
||||
static void
|
||||
slew_sources(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything);
|
||||
|
||||
/* ================================================== */
|
||||
@@ -188,10 +200,6 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
|
||||
|
||||
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) {
|
||||
@@ -206,6 +214,8 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
|
||||
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);
|
||||
if (auto_start_sources)
|
||||
NCR_StartInstance(records[slot].data);
|
||||
return NSR_Success;
|
||||
}
|
||||
}
|
||||
@@ -213,44 +223,90 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
name_resolve_handler(DNS_Status status, IPAddr *ip_addr, void *anything)
|
||||
{
|
||||
struct UnresolvedSource *us, **i, *next;
|
||||
NTP_Remote_Address address;
|
||||
|
||||
us = (struct UnresolvedSource *)anything;
|
||||
|
||||
assert(us == resolving_source);
|
||||
|
||||
switch (status) {
|
||||
case DNS_TryAgain:
|
||||
break;
|
||||
case DNS_Success:
|
||||
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %s", us->name, UTI_IPToString(ip_addr));
|
||||
address.ip_addr = *ip_addr;
|
||||
address.port = us->port;
|
||||
NSR_AddSource(&address, us->type, &us->params);
|
||||
break;
|
||||
case DNS_Failure:
|
||||
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
next = us->next;
|
||||
|
||||
if (status != DNS_TryAgain) {
|
||||
/* Remove the source from the list */
|
||||
for (i = &unresolved_sources; *i; i = &(*i)->next) {
|
||||
if (*i == us) {
|
||||
*i = us->next;
|
||||
Free(us->name);
|
||||
Free(us);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolving_source = next;
|
||||
|
||||
if (next) {
|
||||
/* Continue with the next source in the list */
|
||||
DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name);
|
||||
DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
|
||||
} else {
|
||||
/* This was the last source in the list. If some sources couldn't
|
||||
be resolved, try again in exponentially increasing interval. */
|
||||
if (unresolved_sources) {
|
||||
if (resolving_interval < MIN_RESOLVE_INTERVAL)
|
||||
resolving_interval = MIN_RESOLVE_INTERVAL;
|
||||
else if (resolving_interval < MAX_RESOLVE_INTERVAL)
|
||||
resolving_interval++;
|
||||
resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT *
|
||||
(1 << resolving_interval), resolve_sources, NULL);
|
||||
} else {
|
||||
resolving_interval = 0;
|
||||
}
|
||||
|
||||
/* This round of resolving is done */
|
||||
if (resolving_end_handler)
|
||||
(resolving_end_handler)();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
resolve_sources(void *arg)
|
||||
{
|
||||
NTP_Remote_Address address;
|
||||
struct UnresolvedSource *us, **i;
|
||||
DNS_Status s;
|
||||
struct UnresolvedSource *us;
|
||||
|
||||
memset(&address.local_ip_addr, 0, sizeof (address.local_ip_addr));
|
||||
assert(!resolving_source);
|
||||
|
||||
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;
|
||||
/* Start with the first source in the list, name_resolve_handler
|
||||
will iterate over the rest */
|
||||
us = unresolved_sources;
|
||||
|
||||
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;
|
||||
}
|
||||
resolving_source = us;
|
||||
DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name);
|
||||
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -273,11 +329,14 @@ NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParame
|
||||
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_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
|
||||
{
|
||||
resolving_end_handler = handler;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -286,15 +345,44 @@ void
|
||||
NSR_ResolveSources(void)
|
||||
{
|
||||
/* Try to resolve unresolved sources now */
|
||||
if (resolving_interval) {
|
||||
SCH_RemoveTimeout(resolving_id);
|
||||
resolving_interval--;
|
||||
resolve_sources(NULL);
|
||||
if (unresolved_sources) {
|
||||
/* Make sure no resolving is currently running */
|
||||
if (!resolving_source) {
|
||||
if (resolving_interval) {
|
||||
SCH_RemoveTimeout(resolving_id);
|
||||
resolving_interval--;
|
||||
}
|
||||
resolve_sources(NULL);
|
||||
}
|
||||
} else {
|
||||
/* No unresolved sources, we are done */
|
||||
if (resolving_end_handler)
|
||||
(resolving_end_handler)();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void NSR_StartSources(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < N_RECORDS; i++) {
|
||||
if (!records[i].remote_addr)
|
||||
continue;
|
||||
NCR_StartInstance(records[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void NSR_AutoStartSources(void)
|
||||
{
|
||||
auto_start_sources = 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* 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
|
||||
@@ -341,26 +429,36 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NSR_RemoveAllSources(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < N_RECORDS; i++) {
|
||||
if (!records[i].remote_addr)
|
||||
continue;
|
||||
NCR_DestroyInstance(records[i].data);
|
||||
records[i].remote_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* 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)
|
||||
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_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);
|
||||
NCR_ProcessKnown(message, now, now_err, records[slot].data,
|
||||
local_addr->sock_fd, length);
|
||||
} else {
|
||||
NCR_ProcessUnknown(message, now, now_err, remote_addr, length);
|
||||
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,22 +469,20 @@ slew_sources(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<N_RECORDS; i++) {
|
||||
if (records[i].remote_addr) {
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Sources, "IP=%s dfreq=%f doff=%f",
|
||||
UTI_IPToString(&records[i].remote_addr->ip_addr), dfreq, doffset);
|
||||
#endif
|
||||
|
||||
NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
NCR_ResetInstance(records[i].data);
|
||||
} else {
|
||||
NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -54,14 +55,30 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
|
||||
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. */
|
||||
/* Function type for handlers to be called back when an attempt
|
||||
* (possibly unsuccessful) to resolve unresolved sources ends */
|
||||
typedef void (*NSR_SourceResolvingEndHandler)(void);
|
||||
|
||||
/* Set the handler, or NULL to disable the notification */
|
||||
extern void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler);
|
||||
|
||||
/* Procedure to start resolving unresolved sources */
|
||||
extern void NSR_ResolveSources(void);
|
||||
|
||||
/* Procedure to start all sources */
|
||||
extern void NSR_StartSources(void);
|
||||
|
||||
/* Start new sources automatically */
|
||||
extern void NSR_AutoStartSources(void);
|
||||
|
||||
/* Procedure to remove a source */
|
||||
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
|
||||
|
||||
/* Procedure to remove all sources */
|
||||
extern void NSR_RemoveAllSources(void);
|
||||
|
||||
/* 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);
|
||||
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
|
||||
|
||||
/* Initialisation function */
|
||||
extern void NSR_Initialise(void);
|
||||
|
||||
121
refclock.c
121
refclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -42,6 +42,7 @@
|
||||
extern RefclockDriver RCL_SHM_driver;
|
||||
extern RefclockDriver RCL_SOCK_driver;
|
||||
extern RefclockDriver RCL_PPS_driver;
|
||||
extern RefclockDriver RCL_PHC_driver;
|
||||
|
||||
struct FilterSample {
|
||||
double offset;
|
||||
@@ -56,6 +57,7 @@ struct MedianFilter {
|
||||
int last;
|
||||
int avg_var_n;
|
||||
double avg_var;
|
||||
double max_var;
|
||||
struct FilterSample *samples;
|
||||
int *selected;
|
||||
double *x_data;
|
||||
@@ -73,6 +75,7 @@ struct RCL_Instance_Record {
|
||||
int poll;
|
||||
int leap_status;
|
||||
int pps_rate;
|
||||
int pps_active;
|
||||
struct MedianFilter filter;
|
||||
uint32_t ref_id;
|
||||
uint32_t lock_ref;
|
||||
@@ -94,11 +97,11 @@ 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);
|
||||
double doffset, LCL_ChangeType change_type, void *anything);
|
||||
static void add_dispersion(double dispersion, void *anything);
|
||||
static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
|
||||
|
||||
static void filter_init(struct MedianFilter *filter, int length);
|
||||
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
|
||||
static void filter_fini(struct MedianFilter *filter);
|
||||
static void filter_reset(struct MedianFilter *filter);
|
||||
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
|
||||
@@ -166,6 +169,9 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->driver = &RCL_PPS_driver;
|
||||
inst->precision = 1e-9;
|
||||
pps_source = 1;
|
||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||
inst->driver = &RCL_PHC_driver;
|
||||
inst->precision = 1e-9;
|
||||
} else {
|
||||
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
|
||||
return 0;
|
||||
@@ -184,6 +190,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->driver_polled = 0;
|
||||
inst->leap_status = LEAP_Normal;
|
||||
inst->pps_rate = params->pps_rate;
|
||||
inst->pps_active = 0;
|
||||
inst->lock_ref = params->lock_ref_id;
|
||||
inst->offset = params->offset;
|
||||
inst->delay = params->delay;
|
||||
@@ -239,14 +246,12 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter_init(&inst->filter, params->filter_length);
|
||||
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
|
||||
|
||||
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
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock %s added poll=%d dpoll=%d filter=%d",
|
||||
params->driver_name, inst->poll, inst->driver_poll, params->filter_length);
|
||||
n_sources++;
|
||||
|
||||
Free(params->driver_name);
|
||||
@@ -263,6 +268,7 @@ RCL_StartRefclocks(void)
|
||||
RCL_Instance inst = &refclocks[i];
|
||||
|
||||
SRC_SetSelectable(inst->source);
|
||||
SRC_SetActive(inst->source);
|
||||
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
|
||||
|
||||
if (inst->lock_ref) {
|
||||
@@ -345,7 +351,7 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
|
||||
|
||||
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
|
||||
dispersion += instance->precision + filter_get_avg_sample_dispersion(&instance->filter);
|
||||
dispersion += instance->precision;
|
||||
|
||||
if (!valid_sample_time(instance, sample_time))
|
||||
return 0;
|
||||
@@ -363,6 +369,8 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
|
||||
break;
|
||||
}
|
||||
|
||||
instance->pps_active = 0;
|
||||
|
||||
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
|
||||
|
||||
/* for logging purposes */
|
||||
@@ -378,10 +386,12 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
double correction, dispersion, offset;
|
||||
struct timeval cooked_time;
|
||||
int rate;
|
||||
NTP_Leap leap;
|
||||
|
||||
leap = LEAP_Normal;
|
||||
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
|
||||
dispersion += instance->precision + filter_get_avg_sample_dispersion(&instance->filter);
|
||||
dispersion += instance->precision;
|
||||
|
||||
if (!valid_sample_time(instance, pulse_time))
|
||||
return 0;
|
||||
@@ -403,12 +413,19 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
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))
|
||||
&ref_sample_time, &ref_offset, &ref_dispersion)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ref_dispersion += filter_get_avg_sample_dispersion(&refclocks[instance->lock_ref].filter);
|
||||
|
||||
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
|
||||
if (fabs(sample_diff) >= 2.0 / rate)
|
||||
if (fabs(sample_diff) >= 2.0 / rate) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
|
||||
sample_diff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Align the offset to the reference sample */
|
||||
if ((ref_offset - offset) >= 0.0)
|
||||
@@ -418,18 +435,20 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
|
||||
offset += shift;
|
||||
|
||||
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate)
|
||||
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
|
||||
ref_offset - offset, ref_dispersion, dispersion);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
|
||||
leap = refclocks[instance->lock_ref].leap_status;
|
||||
|
||||
DEBUG_LOG(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 */
|
||||
@@ -439,10 +458,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
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",
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
|
||||
second, is_synchronised, distance);
|
||||
#endif
|
||||
/* Drop also all stored samples */
|
||||
filter_reset(&instance->filter);
|
||||
return 0;
|
||||
@@ -450,7 +467,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
}
|
||||
|
||||
filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
|
||||
instance->leap_status = LEAP_Normal;
|
||||
instance->leap_status = leap;
|
||||
instance->pps_active = 1;
|
||||
|
||||
log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
|
||||
|
||||
@@ -461,6 +479,15 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static double
|
||||
poll_interval(int poll)
|
||||
{
|
||||
if (poll >= 0)
|
||||
return 1 << poll;
|
||||
else
|
||||
return 1.0 / (1 << -poll);
|
||||
}
|
||||
|
||||
static int
|
||||
valid_sample_time(RCL_Instance instance, struct timeval *tv)
|
||||
{
|
||||
@@ -469,8 +496,11 @@ valid_sample_time(RCL_Instance instance, struct timeval *tv)
|
||||
|
||||
LCL_ReadRawTime(&raw_time);
|
||||
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
|
||||
if (diff < 0.0 || diff > 1 << (instance->poll + 1))
|
||||
if (diff < 0.0 || diff > poll_interval(instance->poll + 1)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock sample not valid age=%.6f tv=%s",
|
||||
diff, UTI_TimevalToString(tv));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -494,7 +524,7 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
|
||||
/* 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)
|
||||
refclocks[i].pps_active && refclocks[i].lock_ref == -1)
|
||||
return stratum - 1;
|
||||
}
|
||||
|
||||
@@ -504,7 +534,6 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
|
||||
static void
|
||||
poll_timeout(void *arg)
|
||||
{
|
||||
double next;
|
||||
int poll;
|
||||
|
||||
RCL_Instance inst = (RCL_Instance)arg;
|
||||
@@ -526,7 +555,7 @@ poll_timeout(void *arg)
|
||||
inst->driver_polled = 0;
|
||||
|
||||
if (sample_ok) {
|
||||
if (inst->pps_rate && inst->lock_ref == -1)
|
||||
if (inst->pps_active && inst->lock_ref == -1)
|
||||
/* Handle special case when PPS is used with local stratum */
|
||||
stratum = pps_stratum(inst, &sample_time);
|
||||
else
|
||||
@@ -535,6 +564,7 @@ poll_timeout(void *arg)
|
||||
SRC_UpdateReachability(inst->source, 1);
|
||||
SRC_AccumulateSample(inst->source, &sample_time, offset,
|
||||
inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
|
||||
SRC_SelectSource(inst->source);
|
||||
|
||||
log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
|
||||
} else {
|
||||
@@ -542,22 +572,21 @@ poll_timeout(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
if (poll >= 0)
|
||||
next = 1 << poll;
|
||||
else
|
||||
next = 1.0 / (1 << -poll);
|
||||
|
||||
inst->timeout_id = SCH_AddTimeoutByDelay(next, poll_timeout, arg);
|
||||
inst->timeout_id = SCH_AddTimeoutByDelay(poll_interval(poll), poll_timeout, arg);
|
||||
}
|
||||
|
||||
static void
|
||||
slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
|
||||
double doffset, int is_step_change, void *anything)
|
||||
double doffset, LCL_ChangeType change_type, void *anything)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_sources; i++)
|
||||
filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset);
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
if (change_type == LCL_ChangeUnknownStep)
|
||||
filter_reset(&refclocks[i].filter);
|
||||
else
|
||||
filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -600,7 +629,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
|
||||
}
|
||||
|
||||
static void
|
||||
filter_init(struct MedianFilter *filter, int length)
|
||||
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
|
||||
{
|
||||
if (length < 1)
|
||||
length = 1;
|
||||
@@ -612,6 +641,7 @@ filter_init(struct MedianFilter *filter, int length)
|
||||
/* set first estimate to system precision */
|
||||
filter->avg_var_n = 0;
|
||||
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
|
||||
filter->max_var = max_dispersion * max_dispersion;
|
||||
filter->samples = MallocArray(struct FilterSample, filter->length);
|
||||
filter->selected = MallocArray(int, filter->length);
|
||||
filter->x_data = MallocArray(double, filter->length);
|
||||
@@ -654,6 +684,9 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
|
||||
filter->samples[filter->index].sample_time = *sample_time;
|
||||
filter->samples[filter->index].offset = offset;
|
||||
filter->samples[filter->index].dispersion = dispersion;
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
|
||||
filter->index, UTI_TimevalToString(sample_time), offset, dispersion);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -802,8 +835,6 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
|
||||
y /= n;
|
||||
e /= n;
|
||||
|
||||
e -= sqrt(filter->avg_var);
|
||||
|
||||
if (n >= 4) {
|
||||
double b0, b1, s2, sb0, sb1;
|
||||
|
||||
@@ -836,6 +867,13 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
|
||||
d = sqrt(var);
|
||||
}
|
||||
|
||||
/* drop the sample if variance is larger than allowed maximum */
|
||||
if (filter->max_var > 0.0 && var > filter->max_var) {
|
||||
DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
|
||||
sqrt(var), sqrt(filter->max_var));
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev_avg_var = filter->avg_var;
|
||||
|
||||
/* update exponential moving average of the variance */
|
||||
@@ -870,20 +908,13 @@ static void
|
||||
filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset)
|
||||
{
|
||||
int i;
|
||||
double delta_time, prev_offset;
|
||||
double delta_time;
|
||||
struct timeval *sample;
|
||||
|
||||
for (i = 0; i < filter->used; i++) {
|
||||
sample = &filter->samples[i].sample_time;
|
||||
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ typedef struct {
|
||||
double offset;
|
||||
double delay;
|
||||
double precision;
|
||||
double max_dispersion;
|
||||
SRC_SelectOption sel_option;
|
||||
} RefclockParameters;
|
||||
|
||||
@@ -58,7 +59,6 @@ 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 */
|
||||
|
||||
193
refclock_phc.c
Normal file
193
refclock_phc.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2013
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
PTP hardware clock (PHC) refclock driver.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "refclock.h"
|
||||
|
||||
#ifdef FEAT_PHC
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <linux/ptp_clock.h>
|
||||
|
||||
#include "refclock.h"
|
||||
#include "logging.h"
|
||||
#include "util.h"
|
||||
|
||||
/* From linux/include/linux/posix-timers.h */
|
||||
#define CPUCLOCK_MAX 3
|
||||
#define CLOCKFD CPUCLOCK_MAX
|
||||
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
|
||||
|
||||
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
|
||||
|
||||
#define NUM_READINGS 10
|
||||
|
||||
static int no_sys_offset_ioctl = 0;
|
||||
|
||||
struct phc_reading {
|
||||
struct timespec sys_ts1;
|
||||
struct timespec phc_ts;;
|
||||
struct timespec sys_ts2;
|
||||
};
|
||||
|
||||
static double diff_ts(struct timespec *ts1, struct timespec *ts2)
|
||||
{
|
||||
return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
|
||||
{
|
||||
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
|
||||
struct ptp_sys_offset sys_off;
|
||||
int i;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = n;
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
|
||||
readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
|
||||
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
|
||||
readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
}
|
||||
|
||||
return 1;
|
||||
#else
|
||||
/* Not available */
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
|
||||
{
|
||||
clockid_t phc_id;
|
||||
int i;
|
||||
|
||||
phc_id = FD_TO_CLOCKID(phc_fd);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
|
||||
clock_gettime(phc_id, &readings[i].phc_ts) ||
|
||||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int phc_initialise(RCL_Instance instance)
|
||||
{
|
||||
struct ptp_clock_caps caps;
|
||||
int phc_fd;
|
||||
char *path;
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
phc_fd = open(path, O_RDONLY);
|
||||
if (phc_fd < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure it is a PHC */
|
||||
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
|
||||
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
|
||||
RCL_SetDriverData(instance, (void *)(long)phc_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void phc_finalise(RCL_Instance instance)
|
||||
{
|
||||
close((long)RCL_GetDriverData(instance));
|
||||
}
|
||||
|
||||
static int phc_poll(RCL_Instance instance)
|
||||
{
|
||||
struct phc_reading readings[NUM_READINGS];
|
||||
struct timeval tv;
|
||||
double offset = 0.0, delay, best_delay = 0.0;
|
||||
int i, phc_fd, best;
|
||||
|
||||
phc_fd = (long)RCL_GetDriverData(instance);
|
||||
|
||||
if (!no_sys_offset_ioctl) {
|
||||
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
|
||||
no_sys_offset_ioctl = 1;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!read_phc_user(readings, phc_fd, NUM_READINGS))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the fastest reading */
|
||||
for (i = 0; i < NUM_READINGS; i++) {
|
||||
delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1);
|
||||
|
||||
if (!i || best_delay > delay) {
|
||||
best = i;
|
||||
best_delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0;
|
||||
tv.tv_sec = readings[best].sys_ts2.tv_sec;
|
||||
tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
|
||||
|
||||
return RCL_AddSample(instance, &tv, offset, LEAP_Normal);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_PHC_driver = {
|
||||
phc_initialise,
|
||||
phc_finalise,
|
||||
phc_poll
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
RefclockDriver RCL_PHC_driver = { NULL, NULL, NULL };
|
||||
|
||||
#endif
|
||||
@@ -134,9 +134,7 @@ static int pps_poll(RCL_Instance instance)
|
||||
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
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -149,6 +147,8 @@ static int pps_poll(RCL_Instance instance)
|
||||
}
|
||||
|
||||
if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu",
|
||||
seq, ts.tv_sec, ts.tv_nsec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,9 +100,8 @@ static int shm_poll(RCL_Instance instance)
|
||||
|
||||
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
|
||||
DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
|
||||
t.mode, t.count, t.valid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,23 +57,20 @@ static void read_sample(void *anything)
|
||||
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
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (s != sizeof (sample)) {
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %d", s, sizeof (sample));
|
||||
#endif
|
||||
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
|
||||
s, (long)sizeof (sample));
|
||||
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
|
||||
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
|
||||
sample.magic, SOCK_MAGIC);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
263
reference.c
263
reference.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2013
|
||||
* Copyright (C) Miroslav Lichvar 2009-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -67,6 +67,9 @@ static double correction_time_ratio;
|
||||
/* Flag indicating that we are initialised */
|
||||
static int initialised = 0;
|
||||
|
||||
/* Current operating mode */
|
||||
static REF_Mode mode;
|
||||
|
||||
/* Threshold and update limit for stepping clock */
|
||||
static int make_step_limit;
|
||||
static double make_step_threshold;
|
||||
@@ -86,6 +89,9 @@ static int do_mail_change;
|
||||
static double mail_change_threshold;
|
||||
static char *mail_change_user;
|
||||
|
||||
/* Handler for mode ending */
|
||||
static REF_ModeEndHandler mode_end_handler = NULL;
|
||||
|
||||
/* Filename of the drift file. */
|
||||
static char *drift_file=NULL;
|
||||
static double drift_file_age;
|
||||
@@ -138,11 +144,16 @@ handle_slew(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything)
|
||||
{
|
||||
if (is_step_change) {
|
||||
UTI_AddDoubleToTimeval(&last_ref_update, -doffset, &last_ref_update);
|
||||
double delta;
|
||||
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
last_ref_update.tv_sec = 0;
|
||||
last_ref_update.tv_usec = 0;
|
||||
} else if (last_ref_update.tv_sec) {
|
||||
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +166,7 @@ REF_Initialise(void)
|
||||
double file_freq_ppm, file_skew_ppm;
|
||||
double our_frequency_ppm;
|
||||
|
||||
mode = REF_ModeNormal;
|
||||
are_we_synchronised = 0;
|
||||
our_leap_status = LEAP_Unsynchronised;
|
||||
our_leap_sec = 0;
|
||||
@@ -255,7 +267,7 @@ REF_Finalise(void)
|
||||
LCL_SetLeap(0);
|
||||
}
|
||||
|
||||
if (drift_file && drift_file_age > 0.0) {
|
||||
if (drift_file) {
|
||||
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
|
||||
}
|
||||
|
||||
@@ -266,6 +278,29 @@ REF_Finalise(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void REF_SetMode(REF_Mode new_mode)
|
||||
{
|
||||
mode = new_mode;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
REF_Mode
|
||||
REF_GetMode(void)
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_SetModeEndHandler(REF_ModeEndHandler handler)
|
||||
{
|
||||
mode_end_handler = handler;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
Sqr(double x)
|
||||
{
|
||||
@@ -290,6 +325,7 @@ update_drift_file(double freq_ppm, double skew)
|
||||
struct stat buf;
|
||||
char *temp_drift_file;
|
||||
FILE *out;
|
||||
int r1, r2;
|
||||
|
||||
/* Create a temporary file with a '.tmp' extension. */
|
||||
|
||||
@@ -311,8 +347,9 @@ update_drift_file(double freq_ppm, double skew)
|
||||
}
|
||||
|
||||
/* Write the frequency and skew parameters in ppm */
|
||||
if ((fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew) < 0) |
|
||||
fclose(out)) {
|
||||
r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
|
||||
r2 = fclose(out);
|
||||
if (r1 < 0 || r2) {
|
||||
Free(temp_drift_file);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Could not write to temporary driftfile %s.tmp",
|
||||
drift_file);
|
||||
@@ -322,10 +359,12 @@ update_drift_file(double freq_ppm, double skew)
|
||||
/* 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);
|
||||
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
|
||||
chmod(temp_drift_file,buf.st_mode & 0777)) {
|
||||
LOG(LOGS_WARN, LOGF_Reference,
|
||||
"Could not change ownership or permissions of temporary driftfile %s.tmp",
|
||||
drift_file);
|
||||
}
|
||||
chmod(temp_drift_file,buf.st_mode&0777);
|
||||
}
|
||||
|
||||
/* Rename the temporary file to the correct location (see rename(2) for details). */
|
||||
@@ -387,10 +426,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
|
||||
(freq_ppm - fb_drifts[i].freq);
|
||||
}
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
|
||||
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,23 +474,31 @@ schedule_fb_drift(struct timeval *now)
|
||||
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
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c);
|
||||
}
|
||||
|
||||
if (i <= fb_drift_max) {
|
||||
next_fb_drift = i;
|
||||
UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when);
|
||||
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d scheduled", i);
|
||||
#endif
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
end_ref_mode(int result)
|
||||
{
|
||||
mode = REF_ModeIgnore;
|
||||
|
||||
/* Dispatch the handler */
|
||||
if (mode_end_handler)
|
||||
(mode_end_handler)(result);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#define BUFLEN 255
|
||||
#define S_MAX_USER_LEN "128"
|
||||
|
||||
@@ -506,15 +551,15 @@ maybe_log_offset(double offset, time_t now)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
maybe_make_step()
|
||||
static int
|
||||
is_step_limit_reached(double offset, double offset_correction)
|
||||
{
|
||||
if (make_step_limit == 0) {
|
||||
return;
|
||||
return 0;
|
||||
} else if (make_step_limit > 0) {
|
||||
make_step_limit--;
|
||||
}
|
||||
LCL_MakeStep(make_step_threshold);
|
||||
return fabs(offset - offset_correction) > make_step_threshold;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -534,9 +579,9 @@ is_offset_ok(double 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");
|
||||
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
|
||||
if (!max_offset_ignore)
|
||||
SCH_QuitProgram();
|
||||
end_ref_mode(0);
|
||||
else if (max_offset_ignore > 0)
|
||||
max_offset_ignore--;
|
||||
return 0;
|
||||
@@ -546,6 +591,15 @@ is_offset_ok(double offset)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
is_leap_second_day(struct tm *stm) {
|
||||
/* Allow leap second only on the last day of June and December */
|
||||
return (stm->tm_mon == 5 && stm->tm_mday == 30) ||
|
||||
(stm->tm_mon == 11 && stm->tm_mday == 31);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static NTP_Leap
|
||||
get_tz_leap(time_t when)
|
||||
{
|
||||
@@ -563,12 +617,7 @@ get_tz_leap(time_t when)
|
||||
|
||||
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
|
||||
if (!is_leap_second_day(&stm))
|
||||
return tz_leap;
|
||||
|
||||
/* Temporarily switch to the timezone containing leap seconds */
|
||||
@@ -610,7 +659,6 @@ get_tz_leap(time_t when)
|
||||
static void
|
||||
update_leap_status(NTP_Leap leap, time_t now)
|
||||
{
|
||||
struct tm stm;
|
||||
int leap_sec;
|
||||
|
||||
leap_sec = 0;
|
||||
@@ -619,24 +667,20 @@ update_leap_status(NTP_Leap leap, time_t now)
|
||||
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 */
|
||||
/* Check that leap second is allowed today */
|
||||
|
||||
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 (is_leap_second_day(gmtime(&now))) {
|
||||
if (leap == LEAP_InsertSecond) {
|
||||
leap_sec = 1;
|
||||
} else {
|
||||
leap_sec = -1;
|
||||
}
|
||||
} else {
|
||||
leap = LEAP_Normal;
|
||||
}
|
||||
}
|
||||
|
||||
if (leap_sec != our_leap_sec) {
|
||||
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
|
||||
LCL_SetLeap(leap_sec);
|
||||
our_leap_sec = leap_sec;
|
||||
}
|
||||
@@ -662,6 +706,62 @@ write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
special_mode_sync(int valid, double offset)
|
||||
{
|
||||
int step;
|
||||
|
||||
switch (mode) {
|
||||
case REF_ModeInitStepSlew:
|
||||
if (!valid) {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
|
||||
end_ref_mode(0);
|
||||
break;
|
||||
}
|
||||
|
||||
step = fabs(offset) >= CNF_GetInitStepThreshold();
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Reference,
|
||||
"System's initial offset : %.6f seconds %s of true (%s)",
|
||||
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
|
||||
|
||||
if (step)
|
||||
LCL_ApplyStepOffset(offset);
|
||||
else
|
||||
LCL_AccumulateOffset(offset, 0.0);
|
||||
|
||||
end_ref_mode(1);
|
||||
|
||||
break;
|
||||
case REF_ModeUpdateOnce:
|
||||
case REF_ModePrintOnce:
|
||||
if (!valid) {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation");
|
||||
end_ref_mode(0);
|
||||
break;
|
||||
}
|
||||
|
||||
step = mode == REF_ModeUpdateOnce;
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Reference, "System clock wrong by %.6f seconds (%s)",
|
||||
-offset, step ? "step" : "ignored");
|
||||
|
||||
if (step)
|
||||
LCL_ApplyStepOffset(offset);
|
||||
|
||||
end_ref_mode(1);
|
||||
|
||||
break;
|
||||
case REF_ModeIgnore:
|
||||
/* Do nothing until the mode is changed */
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_SetReference(int stratum,
|
||||
NTP_Leap leap,
|
||||
@@ -688,11 +788,17 @@ REF_SetReference(int stratum,
|
||||
double update_interval;
|
||||
double elapsed;
|
||||
double correction_rate;
|
||||
double uncorrected_offset;
|
||||
struct timeval now, raw_now, ev_now, ev_raw_now;
|
||||
double uncorrected_offset, accumulate_offset, step_offset;
|
||||
struct timeval now, raw_now;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
/* Special modes are implemented elsewhere */
|
||||
if (mode != REF_ModeNormal) {
|
||||
special_mode_sync(1, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Guard against dividing by zero */
|
||||
if (skew < MIN_SKEW)
|
||||
skew = MIN_SKEW;
|
||||
@@ -716,10 +822,7 @@ REF_SetReference(int stratum,
|
||||
}
|
||||
|
||||
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);
|
||||
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
|
||||
UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now);
|
||||
|
||||
UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
|
||||
@@ -763,6 +866,16 @@ REF_SetReference(int stratum,
|
||||
|
||||
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
|
||||
|
||||
/* Check if the clock should be stepped */
|
||||
if (is_step_limit_reached(our_offset, uncorrected_offset)) {
|
||||
/* Cancel the uncorrected offset and correct the total offset by step */
|
||||
accumulate_offset = uncorrected_offset;
|
||||
step_offset = our_offset - uncorrected_offset;
|
||||
} else {
|
||||
accumulate_offset = our_offset;
|
||||
step_offset = 0.0;
|
||||
}
|
||||
|
||||
/* Eliminate updates that are based on totally unreliable frequency
|
||||
information. Ignore this limit with manual reference. */
|
||||
|
||||
@@ -797,21 +910,23 @@ REF_SetReference(int stratum,
|
||||
|
||||
our_residual_freq = new_freq - our_frequency;
|
||||
|
||||
LCL_AccumulateFrequencyAndOffset(our_frequency, our_offset, correction_rate);
|
||||
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
|
||||
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset);
|
||||
|
||||
#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);
|
||||
LCL_AccumulateOffset(accumulate_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();
|
||||
|
||||
if (step_offset != 0.0) {
|
||||
LCL_ApplyStepOffset(step_offset);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
|
||||
}
|
||||
|
||||
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
|
||||
|
||||
@@ -884,9 +999,15 @@ REF_SetUnsynchronised(void)
|
||||
|
||||
assert(initialised);
|
||||
|
||||
/* This is cheaper than calling LCL_CookTime */
|
||||
SCH_GetLastEventTime(&now, NULL, &now_raw);
|
||||
UTI_DiffTimevalsToDouble(&uncorrected_offset, &now, &now_raw);
|
||||
/* Special modes are implemented elsewhere */
|
||||
if (mode != REF_ModeNormal) {
|
||||
special_mode_sync(0, 0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
LCL_ReadRawTime(&now_raw);
|
||||
LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL);
|
||||
UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now);
|
||||
|
||||
if (fb_drifts) {
|
||||
schedule_fb_drift(&now);
|
||||
@@ -1000,9 +1121,6 @@ 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
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1032,6 +1150,31 @@ REF_IsLocalActive(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#define LEAP_SECOND_CLOSE 5
|
||||
|
||||
int REF_IsLeapSecondClose(void)
|
||||
{
|
||||
struct timeval now, now_raw;
|
||||
time_t t;
|
||||
|
||||
if (!our_leap_sec)
|
||||
return 0;
|
||||
|
||||
SCH_GetLastEventTime(&now, NULL, &now_raw);
|
||||
|
||||
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
|
||||
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
|
||||
return 1;
|
||||
|
||||
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
|
||||
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_GetTrackingReport(RPT_TrackingReport *rep)
|
||||
{
|
||||
@@ -1084,5 +1227,3 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
27
reference.h
27
reference.h
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -40,6 +41,26 @@ extern void REF_Initialise(void);
|
||||
/* Fini function */
|
||||
extern void REF_Finalise(void);
|
||||
|
||||
typedef enum {
|
||||
REF_ModeNormal,
|
||||
REF_ModeInitStepSlew,
|
||||
REF_ModeUpdateOnce,
|
||||
REF_ModePrintOnce,
|
||||
REF_ModeIgnore,
|
||||
} REF_Mode;
|
||||
|
||||
/* Set reference update mode */
|
||||
extern void REF_SetMode(REF_Mode mode);
|
||||
|
||||
/* Get reference update mode */
|
||||
extern REF_Mode REF_GetMode(void);
|
||||
|
||||
/* Function type for handlers to be called back when mode ends */
|
||||
typedef void (*REF_ModeEndHandler)(int result);
|
||||
|
||||
/* Set the handler for being notified of mode ending */
|
||||
extern void REF_SetModeEndHandler(REF_ModeEndHandler handler);
|
||||
|
||||
/* 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.
|
||||
@@ -129,7 +150,7 @@ extern void REF_SetManualReference
|
||||
extern void
|
||||
REF_SetUnsynchronised(void);
|
||||
|
||||
/* Return the current stratum of this host or zero if the host is not
|
||||
/* Return the current stratum of this host or 16 if the host is not
|
||||
synchronised */
|
||||
extern int REF_GetOurStratum(void);
|
||||
|
||||
@@ -140,6 +161,10 @@ extern void REF_EnableLocal(int stratum);
|
||||
extern void REF_DisableLocal(void);
|
||||
extern int REF_IsLocalActive(void);
|
||||
|
||||
/* Check if current raw or cooked time is close to a leap second
|
||||
and is better to discard any measurements */
|
||||
extern int REF_IsLeapSecondClose(void);
|
||||
|
||||
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
|
||||
|
||||
#endif /* GOT_REFERENCE_H */
|
||||
|
||||
11
regress.c
11
regress.c
@@ -209,8 +209,6 @@ n_runs_from_residuals(double *resid, int n)
|
||||
/* Return a boolean indicating whether we had enough points for
|
||||
regression */
|
||||
|
||||
#define MIN_SAMPLES_FOR_REGRESS 3
|
||||
|
||||
int
|
||||
RGR_FindBestRegression
|
||||
(double *x, /* independent variable */
|
||||
@@ -255,7 +253,7 @@ RGR_FindBestRegression
|
||||
int start, resid_start, nruns, npoints;
|
||||
int i;
|
||||
|
||||
assert(n <= MAX_POINTS);
|
||||
assert(n <= MAX_POINTS && m >= 0);
|
||||
assert(n * REGRESS_RUNS_RATIO < sizeof (critical_runs) / sizeof (critical_runs[0]));
|
||||
|
||||
if (n < MIN_SAMPLES_FOR_REGRESS) {
|
||||
@@ -392,7 +390,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
|
||||
l = u + 1;
|
||||
r = v;
|
||||
do {
|
||||
while (x[l] < piv && l < v) l++;
|
||||
while (l < v && x[l] < piv) l++;
|
||||
while (x[r] > piv) r--;
|
||||
if (r <= l) break;
|
||||
EXCH(x[l], x[r]);
|
||||
@@ -572,11 +570,6 @@ RGR_FindBestRobustRegression
|
||||
b = X / V;
|
||||
a = my - b*mx;
|
||||
|
||||
|
||||
#if 0
|
||||
printf("my=%20.12f mx=%20.12f a=%20.12f b=%20.12f\n", my, mx, a, b);
|
||||
#endif
|
||||
|
||||
s2 = 0.0;
|
||||
for (i=start; i<n; i++) {
|
||||
resid = y[i] - a - b * x[i];
|
||||
|
||||
@@ -66,6 +66,9 @@ extern double RGR_GetChi2Coef(int dof);
|
||||
points */
|
||||
#define REGRESS_RUNS_RATIO 2
|
||||
|
||||
/* Minimum number of samples for regression */
|
||||
#define MIN_SAMPLES_FOR_REGRESS 3
|
||||
|
||||
/* Return a status indicating whether there were enough points to
|
||||
carry out the regression */
|
||||
|
||||
|
||||
76
rtc.c
76
rtc.c
@@ -28,6 +28,7 @@
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "rtc.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "conf.h"
|
||||
|
||||
@@ -42,7 +43,7 @@ static int driver_initialised = 0;
|
||||
static struct {
|
||||
int (*init)(void);
|
||||
void (*fini)(void);
|
||||
void (*time_pre_init)(void);
|
||||
int (*time_pre_init)(void);
|
||||
void (*time_init)(void (*after_hook)(void*), void *anything);
|
||||
void (*start_measurements)(void);
|
||||
int (*write_parameters)(void);
|
||||
@@ -71,42 +72,68 @@ static struct {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
/* Set the system clock to the time of last modification of driftfile
|
||||
if it's in the future */
|
||||
|
||||
static void
|
||||
fallback_time_init(void)
|
||||
{
|
||||
struct timeval now;
|
||||
struct stat buf;
|
||||
char *drift_file;
|
||||
|
||||
drift_file = CNF_GetDriftFile();
|
||||
if (!drift_file)
|
||||
return;
|
||||
|
||||
if (stat(drift_file, &buf))
|
||||
return;
|
||||
|
||||
LCL_ReadCookedTime(&now, NULL);
|
||||
|
||||
if (now.tv_sec < buf.st_mtime) {
|
||||
LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime);
|
||||
LOG(LOGS_INFO, LOGF_Rtc,
|
||||
"System clock set from driftfile %s", drift_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
RTC_Initialise(void)
|
||||
RTC_Initialise(int initial_set)
|
||||
{
|
||||
char *file_name;
|
||||
int ok;
|
||||
|
||||
/* Do an initial read of the RTC and set the system time to it. This
|
||||
is analogous to what /sbin/hwclock -s would do on Linux. If that fails
|
||||
or RTC is not supported, set the clock to the time of the last
|
||||
modification of driftfile, so we at least get closer to the truth. */
|
||||
if (initial_set) {
|
||||
if (!driver.time_pre_init || !driver.time_pre_init()) {
|
||||
fallback_time_init();
|
||||
}
|
||||
}
|
||||
|
||||
driver_initialised = 0;
|
||||
|
||||
/* This is how we tell whether the user wants to load the RTC
|
||||
driver, if he is on a machine where it is an option. */
|
||||
file_name = CNF_GetRtcFile();
|
||||
|
||||
if (file_name) {
|
||||
if (CNF_GetRTCSync()) {
|
||||
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;
|
||||
driver_initialised = 1;
|
||||
}
|
||||
} else {
|
||||
ok = 0;
|
||||
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,23 +164,10 @@ 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 */
|
||||
|
||||
|
||||
3
rtc.h
3
rtc.h
@@ -28,9 +28,8 @@
|
||||
|
||||
#include "reports.h"
|
||||
|
||||
extern void RTC_Initialise(void);
|
||||
extern void RTC_Initialise(int initial_set);
|
||||
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);
|
||||
|
||||
201
rtc_linux.c
201
rtc_linux.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2012
|
||||
* Copyright (C) Miroslav Lichvar 2012-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "local.h"
|
||||
#include "util.h"
|
||||
#include "sys_linux.h"
|
||||
#include "reference.h"
|
||||
#include "regress.h"
|
||||
#include "rtc.h"
|
||||
#include "rtc_linux.h"
|
||||
@@ -67,9 +68,7 @@ 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
|
||||
#define N_SAMPLES_PER_REGRESSION 1
|
||||
|
||||
static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
|
||||
|
||||
@@ -125,6 +124,9 @@ static double coef_gain_rate;
|
||||
RTC data file once we have reacquired its offset after the step */
|
||||
static double saved_coef_gain_rate;
|
||||
|
||||
/* Threshold for automatic RTC trimming in seconds, zero when disabled */
|
||||
static double autotrim_threshold;
|
||||
|
||||
/* Filename supplied by config file where RTC coefficients are
|
||||
stored. */
|
||||
static char *coefs_file_name;
|
||||
@@ -263,13 +265,19 @@ static void
|
||||
slew_samples
|
||||
(struct timeval *raw, struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset, int is_step_change,
|
||||
double doffset,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything)
|
||||
{
|
||||
int i;
|
||||
double delta_time;
|
||||
double old_seconds_fast, old_gain_rate;
|
||||
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
/* Drop all samples. */
|
||||
n_samples = 0;
|
||||
}
|
||||
|
||||
for (i=0; i<n_samples; i++) {
|
||||
UTI_AdjustTimeval(system_times + i, cooked, system_times + i, &delta_time,
|
||||
dfreq, doffset);
|
||||
@@ -280,18 +288,13 @@ slew_samples
|
||||
|
||||
if (coefs_valid) {
|
||||
coef_seconds_fast += doffset;
|
||||
coef_gain_rate = (1.0 + dfreq) * (1.0 + coef_gain_rate) - 1.0;
|
||||
coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
|
||||
}
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
|
||||
DEBUG_LOG(LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
|
||||
dfreq, doffset,
|
||||
old_seconds_fast, 1.0e6 * old_gain_rate,
|
||||
coef_seconds_fast, 1.0e6 * coef_gain_rate);
|
||||
#else
|
||||
(void)old_seconds_fast; (void)old_gain_rate;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -369,14 +372,55 @@ t_from_rtc(struct tm *stm) {
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
read_hwclock_file(const char *hwclock_file)
|
||||
{
|
||||
FILE *in;
|
||||
char line[256];
|
||||
int i;
|
||||
|
||||
if (!hwclock_file)
|
||||
return;
|
||||
|
||||
in = fopen(hwclock_file, "r");
|
||||
if (!in) {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open hwclockfile %s",
|
||||
hwclock_file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read third line from the file. */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!fgets(line, sizeof(line), in))
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
|
||||
if (i == 3 && !strncmp(line, "LOCAL", 5)) {
|
||||
rtc_on_utc = 0;
|
||||
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
|
||||
rtc_on_utc = 1;
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read LOCAL/UTC setting from hwclockfile %s",
|
||||
hwclock_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
setup_config(void)
|
||||
{
|
||||
if (CNF_GetRTCOnUTC()) {
|
||||
if (CNF_GetRtcOnUtc()) {
|
||||
rtc_on_utc = 1;
|
||||
} else {
|
||||
rtc_on_utc = 0;
|
||||
}
|
||||
|
||||
read_hwclock_file(CNF_GetHwclockFile());
|
||||
|
||||
autotrim_threshold = CNF_GetRtcAutotrim();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -387,7 +431,6 @@ static void
|
||||
read_coefs_from_file(void)
|
||||
{
|
||||
FILE *in;
|
||||
char line[256];
|
||||
|
||||
if (!tried_to_load_coefs) {
|
||||
|
||||
@@ -395,26 +438,17 @@ read_coefs_from_file(void)
|
||||
|
||||
tried_to_load_coefs = 1;
|
||||
|
||||
in = fopen(coefs_file_name, "r");
|
||||
if (in) {
|
||||
if (fgets(line, sizeof(line), in)) {
|
||||
if (sscanf(line, "%d%ld%lf%lf",
|
||||
&valid_coefs_from_file,
|
||||
&file_ref_time,
|
||||
&file_ref_offset,
|
||||
&file_rate_ppm) == 4) {
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not parse coefficients line from RTC file %s",
|
||||
coefs_file_name);
|
||||
}
|
||||
if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) {
|
||||
if (fscanf(in, "%d%ld%lf%lf",
|
||||
&valid_coefs_from_file,
|
||||
&file_ref_time,
|
||||
&file_ref_offset,
|
||||
&file_rate_ppm) == 4) {
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read first line from RTC file %s",
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from RTC file %s",
|
||||
coefs_file_name);
|
||||
}
|
||||
fclose(in);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open RTC file %s for reading",
|
||||
coefs_file_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,6 +463,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
struct stat buf;
|
||||
char *temp_coefs_file_name;
|
||||
FILE *out;
|
||||
int r1, r2;
|
||||
|
||||
/* Create a temporary file with a '.tmp' extension. */
|
||||
|
||||
@@ -450,9 +485,10 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
}
|
||||
|
||||
/* Gain rate is written out in ppm */
|
||||
if ((fprintf(out, "%1d %ld %.6f %.3f\n",
|
||||
valid,ref_time, offset, 1.0e6 * rate) < 0) |
|
||||
fclose(out)) {
|
||||
r1 = fprintf(out, "%1d %ld %.6f %.3f\n",
|
||||
valid, ref_time, offset, 1.0e6 * rate);
|
||||
r2 = fclose(out);
|
||||
if (r1 < 0 || r2) {
|
||||
Free(temp_coefs_file_name);
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not write to temporary RTC file %s.tmp",
|
||||
coefs_file_name);
|
||||
@@ -462,10 +498,12 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
/* Clone the file attributes from the existing file if there is one. */
|
||||
|
||||
if (!stat(coefs_file_name,&buf)) {
|
||||
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid)) {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not change ownership of temporary RTC file %s.tmp", coefs_file_name);
|
||||
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
|
||||
chmod(temp_coefs_file_name,buf.st_mode & 0777)) {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux,
|
||||
"Could not change ownership or permissions of temporary RTC file %s.tmp",
|
||||
coefs_file_name);
|
||||
}
|
||||
chmod(temp_coefs_file_name,buf.st_mode&0777);
|
||||
}
|
||||
|
||||
/* Rename the temporary file to the correct location (see rename(2) for details). */
|
||||
@@ -502,7 +540,8 @@ RTC_Linux_Initialise(void)
|
||||
|
||||
fd = open (CNF_GetRtcDevice(), O_RDWR);
|
||||
if (fd < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open %s, %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
|
||||
CNF_GetRtcDevice(), strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -670,6 +709,7 @@ handle_relock_after_trim(void)
|
||||
time_t ref;
|
||||
double fast, slope;
|
||||
|
||||
valid = 0;
|
||||
run_regression(1, &valid, &ref, &fast, &slope);
|
||||
|
||||
if (valid) {
|
||||
@@ -687,8 +727,25 @@ handle_relock_after_trim(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Day number of 1 Jan 1970 */
|
||||
#define MJD_1970 40587
|
||||
static void
|
||||
maybe_autotrim(void)
|
||||
{
|
||||
/* Trim only when in normal mode, the coefficients are fresh, the current
|
||||
offset is above the threshold and the system clock is synchronized */
|
||||
|
||||
if (operating_mode != OM_NORMAL || !coefs_valid || n_samples_since_regression)
|
||||
return;
|
||||
|
||||
if (autotrim_threshold <= 0.0 || fabs(coef_seconds_fast) < autotrim_threshold)
|
||||
return;
|
||||
|
||||
if (REF_GetOurStratum() >= 16)
|
||||
return;
|
||||
|
||||
RTC_Linux_Trim();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
process_reading(time_t rtc_time, struct timeval *system_time)
|
||||
@@ -700,9 +757,10 @@ process_reading(time_t rtc_time, struct timeval *system_time)
|
||||
switch (operating_mode) {
|
||||
case OM_NORMAL:
|
||||
|
||||
if (n_samples_since_regression >= /* 4 */ 1 ) {
|
||||
if (n_samples_since_regression >= N_SAMPLES_PER_REGRESSION) {
|
||||
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
|
||||
n_samples_since_regression = 0;
|
||||
maybe_autotrim();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -753,7 +811,6 @@ read_from_device(void *any)
|
||||
/* 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);
|
||||
@@ -905,19 +962,18 @@ RTC_Linux_WriteParameters(void)
|
||||
|
||||
/* ================================================== */
|
||||
/* 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
|
||||
/sbin/hwclock -s would do. We're not as picky about OS version
|
||||
etc in this case, since we have fewer requirements regarding the
|
||||
RTC behaviour than we do for the rest of the module. */
|
||||
|
||||
void
|
||||
int
|
||||
RTC_Linux_TimePreInit(void)
|
||||
{
|
||||
int fd, status;
|
||||
struct rtc_time rtc_raw;
|
||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||
struct tm rtc_tm;
|
||||
time_t rtc_t, estimated_correct_rtc_t;
|
||||
long interval;
|
||||
double accumulated_error = 0.0;
|
||||
time_t rtc_t;
|
||||
double accumulated_error, sys_offset;
|
||||
struct timeval new_sys_time, old_sys_time;
|
||||
|
||||
coefs_file_name = CNF_GetRtcFile();
|
||||
@@ -928,10 +984,23 @@ RTC_Linux_TimePreInit(void)
|
||||
fd = open(CNF_GetRtcDevice(), O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
return; /* Can't open it, and won't be able to later */
|
||||
return 0; /* Can't open it, and won't be able to later */
|
||||
}
|
||||
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
/* Retry reading the rtc until both read attempts give the same sec value.
|
||||
This way the race condition is prevented that the RTC has updated itself
|
||||
during the first read operation. */
|
||||
do {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status >= 0) {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
||||
}
|
||||
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
||||
|
||||
/* Read system clock */
|
||||
LCL_ReadCookedTime(&old_sys_time, NULL);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (status >= 0) {
|
||||
/* Convert to seconds since 1970 */
|
||||
@@ -949,37 +1018,35 @@ RTC_Linux_TimePreInit(void)
|
||||
/* 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);
|
||||
accumulated_error = file_ref_offset +
|
||||
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
|
||||
} else {
|
||||
estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
|
||||
accumulated_error = 0.0;
|
||||
}
|
||||
|
||||
new_sys_time.tv_sec = estimated_correct_rtc_t;
|
||||
new_sys_time.tv_usec = 0;
|
||||
/* Correct time */
|
||||
|
||||
new_sys_time.tv_sec = rtc_t;
|
||||
/* Average error in the RTC reading */
|
||||
new_sys_time.tv_usec = 500000;
|
||||
|
||||
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
|
||||
|
||||
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
|
||||
|
||||
/* Set system time only if the step is larger than 1 second */
|
||||
if (!(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)) {
|
||||
|
||||
if (fabs(sys_offset) >= 1.0) {
|
||||
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");
|
||||
}
|
||||
LCL_ApplyStepOffset(sys_offset);
|
||||
}
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
extern int RTC_Linux_Initialise(void);
|
||||
extern void RTC_Linux_Finalise(void);
|
||||
extern void RTC_Linux_TimePreInit(void);
|
||||
extern int RTC_Linux_TimePreInit(void);
|
||||
extern void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything);
|
||||
extern void RTC_Linux_StartMeasurements(void);
|
||||
|
||||
|
||||
115
sched.c
115
sched.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2013-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -57,18 +57,18 @@ 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)
|
||||
#ifndef FD_SETSIZE
|
||||
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
|
||||
as a fixed size array of bits, possibly embedded inside a record */
|
||||
#define FD_SETSIZE (sizeof(fd_set) * 8)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
SCH_FileHandler handler;
|
||||
SCH_ArbitraryArgument arg;
|
||||
} FileHandlerEntry;
|
||||
|
||||
static FileHandlerEntry file_handlers[FD_SET_SIZE];
|
||||
static FileHandlerEntry file_handlers[FD_SETSIZE];
|
||||
|
||||
/* Timestamp when last select() returned */
|
||||
static struct timeval last_select_ts, last_select_ts_raw;
|
||||
@@ -123,7 +123,7 @@ handle_slew(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything);
|
||||
|
||||
/* ================================================== */
|
||||
@@ -169,6 +169,9 @@ SCH_AddInputFileHandler
|
||||
|
||||
assert(initialised);
|
||||
|
||||
if (fd >= FD_SETSIZE)
|
||||
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
|
||||
|
||||
/* Don't want to allow the same fd to register a handler more than
|
||||
once without deleting a previous association - this suggests
|
||||
a bug somewhere else in the program. */
|
||||
@@ -503,14 +506,18 @@ handle_slew(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything)
|
||||
{
|
||||
TimerQueueEntry *ptr;
|
||||
double delta;
|
||||
int i;
|
||||
|
||||
if (is_step_change) {
|
||||
if (change_type != LCL_ChangeAdjust) {
|
||||
/* Make sure this handler is invoked first in order to not shift new timers
|
||||
added from other handlers */
|
||||
assert(LCL_IsFirstParameterChangeHandler(handle_slew));
|
||||
|
||||
/* If a step change occurs, just shift all raw time stamps by the offset */
|
||||
|
||||
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
|
||||
@@ -529,30 +536,54 @@ handle_slew(struct timeval *raw,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Try to handle unexpected backward time jump */
|
||||
#define JUMP_DETECT_THRESHOLD 10
|
||||
|
||||
static void
|
||||
recover_backjump(struct timeval *raw, struct timeval *cooked, int timeout)
|
||||
static int
|
||||
check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
|
||||
struct timeval *orig_select_tv,
|
||||
struct timeval *rem_select_tv)
|
||||
{
|
||||
double diff, err;
|
||||
struct timeval elapsed_min, elapsed_max;
|
||||
double step, elapsed;
|
||||
|
||||
UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw);
|
||||
/* Get an estimate of the time spent waiting in the select() call. On some
|
||||
systems (e.g. Linux) the timeout timeval is modified to return the
|
||||
remaining time, use that information. */
|
||||
if (timeout) {
|
||||
elapsed_max = elapsed_min = *orig_select_tv;
|
||||
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
|
||||
rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
|
||||
(rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
|
||||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
|
||||
UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv);
|
||||
elapsed_max = elapsed_min;
|
||||
} else {
|
||||
if (rem_select_tv)
|
||||
elapsed_max = *orig_select_tv;
|
||||
else
|
||||
UTI_DiffTimevals(&elapsed_max, raw, prev_raw);
|
||||
elapsed_min.tv_sec = 0;
|
||||
elapsed_min.tv_usec = 0;
|
||||
}
|
||||
|
||||
if (n_timer_queue_entries > 0) {
|
||||
UTI_DiffTimevalsToDouble(&err, &(timer_queue.next->tv), &last_select_ts_raw);
|
||||
} else {
|
||||
err = 0.0;
|
||||
}
|
||||
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
|
||||
raw->tv_sec + JUMP_DETECT_THRESHOLD) {
|
||||
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
|
||||
} else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
|
||||
raw->tv_sec) {
|
||||
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
diff += err;
|
||||
UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw);
|
||||
UTI_TimevalToDouble(&elapsed_min, &elapsed);
|
||||
step += elapsed;
|
||||
|
||||
if (timeout) {
|
||||
err = 1.0;
|
||||
}
|
||||
/* Cooked time may no longer be valid after dispatching the handlers */
|
||||
LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step));
|
||||
|
||||
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected! (correction %.1f +- %.1f seconds)", diff, err);
|
||||
|
||||
LCL_NotifyExternalTimeStep(raw, cooked, diff, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -562,34 +593,43 @@ SCH_MainLoop(void)
|
||||
{
|
||||
fd_set rd;
|
||||
int status, errsv;
|
||||
struct timeval tv, *ptv;
|
||||
struct timeval now, cooked;
|
||||
struct timeval tv, saved_tv, *ptv;
|
||||
struct timeval now, saved_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);
|
||||
saved_now = now;
|
||||
|
||||
/* The timeout handlers may request quit */
|
||||
if (need_to_exit)
|
||||
break;
|
||||
|
||||
/* Check whether there is a timeout and set it up */
|
||||
if (n_timer_queue_entries > 0) {
|
||||
|
||||
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
|
||||
ptv = &tv;
|
||||
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
|
||||
saved_tv = tv;
|
||||
|
||||
} else {
|
||||
ptv = NULL;
|
||||
/* This is needed to fix a compiler warning */
|
||||
saved_tv.tv_sec = 0;
|
||||
}
|
||||
|
||||
/* if there are no file descriptors being waited on and no
|
||||
timeout set, this is clearly ridiculous, so stop the run */
|
||||
assert(ptv || n_read_fds);
|
||||
if (!ptv && !n_read_fds) {
|
||||
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
|
||||
}
|
||||
|
||||
/* Copy current set of read file descriptors */
|
||||
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
|
||||
|
||||
status = select(one_highest_fd, &rd, NULL, NULL, ptv);
|
||||
errsv = errno;
|
||||
@@ -597,9 +637,10 @@ SCH_MainLoop(void)
|
||||
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);
|
||||
/* Check if the time didn't jump unexpectedly */
|
||||
if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
|
||||
/* Cook the time again after handling the step */
|
||||
LCL_CookTime(&now, &cooked, &err);
|
||||
}
|
||||
|
||||
last_select_ts_raw = now;
|
||||
|
||||
236
sources.c
236
sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2013
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "nameserv.h"
|
||||
#include "mkdirpp.h"
|
||||
#include "sched.h"
|
||||
#include "regress.h"
|
||||
|
||||
/* ================================================== */
|
||||
/* Flag indicating that we are initialised */
|
||||
@@ -90,11 +91,14 @@ struct SRC_Instance_Record {
|
||||
/* Flag indicating that we can use this source as a reference */
|
||||
int selectable;
|
||||
|
||||
/* Flag indicating that the source is updating reachability */
|
||||
int active;
|
||||
|
||||
/* Reachability register */
|
||||
int reachability;
|
||||
|
||||
/* Flag indicating that only few samples were accumulated so far */
|
||||
int beginning;
|
||||
/* Number of set bits in the reachability register */
|
||||
int reachability_size;
|
||||
|
||||
/* Updates left before allowing combining */
|
||||
int outlier;
|
||||
@@ -135,9 +139,6 @@ 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
|
||||
|
||||
@@ -153,7 +154,7 @@ static double combine_limit;
|
||||
|
||||
static void
|
||||
slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq,
|
||||
double doffset, int is_step_change, void *anything);
|
||||
double doffset, LCL_ChangeType change_type, void *anything);
|
||||
static void
|
||||
add_dispersion(double dispersion, void *anything);
|
||||
static char *
|
||||
@@ -217,9 +218,10 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOpt
|
||||
result->leap_status = LEAP_Normal;
|
||||
result->ref_id = ref_id;
|
||||
result->ip_addr = addr;
|
||||
result->active = 0;
|
||||
result->selectable = 0;
|
||||
result->reachability = 0;
|
||||
result->beginning = 1;
|
||||
result->reachability_size = 0;
|
||||
result->outlier = 0;
|
||||
result->status = SRC_BAD_STATS;
|
||||
result->type = type;
|
||||
@@ -307,17 +309,34 @@ void SRC_AccumulateSample
|
||||
|
||||
inst->leap_status = leap_status;
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
|
||||
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
|
||||
source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
|
||||
#endif
|
||||
|
||||
if (REF_IsLeapSecondClose()) {
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
|
||||
return;
|
||||
}
|
||||
|
||||
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
|
||||
IS FLIPPED */
|
||||
SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
|
||||
SST_DoNewRegression(inst->stats);
|
||||
/* And redo clock selection */
|
||||
SRC_SelectSource(inst->ref_id);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SRC_SetActive(SRC_Instance inst)
|
||||
{
|
||||
inst->active = 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SRC_UnsetActive(SRC_Instance inst)
|
||||
{
|
||||
inst->active = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -327,9 +346,7 @@ SRC_SetSelectable(SRC_Instance inst)
|
||||
{
|
||||
inst->selectable = 1;
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_Sources, "%s", source_to_string(inst));
|
||||
#endif
|
||||
DEBUG_LOG(LOGF_Sources, "%s", source_to_string(inst));
|
||||
|
||||
/* Don't do selection at this point, though - that will come about
|
||||
in due course when we get some useful data from the source */
|
||||
@@ -342,35 +359,60 @@ SRC_UnsetSelectable(SRC_Instance inst)
|
||||
{
|
||||
inst->selectable = 0;
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_Sources, "%s%s", source_to_string(inst),
|
||||
DEBUG_LOG(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);
|
||||
SRC_SelectSource(NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
special_mode_end(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
/* No updates from inactive sources */
|
||||
if (!sources[i]->active)
|
||||
continue;
|
||||
|
||||
/* Don't expect more updates than from an offline iburst NTP source */
|
||||
if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1)
|
||||
continue;
|
||||
|
||||
/* Check if the source could still have enough samples to be selectable */
|
||||
if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size +
|
||||
SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
||||
{
|
||||
inst->reachability <<= 1;
|
||||
inst->reachability |= !!reachable;
|
||||
inst->reachability &= ~(-1 << REACH_BITS);
|
||||
inst->reachability &= ~(-1 << SOURCE_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 (inst->reachability_size < SOURCE_REACH_BITS)
|
||||
inst->reachability_size++;
|
||||
|
||||
if (!reachable && inst->index == selected_source_index) {
|
||||
/* Try to select a better source */
|
||||
SRC_SelectSource(0);
|
||||
SRC_SelectSource(NULL);
|
||||
}
|
||||
|
||||
/* Check if special reference update mode failed */
|
||||
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
|
||||
REF_SetUnsynchronised();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,12 +425,23 @@ SRC_ResetReachability(SRC_Instance inst)
|
||||
a peer selected even when not reachable */
|
||||
#if 0
|
||||
inst->reachability = 0;
|
||||
inst->reachability_size = 0;
|
||||
SRC_UpdateReachability(inst, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
log_selection_message(char *format, char *arg)
|
||||
{
|
||||
if (REF_GetMode() != REF_ModeNormal)
|
||||
return;
|
||||
LOG(LOGS_INFO, LOGF_Sources, format, arg);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
compare_sort_elements(const void *a, const void *b)
|
||||
{
|
||||
@@ -459,7 +512,9 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
|
||||
(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;
|
||||
/* Use a smaller penalty in first few updates */
|
||||
sources[index]->outlier = sources[index]->reachability_size >= SOURCE_REACH_BITS ?
|
||||
OUTLIER_PENALTY : 1;
|
||||
} else if (sources[index]->outlier) {
|
||||
sources[index]->outlier--;
|
||||
}
|
||||
@@ -472,10 +527,8 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
|
||||
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",
|
||||
DEBUG_LOG(LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
|
||||
index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
|
||||
#endif
|
||||
|
||||
sum_offset_weight += offset_weight;
|
||||
sum_offset += offset_weight * src_offset;
|
||||
@@ -495,10 +548,8 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
|
||||
*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",
|
||||
DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
|
||||
*offset, *offset_sd, *frequency, *skew);
|
||||
#endif
|
||||
|
||||
return combined;
|
||||
}
|
||||
@@ -511,9 +562,9 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
|
||||
or match_refid is equal to the selected reference source refid */
|
||||
|
||||
void
|
||||
SRC_SelectSource(uint32_t match_refid)
|
||||
SRC_SelectSource(SRC_Instance updated_inst)
|
||||
{
|
||||
int i, j, index, old_selected_index;
|
||||
int i, j, index, old_selected_index, sel_prefer;
|
||||
struct timeval now, ref_time;
|
||||
double src_offset, src_offset_sd, src_frequency, src_skew;
|
||||
double src_root_delay, src_root_dispersion;
|
||||
@@ -535,7 +586,7 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
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");
|
||||
log_selection_message("Can't synchronise: no sources", NULL);
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
REF_SetUnsynchronised();
|
||||
}
|
||||
@@ -564,13 +615,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
&(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;
|
||||
|
||||
@@ -609,10 +653,8 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Sources, "badstat_sources=%d sel_sources=%d badstat_reach=%x sel_reach=%x",
|
||||
DEBUG_LOG(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
|
||||
@@ -626,10 +668,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Sources, "n_endpoints=%d", n_endpoints);
|
||||
#endif
|
||||
|
||||
/* Now sort the endpoint list */
|
||||
if (n_endpoints > 0) {
|
||||
|
||||
@@ -663,10 +701,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
best_lo = best_hi = 0.0;
|
||||
|
||||
for (i=0; i<n_endpoints; i++) {
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_Sources, "i=%d t=%f tag=%d addr=%s", i, sort_list[i].offset, sort_list[i].tag,
|
||||
source_to_string(sources[sort_list[i].index]));
|
||||
#endif
|
||||
switch(sort_list[i].tag) {
|
||||
case LOW:
|
||||
depth++;
|
||||
@@ -690,11 +724,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
}
|
||||
}
|
||||
|
||||
#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.
|
||||
@@ -708,7 +737,7 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
*/
|
||||
|
||||
if (selected_source_index != INVALID_SOURCE) {
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no majority");
|
||||
log_selection_message("Can't synchronise: no majority", NULL);
|
||||
}
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
|
||||
@@ -737,14 +766,8 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
(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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -765,10 +788,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
}
|
||||
}
|
||||
|
||||
#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; i<n_sel_sources; i++) {
|
||||
@@ -777,9 +796,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
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
|
||||
@@ -790,7 +806,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
if (index != INVALID_SOURCE) {
|
||||
sources[index]->status = SRC_SELECTABLE;
|
||||
sel_sources[j++] = sel_sources[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
n_sel_sources = j;
|
||||
@@ -822,6 +837,9 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
}
|
||||
if (j > 0) {
|
||||
n_sel_sources = j;
|
||||
sel_prefer = 1;
|
||||
} else {
|
||||
sel_prefer = 0;
|
||||
}
|
||||
|
||||
/* Now find minimum stratum. If none are left now,
|
||||
@@ -836,10 +854,6 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
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;
|
||||
@@ -854,7 +868,8 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
|
||||
/* Reset score for non-selectable sources */
|
||||
if (sources[i]->status != SRC_SELECTABLE) {
|
||||
if (sources[i]->status != SRC_SELECTABLE ||
|
||||
(sel_prefer && sources[i]->sel_option != SRC_SelectPrefer)) {
|
||||
sources[i]->sel_score = 1.0;
|
||||
sources[i]->outlier = OUTLIER_PENALTY;
|
||||
continue;
|
||||
@@ -869,8 +884,8 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
|
||||
/* 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) {
|
||||
if (sources[i] == updated_inst ||
|
||||
sources[selected_source_index] == updated_inst) {
|
||||
|
||||
sources[i]->sel_score *= sel_src_distance / distance;
|
||||
|
||||
@@ -887,10 +902,9 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
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
|
||||
DEBUG_LOG(LOGF_Sources, "select score=%f refid=%x match_refid=%x status=%d dist=%f",
|
||||
sources[i]->sel_score, sources[i]->ref_id, updated_inst ? updated_inst->ref_id : 0,
|
||||
sources[i]->status, distance);
|
||||
|
||||
if (max_score < sources[i]->sel_score) {
|
||||
max_score = sources[i]->sel_score;
|
||||
@@ -910,13 +924,9 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
/* We have to elect a new synchronisation source */
|
||||
|
||||
selected_source_index = max_score_index;
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Selected source %s",
|
||||
log_selection_message("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;
|
||||
@@ -926,10 +936,10 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
|
||||
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) */
|
||||
/* Update local reference only when a new source was selected
|
||||
or the selected source has a new sample */
|
||||
if (selected_source_index != old_selected_index ||
|
||||
match_refid == sources[selected_source_index]->ref_id) {
|
||||
updated_inst == sources[selected_source_index]) {
|
||||
|
||||
/* Now just use the statistics of the selected source combined with
|
||||
the other selectable sources for trimming the local clock */
|
||||
@@ -958,7 +968,7 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
|
||||
} else {
|
||||
if (selected_source_index != INVALID_SOURCE) {
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no selectable sources");
|
||||
log_selection_message("Can't synchronise: no selectable sources", NULL);
|
||||
}
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
}
|
||||
@@ -967,7 +977,7 @@ SRC_SelectSource(uint32_t match_refid)
|
||||
} else {
|
||||
/* No sources provided valid endpoints */
|
||||
if (selected_source_index != INVALID_SOURCE) {
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no reachable sources");
|
||||
log_selection_message("Can't synchronise: no reachable sources", NULL);
|
||||
}
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
}
|
||||
@@ -985,7 +995,7 @@ void
|
||||
SRC_ReselectSource(void)
|
||||
{
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
SRC_SelectSource(0);
|
||||
SRC_SelectSource(NULL);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1037,15 +1047,23 @@ slew_sources(struct timeval *raw,
|
||||
struct timeval *cooked,
|
||||
double dfreq,
|
||||
double doffset,
|
||||
int is_step_change,
|
||||
LCL_ChangeType change_type,
|
||||
void *anything)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<n_sources; i++) {
|
||||
SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset);
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
SST_ResetInstance(sources[i]->stats);
|
||||
} else {
|
||||
SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
/* After resetting no source is selectable, set reference unsynchronised */
|
||||
SRC_SelectSource(NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1154,6 +1172,14 @@ SRC_IsSyncPeer(SRC_Instance inst)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SRC_IsReachable(SRC_Instance inst)
|
||||
{
|
||||
return inst->reachability != 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SRC_ReadNumberOfSources(void)
|
||||
{
|
||||
@@ -1162,6 +1188,20 @@ SRC_ReadNumberOfSources(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SRC_ActiveSources(void)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
for (i = r = 0; i < n_sources; i++)
|
||||
if (sources[i]->active)
|
||||
r++;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
|
||||
{
|
||||
|
||||
18
sources.h
18
sources.h
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2002
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -34,6 +35,9 @@
|
||||
#include "ntp.h"
|
||||
#include "reports.h"
|
||||
|
||||
/* Size of the source reachability register */
|
||||
#define SOURCE_REACH_BITS 8
|
||||
|
||||
/* This datatype is used to hold information about sources. The
|
||||
instance must be passed when calling many of the interface
|
||||
functions */
|
||||
@@ -114,6 +118,12 @@ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
|
||||
|
||||
extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
|
||||
|
||||
/* This routine sets the source as receiving reachability updates */
|
||||
extern void SRC_SetActive(SRC_Instance inst);
|
||||
|
||||
/* This routine sets the source as not receiving reachability updates */
|
||||
extern void SRC_UnsetActive(SRC_Instance inst);
|
||||
|
||||
/* This routine indicates that packets with valid headers are being
|
||||
received from the designated source */
|
||||
extern void SRC_SetSelectable(SRC_Instance instance);
|
||||
@@ -131,11 +141,11 @@ 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
|
||||
if a new source is selected or updated_inst is the selected
|
||||
reference source. (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);
|
||||
extern void SRC_SelectSource(SRC_Instance updated_inst);
|
||||
|
||||
/* Force reselecting the best source */
|
||||
extern void SRC_ReselectSource(void);
|
||||
@@ -161,7 +171,9 @@ extern void SRC_DumpSources(void);
|
||||
extern void SRC_ReloadSources(void);
|
||||
|
||||
extern int SRC_IsSyncPeer(SRC_Instance inst);
|
||||
extern int SRC_IsReachable(SRC_Instance inst);
|
||||
extern int SRC_ReadNumberOfSources(void);
|
||||
extern int SRC_ActiveSources(void);
|
||||
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);
|
||||
|
||||
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2012
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -188,6 +188,26 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr)
|
||||
inst = MallocNew(struct SST_Stats_Record);
|
||||
inst->refid = refid;
|
||||
inst->ip_addr = addr;
|
||||
|
||||
SST_ResetInstance(inst);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* This function deletes an instance of the statistics handler. */
|
||||
|
||||
void
|
||||
SST_DeleteInstance(SST_Stats inst)
|
||||
{
|
||||
Free(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SST_ResetInstance(SST_Stats inst)
|
||||
{
|
||||
inst->n_samples = 0;
|
||||
inst->runs_samples = 0;
|
||||
inst->last_sample = 0;
|
||||
@@ -203,16 +223,6 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr)
|
||||
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);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -258,7 +268,7 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
|
||||
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);
|
||||
SST_ResetInstance(inst);
|
||||
}
|
||||
|
||||
n = inst->last_sample = (inst->last_sample + 1) %
|
||||
@@ -353,10 +363,6 @@ find_best_sample_index(SST_Stats inst, double *times_back)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -490,9 +496,6 @@ SST_DoNewRegression(SST_Stats inst)
|
||||
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;
|
||||
@@ -569,10 +572,8 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
|
||||
|
||||
*select_ok = inst->regression_ok;
|
||||
|
||||
#ifdef TRACEON
|
||||
LOG(LOGS_INFO, LOGF_SourceStats, "n=%d off=%f dist=%f var=%f selok=%d",
|
||||
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f selok=%d",
|
||||
inst->n_samples, offset, *root_distance, *variance, *select_ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -586,6 +587,8 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
|
||||
int i, j;
|
||||
double elapsed_sample;
|
||||
|
||||
assert(inst->n_samples > 0);
|
||||
|
||||
i = get_runsbuf_index(inst, inst->best_single_sample);
|
||||
j = get_buf_index(inst, inst->best_single_sample);
|
||||
|
||||
@@ -599,10 +602,8 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
|
||||
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",
|
||||
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
|
||||
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -626,13 +627,10 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
|
||||
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",
|
||||
|
||||
DEBUG_LOG(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 */
|
||||
@@ -644,14 +642,10 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
|
||||
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",
|
||||
DEBUG_LOG(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
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -733,9 +727,8 @@ SST_IsGoodSample(SST_Stats inst, double offset, double 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
|
||||
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
|
||||
offset, delay, allowed_increase, delay_increase);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -781,8 +774,11 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
||||
unsigned long sec, usec;
|
||||
double weight;
|
||||
|
||||
assert(!inst->n_samples);
|
||||
|
||||
if (fgets(line, sizeof(line), in) &&
|
||||
(sscanf(line, "%u", &inst->n_samples) == 1) && inst->n_samples <= MAX_SAMPLES) {
|
||||
sscanf(line, "%d", &inst->n_samples) == 1 &&
|
||||
inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) {
|
||||
|
||||
line_number = 2;
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ 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 resets an instance */
|
||||
extern void SST_ResetInstance(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
|
||||
@@ -67,11 +70,6 @@ extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, do
|
||||
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
|
||||
|
||||
314
sys_generic.c
Normal file
314
sys_generic.c
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Generic driver functions to complete system-specific drivers
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "sys_generic.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
#include "localp.h"
|
||||
#include "logging.h"
|
||||
#include "sched.h"
|
||||
#include "util.h"
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* System clock frequency drivers */
|
||||
static lcl_ReadFrequencyDriver drv_read_freq;
|
||||
static lcl_SetFrequencyDriver drv_set_freq;
|
||||
|
||||
/* Current frequency as requested by the local module (in ppm) */
|
||||
static double base_freq;
|
||||
|
||||
/* Maximum frequency that can be set by drv_set_freq (in ppm) */
|
||||
static double max_freq;
|
||||
|
||||
/* Maximum expected delay in the actual frequency change (e.g. kernel ticks)
|
||||
in local time */
|
||||
static double max_freq_change_delay;
|
||||
|
||||
/* Maximum allowed frequency offset relative to the base frequency */
|
||||
static double max_corr_freq;
|
||||
|
||||
/* Amount of outstanding offset to process */
|
||||
static double offset_register;
|
||||
|
||||
/* Minimum offset to correct */
|
||||
#define MIN_OFFSET_CORRECTION 1.0e-9
|
||||
|
||||
/* Current frequency offset between base_freq and the real clock frequency
|
||||
as set by drv_set_freq (not in ppm) */
|
||||
static double slew_freq;
|
||||
|
||||
/* Time (raw) of last update of slewing frequency and offset */
|
||||
static struct timeval slew_start;
|
||||
|
||||
/* Limits for the slew timeout */
|
||||
#define MIN_SLEW_TIMEOUT 1.0
|
||||
#define MAX_SLEW_TIMEOUT 1.0e4
|
||||
|
||||
/* Scheduler timeout ID and flag if the timer is currently running */
|
||||
static SCH_TimeoutID slew_timeout_id;
|
||||
static int slew_timer_running;
|
||||
|
||||
/* Suggested offset correction rate (correction time * offset) */
|
||||
static double correction_rate;
|
||||
|
||||
/* Maximum expected offset correction error caused by delayed change in the
|
||||
real frequency of the clock */
|
||||
static double slew_error;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void handle_end_of_slew(void *anything);
|
||||
static void update_slew(void);
|
||||
|
||||
/* ================================================== */
|
||||
/* Adjust slew_start on clock step */
|
||||
|
||||
static void
|
||||
handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
|
||||
double doffset, LCL_ChangeType change_type, void *anything)
|
||||
{
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
/* Reset offset and slewing */
|
||||
slew_start = *raw;
|
||||
offset_register = 0.0;
|
||||
update_slew();
|
||||
} else if (change_type == LCL_ChangeStep) {
|
||||
UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* End currently running slew and start a new one */
|
||||
|
||||
static void
|
||||
update_slew(void)
|
||||
{
|
||||
struct timeval now, end_of_slew;
|
||||
double old_slew_freq, total_freq, corr_freq, duration;
|
||||
|
||||
/* Remove currently running timeout */
|
||||
if (slew_timer_running)
|
||||
SCH_RemoveTimeout(slew_timeout_id);
|
||||
|
||||
LCL_ReadRawTime(&now);
|
||||
|
||||
/* Adjust the offset register by achieved slew */
|
||||
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
|
||||
offset_register -= slew_freq * duration;
|
||||
|
||||
/* Estimate how long should the next slew take */
|
||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
} else {
|
||||
duration = correction_rate / fabs(offset_register);
|
||||
if (duration < MIN_SLEW_TIMEOUT)
|
||||
duration = MIN_SLEW_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Get frequency offset needed to slew the offset in the duration
|
||||
and clamp it to the allowed maximum */
|
||||
corr_freq = offset_register / duration;
|
||||
if (corr_freq < -max_corr_freq)
|
||||
corr_freq = -max_corr_freq;
|
||||
else if (corr_freq > max_corr_freq)
|
||||
corr_freq = max_corr_freq;
|
||||
|
||||
/* Get the new real frequency and clamp it */
|
||||
total_freq = base_freq + corr_freq * (1.0e6 - base_freq);
|
||||
if (total_freq > max_freq)
|
||||
total_freq = max_freq;
|
||||
else if (total_freq < -max_freq)
|
||||
total_freq = -max_freq;
|
||||
|
||||
/* Set the new frequency (the actual frequency returned by the call may be
|
||||
slightly different from the requested frequency due to rounding) */
|
||||
total_freq = (*drv_set_freq)(total_freq);
|
||||
|
||||
/* Compute the new slewing frequency, it's relative to the real frequency to
|
||||
make the calculation in offset_convert() cheaper */
|
||||
old_slew_freq = slew_freq;
|
||||
slew_freq = (total_freq - base_freq) / (1.0e6 - total_freq);
|
||||
|
||||
/* Compute the dispersion introduced by changing frequency and add it
|
||||
to all statistics held at higher levels in the system */
|
||||
slew_error = fabs((old_slew_freq - slew_freq) * max_freq_change_delay);
|
||||
if (slew_error >= MIN_OFFSET_CORRECTION)
|
||||
lcl_InvokeDispersionNotifyHandlers(slew_error);
|
||||
|
||||
/* Compute the duration of the slew and clamp it. If the slewing frequency
|
||||
is zero or has wrong sign (e.g. due to rounding in the frequency driver or
|
||||
when base_freq is larger than max_freq), use maximum timeout and try again
|
||||
on the next update. */
|
||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
|
||||
offset_register * slew_freq <= 0.0) {
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
} else {
|
||||
duration = offset_register / slew_freq;
|
||||
if (duration < MIN_SLEW_TIMEOUT)
|
||||
duration = MIN_SLEW_TIMEOUT;
|
||||
else if (duration > MAX_SLEW_TIMEOUT)
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Restart timer for the next update */
|
||||
UTI_AddDoubleToTimeval(&now, duration, &end_of_slew);
|
||||
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
|
||||
|
||||
slew_start = now;
|
||||
slew_timer_running = 1;
|
||||
|
||||
DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
|
||||
offset_register, correction_rate, base_freq, total_freq, slew_freq,
|
||||
duration, slew_error);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
handle_end_of_slew(void *anything)
|
||||
{
|
||||
slew_timer_running = 0;
|
||||
update_slew();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
read_frequency(void)
|
||||
{
|
||||
return base_freq;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
set_frequency(double freq_ppm)
|
||||
{
|
||||
base_freq = freq_ppm;
|
||||
update_slew();
|
||||
|
||||
return base_freq;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
accrue_offset(double offset, double corr_rate)
|
||||
{
|
||||
offset_register += offset;
|
||||
correction_rate = corr_rate;
|
||||
|
||||
update_slew();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Determine the correction to generate the cooked time for given raw time */
|
||||
|
||||
static void
|
||||
offset_convert(struct timeval *raw,
|
||||
double *corr, double *err)
|
||||
{
|
||||
double duration;
|
||||
|
||||
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
|
||||
|
||||
*corr = slew_freq * duration - offset_register;
|
||||
if (err)
|
||||
*err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Positive means currently fast of true time, i.e. jump backwards */
|
||||
|
||||
static void
|
||||
apply_step_offset(double offset)
|
||||
{
|
||||
struct timeval old_time, new_time;
|
||||
double err;
|
||||
|
||||
LCL_ReadRawTime(&old_time);
|
||||
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
|
||||
|
||||
if (settimeofday(&new_time, NULL) < 0) {
|
||||
LOG_FATAL(LOGF_SysGeneric, "settimeofday() failed");
|
||||
}
|
||||
|
||||
LCL_ReadRawTime(&old_time);
|
||||
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
|
||||
|
||||
lcl_InvokeDispersionNotifyHandlers(fabs(err));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
|
||||
lcl_ReadFrequencyDriver sys_read_freq,
|
||||
lcl_SetFrequencyDriver sys_set_freq,
|
||||
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
|
||||
lcl_SetLeapDriver sys_set_leap)
|
||||
{
|
||||
max_freq = max_set_freq_ppm;
|
||||
max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
|
||||
drv_read_freq = sys_read_freq;
|
||||
drv_set_freq = sys_set_freq;
|
||||
|
||||
base_freq = (*drv_read_freq)();
|
||||
slew_freq = 0.0;
|
||||
offset_register = 0.0;
|
||||
|
||||
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
|
||||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
||||
accrue_offset, sys_apply_step_offset ?
|
||||
sys_apply_step_offset : apply_step_offset,
|
||||
offset_convert, sys_set_leap);
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_step, NULL);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Generic_Finalise(void)
|
||||
{
|
||||
/* Must *NOT* leave a slew running - clock could drift way off
|
||||
if the daemon is not restarted */
|
||||
if (slew_timer_running) {
|
||||
SCH_RemoveTimeout(slew_timeout_id);
|
||||
slew_timer_running = 0;
|
||||
}
|
||||
|
||||
(*drv_set_freq)(base_freq);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
42
sys_generic.h
Normal file
42
sys_generic.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Header file for generic driver
|
||||
*/
|
||||
|
||||
#ifndef GOT_SYS_GENERIC_H
|
||||
#define GOT_SYS_GENERIC_H
|
||||
|
||||
#include "localp.h"
|
||||
|
||||
/* Register a completed driver that implements offset functions on top of
|
||||
provided frequency functions */
|
||||
extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
|
||||
lcl_ReadFrequencyDriver sys_read_freq,
|
||||
lcl_SetFrequencyDriver sys_set_freq,
|
||||
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
|
||||
lcl_SetLeapDriver sys_set_leap);
|
||||
|
||||
extern void SYS_Generic_Finalise(void);
|
||||
|
||||
#endif /* GOT_SYS_GENERIC_H */
|
||||
926
sys_linux.c
926
sys_linux.c
File diff suppressed because it is too large
Load Diff
@@ -281,7 +281,6 @@ SYS_NetBSD_Initialise(void)
|
||||
};
|
||||
|
||||
kvm_t *kt;
|
||||
FILE *fp;
|
||||
|
||||
kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
|
||||
if (!kt) {
|
||||
|
||||
@@ -409,11 +409,6 @@ set_dosynctodr(unsigned long on_off)
|
||||
kvm_close(kt);
|
||||
|
||||
assert(read_back == on_off);
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_SysSolaris, "Set value of dosynctodr to %d", on_off);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -364,11 +364,6 @@ setup_kernel(unsigned long on_off)
|
||||
}
|
||||
|
||||
kvm_close(kt);
|
||||
|
||||
#if 0
|
||||
LOG(LOGS_INFO, LOGF_SysSunOS, "Set value of _dosynctodr to %d", on_off);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
|
||||
#if HAS_STDINT_H
|
||||
#include <stdint.h>
|
||||
#elif defined(HAS_INTTYPES_H)
|
||||
#ifdef HAS_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#elif HAS_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
/* Tough */
|
||||
#endif
|
||||
|
||||
15
tempcomp.c
15
tempcomp.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2011
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2014
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -35,6 +35,9 @@
|
||||
#include "sched.h"
|
||||
#include "tempcomp.h"
|
||||
|
||||
/* Sanity limit (in ppm) */
|
||||
#define MAX_COMP 10.0
|
||||
|
||||
static SCH_TimeoutID timeout_id;
|
||||
|
||||
static LOG_FileID logfileid;
|
||||
@@ -54,8 +57,7 @@ read_timeout(void *arg)
|
||||
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) {
|
||||
if (fabs(comp) <= MAX_COMP) {
|
||||
comp = LCL_SetTempComp(comp);
|
||||
|
||||
if (logfileid != -1) {
|
||||
@@ -65,7 +67,14 @@ read_timeout(void *arg)
|
||||
LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
|
||||
UTI_TimeToLogForm(now.tv_sec), temp, comp);
|
||||
}
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_TempComp,
|
||||
"Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
|
||||
comp, MAX_COMP);
|
||||
}
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_TempComp, "Could not read temperature from %s",
|
||||
filename);
|
||||
}
|
||||
|
||||
if (f)
|
||||
|
||||
13
test/simulation/001-defaults
Executable file
13
test/simulation/001-defaults
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "default test settings"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
22
test/simulation/002-largenetwork
Executable file
22
test/simulation/002-largenetwork
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "large network"
|
||||
|
||||
time_rms_limit=5e-4
|
||||
|
||||
server_strata=3
|
||||
servers=4
|
||||
clients=5
|
||||
|
||||
client_start=2000
|
||||
min_sync_time=2100
|
||||
max_sync_time=2300
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
19
test/simulation/003-largefreqoffset
Executable file
19
test/simulation/003-largefreqoffset
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "large frequency offset"
|
||||
|
||||
max_sync_time=1000
|
||||
|
||||
for freq_offset in -5e-2 -5e-3 5e-3 5e-2; do
|
||||
# Adjust offset so it's close to 0 on first clock update
|
||||
time_offset=$(awk "BEGIN {print -($freq_offset * 130)}")
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
18
test/simulation/004-largetimeoffset
Executable file
18
test/simulation/004-largetimeoffset
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "large time offset"
|
||||
|
||||
min_sync_time=1300
|
||||
max_sync_time=1400
|
||||
|
||||
for time_offset in -1e2 1e2; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
46
test/simulation/005-externalstep
Executable file
46
test/simulation/005-externalstep
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "external time step"
|
||||
|
||||
min_sync_time=1500
|
||||
max_sync_time=1550
|
||||
|
||||
for step in -1e2 1e2; do
|
||||
# Make one step in 150th second
|
||||
client_step="(* $step (equal 0.1 (sum 1.0) 150))"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
min_sync_time=5120
|
||||
max_sync_time=6200
|
||||
client_conf="makestep 1 -1"
|
||||
|
||||
for step in -1e8 -1e5 1e5 1e8; do
|
||||
# Make one step in 5000th second
|
||||
client_step="(* $step (equal 0.1 (sum 1.0) 5000))"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
min_sync_time=$default_min_sync_time
|
||||
max_sync_time=$default_max_sync_time
|
||||
time_max_limit=2e4
|
||||
time_rms_limit=8e3
|
||||
|
||||
for step in -1e4 1e4; do
|
||||
# Make a step every 500 seconds
|
||||
client_step="(* $step (equal 0.1 (% (sum 1.0) 500) 0))"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
21
test/simulation/006-largejitter
Executable file
21
test/simulation/006-largejitter
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "large jitter"
|
||||
|
||||
time_offset=1e0
|
||||
jitter=1e-1
|
||||
|
||||
time_max_limit=5e-1
|
||||
freq_max_limit=2e-1
|
||||
time_rms_limit=1e-1
|
||||
freq_rms_limit=5e-3
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
20
test/simulation/007-largewander
Executable file
20
test/simulation/007-largewander
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "large wander"
|
||||
|
||||
wander=1e-7
|
||||
|
||||
time_max_limit=5e-3
|
||||
freq_max_limit=5e-3
|
||||
time_rms_limit=1e-3
|
||||
freq_rms_limit=1e-4
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
40
test/simulation/008-ntpera
Executable file
40
test/simulation/008-ntpera
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "NTP eras"
|
||||
|
||||
# Assume NTP_ERA_SPLIT is between years 1960 and 1990
|
||||
|
||||
# Set date to 500 seconds before NTP second overflows, this should
|
||||
# work correctly with both 32-bit and 64-bit time_t
|
||||
export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s')
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
# The following tests need 64-bit time_t
|
||||
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
|
||||
|
||||
for year in 1990 2090; do
|
||||
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
for year in 1950 2130; do
|
||||
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
# This check is expected to fail
|
||||
check_sync && test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
28
test/simulation/101-poll
Executable file
28
test/simulation/101-poll
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "minpoll/maxpoll options"
|
||||
|
||||
wander=0.0
|
||||
jitter=1e-6
|
||||
|
||||
time_max_limit=1e-5
|
||||
freq_max_limit=1e-5
|
||||
time_rms_limit=5e-6
|
||||
freq_rms_limit=5e-6
|
||||
client_conf="makestep 1e-2 1"
|
||||
|
||||
for poll in $(seq 2 14); do
|
||||
client_server_options="minpoll $poll maxpoll $poll"
|
||||
limit=$[2**$poll * 10]
|
||||
min_sync_time=$[2**$poll * 2]
|
||||
max_sync_time=$[2**$poll * 21 / 10 + 1]
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
23
test/simulation/102-iburst
Executable file
23
test/simulation/102-iburst
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "iburst option"
|
||||
|
||||
freq_offset=1e-4
|
||||
|
||||
client_conf="makestep 1e-2 1
|
||||
driftfile tmp/drift"
|
||||
client_server_options="iburst"
|
||||
|
||||
min_sync_time=4
|
||||
max_sync_time=6
|
||||
|
||||
echo "100 1.0" > tmp/drift
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
32
test/simulation/103-initstepslew
Executable file
32
test/simulation/103-initstepslew
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "initstepslew directive"
|
||||
|
||||
freq_offset=0.0
|
||||
wander=0.0
|
||||
time_rms_limit=1e-3
|
||||
limit=100
|
||||
|
||||
client_conf="initstepslew 5 192.168.123.1"
|
||||
|
||||
min_sync_time=6
|
||||
max_sync_time=35
|
||||
|
||||
for time_offset in -2.0 -0.2 0.2 2.0; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
min_sync_time=5
|
||||
max_sync_time=5
|
||||
|
||||
for time_offset in -1e8 -1e2 1e2 1e8; do
|
||||
run_test || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
23
test/simulation/104-driftfile
Executable file
23
test/simulation/104-driftfile
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "driftfile directive"
|
||||
|
||||
servers=0
|
||||
time_offset=0.0
|
||||
wander=0.0
|
||||
limit=10
|
||||
freq_max_limit=1e-9
|
||||
min_sync_time=1
|
||||
max_sync_time=1
|
||||
client_conf="driftfile tmp/drift"
|
||||
|
||||
for freq_offset in -5e-2 -5e-4 -5e-6 5e-6 5e-4 5e-2; do
|
||||
awk "BEGIN {printf \"%.9e 1\", 1e6 - 1 / (1 + $freq_offset) * 1e6}" > tmp/drift
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
42
test/simulation/105-ntpauth
Executable file
42
test/simulation/105-ntpauth
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "NTP authentication"
|
||||
|
||||
server_conf="keyfile tmp/keys"
|
||||
client_conf="keyfile tmp/keys"
|
||||
|
||||
cat > tmp/keys <<-EOF
|
||||
1 $(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
|
||||
2 ASCII:$(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
|
||||
3 MD5 ASCII:$(tr -c -d 'a-zA-Z' < /dev/urandom 2> /dev/null | head -c 24)
|
||||
4 MD5 HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | head -c 32)
|
||||
EOF
|
||||
|
||||
for key in 1 2 3 4; do
|
||||
client_server_options="key $key"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
server_conf=""
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
# This check must fail as the server doesn't know the key
|
||||
check_sync && test_fail
|
||||
check_packet_interval || test_fail
|
||||
|
||||
server_conf="keyfile tmp/keys"
|
||||
client_conf=""
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
# This check must fail as the client doesn't know the key
|
||||
check_sync && test_fail
|
||||
check_packet_interval || test_fail
|
||||
test_pass
|
||||
18
test/simulation/106-refclock
Executable file
18
test/simulation/106-refclock
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "SHM refclock"
|
||||
|
||||
servers=0
|
||||
limit=1000
|
||||
refclock_jitter=$jitter
|
||||
min_sync_time=45
|
||||
max_sync_time=70
|
||||
client_conf="refclock SHM 0"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
46
test/simulation/107-allowdeny
Executable file
46
test/simulation/107-allowdeny
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "allow/deny directives"
|
||||
|
||||
limit=500
|
||||
|
||||
# Note that start_client in clknetsim.bash always adds allow to the config
|
||||
|
||||
for server_conf in \
|
||||
"deny" \
|
||||
"deny all" \
|
||||
"deny 192.168.0.0/16" \
|
||||
"deny 192.168.123" \
|
||||
"deny 192.168.123.2" \
|
||||
"deny all
|
||||
allow 192.168.124.0/24"
|
||||
do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
# These checks are expected to fail
|
||||
check_source_selection && test_fail
|
||||
check_sync && test_fail
|
||||
done
|
||||
|
||||
for server_conf in \
|
||||
"deny all
|
||||
allow" \
|
||||
"deny all
|
||||
allow all" \
|
||||
"deny all
|
||||
allow 192.168.123" \
|
||||
"deny all
|
||||
allow 192.168.123/24" \
|
||||
"deny 192.168.124.0/24"
|
||||
do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
33
test/simulation/108-peer
Executable file
33
test/simulation/108-peer
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "NTP peers"
|
||||
|
||||
# Allow and drop packets to the server in 1000 second intervals, so only one
|
||||
# client has access to it and the other is forced to switch to the peer.
|
||||
base_delay=$(cat <<-EOF | tr -d '\n'
|
||||
(+ 1e-4
|
||||
(* -1
|
||||
(equal 0.1 from 2)
|
||||
(equal 0.1 to 1)
|
||||
(equal 0.1 (min (% time 2000) 1000) 1000))
|
||||
(* -1
|
||||
(equal 0.1 from 3)
|
||||
(equal 0.1 to 1)
|
||||
(equal 0.1 (max (% time 2000) 1000) 1000)))
|
||||
EOF
|
||||
)
|
||||
|
||||
clients=2
|
||||
peers=2
|
||||
max_sync_time=1000
|
||||
client_server_options="minpoll 6 maxpoll 6"
|
||||
client_peer_options="minpoll 6 maxpoll 6"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
41
test/simulation/109-makestep
Executable file
41
test/simulation/109-makestep
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "makestep directive"
|
||||
|
||||
client_conf="makestep 0 -1
|
||||
corrtimeratio 1e10"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
limit=200
|
||||
jitter=1e-5
|
||||
client_conf="makestep 2 1"
|
||||
|
||||
min_sync_time=130
|
||||
max_sync_time=150
|
||||
|
||||
for time_offset in -1.0 -0.1 0.1 1.0; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
min_sync_time=120
|
||||
max_sync_time=140
|
||||
|
||||
for time_offset in -1e8 -1e2 1e2 1e8; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
37
test/simulation/110-chronyc
Executable file
37
test/simulation/110-chronyc
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "chronyc"
|
||||
|
||||
chronyc_conf="tracking
|
||||
sources
|
||||
sourcestats"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
|
||||
check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\)
|
||||
Stratum : 2
|
||||
Ref time \(UTC\) : Fri Jan 1 00:1.:.. 2010
|
||||
System time : 0\.0000..... seconds (slow|fast) of NTP time
|
||||
Last offset : [+-]0\.000...... seconds
|
||||
RMS offset : 0\.000...... seconds
|
||||
Frequency : (99|100)\.... ppm fast
|
||||
Residual freq : [+-][0-9]\.... ppm
|
||||
Skew : [0-9]\.... ppm
|
||||
Root delay : 0\.000... seconds
|
||||
Root dispersion : 0\.000... seconds
|
||||
Update interval : [0-9]+\.. seconds
|
||||
Leap status : Normal
|
||||
210 Number of sources = 1
|
||||
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||
===============================================================================
|
||||
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
|
||||
210 Number of sources = 1
|
||||
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
||||
==============================================================================
|
||||
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s$" \
|
||||
|| test_fail
|
||||
|
||||
test_pass
|
||||
17
test/simulation/111-knownclient
Executable file
17
test/simulation/111-knownclient
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "reply to client configured as server"
|
||||
|
||||
server_conf="server 192.168.123.2
|
||||
acquisitionport 123"
|
||||
client_conf="acquisitionport 123"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_port || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
55
test/simulation/112-port
Executable file
55
test/simulation/112-port
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "port and acquisitionport directives"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
# This check is expected to fail
|
||||
check_packet_port && test_fail
|
||||
|
||||
client_conf="acquisitionport 123"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_port || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
client_conf=""
|
||||
for server_conf in \
|
||||
"port 0" \
|
||||
"acquisitionport 123
|
||||
port 0"
|
||||
do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_port || test_fail
|
||||
check_packet_interval || test_fail
|
||||
# These checks are expected to fail
|
||||
check_source_selection && test_fail
|
||||
check_sync && test_fail
|
||||
done
|
||||
|
||||
server_conf="port 124
|
||||
acquisitionport 123"
|
||||
client_server_options="port 124"
|
||||
for client_conf in \
|
||||
"acquisitionport 0" \
|
||||
"acquisitionport 123" \
|
||||
"acquisitionport 124"
|
||||
do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
# This check is expected to fail
|
||||
check_packet_port && test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
21
test/simulation/113-leapsecond
Executable file
21
test/simulation/113-leapsecond
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "leap second"
|
||||
|
||||
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
|
||||
|
||||
limit=$[4 * 24 * 3600]
|
||||
server_conf="refclock SHM 0 dpoll 10 poll 10
|
||||
leapsectz right/UTC"
|
||||
server_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
|
||||
client_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
|
||||
refclock_jitter=1e-9
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
22
test/simulation/114-presend
Executable file
22
test/simulation/114-presend
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
test_start "presend option"
|
||||
|
||||
client_server_options="presend 6"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
base_delay=5
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
24
test/simulation/115-cmdmontime
Executable file
24
test/simulation/115-cmdmontime
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
test_start "cmdmon timestamps"
|
||||
|
||||
# The following tests need 64-bit time_t
|
||||
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
|
||||
|
||||
limit=2
|
||||
client_server_options="noselect"
|
||||
client_conf="local stratum 1"
|
||||
chronyc_start="0.5"
|
||||
chronyc_conf="tracking"
|
||||
|
||||
for year in `seq 1850 100 2300`; do
|
||||
date="Jan 1 00:00:00 $year"
|
||||
export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s')
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
35
test/simulation/201-freqaccumulation
Executable file
35
test/simulation/201-freqaccumulation
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
. test.common
|
||||
|
||||
# Test fix in commit 60d0fa299307076143da94d36deb7b908fa9bdb7
|
||||
|
||||
test_start "frequency accumulation"
|
||||
|
||||
time_offset=100.0
|
||||
jitter=1e-6
|
||||
base_delay=1e-6
|
||||
wander=0.0
|
||||
|
||||
limit=180
|
||||
time_max_limit=1e-5
|
||||
freq_max_limit=1e-7
|
||||
time_rms_limit=1e-5
|
||||
freq_rms_limit=1e-7
|
||||
min_sync_time=120
|
||||
max_sync_time=140
|
||||
|
||||
client_server_options="minpoll 6 maxpoll 6"
|
||||
client_conf="driftfile tmp/drift
|
||||
makestep 1 1"
|
||||
|
||||
for freq_offset in -5e-2 -5e-4 5e-4 5e-2; do
|
||||
for drift in -1e+4 -1e+2 1e+2 1e+4; do
|
||||
echo "$drift 100000" > tmp/drift
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
done
|
||||
done
|
||||
|
||||
test_pass
|
||||
12
test/simulation/README
Normal file
12
test/simulation/README
Normal file
@@ -0,0 +1,12 @@
|
||||
This is a collection of simulation tests. They use clknetsim to simulate
|
||||
multiple systems connected in a network. It's available at
|
||||
|
||||
https://github.com/mlichvar/clknetsim
|
||||
|
||||
If this directory doesn't have a clknetsim subdirectory, a known working
|
||||
revision will be downloaded and compiled automatically.
|
||||
|
||||
Currently it runs only on Linux.
|
||||
|
||||
The tests are written in bash and they can be run directly. The ./run script
|
||||
runs all tests.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user