mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 17:55:07 -05:00
Compare commits
449 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c172268cfe | ||
|
|
94b014865c | ||
|
|
099aaf2cb1 | ||
|
|
4481a8b24f | ||
|
|
b626fe661e | ||
|
|
ba8fcd145d | ||
|
|
981d09de40 | ||
|
|
86a99bb257 | ||
|
|
3093a11cd0 | ||
|
|
058b788d38 | ||
|
|
66a42fa493 | ||
|
|
a85f63cc15 | ||
|
|
bbe1e69dcc | ||
|
|
1b52bba7b9 | ||
|
|
c5c80ef400 | ||
|
|
a78031ce0d | ||
|
|
34e9dd13ce | ||
|
|
6e52a9be7a | ||
|
|
69c6dffd63 | ||
|
|
2ddd0ae231 | ||
|
|
79db0b7eca | ||
|
|
2ebba7fbaa | ||
|
|
e392d1fde9 | ||
|
|
d7c93ec950 | ||
|
|
6af39d63aa | ||
|
|
cc8414b1b3 | ||
|
|
6b44055e3d | ||
|
|
9f9c6cc6ab | ||
|
|
f176193d35 | ||
|
|
e8bc41e862 | ||
|
|
91dbe3c6c2 | ||
|
|
3e876d4218 | ||
|
|
31b1f2e8a1 | ||
|
|
4169e94b1d | ||
|
|
948ecf8431 | ||
|
|
91f3f97ea7 | ||
|
|
65bb65b440 | ||
|
|
ea6e8d85a3 | ||
|
|
add932501f | ||
|
|
89390a738f | ||
|
|
ac4f6ab93b | ||
|
|
dbcb1b9b0b | ||
|
|
6375307798 | ||
|
|
fb78e60d26 | ||
|
|
b822c7164f | ||
|
|
aa295730a0 | ||
|
|
69d70703b2 | ||
|
|
b2b6ef00dc | ||
|
|
5dc86c236b | ||
|
|
2563dd9d29 | ||
|
|
a899e3df33 | ||
|
|
692cea49f8 | ||
|
|
bcedacaa3d | ||
|
|
be3c1b5243 | ||
|
|
e626ec6c37 | ||
|
|
49d52b547f | ||
|
|
74a546a9e7 | ||
|
|
d1777087c1 | ||
|
|
cf7b5363cd | ||
|
|
7f3183cc72 | ||
|
|
f1b8da085b | ||
|
|
09dfca49ec | ||
|
|
88e0ec07aa | ||
|
|
0adc8e8f92 | ||
|
|
5fc7674e36 | ||
|
|
018977044a | ||
|
|
cc49d8e6e6 | ||
|
|
933bd017b4 | ||
|
|
d558b33d85 | ||
|
|
9268bf2cff | ||
|
|
dbf2c22467 | ||
|
|
af4fe92095 | ||
|
|
e034a07be8 | ||
|
|
eb8c9ad601 | ||
|
|
6847536669 | ||
|
|
f5206db9b0 | ||
|
|
6ab2ed0da6 | ||
|
|
7352e470e1 | ||
|
|
5bc9c0d07a | ||
|
|
a2146e82ef | ||
|
|
6e10e6740c | ||
|
|
bfaa10f2b0 | ||
|
|
9f167a7997 | ||
|
|
6908163464 | ||
|
|
671daf06b8 | ||
|
|
b189a5386b | ||
|
|
7889d108c2 | ||
|
|
3cfa8ce9d3 | ||
|
|
570573fe28 | ||
|
|
62b1a11736 | ||
|
|
c00d517e12 | ||
|
|
001f3d5e27 | ||
|
|
6045023a49 | ||
|
|
bba29a0ee7 | ||
|
|
cffc856b50 | ||
|
|
419077e04b | ||
|
|
7db9d4acea | ||
|
|
8d5b86efe7 | ||
|
|
6cf16aea7b | ||
|
|
870545d3cb | ||
|
|
2a030c0d0c | ||
|
|
0b709ab1bc | ||
|
|
a1f2f17385 | ||
|
|
2240eefbd0 | ||
|
|
706d0c281a | ||
|
|
ca73e34f30 | ||
|
|
cca2ef4649 | ||
|
|
05d9edbf8f | ||
|
|
c5bdc52a59 | ||
|
|
74f0c0924a | ||
|
|
05492d1d23 | ||
|
|
eea343b93f | ||
|
|
afff06c88c | ||
|
|
c0717a27f6 | ||
|
|
159bd73f76 | ||
|
|
9931a9166b | ||
|
|
8aa4ae027b | ||
|
|
dcce79fdbe | ||
|
|
189aafde9d | ||
|
|
99e3045df4 | ||
|
|
c498c21fad | ||
|
|
6bef8aa0e9 | ||
|
|
108d112272 | ||
|
|
05078e4252 | ||
|
|
4ceb9e4cd0 | ||
|
|
a9f237a395 | ||
|
|
e7ca560c3d | ||
|
|
d9f86f6f70 | ||
|
|
879d936277 | ||
|
|
5bb2bf9361 | ||
|
|
a8167b7959 | ||
|
|
b33b682356 | ||
|
|
2c47602c33 | ||
|
|
59d1b41716 | ||
|
|
5b75d4afef | ||
|
|
e15c7cd236 | ||
|
|
9bc774d6af | ||
|
|
9b34556952 | ||
|
|
9a6369d8f1 | ||
|
|
49cdd6bf09 | ||
|
|
63fe34e890 | ||
|
|
85465afb62 | ||
|
|
339cb06a49 | ||
|
|
10150bfcab | ||
|
|
e50dc739d8 | ||
|
|
26e08abe71 | ||
|
|
7637faa0d0 | ||
|
|
8a57a28177 | ||
|
|
34db671b57 | ||
|
|
8b9021bf34 | ||
|
|
ce6b896948 | ||
|
|
2962fc6286 | ||
|
|
76bed76289 | ||
|
|
113f2ebec0 | ||
|
|
7c5bd948bb | ||
|
|
8cbc68f28f | ||
|
|
bf7aa52394 | ||
|
|
366345790d | ||
|
|
f881c153bf | ||
|
|
19f3ab2225 | ||
|
|
fd1e80802f | ||
|
|
4b7cb161a8 | ||
|
|
7848794222 | ||
|
|
94822d5156 | ||
|
|
e3f840aae9 | ||
|
|
5aae563277 | ||
|
|
02de782fa3 | ||
|
|
3f6df33feb | ||
|
|
a94f5fe007 | ||
|
|
63f0234748 | ||
|
|
47921c7c0c | ||
|
|
42a85f685e | ||
|
|
feca2399e4 | ||
|
|
d34e611ec8 | ||
|
|
02098ed830 | ||
|
|
aa4228bf1b | ||
|
|
b296441708 | ||
|
|
b827475378 | ||
|
|
78a6698ae1 | ||
|
|
e7b6feb34b | ||
|
|
84be834385 | ||
|
|
e83d808dfd | ||
|
|
35a68d5b59 | ||
|
|
3c593137b0 | ||
|
|
deaf0ffed3 | ||
|
|
af145e871e | ||
|
|
fbca570d0b | ||
|
|
448ef779c2 | ||
|
|
499a69e611 | ||
|
|
58c2915878 | ||
|
|
eda4b111d3 | ||
|
|
c6dd749687 | ||
|
|
d2a96f5fbc | ||
|
|
499f513d40 | ||
|
|
8b1f68b1b4 | ||
|
|
8e4c776900 | ||
|
|
d0eb9427c2 | ||
|
|
7d100b89fc | ||
|
|
a4bd7f1800 | ||
|
|
5308e0a25f | ||
|
|
da862158bf | ||
|
|
7b98443a13 | ||
|
|
4da9f74d24 | ||
|
|
e41042e258 | ||
|
|
5581466c63 | ||
|
|
e79a6c2116 | ||
|
|
666ece122e | ||
|
|
2c7ab98370 | ||
|
|
f0f18a02a7 | ||
|
|
c5d8af0285 | ||
|
|
0ce15a8472 | ||
|
|
da60629201 | ||
|
|
2343e7a89c | ||
|
|
45f27f4f5e | ||
|
|
0bc112f8b4 | ||
|
|
bfc2fa645c | ||
|
|
11111804fd | ||
|
|
87ec67247e | ||
|
|
0df8328ceb | ||
|
|
b563048ee2 | ||
|
|
e8096330be | ||
|
|
b1647dbcb7 | ||
|
|
4ddadd5622 | ||
|
|
3e854006c7 | ||
|
|
2c4c235147 | ||
|
|
6863e43269 | ||
|
|
de8708f331 | ||
|
|
d0b2486036 | ||
|
|
5384a93645 | ||
|
|
4bbc768652 | ||
|
|
fead915b45 | ||
|
|
5422e49026 | ||
|
|
77a1f27a1d | ||
|
|
b45d864f73 | ||
|
|
f35c81c871 | ||
|
|
a349b2803c | ||
|
|
f5d1b8fb74 | ||
|
|
a0fe71eef1 | ||
|
|
154b39cf7a | ||
|
|
6f54210db2 | ||
|
|
f6539449c5 | ||
|
|
b8d546a0d1 | ||
|
|
04e6474b75 | ||
|
|
eb51c500e8 | ||
|
|
6f8fba9a3f | ||
|
|
750afc30f2 | ||
|
|
e0e6ec0d84 | ||
|
|
c9f50fc686 | ||
|
|
83c26b458b | ||
|
|
b711873f45 | ||
|
|
c68ca40ce4 | ||
|
|
51fe80ad95 | ||
|
|
7ffee73524 | ||
|
|
f40b0024bd | ||
|
|
a06c9909a6 | ||
|
|
aee42fada8 | ||
|
|
3e93068c43 | ||
|
|
36291b707b | ||
|
|
6dad2c24bf | ||
|
|
27cbf20d23 | ||
|
|
5c571bbbe7 | ||
|
|
33d65c8614 | ||
|
|
d87db7cdb8 | ||
|
|
45fa4750da | ||
|
|
8472fd8133 | ||
|
|
5ab645e310 | ||
|
|
8ccda538d3 | ||
|
|
b06d74ab73 | ||
|
|
d0964ffa83 | ||
|
|
3d08815efb | ||
|
|
a83f0d3cdc | ||
|
|
702db726d3 | ||
|
|
ed5c43204b | ||
|
|
f91bdd604d | ||
|
|
3a1dbb1354 | ||
|
|
4b511143b8 | ||
|
|
93076e7e1c | ||
|
|
1c51feb3c5 | ||
|
|
c2773dbc2f | ||
|
|
4534db84c4 | ||
|
|
be8215e181 | ||
|
|
ae82bbbace | ||
|
|
2b6ea41062 | ||
|
|
d9f745fe70 | ||
|
|
9aac179367 | ||
|
|
b896bb5a78 | ||
|
|
64c2fd9888 | ||
|
|
2668a12e4e | ||
|
|
e1645966ec | ||
|
|
4f1fc1ee78 | ||
|
|
d70df3daab | ||
|
|
554b9b06de | ||
|
|
f734bd1a7c | ||
|
|
77fc5c42b9 | ||
|
|
ea85bc43e0 | ||
|
|
e8fb11c433 | ||
|
|
01a29c7a11 | ||
|
|
6ec3dc1650 | ||
|
|
0c54cf316d | ||
|
|
bd3fb49a1e | ||
|
|
f6e72a80e1 | ||
|
|
c2ab1426e5 | ||
|
|
fa2c59d78d | ||
|
|
16afa8eb50 | ||
|
|
992590e99c | ||
|
|
0baa35eade | ||
|
|
2e0870ee0c | ||
|
|
43cd119d6d | ||
|
|
62cd319a51 | ||
|
|
d0f789425b | ||
|
|
30e6549692 | ||
|
|
043c7d7c9f | ||
|
|
1c277a8850 | ||
|
|
ccb94ac5fb | ||
|
|
778fce4039 | ||
|
|
9983185d6d | ||
|
|
7bd1c02781 | ||
|
|
760285218f | ||
|
|
4fe0e6b7fd | ||
|
|
0773a1e630 | ||
|
|
4a24368763 | ||
|
|
577290c5bc | ||
|
|
854ff69f78 | ||
|
|
29b0ad894c | ||
|
|
cde0a20307 | ||
|
|
a768578a26 | ||
|
|
5d838729ef | ||
|
|
d6b763dc24 | ||
|
|
95adb52a45 | ||
|
|
707d9a3484 | ||
|
|
1872d4d195 | ||
|
|
17f32c266e | ||
|
|
6207655ab2 | ||
|
|
5e1e31ad5f | ||
|
|
13111c1dd8 | ||
|
|
85c84073c1 | ||
|
|
c2944d8727 | ||
|
|
e118b9b1e8 | ||
|
|
7fb7f95979 | ||
|
|
cc507bffae | ||
|
|
0dbfe020ad | ||
|
|
018a1c42b0 | ||
|
|
c5735ebfe9 | ||
|
|
db93180ce1 | ||
|
|
39da10d939 | ||
|
|
f2da253bc3 | ||
|
|
934d4047f1 | ||
|
|
b799cfd1c4 | ||
|
|
b712c100d7 | ||
|
|
c049bce007 | ||
|
|
46fad717e5 | ||
|
|
ae0c3bbbe8 | ||
|
|
f95d57e0d9 | ||
|
|
a1cbd4eb82 | ||
|
|
6cbeb107db | ||
|
|
3a5566c6c3 | ||
|
|
73c548ad01 | ||
|
|
82203e12c8 | ||
|
|
1ca099473f | ||
|
|
eceb8d9937 | ||
|
|
4ba92bb6d6 | ||
|
|
f31f68ae8e | ||
|
|
cff15f91d4 | ||
|
|
6b74917954 | ||
|
|
1bf2384a1f | ||
|
|
54a12779e2 | ||
|
|
e8b06fef9f | ||
|
|
653d70ec4e | ||
|
|
abb09418b1 | ||
|
|
c103bebd9f | ||
|
|
935d855b47 | ||
|
|
f8f9100a0d | ||
|
|
6de7b98e76 | ||
|
|
c390351c65 | ||
|
|
768bce799b | ||
|
|
d3a30142e5 | ||
|
|
3a635fc51f | ||
|
|
10078566da | ||
|
|
c44346096c | ||
|
|
0ff449e6a6 | ||
|
|
f3a16383b9 | ||
|
|
539ef3f770 | ||
|
|
f282856c72 | ||
|
|
6db8ec1ba2 | ||
|
|
5187c08c90 | ||
|
|
c8076ac10d | ||
|
|
362d155558 | ||
|
|
7b7eb0a6e5 | ||
|
|
d96f49f67d | ||
|
|
43ba5d2126 | ||
|
|
48f7598fed | ||
|
|
510b22e96b | ||
|
|
0a0aff14d8 | ||
|
|
e225ac68bc | ||
|
|
58060c40a5 | ||
|
|
2ac1b3d5c4 | ||
|
|
c174566982 | ||
|
|
60fca19d40 | ||
|
|
8bcb15b02f | ||
|
|
65c2cebcd5 | ||
|
|
2a51b45a43 | ||
|
|
5ac791665e | ||
|
|
a4e3f83611 | ||
|
|
8a837f9c2b | ||
|
|
da2d33e9a8 | ||
|
|
4b98dadae9 | ||
|
|
86acea5c46 | ||
|
|
a60fc73e7b | ||
|
|
50f99ec5f4 | ||
|
|
31b6a14444 | ||
|
|
9df4d36157 | ||
|
|
b70f0b674f | ||
|
|
510784077f | ||
|
|
9800e397fb | ||
|
|
1436d9961f | ||
|
|
98f5d05925 | ||
|
|
7a937c7652 | ||
|
|
b198d76676 | ||
|
|
97d4203354 | ||
|
|
beaaaad162 | ||
|
|
4e78975909 | ||
|
|
99147ed8f2 | ||
|
|
dec0d3bfc2 | ||
|
|
cd84c99e70 | ||
|
|
d5c507975c | ||
|
|
b4235abd36 | ||
|
|
1966085a97 | ||
|
|
e31e7af48f | ||
|
|
adb9123fc3 | ||
|
|
b0f7efd59e | ||
|
|
e28dfada8c | ||
|
|
ac0b28cce6 | ||
|
|
48b16ae66c | ||
|
|
061579ec28 | ||
|
|
f2f834e7e7 | ||
|
|
a7802e9a76 | ||
|
|
8f7ab95ff0 | ||
|
|
042c670747 | ||
|
|
cacbe9976f | ||
|
|
8efec1d640 | ||
|
|
c44d282f0b | ||
|
|
4432f29bd2 | ||
|
|
5fee3ed5e9 | ||
|
|
b76ea64263 | ||
|
|
ed904f08a4 | ||
|
|
96cc80ffc8 | ||
|
|
ab99373cfc | ||
|
|
dbfb49384b | ||
|
|
14bb9f29a3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,9 +1,12 @@
|
||||
.deps
|
||||
.vimrc
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.o
|
||||
*.swp
|
||||
*.dSYM
|
||||
*.DS_Store
|
||||
core.*
|
||||
tags
|
||||
/RELEASES
|
||||
/Makefile
|
||||
|
||||
22
Makefile.in
22
Makefile.in
@@ -36,8 +36,8 @@ DESTDIR=
|
||||
HASH_OBJ = @HASH_OBJ@
|
||||
|
||||
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
|
||||
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
|
||||
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ)
|
||||
reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \
|
||||
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
|
||||
|
||||
EXTRA_OBJS=@EXTRA_OBJECTS@
|
||||
|
||||
@@ -64,13 +64,14 @@ chronyc : $(CLI_OBJS)
|
||||
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
|
||||
|
||||
distclean : clean
|
||||
-rm -f .DS_Store
|
||||
-rm -f Makefile config.h config.log
|
||||
$(MAKE) -C doc distclean
|
||||
$(MAKE) -C test/unit distclean
|
||||
-rm -f .DS_Store
|
||||
-rm -f Makefile config.h config.log
|
||||
|
||||
clean :
|
||||
-rm -f *.o *.s chronyc chronyd core *~
|
||||
-rm -f *.o *.s chronyc chronyd core.* *~
|
||||
-rm -f *.gcda *.gcno
|
||||
-rm -rf .deps
|
||||
-rm -rf *.dSYM
|
||||
|
||||
@@ -109,9 +110,18 @@ install-docs :
|
||||
%.s : %.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
|
||||
|
||||
check : chronyd chronyc
|
||||
quickcheck : chronyd chronyc
|
||||
$(MAKE) -C test/unit check
|
||||
cd test/simulation && ./run
|
||||
cd test/system && ./run
|
||||
|
||||
check : chronyd chronyc
|
||||
$(MAKE) -C test/unit check
|
||||
cd test/simulation && ./run -i 20 -m 2
|
||||
cd test/system && ./run
|
||||
|
||||
print-chronyd-objects :
|
||||
@echo $(OBJS) $(EXTRA_OBJS)
|
||||
|
||||
Makefile : Makefile.in configure
|
||||
@echo
|
||||
|
||||
121
NEWS
121
NEWS
@@ -1,3 +1,121 @@
|
||||
New in version 3.5
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add support for more accurate reading of PHC on Linux 5.0
|
||||
* Add support for memory locking and real-time priority on FreeBSD,
|
||||
NetBSD, Solaris
|
||||
* Update seccomp filter to work on more architectures
|
||||
* Validate refclock driver options
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix bindaddress directive on FreeBSD
|
||||
* Fix transposition of hardware RX timestamp on Linux 4.13 and later
|
||||
* Fix building on non-glibc systems
|
||||
|
||||
New in version 3.4
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add filter option to server/pool/peer directive
|
||||
* Add minsamples and maxsamples options to hwtimestamp directive
|
||||
* Add support for faster frequency adjustments in Linux 4.19
|
||||
* Change default pidfile to /var/run/chrony/chronyd.pid to allow
|
||||
chronyd without root privileges to remove it on exit
|
||||
* Disable sub-second polling intervals for distant NTP sources
|
||||
* Extend range of supported sub-second polling intervals
|
||||
* Get/set IPv4 destination/source address of NTP packets on FreeBSD
|
||||
* Make burst options and command useful with short polling intervals
|
||||
* Modify auto_offline option to activate when sending request failed
|
||||
* Respond from interface that received NTP request if possible
|
||||
* Add onoffline command to switch between online and offline state
|
||||
according to current system network configuration
|
||||
* Improve example NetworkManager dispatcher script
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Avoid waiting in Linux getrandom system call
|
||||
* Fix PPS support on FreeBSD and NetBSD
|
||||
|
||||
New in version 3.3
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add burst option to server/pool directive
|
||||
* Add stratum and tai options to refclock directive
|
||||
* Add support for Nettle crypto library
|
||||
* Add workaround for missing kernel receive timestamps on Linux
|
||||
* Wait for late hardware transmit timestamps
|
||||
* Improve source selection with unreachable sources
|
||||
* Improve protection against replay attacks on symmetric mode
|
||||
* Allow PHC refclock to use socket in /var/run/chrony
|
||||
* Add shutdown command to stop chronyd
|
||||
* Simplify format of response to manual list command
|
||||
* Improve handling of unknown responses in chronyc
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Respond to NTPv1 client requests with zero mode
|
||||
* Fix -x option to not require CAP_SYS_TIME under non-root user
|
||||
* Fix acquisitionport directive to work with privilege separation
|
||||
* Fix handling of socket errors on Linux to avoid high CPU usage
|
||||
* Fix chronyc to not get stuck in infinite loop after clock step
|
||||
|
||||
New in version 3.2
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Improve stability with NTP sources and reference clocks
|
||||
* Improve stability with hardware timestamping
|
||||
* Improve support for NTP interleaved modes
|
||||
* Control frequency of system clock on macOS 10.13 and later
|
||||
* Set TAI-UTC offset of system clock with leapsectz directive
|
||||
* Minimise data in client requests to improve privacy
|
||||
* Allow transmit-only hardware timestamping
|
||||
* Add support for new timestamping options introduced in Linux 4.13
|
||||
* Add root delay, root dispersion and maximum error to tracking log
|
||||
* Add mindelay and asymmetry options to server/peer/pool directive
|
||||
* Add extpps option to PHC refclock to timestamp external PPS signal
|
||||
* Add pps option to refclock directive to treat any refclock as PPS
|
||||
* Add width option to refclock directive to filter wrong pulse edges
|
||||
* Add rxfilter option to hwtimestamp directive
|
||||
* Add -x option to disable control of system clock
|
||||
* Add -l option to log to specified file instead of syslog
|
||||
* Allow multiple command-line options to be specified together
|
||||
* Allow starting without root privileges with -Q option
|
||||
* Update seccomp filter for new glibc versions
|
||||
* Dump history on exit by default with dumpdir directive
|
||||
* Use hardening compiler options by default
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Don't drop PHC samples with low-resolution system clock
|
||||
* Ignore outliers in PHC tracking, RTC tracking, manual input
|
||||
* Increase polling interval when peer is not responding
|
||||
* Exit with error message when include directive fails
|
||||
* Don't allow slash after hostname in allow/deny directive/command
|
||||
* Try to connect to all addresses in chronyc before giving up
|
||||
|
||||
New in version 3.1
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add support for precise cross timestamping of PHC on Linux
|
||||
* Add minpoll, precision, nocrossts options to hwtimestamp directive
|
||||
* Add rawmeasurements option to log directive and modify measurements
|
||||
option to log only valid measurements from synchronised sources
|
||||
* Allow sub-second polling interval with NTP sources
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix time smoothing in interleaved mode
|
||||
|
||||
New in version 3.0
|
||||
==================
|
||||
|
||||
@@ -16,8 +134,7 @@ Enhancements
|
||||
* Add -t option to chronyd to exit after specified time
|
||||
* Add partial protection against replay attacks on symmetric mode
|
||||
* Don't reset polling interval when switching sources to online state
|
||||
* Enable NTP response rate limiting by default
|
||||
(1024 packets per second per IP address and 25% leak)
|
||||
* Allow rate limiting with very short intervals
|
||||
* Improve maximum server throughput on Linux and NetBSD
|
||||
* Remove dump files after start
|
||||
* Add tab-completion to chronyc with libedit/readline
|
||||
|
||||
127
README
127
README
@@ -4,7 +4,7 @@ What is chrony?
|
||||
===============
|
||||
|
||||
chrony is a versatile implementation of the Network Time Protocol (NTP).
|
||||
It can synchronize the system clock with NTP servers, reference clocks
|
||||
It can synchronise the system clock with NTP servers, reference clocks
|
||||
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
|
||||
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
|
||||
a time service to other computers in the network.
|
||||
@@ -16,7 +16,7 @@ and systems that do not run continuosly, or run on a virtual machine.
|
||||
|
||||
Typical accuracy between two machines synchronised over the Internet is
|
||||
within a few milliseconds; on a LAN, accuracy is typically in tens of
|
||||
microseconds. With hardware timestamping or a hardware reference clock
|
||||
microseconds. With hardware timestamping, or a hardware reference clock,
|
||||
sub-microsecond accuracy may be possible.
|
||||
|
||||
Two programs are included in chrony, chronyd is a daemon that can be
|
||||
@@ -37,20 +37,16 @@ How do I set it up?
|
||||
===================
|
||||
|
||||
The file INSTALL gives instructions. On supported systems the
|
||||
compilation process should be automatic.
|
||||
|
||||
You will need an ANSI C compiler -- gcc is recommended.
|
||||
|
||||
The manual (in texinfo and text formats) describes how to set the
|
||||
software up for the less straightforward cases.
|
||||
compilation process should be automatic. You will need a C compiler,
|
||||
e.g. gcc or clang.
|
||||
|
||||
What documentation is there?
|
||||
============================
|
||||
|
||||
A manual is supplied in Texinfo format (chrony.texi) and
|
||||
ready-formatted plain text (chrony.txt) in the distribution.
|
||||
The distribution includes manual pages and a document containing
|
||||
Frequently Asked Questions (FAQ).
|
||||
|
||||
There is also information available on the chrony web pages, accessible
|
||||
The documentation is also available on the chrony web pages, accessible
|
||||
through the URL
|
||||
|
||||
https://chrony.tuxfamily.org/
|
||||
@@ -112,124 +108,57 @@ The following people have provided patches and other major contributions
|
||||
to the program :
|
||||
|
||||
Lonnie Abelbeck <lonnie@abelbeck.com>
|
||||
Patch to add tab-completion to chronyc
|
||||
|
||||
Benny Lyne Amorsen <benny@amorsen.dk>
|
||||
Patch to add minstratum option
|
||||
|
||||
Andrew Bishop <amb@gedanken.demon.co.uk>
|
||||
Fixes for bugs in logging when in daemon mode
|
||||
Fixes for compiler warnings
|
||||
Robustness improvements for drift file
|
||||
Improve installation (directory checking etc)
|
||||
Entries in contrib directory
|
||||
Improvements to 'sources' and 'sourcestats' output from chronyc
|
||||
Improvements to documentation
|
||||
Investigation of required dosynctodr behaviour for various Solaris
|
||||
versions.
|
||||
|
||||
Vincent Blut <vincent.debian@free.fr>
|
||||
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
|
||||
Entries in contrib directory
|
||||
|
||||
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
|
||||
Leigh Brown <leigh@solinno.co.uk>
|
||||
Erik Bryer <ebryer@spots.ab.ca>
|
||||
Entries in contrib directory
|
||||
|
||||
Jonathan Cameron <jic23@cam.ac.uk>
|
||||
Bryan Christianson <bryan@whatroute.net>
|
||||
Support for macOS
|
||||
Support for privilege separation
|
||||
Entries in contrib directory
|
||||
|
||||
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
||||
Fix install rule in Makefile if chronyd file is in use.
|
||||
|
||||
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
||||
Paul Elliott <pelliott@io.com>
|
||||
DNSchrony (in contrib directory), a tool for handling NTP servers
|
||||
with variable IP addresses.
|
||||
|
||||
Stefan R. Filipek <srfilipek@gmail.com>
|
||||
Mike Fleetwood <mike@rockover.demon.co.uk>
|
||||
Fixes for compiler warnings
|
||||
|
||||
Alexander Gretencord <arutha@gmx.de>
|
||||
Changes to installation directory system to make it easier for
|
||||
package builders.
|
||||
|
||||
Andrew Griffiths <agriffit@redhat.com>
|
||||
Patch to add support for seccomp filter
|
||||
|
||||
Walter Haidinger <walter.haidinger@gmx.at>
|
||||
Providing me with login access to a Linux installation where v1.12
|
||||
wouldn't compile, so I could develop the fixes for v1.13. Also, for
|
||||
providing the disc space so I can keep an independent backup of the
|
||||
sources.
|
||||
|
||||
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
|
||||
Port to NetBSD
|
||||
|
||||
John Hasler <john@dhh.gt.org>
|
||||
Project and website at tuxfamily.org
|
||||
Changes to support 64 bit machines (i.e. those where
|
||||
sizeof(unsigned long) > 4)
|
||||
Bug fix to initstepslew directive
|
||||
Fix to remove potential buffer overrun errors.
|
||||
Memory locking and real-time scheduler support
|
||||
Fix fault where chronyd enters an endless loop
|
||||
|
||||
Tjalling Hattink <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
|
||||
|
||||
Jachym Holecek <jakym@volny.cz>
|
||||
Patch to make Linux real time clock work with devfs
|
||||
|
||||
Håkan Johansson <f96hajo@chalmers.se>
|
||||
Patch to avoid large values in sources and sourcestats output
|
||||
|
||||
Jim Knoble <jmknoble@pobox.com>
|
||||
Fixes for compiler warnings
|
||||
|
||||
Antti Jrvinen <costello@iki.fi>
|
||||
Advice on configuring for BSD/386
|
||||
|
||||
Eric Lammerts <eric@lammerts.org>
|
||||
Stefan Lucke <stefan@lucke.in-berlin.de>
|
||||
Victor Lum <viclum@vanu.com>
|
||||
Kevin Lyda <kevin@ie.suberic.net>
|
||||
Paul Menzel <paulepanter@users.sourceforge.net>
|
||||
Vladimir Michl <vladimir.michl@seznam.cz>
|
||||
Victor Moroz <vim@prv.adlum.ru>
|
||||
Patch to support Linux with HZ!=100
|
||||
|
||||
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
||||
acquisitionport support
|
||||
|
||||
Frank Otto <sandwichmacher@web.de>
|
||||
Handling arbitrary HZ values
|
||||
|
||||
Denny Page <dennypage@me.com>
|
||||
Chris Perl <cperl@janestreet.com>
|
||||
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
||||
Patch to add refresh command to chronyc
|
||||
|
||||
Andreas Piesk <apiesk@virbus.de>
|
||||
Patch to make chronyc use the readline library if available
|
||||
|
||||
Andreas Steinmetz <ast@domdv.de>
|
||||
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
|
||||
Timo Teras <timo.teras@iki.fi>
|
||||
Patch to reply correctly on multihomed hosts
|
||||
|
||||
Bill Unruh <unruh@physics.ubc.ca>
|
||||
Advice on statistics
|
||||
|
||||
Stephen Wadeley <swadeley@redhat.com>
|
||||
Improvements to man pages
|
||||
|
||||
Bernhard Weiss <lisnablagh@web.de>
|
||||
Wolfgang Weisselberg <weissel@netcologne.de>
|
||||
Entries in contrib directory
|
||||
|
||||
Bernhard M. Wiedemann <bwiedemann@suse.de>
|
||||
Joachim Wiedorn <ad_debian@joonet.de>
|
||||
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
|
||||
Many robustness and security improvements
|
||||
|
||||
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
|
||||
Providing me with information about the Linux 2.2 kernel
|
||||
functionality compared to 2.0.
|
||||
|
||||
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
|
||||
Doug Woodward <dougw@whistler.com>
|
||||
Advice on configuring for Solaris 2.8 on x86
|
||||
Thomas Zajic <zlatko@zlatko.fdns.net>
|
||||
|
||||
Many other people have contributed bug reports and suggestions. We are sorry
|
||||
we cannot identify all of you individually.
|
||||
|
||||
@@ -50,8 +50,11 @@ typedef struct {
|
||||
unsigned short port;
|
||||
} NTP_Remote_Address;
|
||||
|
||||
#define INVALID_IF_INDEX -1
|
||||
|
||||
typedef struct {
|
||||
IPAddr ip_addr;
|
||||
int if_index;
|
||||
int sock_fd;
|
||||
} NTP_Local_Address;
|
||||
|
||||
|
||||
7
array.c
7
array.c
@@ -66,8 +66,6 @@ ARR_DestroyInstance(ARR_Instance array)
|
||||
static void
|
||||
realloc_array(ARR_Instance array, unsigned int min_size)
|
||||
{
|
||||
size_t data_size;
|
||||
|
||||
assert(min_size <= 2 * min_size);
|
||||
if (array->allocated >= min_size && array->allocated <= 2 * min_size)
|
||||
return;
|
||||
@@ -79,10 +77,7 @@ realloc_array(ARR_Instance array, unsigned int min_size)
|
||||
array->allocated = min_size;
|
||||
}
|
||||
|
||||
data_size = (size_t)array->elem_size * array->allocated;
|
||||
assert(data_size / array->elem_size == array->allocated);
|
||||
|
||||
array->data = Realloc(array->data, data_size);
|
||||
array->data = Realloc2(array->data, array->allocated, array->elem_size);
|
||||
}
|
||||
|
||||
void *
|
||||
|
||||
22
candm.h
22
candm.h
@@ -97,7 +97,11 @@
|
||||
#define REQ_NTP_DATA 57
|
||||
#define REQ_ADD_SERVER2 58
|
||||
#define REQ_ADD_PEER2 59
|
||||
#define N_REQUEST_TYPES 60
|
||||
#define REQ_ADD_SERVER3 60
|
||||
#define REQ_ADD_PEER3 61
|
||||
#define REQ_SHUTDOWN 62
|
||||
#define REQ_ONOFFLINE 63
|
||||
#define N_REQUEST_TYPES 64
|
||||
|
||||
/* Structure used to exchange timespecs independent of time_t size */
|
||||
typedef struct {
|
||||
@@ -250,6 +254,7 @@ typedef struct {
|
||||
#define REQ_ADDSRC_TRUST 0x20
|
||||
#define REQ_ADDSRC_REQUIRE 0x40
|
||||
#define REQ_ADDSRC_INTERLEAVED 0x80
|
||||
#define REQ_ADDSRC_BURST 0x100
|
||||
|
||||
typedef struct {
|
||||
IPAddr ip_addr;
|
||||
@@ -267,8 +272,12 @@ typedef struct {
|
||||
Float max_delay;
|
||||
Float max_delay_ratio;
|
||||
Float max_delay_dev_ratio;
|
||||
Float min_delay;
|
||||
Float asymmetry;
|
||||
Float offset;
|
||||
uint32_t flags;
|
||||
int32_t filter_length;
|
||||
uint32_t reserved[3];
|
||||
int32_t EOR;
|
||||
} REQ_NTP_Source;
|
||||
|
||||
@@ -362,8 +371,9 @@ typedef struct {
|
||||
domain socket.
|
||||
|
||||
Version 6 (no authentication) : changed format of client accesses by index
|
||||
(using new request/reply types), new fields and flags in NTP source request
|
||||
and report, new commands: ntpdata, refresh, serverstats
|
||||
(using new request/reply types) and manual timestamp, added new fields and
|
||||
flags to NTP source request and report, made length of manual list constant,
|
||||
added new commands: ntpdata, refresh, serverstats, shutdown
|
||||
*/
|
||||
|
||||
#define PROTO_VERSION_NUMBER 6
|
||||
@@ -461,7 +471,9 @@ typedef struct {
|
||||
#define RPY_SERVER_STATS 14
|
||||
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
|
||||
#define RPY_NTP_DATA 16
|
||||
#define N_REPLY_TYPES 17
|
||||
#define RPY_MANUAL_TIMESTAMP2 17
|
||||
#define RPY_MANUAL_LIST2 18
|
||||
#define N_REPLY_TYPES 19
|
||||
|
||||
/* Status codes */
|
||||
#define STT_SUCCESS 0
|
||||
@@ -569,7 +581,7 @@ typedef struct {
|
||||
} RPY_Rtc;
|
||||
|
||||
typedef struct {
|
||||
uint32_t centiseconds;
|
||||
Float offset;
|
||||
Float dfreq_ppm;
|
||||
Float new_afreq_ppm;
|
||||
int32_t EOR;
|
||||
|
||||
438
client.c
438
client.c
@@ -3,7 +3,8 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016
|
||||
* Copyright (C) Lonnie Abelbeck 2016, 2018
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -80,8 +81,7 @@ int log_debug_enabled = 0;
|
||||
|
||||
void LOG_Message(LOG_Severity severity,
|
||||
#if DEBUG > 0
|
||||
LOG_Facility facility, int line_number,
|
||||
const char *filename, const char *function_name,
|
||||
int line_number, const char *filename, const char *function_name,
|
||||
#endif
|
||||
const char *format, ...)
|
||||
{
|
||||
@@ -111,7 +111,7 @@ read_line(void)
|
||||
char *cmd;
|
||||
|
||||
rl_attempted_completion_function = command_name_completion;
|
||||
rl_basic_word_break_characters = "\t\n\r";
|
||||
rl_basic_word_break_characters = " \t\n\r";
|
||||
|
||||
/* save line only if not empty */
|
||||
cmd = readline(prompt);
|
||||
@@ -167,18 +167,18 @@ get_sockaddrs(const char *hostnames, int port)
|
||||
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
|
||||
if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >=
|
||||
sizeof (addr->un.sun_path))
|
||||
LOG_FATAL(LOGF_Client, "Unix socket path too long");
|
||||
LOG_FATAL("Unix socket path too long");
|
||||
addr->un.sun_family = AF_UNIX;
|
||||
} else {
|
||||
if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not get IP address for %s", hostname);
|
||||
break;
|
||||
DEBUG_LOG("Could not get IP address for %s", hostname);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
|
||||
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
|
||||
UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
|
||||
DEBUG_LOG(LOGF_Client, "Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
|
||||
DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ prepare_socket(union sockaddr_all *addr)
|
||||
sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock_fd < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not create socket : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not create socket : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ prepare_socket(union sockaddr_all *addr)
|
||||
dir = UTI_PathToDir(addr->un.sun_path);
|
||||
if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path),
|
||||
"%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path))
|
||||
LOG_FATAL(LOGF_Client, "Unix socket path too long");
|
||||
LOG_FATAL("Unix socket path too long");
|
||||
Free(dir);
|
||||
|
||||
sa_un.sun_family = AF_UNIX;
|
||||
@@ -236,19 +236,19 @@ prepare_socket(union sockaddr_all *addr)
|
||||
|
||||
/* Bind the socket to the path */
|
||||
if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not bind socket : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not bind socket : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow server without root privileges to send replies to our socket */
|
||||
if (chmod(sa_un.sun_path, 0666) < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not change socket permissions : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not change socket permissions : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(sock_fd, &addr->sa, addr_len) < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not connect socket : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not connect socket : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ close_io(void)
|
||||
|
||||
/* Remove our Unix domain socket */
|
||||
if (getsockname(sock_fd, &addr.sa, &addr_len) < 0)
|
||||
LOG_FATAL(LOGF_Client, "getsockname() failed : %s", strerror(errno));
|
||||
LOG_FATAL("getsockname() failed : %s", strerror(errno));
|
||||
if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) &&
|
||||
addr.sa.sa_family == AF_UNIX)
|
||||
unlink(addr.un.sun_path);
|
||||
@@ -294,8 +294,7 @@ open_io(void)
|
||||
/* Find an address for which a socket can be opened and connected */
|
||||
for (; address_index < ARR_GetSize(sockaddrs); address_index++) {
|
||||
addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index);
|
||||
DEBUG_LOG(LOGF_Client, "Opening connection to %s",
|
||||
UTI_SockaddrToString(&addr->sa));
|
||||
DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa));
|
||||
|
||||
if (prepare_socket(addr))
|
||||
return 1;
|
||||
@@ -316,7 +315,7 @@ bits_to_mask(int bits, int family, IPAddr *mask)
|
||||
mask->family = family;
|
||||
switch (family) {
|
||||
case IPADDR_INET4:
|
||||
if (bits < 0)
|
||||
if (bits > 32 || bits < 0)
|
||||
bits = 32;
|
||||
if (bits > 0) {
|
||||
mask->addr.in4 = -1;
|
||||
@@ -372,13 +371,13 @@ read_mask_address(char *line, IPAddr *mask, IPAddr *address)
|
||||
bits_to_mask(-1, address->family, mask);
|
||||
return 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
|
||||
LOG(LOGS_ERR, "Could not get address for hostname");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for mask/address");
|
||||
LOG(LOGS_ERR, "Invalid syntax for mask/address");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -427,6 +426,14 @@ process_cmd_online(CMD_Request *msg, char *line)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
process_cmd_onoffline(CMD_Request *msg, char *line)
|
||||
{
|
||||
msg->command = htons(REQ_ONOFFLINE);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
read_address_integer(char *line, IPAddr *address, int *value)
|
||||
{
|
||||
@@ -437,11 +444,11 @@ read_address_integer(char *line, IPAddr *address, int *value)
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (sscanf(line, "%d", value) != 1) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value");
|
||||
LOG(LOGS_ERR, "Invalid syntax for address value");
|
||||
ok = 0;
|
||||
} else {
|
||||
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
|
||||
LOG(LOGS_ERR, "Could not get address for hostname");
|
||||
ok = 0;
|
||||
} else {
|
||||
ok = 1;
|
||||
@@ -465,11 +472,11 @@ read_address_double(char *line, IPAddr *address, double *value)
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (sscanf(line, "%lf", value) != 1) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address value");
|
||||
LOG(LOGS_ERR, "Invalid syntax for address value");
|
||||
ok = 0;
|
||||
} else {
|
||||
if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
|
||||
LOG(LOGS_ERR, "Could not get address for hostname");
|
||||
ok = 0;
|
||||
} else {
|
||||
ok = 1;
|
||||
@@ -702,7 +709,7 @@ process_cmd_burst(CMD_Request *msg, char *line)
|
||||
CPS_SplitWord(s2);
|
||||
|
||||
if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for burst command");
|
||||
LOG(LOGS_ERR, "Invalid syntax for burst command");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -734,7 +741,7 @@ process_cmd_local(CMD_Request *msg, char *line)
|
||||
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
|
||||
on_off = 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for local command");
|
||||
LOG(LOGS_ERR, "Invalid syntax for local command");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -763,7 +770,7 @@ process_cmd_manual(CMD_Request *msg, const char *line)
|
||||
} else if (!strcmp(p, "reset")) {
|
||||
msg->data.manual.option = htonl(2);
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for manual command");
|
||||
LOG(LOGS_ERR, "Invalid syntax for manual command");
|
||||
return 0;
|
||||
}
|
||||
msg->command = htons(REQ_MANUAL);
|
||||
@@ -797,8 +804,8 @@ parse_allow_deny(CMD_Request *msg, char *line)
|
||||
(n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) {
|
||||
|
||||
/* Try to parse as the name of a machine */
|
||||
if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
|
||||
if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, "Could not read address");
|
||||
return 0;
|
||||
} else {
|
||||
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
|
||||
@@ -851,7 +858,7 @@ parse_allow_deny(CMD_Request *msg, char *line)
|
||||
if (n == 1) {
|
||||
msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_Client, "Warning: badly formatted subnet size, using %d",
|
||||
LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d",
|
||||
(int)ntohl(msg->data.allow_deny.subnet_bits));
|
||||
}
|
||||
}
|
||||
@@ -986,7 +993,7 @@ process_cmd_accheck(CMD_Request *msg, char *line)
|
||||
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
|
||||
return 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
|
||||
LOG(LOGS_ERR, "Could not read address");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1002,7 +1009,7 @@ process_cmd_cmdaccheck(CMD_Request *msg, char *line)
|
||||
UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip);
|
||||
return 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not read address");
|
||||
LOG(LOGS_ERR, "Could not read address");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1072,17 +1079,17 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
|
||||
status = CPS_ParseNTPSourceAdd(line, &data);
|
||||
switch (status) {
|
||||
case 0:
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for add command");
|
||||
LOG(LOGS_ERR, "Invalid syntax for add command");
|
||||
break;
|
||||
default:
|
||||
if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid host/IP address");
|
||||
LOG(LOGS_ERR, "Invalid host/IP address");
|
||||
break;
|
||||
}
|
||||
|
||||
opt_name = NULL;
|
||||
if (opt_name) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "%s can't be set in chronyc", opt_name);
|
||||
LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1102,16 +1109,22 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
|
||||
msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio);
|
||||
msg->data.ntp_source.max_delay_dev_ratio =
|
||||
UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio);
|
||||
msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay);
|
||||
msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
|
||||
msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
|
||||
msg->data.ntp_source.flags = htonl(
|
||||
(data.params.online ? REQ_ADDSRC_ONLINE : 0) |
|
||||
(data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
|
||||
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
|
||||
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
|
||||
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
|
||||
(data.params.burst ? REQ_ADDSRC_BURST : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
|
||||
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
|
||||
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
|
||||
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
|
||||
|
||||
result = 1;
|
||||
|
||||
break;
|
||||
@@ -1125,7 +1138,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
|
||||
static int
|
||||
process_cmd_add_server(CMD_Request *msg, char *line)
|
||||
{
|
||||
msg->command = htons(REQ_ADD_SERVER2);
|
||||
msg->command = htons(REQ_ADD_SERVER3);
|
||||
return process_cmd_add_server_or_peer(msg, line);
|
||||
}
|
||||
|
||||
@@ -1134,7 +1147,7 @@ process_cmd_add_server(CMD_Request *msg, char *line)
|
||||
static int
|
||||
process_cmd_add_peer(CMD_Request *msg, char *line)
|
||||
{
|
||||
msg->command = htons(REQ_ADD_PEER2);
|
||||
msg->command = htons(REQ_ADD_PEER3);
|
||||
return process_cmd_add_server_or_peer(msg, line);
|
||||
}
|
||||
|
||||
@@ -1152,11 +1165,11 @@ process_cmd_delete(CMD_Request *msg, char *line)
|
||||
CPS_SplitWord(line);
|
||||
|
||||
if (!*hostname) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for address");
|
||||
LOG(LOGS_ERR, "Invalid syntax for address");
|
||||
ok = 0;
|
||||
} else {
|
||||
if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
|
||||
LOG(LOGS_ERR, "Could not get address for hostname");
|
||||
ok = 0;
|
||||
} else {
|
||||
UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr);
|
||||
@@ -1204,6 +1217,8 @@ give_help(void)
|
||||
"minstratum <address> <stratum>\0Modify minimum stratum\0"
|
||||
"offline [<mask>/<address>]\0Set sources in subnet to offline status\0"
|
||||
"online [<mask>/<address>]\0Set sources in subnet to online status\0"
|
||||
"onoffline\0Set all sources to online or offline status\0"
|
||||
"\0according to network configuration\0"
|
||||
"polltarget <address> <target>\0Modify poll target\0"
|
||||
"refresh\0Refresh IP addresses\0"
|
||||
"\0\0"
|
||||
@@ -1241,6 +1256,8 @@ give_help(void)
|
||||
"Other daemon commands:\0\0"
|
||||
"cyclelogs\0Close and re-open log files\0"
|
||||
"dump\0Dump all measurements to save files\0"
|
||||
"rekey\0Re-read keys from key file\0"
|
||||
"shutdown\0Stop daemon\0"
|
||||
"\0\0"
|
||||
"Client commands:\0\0"
|
||||
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
|
||||
@@ -1264,30 +1281,52 @@ give_help(void)
|
||||
/* Tab-completion when editline/readline is available */
|
||||
|
||||
#ifdef FEAT_READLINE
|
||||
|
||||
enum {
|
||||
TAB_COMPLETE_BASE_CMDS,
|
||||
TAB_COMPLETE_ADD_OPTS,
|
||||
TAB_COMPLETE_MANUAL_OPTS,
|
||||
TAB_COMPLETE_SOURCES_OPTS,
|
||||
TAB_COMPLETE_SOURCESTATS_OPTS,
|
||||
TAB_COMPLETE_MAX_INDEX
|
||||
};
|
||||
|
||||
static int tab_complete_index;
|
||||
|
||||
static char *
|
||||
command_name_generator(const char *text, int state)
|
||||
{
|
||||
const char *name, *names[] = {
|
||||
"accheck", "activity", "add peer", "add server", "allow", "burst",
|
||||
const char *name, **names[TAB_COMPLETE_MAX_INDEX];
|
||||
const char *base_commands[] = {
|
||||
"accheck", "activity", "add", "allow", "burst",
|
||||
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
|
||||
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
|
||||
"manual on", "manual off", "manual delete", "manual list", "manual reset",
|
||||
"maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
|
||||
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
|
||||
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
|
||||
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
|
||||
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
|
||||
"retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
|
||||
"sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
|
||||
"tracking", "trimrtc", "waitsync", "writertc",
|
||||
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
|
||||
"smoothtime", "sources", "sourcestats",
|
||||
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
|
||||
NULL
|
||||
};
|
||||
const char *add_options[] = { "peer", "server", NULL };
|
||||
const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
|
||||
const char *sources_options[] = { "-v", NULL };
|
||||
const char *sourcestats_options[] = { "-v", NULL };
|
||||
static int list_index, len;
|
||||
|
||||
names[TAB_COMPLETE_BASE_CMDS] = base_commands;
|
||||
names[TAB_COMPLETE_ADD_OPTS] = add_options;
|
||||
names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
|
||||
names[TAB_COMPLETE_SOURCES_OPTS] = sources_options;
|
||||
names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options;
|
||||
|
||||
if (!state) {
|
||||
list_index = 0;
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while ((name = names[list_index++])) {
|
||||
while ((name = names[tab_complete_index][list_index++])) {
|
||||
if (strncmp(name, text, len) == 0) {
|
||||
return strdup(name);
|
||||
}
|
||||
@@ -1301,7 +1340,25 @@ command_name_generator(const char *text, int state)
|
||||
static char **
|
||||
command_name_completion(const char *text, int start, int end)
|
||||
{
|
||||
char first[32];
|
||||
|
||||
snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
if (!strcmp(first, "add ")) {
|
||||
tab_complete_index = TAB_COMPLETE_ADD_OPTS;
|
||||
} else if (!strcmp(first, "manual ")) {
|
||||
tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
|
||||
} else if (!strcmp(first, "sources ")) {
|
||||
tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
|
||||
} else if (!strcmp(first, "sourcestats ")) {
|
||||
tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
|
||||
} else if (first[0] == '\0') {
|
||||
tab_complete_index = TAB_COMPLETE_BASE_CMDS;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rl_completion_matches(text, command_name_generator);
|
||||
}
|
||||
#endif
|
||||
@@ -1320,18 +1377,16 @@ static int proto_version = PROTO_VERSION_NUMBER;
|
||||
static int
|
||||
submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
{
|
||||
int bad_length, bad_sequence, bad_header;
|
||||
int select_status;
|
||||
int recv_status;
|
||||
int read_length;
|
||||
int expected_length;
|
||||
int command_length;
|
||||
int padding_length;
|
||||
struct timespec ts_now, ts_start;
|
||||
struct timeval tv;
|
||||
int n_attempts, new_attempt;
|
||||
double timeout;
|
||||
fd_set rdfd, wrfd, exfd;
|
||||
fd_set rdfd;
|
||||
|
||||
request->pkt_type = PKT_TYPE_CMD_REQUEST;
|
||||
request->res1 = 0;
|
||||
@@ -1343,15 +1398,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
new_attempt = 1;
|
||||
|
||||
do {
|
||||
if (gettimeofday(&tv, NULL))
|
||||
return 0;
|
||||
|
||||
if (new_attempt) {
|
||||
new_attempt = 0;
|
||||
|
||||
if (n_attempts > max_retries)
|
||||
return 0;
|
||||
|
||||
if (gettimeofday(&tv, NULL))
|
||||
return 0;
|
||||
|
||||
UTI_TimevalToTimespec(&tv, &ts_start);
|
||||
|
||||
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
|
||||
@@ -1367,22 +1422,18 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
memset(((char *)request) + command_length - padding_length, 0, padding_length);
|
||||
|
||||
if (sock_fd < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "No socket to send request");
|
||||
DEBUG_LOG("No socket to send request");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (send(sock_fd, (void *)request, command_length, 0) < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "Could not send %d bytes : %s",
|
||||
command_length, strerror(errno));
|
||||
DEBUG_LOG("Could not send %d bytes : %s", command_length, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
|
||||
DEBUG_LOG("Sent %d bytes", command_length);
|
||||
}
|
||||
|
||||
if (gettimeofday(&tv, NULL))
|
||||
return 0;
|
||||
|
||||
UTI_TimevalToTimespec(&tv, &ts_now);
|
||||
|
||||
/* Check if the clock wasn't stepped back */
|
||||
@@ -1391,22 +1442,27 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
|
||||
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
|
||||
UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
|
||||
DEBUG_LOG("Timeout %f seconds", timeout);
|
||||
|
||||
/* Avoid calling select() with an invalid timeout */
|
||||
if (timeout <= 0.0) {
|
||||
new_attempt = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
UTI_DoubleToTimeval(timeout, &tv);
|
||||
DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout);
|
||||
|
||||
FD_ZERO(&rdfd);
|
||||
FD_ZERO(&wrfd);
|
||||
FD_ZERO(&exfd);
|
||||
|
||||
FD_SET(sock_fd, &rdfd);
|
||||
|
||||
if (quit)
|
||||
return 0;
|
||||
|
||||
select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv);
|
||||
select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
|
||||
|
||||
if (select_status < 0) {
|
||||
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno));
|
||||
DEBUG_LOG("select failed : %s", strerror(errno));
|
||||
return 0;
|
||||
} else if (select_status == 0) {
|
||||
/* Timeout must have elapsed, try a resend? */
|
||||
new_attempt = 1;
|
||||
@@ -1416,40 +1472,24 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
if (recv_status < 0) {
|
||||
/* If we get connrefused here, it suggests the sendto is
|
||||
going to a dead port */
|
||||
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not receive : %s", strerror(errno));
|
||||
new_attempt = 1;
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status);
|
||||
DEBUG_LOG("Received %d bytes", recv_status);
|
||||
|
||||
read_length = recv_status;
|
||||
if (read_length >= offsetof(CMD_Reply, data)) {
|
||||
expected_length = PKL_ReplyLength(reply);
|
||||
} else {
|
||||
expected_length = 0;
|
||||
}
|
||||
|
||||
bad_length = (read_length < expected_length ||
|
||||
expected_length < offsetof(CMD_Reply, data));
|
||||
|
||||
if (!bad_length) {
|
||||
bad_sequence = reply->sequence != request->sequence;
|
||||
} else {
|
||||
bad_sequence = 0;
|
||||
}
|
||||
|
||||
if (bad_length || bad_sequence) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bad_header = ((reply->version != proto_version &&
|
||||
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
|
||||
ntohs(reply->status) == STT_BADPKTVERSION)) ||
|
||||
(reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
|
||||
(reply->res1 != 0) ||
|
||||
(reply->res2 != 0) ||
|
||||
(reply->command != request->command));
|
||||
|
||||
if (bad_header) {
|
||||
/* Check if the header is valid */
|
||||
if (read_length < offsetof(CMD_Reply, data) ||
|
||||
(reply->version != proto_version &&
|
||||
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
|
||||
ntohs(reply->status) == STT_BADPKTVERSION)) ||
|
||||
reply->pkt_type != PKT_TYPE_CMD_REPLY ||
|
||||
reply->res1 != 0 ||
|
||||
reply->res2 != 0 ||
|
||||
reply->command != request->command ||
|
||||
reply->sequence != request->sequence) {
|
||||
DEBUG_LOG("Invalid reply");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1468,8 +1508,17 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
|
||||
#error unknown compatibility with PROTO_VERSION - 1
|
||||
#endif
|
||||
|
||||
/* Check that the packet contains all data it is supposed to have.
|
||||
Unknown responses will always pass this test as their expected
|
||||
length is zero. */
|
||||
if (read_length < PKL_ReplyLength(reply)) {
|
||||
DEBUG_LOG("Reply too short");
|
||||
new_attempt = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Good packet received, print out results */
|
||||
DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d",
|
||||
DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
|
||||
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
|
||||
break;
|
||||
}
|
||||
@@ -1574,6 +1623,9 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure an unknown response was not requested */
|
||||
assert(PKL_ReplyLength(reply));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1587,17 +1639,17 @@ print_seconds(unsigned long s)
|
||||
if (s == (uint32_t)-1) {
|
||||
printf(" -");
|
||||
} else if (s < 1200) {
|
||||
printf("%4ld", s);
|
||||
printf("%4lu", s);
|
||||
} else if (s < 36000) {
|
||||
printf("%3ldm", s / 60);
|
||||
printf("%3lum", s / 60);
|
||||
} else if (s < 345600) {
|
||||
printf("%3ldh", s / 3600);
|
||||
printf("%3luh", s / 3600);
|
||||
} else {
|
||||
d = s / 86400;
|
||||
if (d > 999) {
|
||||
printf("%3ldy", d / 365);
|
||||
printf("%3luy", d / 365);
|
||||
} else {
|
||||
printf("%3ldd", d);
|
||||
printf("%3lud", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2059,7 +2111,8 @@ process_cmd_sources(char *line)
|
||||
|
||||
mode = ntohs(reply.data.source_data.mode);
|
||||
UTI_IPNetworkToHost(&reply.data.source_data.ip_addr, &ip_addr);
|
||||
format_name(name, sizeof (name), 25, mode == RPY_SD_MD_REF,
|
||||
format_name(name, sizeof (name), 25,
|
||||
mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
|
||||
ip_addr.addr.in4, &ip_addr);
|
||||
|
||||
switch (mode) {
|
||||
@@ -2214,8 +2267,8 @@ process_cmd_tracking(char *line)
|
||||
"Frequency : %.3F\n"
|
||||
"Residual freq : %+.3f ppm\n"
|
||||
"Skew : %.3f ppm\n"
|
||||
"Root delay : %.6f seconds\n"
|
||||
"Root dispersion : %.6f seconds\n"
|
||||
"Root delay : %.9f seconds\n"
|
||||
"Root dispersion : %.9f seconds\n"
|
||||
"Update interval : %.1f seconds\n"
|
||||
"Leap status : %L\n",
|
||||
(unsigned long)ref_id, name,
|
||||
@@ -2262,7 +2315,7 @@ process_cmd_ntpdata(char *line)
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
if (specified_addr) {
|
||||
if (DNS_Name2IPAddress(line, &remote_addr, 1) != DNS_Success) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Could not get address for hostname");
|
||||
LOG(LOGS_ERR, "Could not get address for hostname");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
@@ -2301,7 +2354,7 @@ process_cmd_ntpdata(char *line)
|
||||
"Precision : %d (%.9f seconds)\n"
|
||||
"Root delay : %.6f seconds\n"
|
||||
"Root dispersion : %.6f seconds\n"
|
||||
"Reference ID : %R\n"
|
||||
"Reference ID : %R (%s)\n"
|
||||
"Reference time : %T\n"
|
||||
"Offset : %+.9f seconds\n"
|
||||
"Peer delay : %.9f seconds\n"
|
||||
@@ -2325,7 +2378,10 @@ process_cmd_ntpdata(char *line)
|
||||
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
|
||||
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
|
||||
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
|
||||
(unsigned long)ntohl(reply.data.ntp_data.ref_id), &ref_time,
|
||||
(unsigned long)ntohl(reply.data.ntp_data.ref_id),
|
||||
reply.data.ntp_data.stratum <= 1 ?
|
||||
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
|
||||
&ref_time,
|
||||
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
|
||||
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_delay),
|
||||
UTI_FloatNetworkToHost(reply.data.ntp_data.peer_dispersion),
|
||||
@@ -2416,7 +2472,7 @@ process_cmd_smoothtime(CMD_Request *msg, const char *line)
|
||||
} else if (!strcmp(line, "activate")) {
|
||||
msg->data.smoothtime.option = htonl(REQ_SMOOTHTIME_ACTIVATE);
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid syntax for smoothtime command");
|
||||
LOG(LOGS_ERR, "Invalid syntax for smoothtime command");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2533,7 +2589,7 @@ process_cmd_manual_list(const char *line)
|
||||
struct timespec when;
|
||||
|
||||
request.command = htons(REQ_MANUAL_LIST);
|
||||
if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
|
||||
if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
|
||||
return 0;
|
||||
|
||||
n_samples = ntohl(reply.data.manual_list.n_samples);
|
||||
@@ -2541,7 +2597,7 @@ process_cmd_manual_list(const char *line)
|
||||
|
||||
print_header("# Date Time(UTC) Slewed Original Residual");
|
||||
|
||||
for (i = 0; i < n_samples; i++) {
|
||||
for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
|
||||
sample = &reply.data.manual_list.samples[i];
|
||||
UTI_TimespecNetworkToHost(&sample->when, &when);
|
||||
|
||||
@@ -2564,7 +2620,7 @@ process_cmd_manual_delete(CMD_Request *msg, const char *line)
|
||||
int index;
|
||||
|
||||
if (sscanf(line, "%d", &index) != 1) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for manual delete command");
|
||||
LOG(LOGS_ERR, "Bad syntax for manual delete command");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2582,7 +2638,6 @@ process_cmd_settime(char *line)
|
||||
time_t now, new_time;
|
||||
CMD_Request request;
|
||||
CMD_Reply reply;
|
||||
long offset_cs;
|
||||
double dfreq_ppm, new_afreq_ppm;
|
||||
double offset;
|
||||
|
||||
@@ -2596,9 +2651,8 @@ process_cmd_settime(char *line)
|
||||
ts.tv_nsec = 0;
|
||||
UTI_TimespecHostToNetwork(&ts, &request.data.settime.ts);
|
||||
request.command = htons(REQ_SETTIME);
|
||||
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP, 1)) {
|
||||
offset_cs = ntohl(reply.data.manual_timestamp.centiseconds);
|
||||
offset = 0.01 * (double)(int32_t)offset_cs;
|
||||
if (request_reply(&request, &reply, RPY_MANUAL_TIMESTAMP2, 1)) {
|
||||
offset = UTI_FloatNetworkToHost(reply.data.manual_timestamp.offset);
|
||||
dfreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.dfreq_ppm);
|
||||
new_afreq_ppm = UTI_FloatNetworkToHost(reply.data.manual_timestamp.new_afreq_ppm);
|
||||
printf("Clock was %.2f seconds fast. Frequency change = %.2fppm, new frequency = %.2fppm\n",
|
||||
@@ -2627,7 +2681,7 @@ process_cmd_makestep(CMD_Request *msg, char *line)
|
||||
|
||||
if (*line) {
|
||||
if (sscanf(line, "%lf %d", &threshold, &limit) != 2) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Bad syntax for makestep command");
|
||||
LOG(LOGS_ERR, "Bad syntax for makestep command");
|
||||
return 0;
|
||||
}
|
||||
msg->command = htons(REQ_MODIFY_MAKESTEP);
|
||||
@@ -2704,6 +2758,14 @@ process_cmd_refresh(CMD_Request *msg, char *line)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
process_cmd_shutdown(CMD_Request *msg, char *line)
|
||||
{
|
||||
msg->command = htons(REQ_SHUTDOWN);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
process_cmd_waitsync(char *line)
|
||||
{
|
||||
@@ -2720,7 +2782,8 @@ process_cmd_waitsync(char *line)
|
||||
max_skew_ppm = 0.0;
|
||||
interval = 10.0;
|
||||
|
||||
sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval);
|
||||
if (sscanf(line, "%d %lf %lf %lf", &max_tries, &max_correction, &max_skew_ppm, &interval))
|
||||
;
|
||||
|
||||
/* Don't allow shorter interval than 0.1 seconds */
|
||||
if (interval < 0.1)
|
||||
@@ -2775,7 +2838,7 @@ process_cmd_dns(const char *line)
|
||||
} else if (!strcmp(line, "+n")) {
|
||||
no_dns = 0;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Unrecognized dns command");
|
||||
LOG(LOGS_ERR, "Unrecognized dns command");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -2790,7 +2853,7 @@ process_cmd_timeout(const char *line)
|
||||
|
||||
timeout = atoi(line);
|
||||
if (timeout < 100) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Timeout %d is too short", timeout);
|
||||
LOG(LOGS_ERR, "Timeout %d is too short", timeout);
|
||||
return 0;
|
||||
}
|
||||
initial_timeout = timeout;
|
||||
@@ -2805,8 +2868,8 @@ process_cmd_retries(const char *line)
|
||||
int retries;
|
||||
|
||||
retries = atoi(line);
|
||||
if (retries < 0) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Invalid maximum number of retries");
|
||||
if (retries < 0 || retries > 30) {
|
||||
LOG(LOGS_ERR, "Invalid maximum number of retries");
|
||||
return 0;
|
||||
}
|
||||
max_retries = retries;
|
||||
@@ -2828,11 +2891,12 @@ process_cmd_keygen(char *line)
|
||||
snprintf(hash_name, sizeof (hash_name), "MD5");
|
||||
#endif
|
||||
|
||||
sscanf(line, "%u %16s %d", &id, hash_name, &bits);
|
||||
if (sscanf(line, "%u %16s %u", &id, hash_name, &bits))
|
||||
;
|
||||
|
||||
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
|
||||
if (HSH_GetHashId(hash_name) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Unknown hash function %s", hash_name);
|
||||
LOG(LOGS_ERR, "Unknown hash function %s", hash_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2971,6 +3035,8 @@ process_line(char *line)
|
||||
do_normal_submit = process_cmd_offline(&tx_message, line);
|
||||
} else if (!strcmp(command, "online")) {
|
||||
do_normal_submit = process_cmd_online(&tx_message, line);
|
||||
} else if (!strcmp(command, "onoffline")) {
|
||||
process_cmd_onoffline(&tx_message, line);
|
||||
} else if (!strcmp(command, "polltarget")) {
|
||||
do_normal_submit = process_cmd_polltarget(&tx_message, line);
|
||||
} else if (!strcmp(command, "quit")) {
|
||||
@@ -2997,6 +3063,8 @@ process_line(char *line)
|
||||
} else if (!strcmp(command, "settime")) {
|
||||
do_normal_submit = 0;
|
||||
ret = process_cmd_settime(line);
|
||||
} else if (!strcmp(command, "shutdown")) {
|
||||
process_cmd_shutdown(&tx_message, line);
|
||||
} else if (!strcmp(command, "smoothing")) {
|
||||
do_normal_submit = 0;
|
||||
ret = process_cmd_smoothing(line);
|
||||
@@ -3024,11 +3092,11 @@ process_line(char *line)
|
||||
} else if (!strcmp(command, "authhash") ||
|
||||
!strcmp(command, "password")) {
|
||||
/* Warn, but don't return error to not break scripts */
|
||||
LOG(LOGS_WARN, LOGF_Client, "Authentication is no longer supported");
|
||||
LOG(LOGS_WARN, "Authentication is no longer supported");
|
||||
do_normal_submit = 0;
|
||||
ret = 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Client, "Unrecognized command");
|
||||
LOG(LOGS_ERR, "Unrecognized command");
|
||||
do_normal_submit = 0;
|
||||
}
|
||||
|
||||
@@ -3045,7 +3113,7 @@ process_line(char *line)
|
||||
static int
|
||||
process_args(int argc, char **argv, int multi)
|
||||
{
|
||||
int total_length, i, ret;
|
||||
int total_length, i, ret = 0;
|
||||
char *line;
|
||||
|
||||
total_length = 0;
|
||||
@@ -3091,7 +3159,7 @@ static void
|
||||
display_gpl(void)
|
||||
{
|
||||
printf("chrony version %s\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2016 Richard P. Curnow and others\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2018 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",
|
||||
@@ -3100,54 +3168,80 @@ display_gpl(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_help(const char *progname)
|
||||
{
|
||||
printf("Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_version(void)
|
||||
{
|
||||
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *line;
|
||||
const char *progname = argv[0];
|
||||
const char *hostnames = NULL;
|
||||
int ret = 1, multi = 0, family = IPADDR_UNSPEC;
|
||||
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
|
||||
int port = DEFAULT_CANDM_PORT;
|
||||
|
||||
/* Parse command line options */
|
||||
while (++argv, --argc) {
|
||||
if (!strcmp(*argv, "-h")) {
|
||||
++argv, --argc;
|
||||
if (*argv) {
|
||||
hostnames = *argv;
|
||||
}
|
||||
} else if (!strcmp(*argv, "-p")) {
|
||||
++argv, --argc;
|
||||
if (*argv) {
|
||||
port = atoi(*argv);
|
||||
}
|
||||
} else if (!strcmp(*argv, "-f")) {
|
||||
++argv, --argc;
|
||||
/* For compatibility */
|
||||
} else if (!strcmp(*argv, "-a")) {
|
||||
/* For compatibility */
|
||||
} else if (!strcmp(*argv, "-c")) {
|
||||
csv_mode = 1;
|
||||
} else if (!strcmp(*argv, "-d")) {
|
||||
log_debug_enabled = 1;
|
||||
} else if (!strcmp(*argv, "-m")) {
|
||||
multi = 1;
|
||||
} else if (!strcmp(*argv, "-n")) {
|
||||
no_dns = 1;
|
||||
} else if (!strcmp(*argv, "-4")) {
|
||||
family = IPADDR_INET4;
|
||||
} else if (!strcmp(*argv, "-6")) {
|
||||
family = IPADDR_INET6;
|
||||
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
|
||||
printf("chronyc (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYC_FEATURES);
|
||||
/* Parse (undocumented) long command-line options */
|
||||
for (optind = 1; optind < argc; optind++) {
|
||||
if (!strcmp("--help", argv[optind])) {
|
||||
print_help(progname);
|
||||
return 0;
|
||||
} else if (!strncmp(*argv, "-", 1)) {
|
||||
LOG(LOGS_ERR, LOGF_Client,
|
||||
"Usage: %s [-h HOST] [-p PORT] [-n] [-c] [-d] [-4|-6] [-m] [COMMAND]",
|
||||
progname);
|
||||
return 1;
|
||||
} else {
|
||||
break; /* And process remainder of line as a command */
|
||||
} else if (!strcmp("--version", argv[optind])) {
|
||||
print_version();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
optind = 1;
|
||||
|
||||
/* Parse short command-line options */
|
||||
while ((opt = getopt(argc, argv, "+46acdf:h:mnp:v")) != -1) {
|
||||
switch (opt) {
|
||||
case '4':
|
||||
case '6':
|
||||
family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
|
||||
break;
|
||||
case 'a':
|
||||
case 'f':
|
||||
/* For compatibility only */
|
||||
break;
|
||||
case 'c':
|
||||
csv_mode = 1;
|
||||
break;
|
||||
case 'd':
|
||||
log_debug_enabled = 1;
|
||||
break;
|
||||
case 'h':
|
||||
hostnames = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
multi = 1;
|
||||
break;
|
||||
case 'n':
|
||||
no_dns = 1;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
print_version();
|
||||
return 0;
|
||||
default:
|
||||
print_help(progname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3155,7 +3249,7 @@ main(int argc, char **argv)
|
||||
on_terminal = 1;
|
||||
}
|
||||
|
||||
if (on_terminal && (argc == 0)) {
|
||||
if (on_terminal && optind == argc) {
|
||||
display_gpl();
|
||||
}
|
||||
|
||||
@@ -3165,15 +3259,15 @@ main(int argc, char **argv)
|
||||
hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
|
||||
}
|
||||
|
||||
UTI_SetQuitSignalsHandler(signal_handler);
|
||||
UTI_SetQuitSignalsHandler(signal_handler, 0);
|
||||
|
||||
sockaddrs = get_sockaddrs(hostnames, port);
|
||||
|
||||
if (!open_io())
|
||||
LOG_FATAL(LOGF_Client, "Could not open connection to daemon");
|
||||
LOG_FATAL("Could not open connection to daemon");
|
||||
|
||||
if (argc > 0) {
|
||||
ret = process_args(argc, argv, multi);
|
||||
if (optind < argc) {
|
||||
ret = process_args(argc - optind, argv + optind, multi);
|
||||
} else {
|
||||
do {
|
||||
line = read_line();
|
||||
|
||||
55
clientlog.c
55
clientlog.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -119,7 +119,7 @@ static int cmd_token_shift;
|
||||
prevent an attacker sending requests with spoofed source address
|
||||
from blocking responses to the address completely. */
|
||||
|
||||
#define MIN_LEAK_RATE 0
|
||||
#define MIN_LEAK_RATE 1
|
||||
#define MAX_LEAK_RATE 4
|
||||
|
||||
static int ntp_leak_rate;
|
||||
@@ -128,6 +128,9 @@ static int cmd_leak_rate;
|
||||
/* Flag indicating whether the last response was dropped */
|
||||
#define FLAG_NTP_DROPPED 0x1
|
||||
|
||||
/* NTP limit interval in log2 */
|
||||
static int ntp_limit_interval;
|
||||
|
||||
/* Flag indicating whether facility is turned on or not */
|
||||
static int active;
|
||||
|
||||
@@ -294,7 +297,7 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
|
||||
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
|
||||
*max_tokens = *tokens_per_packet * burst;
|
||||
|
||||
DEBUG_LOG(LOGF_ClientLog, "Tokens max %d packet %d shift %d",
|
||||
DEBUG_LOG("Tokens max %d packet %d shift %d",
|
||||
*max_tokens, *tokens_per_packet, *token_shift);
|
||||
}
|
||||
|
||||
@@ -305,19 +308,31 @@ CLG_Initialise(void)
|
||||
{
|
||||
int interval, burst, leak_rate;
|
||||
|
||||
CNF_GetNTPRateLimit(&interval, &burst, &leak_rate);
|
||||
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
|
||||
&ntp_token_shift);
|
||||
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
|
||||
max_ntp_tokens = max_cmd_tokens = 0;
|
||||
ntp_tokens_per_packet = cmd_tokens_per_packet = 0;
|
||||
ntp_token_shift = cmd_token_shift = 0;
|
||||
ntp_leak_rate = cmd_leak_rate = 0;
|
||||
ntp_limit_interval = MIN_LIMIT_INTERVAL;
|
||||
|
||||
CNF_GetCommandRateLimit(&interval, &burst, &leak_rate);
|
||||
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
|
||||
&cmd_token_shift);
|
||||
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
|
||||
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
|
||||
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
|
||||
&ntp_token_shift);
|
||||
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
|
||||
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
|
||||
}
|
||||
|
||||
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
|
||||
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
|
||||
&cmd_token_shift);
|
||||
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
|
||||
}
|
||||
|
||||
active = !CNF_GetNoClientLog();
|
||||
if (!active)
|
||||
if (!active) {
|
||||
if (ntp_leak_rate || cmd_leak_rate)
|
||||
LOG_FATAL("ratelimit cannot be used with noclientlog");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate the maximum number of slots that can be allocated in the
|
||||
configured memory limit. Take into account expanding of the hash
|
||||
@@ -460,7 +475,7 @@ CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
|
||||
record->flags & FLAG_NTP_DROPPED ?
|
||||
&record->ntp_timeout_rate : &record->ntp_rate);
|
||||
|
||||
DEBUG_LOG(LOGF_ClientLog, "NTP hits %"PRIu32" rate %d trate %d tokens %d",
|
||||
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
|
||||
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
|
||||
record->ntp_tokens);
|
||||
|
||||
@@ -484,7 +499,7 @@ CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
|
||||
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
|
||||
&record->cmd_rate);
|
||||
|
||||
DEBUG_LOG(LOGF_ClientLog, "Cmd hits %"PRIu32" rate %d tokens %d",
|
||||
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
|
||||
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
|
||||
|
||||
return get_index(record);
|
||||
@@ -520,7 +535,7 @@ CLG_LimitNTPResponseRate(int index)
|
||||
Record *record;
|
||||
int drop;
|
||||
|
||||
if (!ntp_leak_rate)
|
||||
if (!ntp_tokens_per_packet)
|
||||
return 0;
|
||||
|
||||
record = ARR_GetElement(records, index);
|
||||
@@ -561,7 +576,7 @@ CLG_LimitCommandResponseRate(int index)
|
||||
{
|
||||
Record *record;
|
||||
|
||||
if (!cmd_leak_rate)
|
||||
if (!cmd_tokens_per_packet)
|
||||
return 0;
|
||||
|
||||
record = ARR_GetElement(records, index);
|
||||
@@ -596,6 +611,14 @@ void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_GetNtpMinPoll(void)
|
||||
{
|
||||
return ntp_limit_interval;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CLG_GetNumberOfIndices(void)
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
|
||||
extern int CLG_LimitNTPResponseRate(int index);
|
||||
extern int CLG_LimitCommandResponseRate(int index);
|
||||
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
|
||||
extern int CLG_GetNtpMinPoll(void);
|
||||
|
||||
/* And some reporting functions, for use by chronyc. */
|
||||
|
||||
|
||||
115
cmdmon.c
115
cmdmon.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -136,6 +136,10 @@ static const char permissions[] = {
|
||||
PERMIT_AUTH, /* NTP_DATA */
|
||||
PERMIT_AUTH, /* ADD_SERVER2 */
|
||||
PERMIT_AUTH, /* ADD_PEER2 */
|
||||
PERMIT_AUTH, /* ADD_SERVER3 */
|
||||
PERMIT_AUTH, /* ADD_PEER3 */
|
||||
PERMIT_AUTH, /* SHUTDOWN */
|
||||
PERMIT_AUTH, /* ONOFFLINE */
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
@@ -161,7 +165,7 @@ prepare_socket(int family, int port_number)
|
||||
|
||||
sock_fd = socket(family, SOCK_DGRAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not open %s command socket : %s",
|
||||
LOG(LOGS_ERR, "Could not open %s command socket : %s",
|
||||
UTI_SockaddrFamilyToString(family), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
@@ -172,14 +176,14 @@ prepare_socket(int family, int port_number)
|
||||
if (family != AF_UNIX) {
|
||||
/* Allow reuse of port number */
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
|
||||
LOG(LOGS_ERR, "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");
|
||||
LOG(LOGS_ERR, "Could not set free bind socket option");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -188,7 +192,7 @@ prepare_socket(int family, int port_number)
|
||||
#ifdef IPV6_V6ONLY
|
||||
/* Receive IPv6 packets only */
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not request IPV6_V6ONLY socket option");
|
||||
LOG(LOGS_ERR, "Could not request IPV6_V6ONLY socket option");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -230,7 +234,7 @@ prepare_socket(int family, int port_number)
|
||||
my_addr.un.sun_family = family;
|
||||
if (snprintf(my_addr.un.sun_path, sizeof (my_addr.un.sun_path), "%s",
|
||||
CNF_GetBindCommandPath()) >= sizeof (my_addr.un.sun_path))
|
||||
LOG_FATAL(LOGF_CmdMon, "Unix socket path too long");
|
||||
LOG_FATAL("Unix socket path too long");
|
||||
unlink(my_addr.un.sun_path);
|
||||
break;
|
||||
default:
|
||||
@@ -238,7 +242,7 @@ prepare_socket(int family, int port_number)
|
||||
}
|
||||
|
||||
if (bind(sock_fd, &my_addr.sa, my_addr_len) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_CmdMon, "Could not bind %s command socket : %s",
|
||||
LOG(LOGS_ERR, "Could not bind %s command socket : %s",
|
||||
UTI_SockaddrFamilyToString(family), strerror(errno));
|
||||
close(sock_fd);
|
||||
return -1;
|
||||
@@ -276,7 +280,6 @@ do_size_checks(void)
|
||||
for (i = 1; i < N_REPLY_TYPES; i++) {
|
||||
reply.reply = htons(i);
|
||||
reply.status = STT_SUCCESS;
|
||||
reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
|
||||
reply_length = PKL_ReplyLength(&reply);
|
||||
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
|
||||
reply_length > sizeof (CMD_Reply))
|
||||
@@ -315,7 +318,7 @@ CAM_Initialise(int family)
|
||||
&& sock_fd6 < 0
|
||||
#endif
|
||||
) {
|
||||
LOG_FATAL(LOGF_CmdMon, "Could not open any command socket");
|
||||
LOG_FATAL("Could not open any command socket");
|
||||
}
|
||||
|
||||
access_auth_table = ADF_CreateTable();
|
||||
@@ -396,12 +399,12 @@ transmit_reply(CMD_Reply *msg, union sockaddr_all *where_to)
|
||||
&where_to->sa, addrlen);
|
||||
|
||||
if (status < 0) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Could not send to %s fd %d : %s",
|
||||
DEBUG_LOG("Could not send to %s fd %d : %s",
|
||||
UTI_SockaddrToString(&where_to->sa), sock_fd, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_CmdMon, "Sent %d bytes to %s fd %d", status,
|
||||
DEBUG_LOG("Sent %d bytes to %s fd %d", status,
|
||||
UTI_SockaddrToString(&where_to->sa), sock_fd);
|
||||
}
|
||||
|
||||
@@ -422,7 +425,7 @@ handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
|
||||
UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
|
||||
UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
|
||||
if (!NSR_TakeSourcesOnline(&mask, &address))
|
||||
if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
|
||||
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||
}
|
||||
|
||||
@@ -435,12 +438,24 @@ handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
|
||||
UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
|
||||
UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
|
||||
if (!NSR_TakeSourcesOffline(&mask, &address))
|
||||
if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
|
||||
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
{
|
||||
IPAddr address, mask;
|
||||
|
||||
address.family = mask.family = IPADDR_UNSPEC;
|
||||
if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
|
||||
;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
{
|
||||
@@ -568,14 +583,13 @@ static void
|
||||
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
{
|
||||
struct timespec ts;
|
||||
long offset_cs;
|
||||
double dfreq_ppm, new_afreq_ppm;
|
||||
double offset, dfreq_ppm, new_afreq_ppm;
|
||||
UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
|
||||
if (!MNL_IsEnabled()) {
|
||||
tx_message->status = htons(STT_NOTENABLED);
|
||||
} else if (MNL_AcceptTimestamp(&ts, &offset_cs, &dfreq_ppm, &new_afreq_ppm)) {
|
||||
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP);
|
||||
tx_message->data.manual_timestamp.centiseconds = htonl((int32_t)offset_cs);
|
||||
} else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) {
|
||||
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2);
|
||||
tx_message->data.manual_timestamp.offset = UTI_FloatHostToNetwork(offset);
|
||||
tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
|
||||
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
|
||||
} else {
|
||||
@@ -786,18 +800,23 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
|
||||
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
|
||||
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
|
||||
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
|
||||
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
|
||||
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
|
||||
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
|
||||
params.max_delay_ratio =
|
||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
||||
params.max_delay_dev_ratio =
|
||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
||||
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
||||
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
||||
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
||||
|
||||
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
|
||||
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
|
||||
SRC_ONLINE : SRC_OFFLINE;
|
||||
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
|
||||
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
|
||||
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
|
||||
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
|
||||
params.sel_options =
|
||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
||||
@@ -874,7 +893,7 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
double dfreq;
|
||||
dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq);
|
||||
LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6);
|
||||
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta freq of %.3fppm", dfreq);
|
||||
LOG(LOGS_INFO, "Accumulated delta freq of %.3fppm", dfreq);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -887,7 +906,7 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
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);
|
||||
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
|
||||
LCL_AccumulateOffset(doffset, 0.0);
|
||||
}
|
||||
|
||||
@@ -1065,9 +1084,6 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
|
||||
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
|
||||
|
||||
memset(tx_message->data.client_accesses_by_index.clients, 0,
|
||||
sizeof (tx_message->data.client_accesses_by_index.clients));
|
||||
|
||||
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
|
||||
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
|
||||
continue;
|
||||
@@ -1100,10 +1116,11 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
RPY_ManualListSample *sample;
|
||||
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
|
||||
|
||||
tx_message->reply = htons(RPY_MANUAL_LIST);
|
||||
tx_message->reply = htons(RPY_MANUAL_LIST2);
|
||||
|
||||
MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
|
||||
tx_message->data.manual_list.n_samples = htonl(n_samples);
|
||||
|
||||
for (i=0; i<n_samples; i++) {
|
||||
sample = &tx_message->data.manual_list.samples[i];
|
||||
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
|
||||
@@ -1235,6 +1252,15 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
{
|
||||
LOG(LOGS_INFO, "Received shutdown command");
|
||||
SCH_QuitProgram();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Read a packet and process it */
|
||||
|
||||
@@ -1258,14 +1284,14 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
&where_from.sa, &from_length);
|
||||
|
||||
if (status < 0) {
|
||||
LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d",
|
||||
LOG(LOGS_WARN, "Error [%s] reading from control socket %d",
|
||||
strerror(errno), sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from_length > sizeof (where_from) ||
|
||||
from_length <= sizeof (where_from.sa.sa_family)) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Read command packet without source address");
|
||||
DEBUG_LOG("Read command packet without source address");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1300,7 +1326,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
assert(0);
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_CmdMon, "Received %d bytes from %s fd %d",
|
||||
DEBUG_LOG("Received %d bytes from %s fd %d",
|
||||
status, UTI_SockaddrToString(&where_from.sa), sock_fd);
|
||||
|
||||
if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
|
||||
@@ -1319,29 +1345,24 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
|
||||
/* We don't know how to process anything like this or an error reply
|
||||
would be larger than the request */
|
||||
DEBUG_LOG(LOGF_CmdMon, "Command packet dropped");
|
||||
DEBUG_LOG("Command packet dropped");
|
||||
return;
|
||||
}
|
||||
|
||||
expected_length = PKL_CommandLength(&rx_message);
|
||||
rx_command = ntohs(rx_message.command);
|
||||
|
||||
memset(&tx_message, 0, sizeof (tx_message));
|
||||
|
||||
tx_message.version = PROTO_VERSION_NUMBER;
|
||||
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
|
||||
tx_message.res1 = 0;
|
||||
tx_message.res2 = 0;
|
||||
tx_message.command = rx_message.command;
|
||||
tx_message.reply = htons(RPY_NULL);
|
||||
tx_message.status = htons(STT_SUCCESS);
|
||||
tx_message.pad1 = 0;
|
||||
tx_message.pad2 = 0;
|
||||
tx_message.pad3 = 0;
|
||||
tx_message.sequence = rx_message.sequence;
|
||||
tx_message.pad4 = 0;
|
||||
tx_message.pad5 = 0;
|
||||
|
||||
if (rx_message.version != PROTO_VERSION_NUMBER) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)",
|
||||
DEBUG_LOG("Command packet has invalid version (%d != %d)",
|
||||
rx_message.version, PROTO_VERSION_NUMBER);
|
||||
|
||||
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
|
||||
@@ -1353,7 +1374,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
|
||||
if (rx_command >= N_REQUEST_TYPES ||
|
||||
expected_length < (int)offsetof(CMD_Request, data)) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command);
|
||||
DEBUG_LOG("Command packet has invalid command %d", rx_command);
|
||||
|
||||
tx_message.status = htons(STT_INVALID);
|
||||
transmit_reply(&tx_message, &where_from);
|
||||
@@ -1361,7 +1382,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
}
|
||||
|
||||
if (read_length < expected_length) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length,
|
||||
DEBUG_LOG("Command packet is too short (%d < %d)", read_length,
|
||||
expected_length);
|
||||
|
||||
tx_message.status = htons(STT_BADPKTLENGTH);
|
||||
@@ -1376,7 +1397,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
/* Don't reply to all requests from hosts other than localhost if the rate
|
||||
is excessive */
|
||||
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
|
||||
DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate");
|
||||
DEBUG_LOG("Command packet discarded to limit response rate");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1526,11 +1547,11 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
handle_cmdaccheck(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ADD_SERVER2:
|
||||
case REQ_ADD_SERVER3:
|
||||
handle_add_source(NTP_SERVER, &rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ADD_PEER2:
|
||||
case REQ_ADD_PEER3:
|
||||
handle_add_source(NTP_PEER, &rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
@@ -1626,8 +1647,16 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
handle_ntp_data(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SHUTDOWN:
|
||||
handle_shutdown(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ONOFFLINE:
|
||||
handle_onoffline(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
|
||||
DEBUG_LOG("Unhandled command %d", rx_command);
|
||||
tx_message.status = htons(STT_FAILED);
|
||||
break;
|
||||
}
|
||||
|
||||
19
cmdparse.c
19
cmdparse.c
@@ -48,9 +48,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
src->port = SRC_DEFAULT_PORT;
|
||||
src->params.minpoll = SRC_DEFAULT_MINPOLL;
|
||||
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
|
||||
src->params.online = 1;
|
||||
src->params.connectivity = SRC_ONLINE;
|
||||
src->params.auto_offline = 0;
|
||||
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
|
||||
src->params.burst = 0;
|
||||
src->params.iburst = 0;
|
||||
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
|
||||
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
|
||||
@@ -58,12 +59,15 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
|
||||
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
|
||||
src->params.filter_length = 0;
|
||||
src->params.interleaved = 0;
|
||||
src->params.sel_options = 0;
|
||||
src->params.authkey = INACTIVE_AUTHKEY;
|
||||
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
||||
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
|
||||
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
|
||||
src->params.min_delay = 0.0;
|
||||
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
||||
src->params.offset = 0.0;
|
||||
|
||||
hostname = line;
|
||||
@@ -82,10 +86,12 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
|
||||
if (!strcasecmp(cmd, "auto_offline")) {
|
||||
src->params.auto_offline = 1;
|
||||
} else if (!strcasecmp(cmd, "burst")) {
|
||||
src->params.burst = 1;
|
||||
} else if (!strcasecmp(cmd, "iburst")) {
|
||||
src->params.iburst = 1;
|
||||
} else if (!strcasecmp(cmd, "offline")) {
|
||||
src->params.online = 0;
|
||||
src->params.connectivity = SRC_OFFLINE;
|
||||
} else if (!strcasecmp(cmd, "noselect")) {
|
||||
src->params.sel_options |= SRC_SELECT_NOSELECT;
|
||||
} else if (!strcasecmp(cmd, "prefer")) {
|
||||
@@ -98,6 +104,12 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
|
||||
src->params.authkey == INACTIVE_AUTHKEY)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "asymmetry")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "filter")) {
|
||||
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "maxdelay")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
|
||||
return 0;
|
||||
@@ -116,6 +128,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
} else if (!strcasecmp(cmd, "maxsources")) {
|
||||
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "mindelay")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "minpoll")) {
|
||||
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
|
||||
return 0;
|
||||
|
||||
214
conf.c
214
conf.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009-2017
|
||||
*
|
||||
* This program 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,7 +66,8 @@ static void parse_log(char *);
|
||||
static void parse_mailonchange(char *);
|
||||
static void parse_makestep(char *);
|
||||
static void parse_maxchange(char *);
|
||||
static void parse_ratelimit(char *line, int *interval, int *burst, int *leak);
|
||||
static void parse_ratelimit(char *line, int *enabled, int *interval,
|
||||
int *burst, int *leak);
|
||||
static void parse_refclock(char *);
|
||||
static void parse_smoothtime(char *);
|
||||
static void parse_source(char *line, NTP_Source_Type type, int pool);
|
||||
@@ -78,7 +79,7 @@ static void parse_tempcomp(char *);
|
||||
static int restarted = 0;
|
||||
static char *rtc_device;
|
||||
static int acquisition_port = -1;
|
||||
static int ntp_port = 123;
|
||||
static int ntp_port = NTP_PORT;
|
||||
static char *keys_file = NULL;
|
||||
static char *drift_file = NULL;
|
||||
static char *rtc_file = NULL;
|
||||
@@ -96,13 +97,13 @@ static double combine_limit = 3.0;
|
||||
|
||||
static int cmd_port = DEFAULT_CANDM_PORT;
|
||||
|
||||
static int raw_measurements = 0;
|
||||
static int do_log_measurements = 0;
|
||||
static int do_log_statistics = 0;
|
||||
static int do_log_tracking = 0;
|
||||
static int do_log_rtc = 0;
|
||||
static int do_log_refclocks = 0;
|
||||
static int do_log_tempcomp = 0;
|
||||
static int do_dump_on_exit = 0;
|
||||
static int log_banner = 32;
|
||||
static char *logdir;
|
||||
static char *dumpdir;
|
||||
@@ -190,12 +191,14 @@ static char *ntp_signd_socket = NULL;
|
||||
static char *pidfile;
|
||||
|
||||
/* Rate limiting parameters */
|
||||
static int ntp_ratelimit_interval = -10;
|
||||
static int ntp_ratelimit_burst = 16;
|
||||
static int ntp_ratelimit_enabled = 0;
|
||||
static int ntp_ratelimit_interval = 3;
|
||||
static int ntp_ratelimit_burst = 8;
|
||||
static int ntp_ratelimit_leak = 2;
|
||||
static int cmd_ratelimit_interval = -10;
|
||||
static int cmd_ratelimit_burst = 16;
|
||||
static int cmd_ratelimit_leak = 0;
|
||||
static int cmd_ratelimit_enabled = 0;
|
||||
static int cmd_ratelimit_interval = -4;
|
||||
static int cmd_ratelimit_burst = 8;
|
||||
static int cmd_ratelimit_leak = 2;
|
||||
|
||||
/* Smoothing constants */
|
||||
static double smooth_max_freq = 0.0; /* in ppm */
|
||||
@@ -220,7 +223,7 @@ static char *leapsec_tz = NULL;
|
||||
/* Name of the user to which will be dropped root privileges. */
|
||||
static char *user;
|
||||
|
||||
/* Array of strings for interfaces with HW timestamping */
|
||||
/* Array of CNF_HwTsInterface */
|
||||
static ARR_Instance hwts_interfaces;
|
||||
|
||||
typedef struct {
|
||||
@@ -267,7 +270,7 @@ static const char *processed_command;
|
||||
static void
|
||||
command_parse_error(void)
|
||||
{
|
||||
LOG_FATAL(LOGF_Configure, "Could not parse %s directive at line %d%s%s",
|
||||
LOG_FATAL("Could not parse %s directive at line %d%s%s",
|
||||
processed_command, line_number, processed_file ? " in file " : "",
|
||||
processed_file ? processed_file : "");
|
||||
}
|
||||
@@ -277,7 +280,7 @@ command_parse_error(void)
|
||||
static void
|
||||
other_parse_error(const char *message)
|
||||
{
|
||||
LOG_FATAL(LOGF_Configure, "%s at line %d%s%s",
|
||||
LOG_FATAL("%s at line %d%s%s",
|
||||
message, line_number, processed_file ? " in file " : "",
|
||||
processed_file ? processed_file : "");
|
||||
}
|
||||
@@ -310,7 +313,7 @@ check_number_of_args(char *line, int num)
|
||||
num -= get_number_of_args(line);
|
||||
|
||||
if (num) {
|
||||
LOG_FATAL(LOGF_Configure, "%s arguments for %s directive at line %d%s%s",
|
||||
LOG_FATAL("%s arguments for %s directive at line %d%s%s",
|
||||
num > 0 ? "Missing" : "Too many",
|
||||
processed_command, line_number, processed_file ? " in file " : "",
|
||||
processed_file ? processed_file : "");
|
||||
@@ -320,11 +323,11 @@ check_number_of_args(char *line, int num)
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
CNF_Initialise(int r)
|
||||
CNF_Initialise(int r, int client_only)
|
||||
{
|
||||
restarted = r;
|
||||
|
||||
hwts_interfaces = ARR_CreateInstance(sizeof (char *));
|
||||
hwts_interfaces = ARR_CreateInstance(sizeof (CNF_HwTsInterface));
|
||||
|
||||
init_sources = ARR_CreateInstance(sizeof (IPAddr));
|
||||
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
|
||||
@@ -336,11 +339,18 @@ CNF_Initialise(int r)
|
||||
|
||||
dumpdir = Strdup("");
|
||||
logdir = Strdup("");
|
||||
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
|
||||
pidfile = Strdup(DEFAULT_PID_FILE);
|
||||
rtc_device = Strdup(DEFAULT_RTC_DEVICE);
|
||||
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
|
||||
user = Strdup(DEFAULT_USER);
|
||||
|
||||
if (client_only) {
|
||||
cmd_port = ntp_port = 0;
|
||||
bind_cmd_path = Strdup("");
|
||||
pidfile = Strdup("");
|
||||
} else {
|
||||
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
|
||||
pidfile = Strdup(DEFAULT_PID_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -351,7 +361,7 @@ CNF_Finalise(void)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARR_GetSize(hwts_interfaces); i++)
|
||||
Free(*(char **)ARR_GetElement(hwts_interfaces, i));
|
||||
Free(((CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, i))->name);
|
||||
ARR_DestroyInstance(hwts_interfaces);
|
||||
|
||||
for (i = 0; i < ARR_GetSize(ntp_sources); i++)
|
||||
@@ -394,12 +404,12 @@ CNF_ReadFile(const char *filename)
|
||||
|
||||
in = fopen(filename, "r");
|
||||
if (!in) {
|
||||
LOG_FATAL(LOGF_Configure, "Could not open configuration file %s : %s",
|
||||
LOG_FATAL("Could not open configuration file %s : %s",
|
||||
filename, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Configure, "Reading %s", filename);
|
||||
DEBUG_LOG("Reading %s", filename);
|
||||
|
||||
for (i = 1; fgets(line, sizeof(line), in); i++) {
|
||||
CNF_ParseLine(filename, i, line);
|
||||
@@ -452,7 +462,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "cmdport")) {
|
||||
parse_int(p, &cmd_port);
|
||||
} else if (!strcasecmp(command, "cmdratelimit")) {
|
||||
parse_ratelimit(p, &cmd_ratelimit_interval, &cmd_ratelimit_burst, &cmd_ratelimit_leak);
|
||||
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
|
||||
&cmd_ratelimit_burst, &cmd_ratelimit_leak);
|
||||
} else if (!strcasecmp(command, "combinelimit")) {
|
||||
parse_double(p, &combine_limit);
|
||||
} else if (!strcasecmp(command, "corrtimeratio")) {
|
||||
@@ -464,7 +475,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "dumpdir")) {
|
||||
parse_string(p, &dumpdir);
|
||||
} else if (!strcasecmp(command, "dumponexit")) {
|
||||
do_dump_on_exit = parse_null(p);
|
||||
/* Silently ignored */
|
||||
} else if (!strcasecmp(command, "fallbackdrift")) {
|
||||
parse_fallbackdrift(p);
|
||||
} else if (!strcasecmp(command, "hwclockfile")) {
|
||||
@@ -532,7 +543,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "port")) {
|
||||
parse_int(p, &ntp_port);
|
||||
} else if (!strcasecmp(command, "ratelimit")) {
|
||||
parse_ratelimit(p, &ntp_ratelimit_interval, &ntp_ratelimit_burst, &ntp_ratelimit_leak);
|
||||
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
|
||||
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
|
||||
} else if (!strcasecmp(command, "refclock")) {
|
||||
parse_refclock(p);
|
||||
} else if (!strcasecmp(command, "reselectdist")) {
|
||||
@@ -563,7 +575,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
!strcasecmp(command, "generatecommandkey") ||
|
||||
!strcasecmp(command, "linux_freq_scale") ||
|
||||
!strcasecmp(command, "linux_hz")) {
|
||||
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
|
||||
LOG(LOGS_WARN, "%s directive is no longer supported", command);
|
||||
} else {
|
||||
other_parse_error("Invalid command");
|
||||
}
|
||||
@@ -637,11 +649,13 @@ parse_source(char *line, NTP_Source_Type type, int pool)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_ratelimit(char *line, int *interval, int *burst, int *leak)
|
||||
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
|
||||
{
|
||||
int n, val;
|
||||
char *opt;
|
||||
|
||||
*enabled = 1;
|
||||
|
||||
while (*line) {
|
||||
opt = line;
|
||||
line = CPS_SplitWord(line);
|
||||
@@ -667,9 +681,9 @@ static void
|
||||
parse_refclock(char *line)
|
||||
{
|
||||
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
|
||||
int max_lock_age;
|
||||
int max_lock_age, pps_forced, stratum, tai;
|
||||
uint32_t ref_id, lock_ref_id;
|
||||
double offset, delay, precision, max_dispersion;
|
||||
double offset, delay, precision, max_dispersion, pulse_width;
|
||||
char *p, *cmd, *name, *param;
|
||||
unsigned char ref[5];
|
||||
RefclockParameters *refclock;
|
||||
@@ -677,6 +691,7 @@ parse_refclock(char *line)
|
||||
poll = 4;
|
||||
dpoll = 0;
|
||||
filter_length = 64;
|
||||
pps_forced = 0;
|
||||
pps_rate = 0;
|
||||
min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||
max_samples = SRC_DEFAULT_MAXSAMPLES;
|
||||
@@ -685,9 +700,12 @@ parse_refclock(char *line)
|
||||
delay = 1e-9;
|
||||
precision = 0.0;
|
||||
max_dispersion = 0.0;
|
||||
pulse_width = 0.0;
|
||||
ref_id = 0;
|
||||
max_lock_age = 2;
|
||||
lock_ref_id = 0;
|
||||
stratum = 0;
|
||||
tai = 0;
|
||||
|
||||
if (!*line) {
|
||||
command_parse_error();
|
||||
@@ -749,12 +767,25 @@ parse_refclock(char *line)
|
||||
} else if (!strcasecmp(cmd, "delay")) {
|
||||
if (sscanf(line, "%lf%n", &delay, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "pps")) {
|
||||
n = 0;
|
||||
pps_forced = 1;
|
||||
} else if (!strcasecmp(cmd, "precision")) {
|
||||
if (sscanf(line, "%lf%n", &precision, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "maxdispersion")) {
|
||||
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "stratum")) {
|
||||
if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
|
||||
stratum >= NTP_MAX_STRATUM || stratum < 0)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "tai")) {
|
||||
n = 0;
|
||||
tai = 1;
|
||||
} else if (!strcasecmp(cmd, "width")) {
|
||||
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "noselect")) {
|
||||
n = 0;
|
||||
sel_options |= SRC_SELECT_NOSELECT;
|
||||
@@ -784,14 +815,18 @@ parse_refclock(char *line)
|
||||
refclock->driver_poll = dpoll;
|
||||
refclock->poll = poll;
|
||||
refclock->filter_length = filter_length;
|
||||
refclock->pps_forced = pps_forced;
|
||||
refclock->pps_rate = pps_rate;
|
||||
refclock->min_samples = min_samples;
|
||||
refclock->max_samples = max_samples;
|
||||
refclock->sel_options = sel_options;
|
||||
refclock->stratum = stratum;
|
||||
refclock->tai = tai;
|
||||
refclock->offset = offset;
|
||||
refclock->delay = delay;
|
||||
refclock->precision = precision;
|
||||
refclock->max_dispersion = max_dispersion;
|
||||
refclock->pulse_width = pulse_width;
|
||||
refclock->ref_id = ref_id;
|
||||
refclock->max_lock_age = max_lock_age;
|
||||
refclock->lock_ref_id = lock_ref_id;
|
||||
@@ -807,7 +842,10 @@ parse_log(char *line)
|
||||
log_name = line;
|
||||
line = CPS_SplitWord(line);
|
||||
if (*log_name) {
|
||||
if (!strcmp(log_name, "measurements")) {
|
||||
if (!strcmp(log_name, "rawmeasurements")) {
|
||||
do_log_measurements = 1;
|
||||
raw_measurements = 1;
|
||||
} else if (!strcmp(log_name, "measurements")) {
|
||||
do_log_measurements = 1;
|
||||
} else if (!strcmp(log_name, "statistics")) {
|
||||
do_log_statistics = 1;
|
||||
@@ -867,7 +905,7 @@ parse_initstepslew(char *line)
|
||||
if (DNS_Name2IPAddress(hostname, &ip_addr, 1) == DNS_Success) {
|
||||
ARR_AppendElement(init_sources, &ip_addr);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_Configure, "Could not resolve address of initstepslew server %s", hostname);
|
||||
LOG(LOGS_WARN, "Could not resolve address of initstepslew server %s", hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1044,7 +1082,7 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
|
||||
}
|
||||
|
||||
} else {
|
||||
if (DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
|
||||
if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
|
||||
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
|
||||
new_node->allow = allow;
|
||||
new_node->all = all;
|
||||
@@ -1159,7 +1197,7 @@ parse_broadcast(char *line)
|
||||
}
|
||||
} else {
|
||||
/* default port */
|
||||
port = 123;
|
||||
port = NTP_PORT;
|
||||
}
|
||||
|
||||
destination = (NTP_Broadcast_Destination *)ARR_GetNewElement(broadcasts);
|
||||
@@ -1238,8 +1276,71 @@ parse_tempcomp(char *line)
|
||||
static void
|
||||
parse_hwtimestamp(char *line)
|
||||
{
|
||||
check_number_of_args(line, 1);
|
||||
*(char **)ARR_GetNewElement(hwts_interfaces) = Strdup(line);
|
||||
CNF_HwTsInterface *iface;
|
||||
char *p, filter[5];
|
||||
int n;
|
||||
|
||||
if (!*line) {
|
||||
command_parse_error();
|
||||
return;
|
||||
}
|
||||
|
||||
p = line;
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
iface = ARR_GetNewElement(hwts_interfaces);
|
||||
iface->name = Strdup(p);
|
||||
iface->minpoll = 0;
|
||||
iface->min_samples = 2;
|
||||
iface->max_samples = 16;
|
||||
iface->nocrossts = 0;
|
||||
iface->rxfilter = CNF_HWTS_RXFILTER_ANY;
|
||||
iface->precision = 100.0e-9;
|
||||
iface->tx_comp = 0.0;
|
||||
iface->rx_comp = 0.0;
|
||||
|
||||
for (p = line; *p; line += n, p = line) {
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (!strcasecmp(p, "maxsamples")) {
|
||||
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "minpoll")) {
|
||||
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "minsamples")) {
|
||||
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "precision")) {
|
||||
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "rxcomp")) {
|
||||
if (sscanf(line, "%lf%n", &iface->rx_comp, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "txcomp")) {
|
||||
if (sscanf(line, "%lf%n", &iface->tx_comp, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(p, "rxfilter")) {
|
||||
if (sscanf(line, "%4s%n", filter, &n) != 1)
|
||||
break;
|
||||
if (!strcasecmp(filter, "none"))
|
||||
iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
|
||||
else if (!strcasecmp(filter, "ntp"))
|
||||
iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
|
||||
else if (!strcasecmp(filter, "all"))
|
||||
iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
|
||||
else
|
||||
break;
|
||||
} else if (!strcasecmp(p, "nocrossts")) {
|
||||
n = 0;
|
||||
iface->nocrossts = 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p)
|
||||
command_parse_error();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1249,11 +1350,19 @@ parse_include(char *line)
|
||||
{
|
||||
glob_t gl;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
check_number_of_args(line, 1);
|
||||
|
||||
if (glob(line, 0, NULL, &gl)) {
|
||||
DEBUG_LOG(LOGF_Configure, "glob of %s failed", line);
|
||||
if ((r = glob(line,
|
||||
#ifdef GLOB_NOMAGIC
|
||||
GLOB_NOMAGIC |
|
||||
#endif
|
||||
GLOB_ERR, NULL, &gl)) != 0) {
|
||||
if (r != GLOB_NOMATCH)
|
||||
LOG_FATAL("Could not search for files matching %s", line);
|
||||
|
||||
DEBUG_LOG("glob of %s failed", line);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1279,7 +1388,7 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
|
||||
existed. It MUST NOT be accessible by others as permissions on Unix
|
||||
domain sockets are ignored on some systems (e.g. Solaris). */
|
||||
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
|
||||
LOG(LOGS_WARN, LOGF_Configure, "Disabled command socket %s", bind_cmd_path);
|
||||
LOG(LOGS_WARN, "Disabled command socket %s", bind_cmd_path);
|
||||
bind_cmd_path[0] = '\0';
|
||||
}
|
||||
|
||||
@@ -1310,7 +1419,7 @@ CNF_AddInitSources(void)
|
||||
ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
|
||||
ntp_addr.port = cps_source.port;
|
||||
cps_source.params.iburst = 1;
|
||||
cps_source.params.online = 0;
|
||||
cps_source.params.connectivity = SRC_OFFLINE;
|
||||
|
||||
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
|
||||
}
|
||||
@@ -1418,8 +1527,9 @@ CNF_GetDumpDir(void)
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetLogMeasurements(void)
|
||||
CNF_GetLogMeasurements(int *raw)
|
||||
{
|
||||
*raw = raw_measurements;
|
||||
return do_log_measurements;
|
||||
}
|
||||
|
||||
@@ -1497,14 +1607,6 @@ CNF_GetRtcDevice(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetDumpOnExit(void)
|
||||
{
|
||||
return do_dump_on_exit;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
CNF_GetMaxUpdateSkew(void)
|
||||
{
|
||||
@@ -1685,7 +1787,7 @@ CNF_SetupAccessRestrictions(void)
|
||||
node = ARR_GetElement(ntp_restrictions, i);
|
||||
status = NCR_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
|
||||
if (!status) {
|
||||
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
|
||||
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1693,7 +1795,7 @@ CNF_SetupAccessRestrictions(void)
|
||||
node = ARR_GetElement(cmd_restrictions, i);
|
||||
status = CAM_AddAccessRestriction(&node->ip, node->subnet_bits, node->allow, node->all);
|
||||
if (!status) {
|
||||
LOG_FATAL(LOGF_Configure, "Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
|
||||
LOG_FATAL("Bad subnet in %s/%d", UTI_IPToString(&node->ip), node->subnet_bits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1823,20 +1925,22 @@ CNF_GetLockMemory(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
|
||||
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
|
||||
{
|
||||
*interval = ntp_ratelimit_interval;
|
||||
*burst = ntp_ratelimit_burst;
|
||||
*leak = ntp_ratelimit_leak;
|
||||
return ntp_ratelimit_enabled;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
|
||||
int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
|
||||
{
|
||||
*interval = cmd_ratelimit_interval;
|
||||
*burst = cmd_ratelimit_burst;
|
||||
*leak = cmd_ratelimit_leak;
|
||||
return cmd_ratelimit_enabled;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1921,8 +2025,12 @@ CNF_GetInitStepThreshold(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
ARR_Instance
|
||||
CNF_GetHwTsInterfaces(void)
|
||||
int
|
||||
CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
|
||||
{
|
||||
return hwts_interfaces;
|
||||
if (index >= ARR_GetSize(hwts_interfaces))
|
||||
return 0;
|
||||
|
||||
*iface = (CNF_HwTsInterface *)ARR_GetElement(hwts_interfaces, index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
31
conf.h
31
conf.h
@@ -29,10 +29,9 @@
|
||||
#define GOT_CONF_H
|
||||
|
||||
#include "addressing.h"
|
||||
#include "array.h"
|
||||
#include "reference.h"
|
||||
|
||||
extern void CNF_Initialise(int restarted);
|
||||
extern void CNF_Initialise(int restarted, int client_only);
|
||||
extern void CNF_Finalise(void);
|
||||
|
||||
extern char *CNF_GetRtcDevice(void);
|
||||
@@ -53,7 +52,7 @@ extern char *CNF_GetDriftFile(void);
|
||||
extern char *CNF_GetLogDir(void);
|
||||
extern char *CNF_GetDumpDir(void);
|
||||
extern int CNF_GetLogBanner(void);
|
||||
extern int CNF_GetLogMeasurements(void);
|
||||
extern int CNF_GetLogMeasurements(int *raw);
|
||||
extern int CNF_GetLogStatistics(void);
|
||||
extern int CNF_GetLogTracking(void);
|
||||
extern int CNF_GetLogRtc(void);
|
||||
@@ -61,7 +60,6 @@ extern int CNF_GetLogRefclocks(void);
|
||||
extern int CNF_GetLogTempComp(void);
|
||||
extern char *CNF_GetKeysFile(void);
|
||||
extern char *CNF_GetRtcFile(void);
|
||||
extern int CNF_GetDumpOnExit(void);
|
||||
extern int CNF_GetManualEnabled(void);
|
||||
extern int CNF_GetCommandPort(void);
|
||||
extern int CNF_GetRtcOnUtc(void);
|
||||
@@ -102,8 +100,8 @@ extern void CNF_SetupAccessRestrictions(void);
|
||||
extern int CNF_GetSchedPriority(void);
|
||||
extern int CNF_GetLockMemory(void);
|
||||
|
||||
extern void CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
|
||||
extern void CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
|
||||
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
|
||||
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
|
||||
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
|
||||
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
|
||||
|
||||
@@ -120,6 +118,25 @@ extern char *CNF_GetHwclockFile(void);
|
||||
extern int CNF_GetInitSources(void);
|
||||
extern double CNF_GetInitStepThreshold(void);
|
||||
|
||||
extern ARR_Instance CNF_GetHwTsInterfaces(void);
|
||||
typedef enum {
|
||||
CNF_HWTS_RXFILTER_ANY,
|
||||
CNF_HWTS_RXFILTER_NONE,
|
||||
CNF_HWTS_RXFILTER_NTP,
|
||||
CNF_HWTS_RXFILTER_ALL,
|
||||
} CNF_HwTs_RxFilter;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int minpoll;
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int nocrossts;
|
||||
CNF_HwTs_RxFilter rxfilter;
|
||||
double precision;
|
||||
double tx_comp;
|
||||
double rx_comp;
|
||||
} CNF_HwTsInterface;
|
||||
|
||||
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
|
||||
|
||||
#endif /* GOT_CONF_H */
|
||||
|
||||
160
configure
vendored
160
configure
vendored
@@ -4,7 +4,8 @@
|
||||
# chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
#
|
||||
# Copyright (C) Richard P. Curnow 1997-2003
|
||||
# Copyright (C) Miroslav Lichvar 2009, 2012-2015
|
||||
# Copyright (C) Bryan Christianson 2016
|
||||
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
|
||||
#
|
||||
# =======================================================================
|
||||
|
||||
@@ -84,6 +85,7 @@ For better control, use the options below.
|
||||
--with-readline-library=DIR Specify where readline lib directory is
|
||||
--with-ncurses-library=DIR Specify where ncurses lib directory is
|
||||
--disable-sechash Disable support for hashes other than MD5
|
||||
--without-nettle Don't use nettle even if it is available
|
||||
--without-nss Don't use NSS even if it is available
|
||||
--without-tomcrypt Don't use libtomcrypt even if it is available
|
||||
--disable-cmdmon Disable command and monitoring support
|
||||
@@ -106,7 +108,7 @@ For better control, use the options below.
|
||||
since 1970-01-01 [50*365 days ago]
|
||||
--with-user=USER Specify default chronyd user [root]
|
||||
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
|
||||
--with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
|
||||
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
|
||||
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
|
||||
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
|
||||
--enable-debug Enable debugging support
|
||||
@@ -197,6 +199,7 @@ feat_readline=1
|
||||
try_readline=1
|
||||
try_editline=1
|
||||
feat_sechash=1
|
||||
try_nettle=1
|
||||
try_nss=1
|
||||
try_tomcrypt=1
|
||||
feat_rtc=1
|
||||
@@ -224,9 +227,10 @@ feat_timestamping=1
|
||||
try_timestamping=0
|
||||
feat_ntp_signd=0
|
||||
ntp_era_split=""
|
||||
use_pthread=0
|
||||
default_user="root"
|
||||
default_hwclockfile=""
|
||||
default_pidfile="/var/run/chronyd.pid"
|
||||
default_pidfile="/var/run/chrony/chronyd.pid"
|
||||
default_rtcdevice="/dev/rtc"
|
||||
mail_program="/usr/lib/sendmail"
|
||||
|
||||
@@ -359,6 +363,9 @@ do
|
||||
--disable-sechash )
|
||||
feat_sechash=0
|
||||
;;
|
||||
--without-nettle )
|
||||
try_nettle=0
|
||||
;;
|
||||
--without-nss )
|
||||
try_nss=0
|
||||
;;
|
||||
@@ -389,7 +396,7 @@ SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
|
||||
|
||||
case $OPERATINGSYSTEM in
|
||||
Linux)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o sys_posix.o"
|
||||
[ $try_libcap != "0" ] && try_libcap=1
|
||||
try_rtc=1
|
||||
[ $try_seccomp != "0" ] && try_seccomp=1
|
||||
@@ -404,7 +411,9 @@ case $OPERATINGSYSTEM in
|
||||
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just
|
||||
# a wrapper around recvmsg()
|
||||
try_recvmmsg=0
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def FREEBSD
|
||||
if [ $feat_droproot = "1" ]; then
|
||||
add_def FEAT_PRIVDROP
|
||||
@@ -413,8 +422,10 @@ case $OPERATINGSYSTEM in
|
||||
echo "Configuring for $SYSTEM"
|
||||
;;
|
||||
NetBSD)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o sys_posix.o"
|
||||
try_clockctl=1
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def NETBSD
|
||||
echo "Configuring for $SYSTEM"
|
||||
;;
|
||||
@@ -427,12 +438,23 @@ case $OPERATINGSYSTEM in
|
||||
add_def FEAT_PRIVDROP
|
||||
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
|
||||
fi
|
||||
major=`echo $VERSION | cut -d. -f1`
|
||||
# ntp_adjtime is not available in macOS 10.12 (Darwin 16.x.x) and earlier
|
||||
if [ $major -gt "16" ]; then
|
||||
add_def HAVE_MACOS_SYS_TIMEX
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_generic.o sys_netbsd.o sys_timex.o"
|
||||
if [ $feat_droproot = "1" ]; then
|
||||
priv_ops="$priv_ops ADJUSTTIMEX"
|
||||
fi
|
||||
fi
|
||||
echo "Configuring for macOS (" $SYSTEM "macOS version" $VERSION ")"
|
||||
;;
|
||||
SunOS)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
|
||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
||||
EXTRA_LIBS="-lsocket -lnsl -lresolv"
|
||||
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def SOLARIS
|
||||
# These are needed to have msg_control in struct msghdr
|
||||
add_def __EXTENSIONS__
|
||||
@@ -489,14 +511,16 @@ MYCPPFLAGS="$CPPFLAGS"
|
||||
MYLDFLAGS="$LDFLAGS"
|
||||
|
||||
if [ "x$MYCC" = "x" ]; then
|
||||
MYCC=gcc
|
||||
if ! test_code "$MYCC" '' '' '' ''; then
|
||||
MYCC=cc
|
||||
if ! test_code "$MYCC" '' '' '' ''; then
|
||||
for cc in gcc clang cc ""; do
|
||||
if [ "x$cc" = "x" ]; then
|
||||
echo "error: no C compiler found"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
MYCC=$cc
|
||||
if test_code "$MYCC" '' '' '' ''; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
if ! test_code "$MYCC" '' '' '' ''; then
|
||||
echo "error: C compiler $MYCC cannot create executables"
|
||||
@@ -506,9 +530,25 @@ fi
|
||||
|
||||
if [ "x$MYCFLAGS" = "x" ]; then
|
||||
MYCFLAGS="-O2 -g"
|
||||
|
||||
TESTCFLAGS="-D_FORTIFY_SOURCE=2 -fPIE"
|
||||
TESTLDFLAGS="-pie -Wl,-z,relro,-z,now"
|
||||
if test_code 'hardening compiler options' '' "$TESTCFLAGS" "$TESTLDFLAGS" ''; then
|
||||
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
|
||||
MYLDFLAGS="$MYLDFLAGS $TESTLDFLAGS"
|
||||
fi
|
||||
TESTCFLAGS="-fstack-protector-strong --param=ssp-buffer-size=4"
|
||||
if test_code '-fstack-protector-strong' '' "$TESTCFLAGS" '' ''; then
|
||||
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
|
||||
else
|
||||
TESTCFLAGS="-fstack-protector --param=ssp-buffer-size=4"
|
||||
if test_code '-fstack-protector' '' "$TESTCFLAGS" '' ''; then
|
||||
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "x$MYCC" = "xgcc" ]; then
|
||||
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
|
||||
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
|
||||
fi
|
||||
|
||||
@@ -522,7 +562,11 @@ then
|
||||
split_seconds=$ntp_era_split
|
||||
split_days=0
|
||||
else
|
||||
split_seconds=`date '+%s'`
|
||||
if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then
|
||||
split_seconds=$SOURCE_DATE_EPOCH
|
||||
else
|
||||
split_seconds=`date '+%s'`
|
||||
fi
|
||||
if [ "x$split_seconds" = "x" ]; then
|
||||
echo "error: could not get current time, --with-ntp-era option is needed"
|
||||
exit 1
|
||||
@@ -558,14 +602,6 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
|
||||
add_def HAVE_STDINT_H
|
||||
fi
|
||||
|
||||
if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
|
||||
add_def HAVE_INTTYPES_H
|
||||
fi
|
||||
|
||||
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
|
||||
struct in_pktinfo ipi;
|
||||
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
|
||||
@@ -618,18 +654,23 @@ fi
|
||||
|
||||
if [ $feat_asyncdns = "1" ] && \
|
||||
test_code 'pthread' 'pthread.h' '-pthread' '' \
|
||||
'return pthread_create((void *)1, NULL, (void *)1, NULL);'
|
||||
'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);'
|
||||
then
|
||||
add_def FEAT_ASYNCDNS
|
||||
add_def USE_PTHREAD_ASYNCDNS
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
|
||||
add_def HAVE_ARC4RANDOM
|
||||
fi
|
||||
|
||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
||||
'return getrandom(NULL, 256, 0);'; then
|
||||
add_def HAVE_GETRANDOM
|
||||
fi
|
||||
|
||||
RECVMMSG_CODE='
|
||||
struct mmsghdr hdr;
|
||||
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
|
||||
@@ -657,15 +698,27 @@ if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
|
||||
then
|
||||
add_def HAVE_LINUX_TIMESTAMPING
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
|
||||
|
||||
if test_code 'other timestamping options' \
|
||||
'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' '
|
||||
struct scm_ts_pktinfo pktinfo;
|
||||
pktinfo.if_index = pktinfo.pkt_length = 0;
|
||||
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
|
||||
SCM_TIMESTAMPING_PKTINFO +
|
||||
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
|
||||
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
|
||||
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
|
||||
add_def HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1
|
||||
fi
|
||||
fi
|
||||
|
||||
timepps_h=""
|
||||
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
|
||||
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
|
||||
if test_code '<sys/timepps.h>' 'inttypes.h time.h sys/timepps.h' '' '' ''; then
|
||||
timepps_h="sys/timepps.h"
|
||||
add_def HAVE_SYS_TIMEPPS_H
|
||||
else
|
||||
if test_code '<timepps.h>' 'timepps.h' '' '' ''; then
|
||||
if test_code '<timepps.h>' 'inttypes.h time.h timepps.h' '' '' ''; then
|
||||
timepps_h="timepps.h"
|
||||
add_def HAVE_TIMEPPS_H
|
||||
fi
|
||||
@@ -673,7 +726,7 @@ if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
|
||||
fi
|
||||
|
||||
if [ "x$timepps_h" != "x" ] && \
|
||||
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
|
||||
test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
|
||||
pps_handle_t h = 0;
|
||||
pps_info_t i;
|
||||
struct timespec ts;
|
||||
@@ -708,7 +761,7 @@ then
|
||||
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
|
||||
# process works on one request at the time and the async resolver could
|
||||
# block the main thread
|
||||
priv_ops="NAME2IPADDRESS"
|
||||
priv_ops="NAME2IPADDRESS RELOADDNS"
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
|
||||
fi
|
||||
|
||||
@@ -731,32 +784,42 @@ fi
|
||||
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
|
||||
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
|
||||
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
|
||||
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
|
||||
'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);'
|
||||
then
|
||||
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null ||
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
|
||||
add_def FEAT_PHC
|
||||
fi
|
||||
|
||||
if [ $try_setsched = "1" ] && \
|
||||
test_code \
|
||||
'sched_setscheduler()' \
|
||||
'sched.h' '' '' '
|
||||
'pthread_setschedparam()' \
|
||||
'pthread.h sched.h' '-pthread' '' '
|
||||
struct sched_param sched;
|
||||
sched_get_priority_max(SCHED_FIFO);
|
||||
sched_setscheduler(0, SCHED_FIFO, &sched);'
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
|
||||
then
|
||||
add_def HAVE_SCHED_SETSCHEDULER
|
||||
add_def HAVE_PTHREAD_SETSCHEDPARAM
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if [ $try_lockmem = "1" ] && \
|
||||
test_code \
|
||||
'mlockall()' \
|
||||
'sys/mman.h sys/resource.h' '' '' '
|
||||
struct rlimit rlim;
|
||||
setrlimit(RLIMIT_MEMLOCK, &rlim);
|
||||
'sys/mman.h' '' '' '
|
||||
mlockall(MCL_CURRENT|MCL_FUTURE);'
|
||||
then
|
||||
add_def HAVE_MLOCKALL
|
||||
fi
|
||||
if [ $try_lockmem = "1" ] && \
|
||||
test_code \
|
||||
'setrlimit(RLIMIT_MEMLOCK, ...)' \
|
||||
'sys/resource.h' '' '' '
|
||||
struct rlimit rlim;
|
||||
setrlimit(RLIMIT_MEMLOCK, &rlim);'
|
||||
then
|
||||
add_def HAVE_SETRLIMIT_MEMLOCK
|
||||
fi
|
||||
|
||||
if [ $feat_forcednsretry = "1" ]
|
||||
then
|
||||
@@ -806,7 +869,22 @@ fi
|
||||
HASH_OBJ="hash_intmd5.o"
|
||||
HASH_LINK=""
|
||||
|
||||
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
|
||||
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ]; then
|
||||
test_cflags="`pkg_config --cflags nettle`"
|
||||
test_link="`pkg_config --libs nettle`"
|
||||
if test_code 'nettle' 'nettle/nettle-meta.h nettle/sha2.h' \
|
||||
"$test_cflags" "$test_link" \
|
||||
'return nettle_hashes[0]->context_size;'
|
||||
then
|
||||
HASH_OBJ="hash_nettle.o"
|
||||
HASH_LINK="$test_link"
|
||||
LIBS="$LIBS $HASH_LINK"
|
||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||
add_def FEAT_SECHASH
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
|
||||
test_cflags="`pkg_config --cflags nss`"
|
||||
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
|
||||
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
|
||||
@@ -833,6 +911,10 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $use_pthread = "1" ]; then
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
fi
|
||||
|
||||
SYSCONFDIR=/etc
|
||||
if [ "x$SETSYSCONFDIR" != "x" ]; then
|
||||
SYSCONFDIR=$SETSYSCONFDIR
|
||||
@@ -896,9 +978,9 @@ add_def DEFAULT_USER "\"$default_user\""
|
||||
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
|
||||
add_def MAIL_PROGRAM "\"$mail_program\""
|
||||
|
||||
common_features="`get_features IPV6 DEBUG`"
|
||||
common_features="`get_features SECHASH IPV6 DEBUG`"
|
||||
chronyc_features="`get_features READLINE`"
|
||||
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
|
||||
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
|
||||
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
|
||||
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
|
||||
echo "Features : $chronyd_features $chronyc_features $common_features"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// This file is part of chrony
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Miroslav Lichvar 2009-2016
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Bryan Christianson 2017
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -64,30 +66,47 @@ server, or its IP address. The *server* directive supports the following
|
||||
options:
|
||||
+
|
||||
*minpoll* _poll_:::
|
||||
Although *chronyd* will trim the rate at which it samples the server during
|
||||
normal operation, the user might want to constrain the minimum polling interval.
|
||||
This is always defined as a power of 2, so *minpoll 5* would mean that the
|
||||
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds).
|
||||
This option specifies the minimum interval between requests sent to the server
|
||||
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
|
||||
polling interval should not drop below 32 seconds. The default is 6 (64
|
||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
||||
months). Note that intervals shorter than 6 (64 seconds) should generally not
|
||||
be used with public servers on the Internet, because it might be considered
|
||||
abuse. A sub-second interval will be enabled only when the server is reachable
|
||||
and the round-trip delay is shorter than 10 milliseconds, i.e. the server
|
||||
should be in a local network.
|
||||
*maxpoll* _poll_:::
|
||||
In a similar way, the user might want to constrain the maximum polling interval.
|
||||
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
|
||||
interval must stay at or below 512 seconds. The default is 10 (1024 seconds).
|
||||
This option specifies the maximum interval between requests sent to the server
|
||||
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
|
||||
interval should stay at or below 9 (512 seconds). The default is 10 (1024
|
||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
||||
months).
|
||||
*iburst*:::
|
||||
If this option is set, the interval between the first four polls will be 2
|
||||
seconds instead of _minpoll_. This is useful to quickly get the first update of
|
||||
the clock after *chronyd* is started.
|
||||
*key* _id_:::
|
||||
The NTP protocol supports the inclusion of checksums in the packets, to prevent
|
||||
With this option, the interval between the first four requests sent to the
|
||||
server will be 2 seconds or less instead of the interval specified by the
|
||||
*minpoll* option, which allows *chronyd* to make the first update of the clock
|
||||
shortly after start.
|
||||
*burst*:::
|
||||
With this option, *chronyd* will shorten the interval between up to four
|
||||
requests to 2 seconds or less when it cannot get a good measurement from the
|
||||
server. The number of requests in the burst is limited by the current polling
|
||||
interval to keep the average interval at or above the minimum interval, i.e.
|
||||
the current interval needs to be at least two times longer than the minimum
|
||||
interval in order to allow a burst with two requests.
|
||||
*key* _ID_:::
|
||||
The NTP protocol supports a message authentication code (MAC) to prevent
|
||||
computers having their system time upset by rogue packets being sent to them.
|
||||
The checksums are generated as a function of a password, using the
|
||||
cryptographic hash function set in the key file, which is specified by the
|
||||
<<keyfile,*keyfile*>> directive.
|
||||
The MAC is generated as a function of a password specified in the key file,
|
||||
which is specified by the <<keyfile,*keyfile*>> directive.
|
||||
+
|
||||
If the key option is present, *chronyd* will attempt to use authenticated
|
||||
packets when communicating with this server. The key number used will be the
|
||||
single argument to the key option (an unsigned integer in the range 1 through
|
||||
2^32-1). The server must have the same password for this key number configured,
|
||||
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
|
||||
should *chronyd* use to authenticate requests sent to the server and verify its
|
||||
responses. The server must have the same key for this number configured,
|
||||
otherwise no relationship between the computers will be possible.
|
||||
+
|
||||
If the server is running *ntpd* and the output size of the hash function used
|
||||
by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to
|
||||
be set to 4 for compatibility.
|
||||
*maxdelay* _delay_:::
|
||||
*chronyd* uses the network round-trip delay to the server to determine how
|
||||
accurate a particular measurement is likely to be. Long round-trip delays
|
||||
@@ -105,18 +124,30 @@ If the user knows that round trip delays above a certain level should cause the
|
||||
measurement to be ignored, this level can be defined with the *maxdelay*
|
||||
option. For example, *maxdelay 0.3* would indicate that measurements with a
|
||||
round-trip delay of 0.3 seconds or more should be ignored. The default value is
|
||||
3 seconds.
|
||||
3 seconds and the maximum value is 1000 seconds.
|
||||
*maxdelayratio* _ratio_:::
|
||||
This option is similar to the maxdelay option above. *chronyd* keeps a record
|
||||
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
|
||||
of the minimum round-trip delay amongst the previous measurements that it has
|
||||
buffered. If a measurement has a round trip delay that is greater than the
|
||||
maxdelayratio times the minimum delay, it will be rejected. This option works
|
||||
only in the *server* directive when not in the interleaved mode.
|
||||
maxdelayratio times the minimum delay, it will be rejected.
|
||||
*maxdelaydevratio* _ratio_:::
|
||||
If a measurement has a ratio of the increase in the round-trip delay from the
|
||||
minimum delay amongst the previous measurements to the standard deviation of
|
||||
the previous measurements that is greater than the specified ratio, it will be
|
||||
rejected. The default is 10.0.
|
||||
*mindelay* _delay_:::
|
||||
This option specifies a fixed minimum round-trip delay to be used instead of
|
||||
the minimum amongst the previous measurements. This can be useful in networks
|
||||
with static configuration to improve the stability of corrections for
|
||||
asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and
|
||||
*maxdelaydevratio* tests. The value should be set accurately in order to have a
|
||||
positive effect on the synchronisation.
|
||||
*asymmetry* _ratio_:::
|
||||
This option specifies the asymmetry of the network jitter on the path to the
|
||||
source, which is used to correct the measured offset according to the delay.
|
||||
The asymmetry can be between -0.5 and +0.5. A negative value means the delay of
|
||||
packets sent to the source is more variable than the delay of packets sent from
|
||||
the source back. By default, *chronyd* estimates the asymmetry automatically.
|
||||
*offset* _offset_:::
|
||||
This option specifies a correction (in seconds) which will be applied to
|
||||
offsets measured with this source. It's particularly useful to compensate for a
|
||||
@@ -130,20 +161,25 @@ Set the minimum number of samples kept for this source. This overrides the
|
||||
*maxsamples* _samples_:::
|
||||
Set the maximum number of samples kept for this source. This overrides the
|
||||
<<maxsamples,*maxsamples*>> directive.
|
||||
*filter* _samples_:::
|
||||
This option enables a median filter to reduce noise in NTP measurements. The
|
||||
filter will reduce the specified number of samples to a single sample. It is
|
||||
intended to be used with very short polling intervals in local networks where
|
||||
it is acceptable to generate a lot of NTP traffic.
|
||||
*offline*:::
|
||||
If the server will not be reachable when *chronyd* is started, the *offline*
|
||||
option can be specified. *chronyd* will not try to poll the server until it is
|
||||
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
|
||||
*chronyc*).
|
||||
*auto_offline*:::
|
||||
If this option is set, the server will be assumed to have gone offline when 2
|
||||
requests have been sent to it without receiving a response. This option avoids
|
||||
With this option, the server will be assumed to have gone offline when sending
|
||||
a request fails, e.g. due to a missing route to the network. This option avoids
|
||||
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
|
||||
when disconnecting the network link. (It will still be necessary to use the
|
||||
<<chronyc.adoc#online,*online*>> command when the link has been established, to
|
||||
enable measurements to start.)
|
||||
*prefer*:::
|
||||
Prefer this source over sources without prefer option.
|
||||
Prefer this source over sources without the *prefer* option.
|
||||
*noselect*:::
|
||||
Never select this source. This is particularly useful for monitoring.
|
||||
*trust*:::
|
||||
@@ -160,9 +196,8 @@ synchronisation only if they agree with the trusted and required source.
|
||||
*xleave*:::
|
||||
This option enables an interleaved mode which allows the server or the peer to
|
||||
send transmit timestamps captured after the actual transmission (e.g. when the
|
||||
server or the peer is running *chronyd* with HW timestamping enabled by the
|
||||
<<hwtimestamp,*hwtimestamp*>> directive). This can significantly improve the
|
||||
accuracy of the measurements.
|
||||
server or the peer is running *chronyd* with software (kernel) or hardware
|
||||
timestamping). This can significantly improve the accuracy of the measurements.
|
||||
+
|
||||
The interleaved mode is compatible with servers that support only the basic
|
||||
mode, but peers must both support and have enabled the interleaved mode,
|
||||
@@ -172,9 +207,11 @@ the interleaved mode requires the servers to keep some state for each client
|
||||
and the state might be dropped when there are too many clients (e.g.
|
||||
<<clientloglimit,*clientloglimit*>> is too small), or it might be overwritten
|
||||
by other clients that have the same IP address (e.g. computers behind NAT or
|
||||
someone sending requests with a spoofed source address). The *presend* option
|
||||
can be used to shorten the interval in which the server has to keep the state
|
||||
for this computer and be able to respond in the interleaved mode.
|
||||
someone sending requests with a spoofed source address).
|
||||
+
|
||||
The *xleave* option can be combined with the *presend* option in order to
|
||||
shorten the interval in which the server has to keep the state to be able to
|
||||
respond in the interleaved mode.
|
||||
*polltarget* _target_:::
|
||||
Target number of measurements to use for the regression algorithm which
|
||||
*chronyd* will try to maintain by adjusting the polling interval between
|
||||
@@ -337,47 +374,56 @@ for *initstepslew* to finish before exiting. This is useful to prevent programs
|
||||
started in the boot sequence after *chronyd* from reading the clock before it
|
||||
has been stepped.
|
||||
|
||||
[[refclock]]*refclock* _driver_ _parameter_ [_option_]...::
|
||||
[[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...::
|
||||
The *refclock* directive specifies a hardware reference clock to be used as a
|
||||
time source. It has two mandatory parameters, a driver name and a
|
||||
driver-specific parameter.
|
||||
driver-specific parameter. The two parameters are followed by zero or more
|
||||
refclock options. Some drivers have special options, which can be appended to
|
||||
the driver-specific parameter using the *:* character.
|
||||
+
|
||||
There are currently four drivers included:
|
||||
There are four drivers included in *chronyd*:
|
||||
+
|
||||
*PPS*:::
|
||||
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
|
||||
the PPS device (typically _/dev/pps?_). The assert events are used for
|
||||
synchronisation by default. String *:clear* can be appended to the path to use
|
||||
the clear events instead.
|
||||
the PPS device (typically _/dev/pps?_). As PPS refclocks do not supply full
|
||||
time, another time source (e.g. NTP server or non-PPS refclock) is needed to
|
||||
complete samples from the PPS refclock. An alternative is to enable the
|
||||
<<local,*local*>> directive to allow synchronisation with some unknown but
|
||||
constant offset. The driver supports the following option:
|
||||
+
|
||||
As PPS refclocks don't supply full time, *chronyd* needs to be configured with
|
||||
another time source (NTP or non-PPS refclock) in order to complete samples from
|
||||
the PPS refclock. An alternative is to enable the <<local,*local*>> directive
|
||||
to allow synchronisation with some unknown but constant offset.
|
||||
For example:
|
||||
*clear*::::
|
||||
By default, the PPS refclock uses assert events (rising edge) for
|
||||
synchronisation. With this option, it will use clear events (falling edge)
|
||||
instead.
|
||||
+
|
||||
:::
|
||||
Examples:
|
||||
+
|
||||
----
|
||||
refclock PPS /dev/pps0 lock NMEA
|
||||
refclock PPS /dev/pps0 lock NMEA refid GPS
|
||||
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
|
||||
refclock PPS /dev/pps1:clear refid GPS2
|
||||
----
|
||||
+
|
||||
*SHM*:::
|
||||
NTP shared memory driver. This driver uses a shared memory segment to receive
|
||||
samples from another process. The parameter is the number of the shared memory
|
||||
segment, typically 0, 1, 2 or 3. For example:
|
||||
samples from another process (e.g. *gpsd*). The parameter is the number of the
|
||||
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
|
||||
supports the following option:
|
||||
+
|
||||
*perm*=_mode_::::
|
||||
This option specifies the permissions of the shared memory segment created by
|
||||
*chronyd*. They are specified as a numeric mode. The default value is 0600
|
||||
(read-write access for owner only).
|
||||
:::
|
||||
+
|
||||
Examples:
|
||||
+
|
||||
----
|
||||
refclock SHM 1 poll 3 refid GPS1
|
||||
refclock SHM 0 poll 3 refid GPS1
|
||||
refclock SHM 1:perm=0644 refid GPS2
|
||||
----
|
||||
+
|
||||
A driver option in form of *:perm=NNN* can be appended to the segment number to
|
||||
create the segment with permissions other than the default 0600.
|
||||
+
|
||||
Examples of applications that can be used as SHM refclocks are
|
||||
http://www.catb.org/gpsd/[*gpsd*],
|
||||
http://www.buzzard.me.uk/jonathan/radioclock.html[*radioclk*], and
|
||||
https://www.vanheusden.com/time/omnisync/[*omnisync*].
|
||||
+
|
||||
*SOCK*:::
|
||||
Unix domain socket driver. It is similar to the SHM driver, but samples are
|
||||
received from a Unix domain socket instead of shared memory and the messages
|
||||
@@ -397,17 +443,44 @@ refclock SOCK /var/run/chrony.ttyS0.sock
|
||||
+
|
||||
*PHC*:::
|
||||
PTP hardware clock (PHC) driver. The parameter is the path to the device of
|
||||
the PTP clock, which for example can be synchronised by *ptp4l* from
|
||||
http://linuxptp.sourceforge.net[*linuxptp*]. PTP clocks are typically kept in
|
||||
TAI instead of UTC, so the *offset* option should be used to compensate for the
|
||||
current UTC-TAI offset. For example:
|
||||
the PTP clock which should be used as a time source. If the clock is kept in
|
||||
TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current
|
||||
UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the
|
||||
*pps* refclock option can be enabled to treat the PHC as a PPS refclock, using
|
||||
only the sub-second offset for synchronisation. The driver supports the
|
||||
following options:
|
||||
+
|
||||
*nocrossts*::::
|
||||
This option disables use of precise cross timestamping.
|
||||
*extpps*::::
|
||||
This option enables a PPS mode in which the PTP clock is timestamping pulses
|
||||
of an external PPS signal connected to the clock. The clock does not need to be
|
||||
synchronised, but another time source is needed to complete the PPS samples.
|
||||
Note that some PTP clocks cannot be configured to timestamp only assert or
|
||||
clear events, and it is necessary to use the *width* option to filter wrong
|
||||
PPS samples.
|
||||
*pin*=_index_::::
|
||||
This option specifies the index of the pin to which is connected the PPS
|
||||
signal. The default value is 0.
|
||||
*channel*=_index_::::
|
||||
This option specifies the index of the channel for the PPS mode. The default
|
||||
value is 0.
|
||||
*clear*::::
|
||||
This option enables timestamping of clear events (falling edge) instead of
|
||||
assert events (rising edge) in the PPS mode. This may not work with some
|
||||
clocks.
|
||||
:::
|
||||
+
|
||||
Examples:
|
||||
+
|
||||
----
|
||||
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset -36
|
||||
refclock PHC /dev/ptp0 poll 0 dpoll -2 offset -37
|
||||
refclock PHC /dev/ptp1:nocrossts poll 3 pps
|
||||
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
|
||||
----
|
||||
+
|
||||
::
|
||||
The *refclock* directive also supports a number of options:
|
||||
The *refclock* directive supports the following options:
|
||||
+
|
||||
*poll* _poll_:::
|
||||
Timestamps produced by refclock drivers are not used immediately, but they are
|
||||
@@ -440,6 +513,17 @@ This option specifies in number of pulses how old can be samples from the
|
||||
refclock specified by the *lock* option to be paired with the pulses.
|
||||
Increasing this value is useful when the samples are produced at a lower rate
|
||||
than the pulses. The default is 2.
|
||||
*width* _width_:::
|
||||
This option specifies the width of the pulses (in seconds). It is used to
|
||||
filter PPS samples when the driver provides samples for both rising and falling
|
||||
edges. Note that it reduces the maximum allowed error of the time source which
|
||||
completes the PPS samples. If the duty cycle is configurable, 50% should be
|
||||
preferred in order to maximise the allowed error.
|
||||
*pps*:::
|
||||
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS
|
||||
refclock. This can be useful when the refclock provides time with a variable
|
||||
offset of a whole number of seconds (e.g. it uses TAI instead of UTC). Another
|
||||
time source is needed to complete samples from the refclock.
|
||||
*offset* _offset_:::
|
||||
This option can be used to compensate for a constant error. The specified
|
||||
offset (in seconds) is applied to all samples produced by the reference clock.
|
||||
@@ -450,10 +534,12 @@ is included in the maximum assumed error which is used in the source selection
|
||||
algorithm. Increasing the delay is useful to avoid having no majority in the
|
||||
source selection or to make it prefer other sources. The default is 1e-9 (1
|
||||
nanosecond).
|
||||
*stratum* _stratum_:::
|
||||
This option sets the NTP stratum of the refclock. This can be useful when the
|
||||
refclock provides time with a stratum other than 0. The default is 0.
|
||||
*precision* _precision_:::
|
||||
This option sets the refclock precision (in seconds). The default is 1e-6 (1
|
||||
microsecond) for SHM refclock, and 1e-9 (1 nanosecond) for SOCK, PPS and PHC
|
||||
refclocks.
|
||||
This option sets the precision of the reference clock (in seconds). The default
|
||||
value is the estimated precision of the system clock.
|
||||
*maxdispersion* _dispersion_:::
|
||||
Maximum allowed dispersion for filtered samples (in seconds). Samples with
|
||||
larger estimated dispersion are ignored. By default, this limit is disabled.
|
||||
@@ -481,6 +567,12 @@ but not very precise, reference clock to be safely combined with
|
||||
unauthenticated NTP sources in order to improve the accuracy of the clock. They
|
||||
can be selected and used for synchronisation only if they agree with the
|
||||
trusted and required source.
|
||||
*tai*:::
|
||||
This option indicates that the reference clock keeps time in TAI instead of UTC
|
||||
and that *chronyd* should correct its offset by the current TAI-UTC offset. The
|
||||
<<leapsectz,*leapsectz*>> directive must be used with this option and the
|
||||
database must be kept up to date in order for this correction to work as
|
||||
expected. This option does not make sense with PPS refclocks.
|
||||
*minsamples* _samples_:::
|
||||
Set the minimum number of samples kept for this source. This overrides the
|
||||
<<minsamples,*minsamples*>> directive.
|
||||
@@ -534,19 +626,18 @@ can be specified.
|
||||
To compute the rate of gain or loss of time, *chronyd* has to store a
|
||||
measurement history for each of the time sources it uses.
|
||||
+
|
||||
Certain systems (Linux, FreeBSD, NetBSD, Solaris) have operating system support
|
||||
for setting the rate of gain or loss to compensate for known errors. (On Mac OS
|
||||
X, *chronyd* must simulate such a capability by periodically slewing the system
|
||||
clock forwards or backwards by a suitable amount to compensate for the error
|
||||
built up since the previous slew.)
|
||||
All supported systems, with the exception of macOS 10.12 and earlier, have
|
||||
operating system support for setting the rate of gain or loss to compensate for
|
||||
known errors.
|
||||
(On macOS 10.12 and earlier, *chronyd* must simulate such a capability by
|
||||
periodically slewing the system clock forwards or backwards by a suitable amount
|
||||
to compensate for the error built up since the previous slew.)
|
||||
+
|
||||
For such systems, it is possible to save the measurement history across
|
||||
restarts of *chronyd* (assuming no changes are made to the system clock
|
||||
behaviour whilst it is not running). If this capability is to be used (via the
|
||||
*dumponexit* directive in the configuration file, or the
|
||||
<<chronyc.adoc#dump,*dump*>> command in *chronyc*), the *dumpdir* directive
|
||||
should be used to define the directory where the measurement histories are
|
||||
saved.
|
||||
behaviour whilst it is not running). The *dumpdir* directive defines the
|
||||
directory where the measurement histories are saved when *chronyd* exits,
|
||||
or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
|
||||
+
|
||||
An example of the directive is:
|
||||
+
|
||||
@@ -558,11 +649,6 @@ A source whose IP address is _1.2.3.4_ would have its measurement history saved
|
||||
in the file _@CHRONYRUNDIR@/1.2.3.4.dat_. History of reference clocks is saved
|
||||
to files named by their reference ID in form of _refid:XXXXXXXX.dat_.
|
||||
|
||||
[[dumponexit]]*dumponexit*::
|
||||
If this directive is present, it indicates that *chronyd* should save the
|
||||
measurement history for each of its time sources recorded whenever the program
|
||||
exits. (See the <<dumpdir,*dumpdir*>> directive above.)
|
||||
|
||||
[[maxsamples]]*maxsamples* _samples_::
|
||||
The *maxsamples* directive sets the default maximum number of samples that
|
||||
*chronyd* should keep for each source. This setting can be overridden for
|
||||
@@ -575,6 +661,13 @@ The *minsamples* directive sets the default minimum number of samples that
|
||||
*chronyd* should keep for each source. This setting can be overridden for
|
||||
individual sources in the <<server,*server*>> and <<refclock,*refclock*>>
|
||||
directives. The default value is 6. The useful range is 4 to 64.
|
||||
+
|
||||
Forcing *chronyd* to keep more samples than it would normally keep reduces
|
||||
noise in the estimated frequency and offset, but slows down the response to
|
||||
changes in the frequency and offset of the clock. The offsets in the
|
||||
<<chronyc.adoc#tracking,*tracking*>> and
|
||||
<<chronyc.adoc#sourcestats,*sourcestats*>> reports (and the _tracking.log_ and
|
||||
_statistics.log_ files) may be smaller than the actual offsets.
|
||||
|
||||
=== Source selection
|
||||
|
||||
@@ -588,6 +681,8 @@ The *combinelimit* directive limits which sources are included in the combining
|
||||
algorithm. Their synchronisation distance has to be shorter than the distance
|
||||
of the selected source multiplied by the value of the limit. Also, their
|
||||
measured frequencies have to be close to the frequency of the selected source.
|
||||
If the selected source was specified with the *prefer* option, it can be
|
||||
combined only with other sources specified with this option.
|
||||
+
|
||||
By default, the limit is 3. Setting the limit to 0 effectively disables the
|
||||
source combining algorithm and only the selected source will be used to control
|
||||
@@ -644,8 +739,9 @@ distances are in milliseconds.
|
||||
|
||||
[[corrtimeratio]]*corrtimeratio* _ratio_::
|
||||
When *chronyd* is slewing the system clock to correct an offset, the rate at
|
||||
which it is slewing adds to the frequency error of the clock. On Linux,
|
||||
FreeBSD, NetBSD and Solaris this rate can be controlled.
|
||||
which it is slewing adds to the frequency error of the clock. On all supported
|
||||
systems, with the exception of macOS 12 and earlier, this rate can be
|
||||
controlled.
|
||||
+
|
||||
The *corrtimeratio* directive sets the ratio between the duration in which the
|
||||
clock is slewed for an average correction according to the source history and
|
||||
@@ -691,9 +787,10 @@ driftfile @CHRONYVARDIR@/drift
|
||||
|
||||
[[fallbackdrift]]*fallbackdrift* _min-interval_ _max-interval_::
|
||||
Fallback drifts are long-term averages of the system clock drift calculated
|
||||
over exponentially increasing intervals. They are used when the clock is no
|
||||
longer synchronised to avoid quickly drifting away from true time if there was
|
||||
a short-term deviation in the drift before the synchronisation was lost.
|
||||
over exponentially increasing intervals. They are used to avoid quickly
|
||||
drifting away from true time when the clock was not updated for a longer period
|
||||
of time and there was a short-term deviation in the drift before the updates
|
||||
stopped.
|
||||
+
|
||||
The directive specifies the minimum and maximum interval since the last clock
|
||||
update to switch between fallback drifts. They are defined as a power of 2 (in
|
||||
@@ -705,8 +802,10 @@ fallbackdrift 16 19
|
||||
+
|
||||
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
|
||||
19 (6 days). The system clock frequency will be set to the first fallback 18
|
||||
hours after last clock update, to the second after 36 hours, etc. This might be
|
||||
a good setting to cover daily and weekly temperature fluctuations.
|
||||
hours after last clock update, to the second after 36 hours, and so on. This
|
||||
might be a good setting to cover frequency changes due to daily and weekly
|
||||
temperature fluctuations. When the frequency is set to a fallback, the state of
|
||||
the clock will change to '`Not synchronised`'.
|
||||
+
|
||||
By default (or if the specified maximum or minimum is 0), no fallbacks are used
|
||||
and the clock frequency changes only with new measurements from NTP sources,
|
||||
@@ -728,8 +827,8 @@ that error is corrected. There are four options:
|
||||
When inserting a leap second, the kernel steps the system clock backwards by
|
||||
one second when the clock gets to 00:00:00 UTC. When deleting a leap second, it
|
||||
steps forward by one second when the clock gets to 23:59:59 UTC. This is the
|
||||
default mode when the system driver supports leap seconds (i.e. on Linux,
|
||||
FreeBSD, NetBSD and Solaris).
|
||||
default mode when the system driver supports leap seconds (i.e. all supported
|
||||
systems with the exception of macOS 12 and earlier).
|
||||
*step*:::
|
||||
This is similar to the *system* mode, except the clock is stepped by
|
||||
*chronyd* instead of the kernel. It can be useful to avoid bugs in the kernel
|
||||
@@ -784,15 +883,28 @@ clients to safely synchronise with multiple identically configured leap
|
||||
smearing servers.
|
||||
|
||||
[[leapsectz]]*leapsectz* _timezone_::
|
||||
This directive is used to set the name of the timezone in the system tz
|
||||
database which *chronyd* can use to find out when will the next leap second
|
||||
occur. It will periodically check if the times 23:59:59 and 23:59:60 are valid
|
||||
on Jun 30 and Dec 31 in the timezone. This typically works with the *right/UTC*
|
||||
timezone.
|
||||
This directive specifies a timezone in the system tz database which *chronyd*
|
||||
can use to determine when will the next leap second occur and what is the
|
||||
current offset between TAI and UTC. It will periodically check if 23:59:59 and
|
||||
23:59:60 are valid times in the timezone. This typically works with the
|
||||
_right/UTC_ timezone.
|
||||
+
|
||||
This directive is mainly useful with reference clocks which do not provide
|
||||
leap second information. It is not necessary to restart *chronyd* if the tz
|
||||
database is updated with a new leap second at least 12 hours before the event.
|
||||
When a leap second is announced, the timezone needs to be updated at least 12
|
||||
hours before the leap second. It is not necessary to restart *chronyd*.
|
||||
+
|
||||
This directive is useful with reference clocks and other time sources which do
|
||||
not announce leap seconds, or announce them too late for an NTP server to
|
||||
forward them to its own clients. Clients of leap smearing servers must not
|
||||
use this directive.
|
||||
+
|
||||
It is also useful when the system clock is required to have correct TAI-UTC
|
||||
offset. Note that the offset is set only when leap seconds are handled by the
|
||||
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
|
||||
+
|
||||
The specified timezone is not used as an exclusive source of information about
|
||||
leap seconds. If a majority of time sources announce on the last day of June or
|
||||
December that a leap second should be inserted or deleted, it will be accepted
|
||||
even if it is not included in the timezone.
|
||||
+
|
||||
An example of the directive is:
|
||||
+
|
||||
@@ -864,7 +976,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
|
||||
system clock. It limits the frequency adjustment that *chronyd* is allowed to
|
||||
use to correct the measured drift. It is an additional limit to the maximum
|
||||
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
|
||||
on FreeBSD and NetBSD, 32500 ppm on Solaris).
|
||||
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
|
||||
+
|
||||
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
|
||||
limited by the system driver rather than this directive.
|
||||
@@ -899,14 +1011,18 @@ The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
|
||||
to slew the time. It limits the slew rate controlled by the correction time
|
||||
ratio (which can be set by the <<corrtimeratio,*corrtimeratio*>> directive) and
|
||||
is effective only on systems where *chronyd* is able to control the rate (i.e.
|
||||
Linux, FreeBSD, NetBSD, Solaris).
|
||||
all supported systems with the exception of macOS 12 or earlier).
|
||||
+
|
||||
For each system there is a maximum frequency offset of the clock that
|
||||
can be set by the driver. On Linux it is 100000 ppm, on FreeBSD and NetBSD
|
||||
it is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel
|
||||
limitation, setting *maxslewrate* on FreeBSD and NetBSD to a value between 500
|
||||
For each system there is a maximum frequency offset of the clock that can be set
|
||||
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
|
||||
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
|
||||
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
|
||||
ppm and 5000 ppm will effectively set it to 500 ppm.
|
||||
+
|
||||
In early beta releases of macOS 13 this capability is disabled because of a
|
||||
system kernel bug. When the kernel bug is fixed, chronyd will detect this and
|
||||
re-enable the capability (see above limitations) with no recompilation required.
|
||||
+
|
||||
By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
|
||||
|
||||
[[tempcomp]]
|
||||
@@ -994,7 +1110,7 @@ both a client of its servers, and a server to other clients.
|
||||
Examples of the use of the directive are as follows:
|
||||
+
|
||||
----
|
||||
allow foo.example.net
|
||||
allow 1.2.3.4
|
||||
allow 1.2
|
||||
allow 3.4.5
|
||||
allow 6.7.8/22
|
||||
@@ -1005,7 +1121,8 @@ allow ::/0
|
||||
allow
|
||||
----
|
||||
+
|
||||
The first directive allows the named node to be an NTP client of this computer.
|
||||
The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP
|
||||
client of this computer.
|
||||
The second directive allows any node with an IPv4 address of the form _1.2.x.y_
|
||||
(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise,
|
||||
the third directive allows any node with an IPv4 address of the form _3.4.5.x_
|
||||
@@ -1046,6 +1163,10 @@ Within a configuration file this capability is probably rather moot; however,
|
||||
it is of greater use for reconfiguration at run-time via *chronyc* with the
|
||||
<<chronyc.adoc#allow,*allow all*>> command.
|
||||
+
|
||||
The directive allows a hostname to be specified instead of an IP address, but
|
||||
the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs
|
||||
to be started when the network is already up and DNS is working).
|
||||
+
|
||||
Note, if the <<initstepslew,*initstepslew*>> directive is used in the
|
||||
configuration file, each of the computers listed in that directive must allow
|
||||
client access by this computer for it to work.
|
||||
@@ -1221,8 +1342,8 @@ source port used in NTP client requests can be set by the
|
||||
<<acquisitionport,*acquisitionport*>> directive.
|
||||
|
||||
[[ratelimit]]*ratelimit* [_option_]...::
|
||||
This directive configures response rate limiting for NTP packets. Its purpose
|
||||
is to reduce network traffic with misconfigured or broken NTP clients that are
|
||||
This directive enables response rate limiting for NTP packets. Its purpose is
|
||||
to reduce network traffic with misconfigured or broken NTP clients that are
|
||||
polling the server too frequently. The limits are applied to individual IP
|
||||
addresses. If multiple clients share one IP address (e.g. multiple hosts behind
|
||||
NAT), the sum of their traffic will be limited. If a client that increases its
|
||||
@@ -1237,16 +1358,16 @@ in any order):
|
||||
+
|
||||
*interval*:::
|
||||
This option sets the minimum interval between responses. It is defined as a
|
||||
power of 2 in seconds. The default value is -10 (1/1024 of a second, or 1024
|
||||
packets per second). The minimum value is -19 (524288 packets per second) and
|
||||
the maximum value is 12 (one packet per 4096 seconds). Note that with values
|
||||
below -4 the rate limiting is coarse (responses are allowed in bursts, even if
|
||||
the interval between them is shorter than the specified interval).
|
||||
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
|
||||
is -19 (524288 packets per second) and the maximum value is 12 (one packet per
|
||||
4096 seconds). Note that with values below -4 the rate limiting is coarse
|
||||
(responses are allowed in bursts, even if the interval between them is shorter
|
||||
than the specified interval).
|
||||
*burst*:::
|
||||
This option sets the maximum number of responses that can be sent in a burst,
|
||||
temporarily exceeding the limit specified by the *interval* option. This is
|
||||
useful for clients that make rapid measurements on start (e.g. *chronyd* with
|
||||
the *iburst* option). The default value is 16. The minimum value is 1 and the
|
||||
the *iburst* option). The default value is 8. The minimum value is 1 and the
|
||||
maximum value is 255.
|
||||
*leak*:::
|
||||
This option sets the rate at which responses are randomly allowed even if the
|
||||
@@ -1254,23 +1375,19 @@ limits specified by the *interval* and *burst* options are exceeded. This is
|
||||
necessary to prevent an attacker who is sending requests with a spoofed
|
||||
source address from completely blocking responses to that address. The leak
|
||||
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
|
||||
least every fourth request has a response. The minimum value is 0, which
|
||||
disables the rate limiting, and the maximum value is 4 (one response per 16
|
||||
requests).
|
||||
least every fourth request has a response. The minimum value is 1 and the
|
||||
maximum value is 4.
|
||||
::
|
||||
+
|
||||
An example use of the directive is:
|
||||
+
|
||||
----
|
||||
ratelimit interval 1 burst 8
|
||||
ratelimit interval 1 burst 16
|
||||
----
|
||||
+
|
||||
This would reduce the response rate for IP addresses sending packets on average
|
||||
more than once per 2 seconds, or sending packets in bursts of more than 8
|
||||
more than once per 2 seconds, or sending packets in bursts of more than 16
|
||||
packets, by up to 75% (with default *leak* of 2).
|
||||
+
|
||||
Rate limiting can be disabled by setting the *leak* option to 0, or by the
|
||||
<<noclientlog,*noclientlog*>> directive.
|
||||
|
||||
[[smoothtime]]*smoothtime* _max-freq_ _max-wander_ [*leaponly*]::
|
||||
The *smoothtime* directive can be used to enable smoothing of the time that
|
||||
@@ -1397,18 +1514,16 @@ This would make *chronyd* use UDP 257 as its command port. (*chronyc* would
|
||||
need to be run with the *-p 257* switch to inter-operate correctly.)
|
||||
|
||||
[[cmdratelimit]]*cmdratelimit* [_option_]...::
|
||||
This directive is identical to the <<ratelimit,*ratelimit*>> directive, except
|
||||
it configures rate limiting for command packets and responses to localhost are
|
||||
never limited. It is disabled by default (the default *leak* is 0).
|
||||
This directive enables response rate limiting for command packets. It is
|
||||
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
|
||||
localhost are never limited and the default interval is -4 (16 packets per
|
||||
second).
|
||||
+
|
||||
An example of the use of the directive is:
|
||||
+
|
||||
----
|
||||
cmdratelimit interval -2 burst 128 leak 2
|
||||
cmdratelimit interval 2
|
||||
----
|
||||
+
|
||||
This would reduce response rate for addresses that send more than 4 requests
|
||||
per second, or bursts of more than 128 packets, by up to 75%.
|
||||
|
||||
=== Real-time clock (RTC)
|
||||
|
||||
@@ -1488,8 +1603,8 @@ If the *rtconutc* directive appears, it means the RTC is required to keep UTC.
|
||||
The directive takes no arguments. It is equivalent to specifying the *-u*
|
||||
switch to the Linux *hwclock* program.
|
||||
+
|
||||
Note that this setting is overridden when the <<hwclockfile,*hwclockfile*>>
|
||||
directive is specified.
|
||||
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
|
||||
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
|
||||
|
||||
[[rtcsync]]*rtcsync*::
|
||||
The *rtcsync* directive enables a mode where the system time is periodically
|
||||
@@ -1511,10 +1626,12 @@ The log files are written to the directory specified by the <<logdir,*logdir*>>
|
||||
directive. A banner is periodically written to the files to indicate the
|
||||
meanings of the columns.
|
||||
+
|
||||
*measurements*:::
|
||||
*rawmeasurements*:::
|
||||
This option logs the raw NTP measurements and related information to a file
|
||||
called _measurements.log_. An example line (which actually appears as a single
|
||||
line in the file) from the log file is shown below.
|
||||
called _measurements.log_. An entry is made for each packet received from the
|
||||
source. This can be useful when debugging a problem. An example line (which
|
||||
actually appears as a single line in the file) from the log file is shown
|
||||
below.
|
||||
+
|
||||
----
|
||||
2016-11-09 05:40:50 203.0.113.15 N 2 111 111 1111 10 10 1.0 \
|
||||
@@ -1550,12 +1667,18 @@ from the example line above):
|
||||
. The root dispersion (_EPSILON_ in RFC 5905). [7.446e-03]
|
||||
. Reference ID of the server's source as a hexadecimal number. [CB00717B]
|
||||
. NTP mode of the received packet (_1_=active peer, _2_=passive peer,
|
||||
_3_=server, _B_=basic, _I_=interleaved). [4B]
|
||||
_4_=server, _B_=basic, _I_=interleaved). [4B]
|
||||
. Source of the local transmit timestamp
|
||||
(_D_=daemon, _K_=kernel, _H_=hardware). [D]
|
||||
. Source of the local receive timestamp
|
||||
(_D_=daemon, _K_=kernel, _H_=hardware). [K]
|
||||
+
|
||||
*measurements*:::
|
||||
This option is identical to the *rawmeasurements* option, except it logs only
|
||||
valid measurements from synchronised sources, i.e. measurements which passed
|
||||
the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the
|
||||
source's performance.
|
||||
+
|
||||
*statistics*:::
|
||||
This option logs information about the regression processing to a file called
|
||||
_statistics.log_. An example line (which actually appears as a single line in
|
||||
@@ -1598,11 +1721,11 @@ from the example line above):
|
||||
to be discarded. The number of runs for the data that is being retained is
|
||||
tabulated. Values of approximately half the number of samples are expected.
|
||||
[8]
|
||||
. The estimated asymmetry of network jitter on the path to the source which was
|
||||
used to correct the measured offsets. The asymmetry can be between -0.5 and
|
||||
0.5. A negative value means the delay of packets sent to the source is
|
||||
more variable than the delay of packets sent from the source back. [0.00,
|
||||
i.e. no correction for asymmetry]
|
||||
. The estimated or configured asymmetry of network jitter on the path to the
|
||||
source which was used to correct the measured offsets. The asymmetry can be
|
||||
between -0.5 and +0.5. A negative value means the delay of packets sent to
|
||||
the source is more variable than the delay of packets sent from the source
|
||||
back. [0.00, i.e. no correction for asymmetry]
|
||||
+
|
||||
*tracking*:::
|
||||
This option logs changes to the estimate of the system's gain or loss rate, and
|
||||
@@ -1611,33 +1734,42 @@ actually appears as a single line in the file) from the log file is shown
|
||||
below.
|
||||
+
|
||||
----
|
||||
2015-02-23 05:40:50 203.0.113.15 3 340.529 1.606 1.046e-03 N \
|
||||
4 6.849e-03 -4.670e-04
|
||||
2017-08-22 13:22:36 203.0.113.15 2 -3.541 0.075 -8.621e-06 N \
|
||||
2 2.940e-03 -2.084e-04 1.534e-02 3.472e-04 8.304e-03
|
||||
----
|
||||
+
|
||||
The columns are as follows (the quantities in square brackets are the
|
||||
values from the example line above) :
|
||||
+
|
||||
. Date [2015-02-03]
|
||||
. Date [2017-08-22]
|
||||
. Hour:Minute:Second. Note that the date-time pair is expressed in UTC, not the
|
||||
local time zone. [05:40:50]
|
||||
local time zone. [13:22:36]
|
||||
. The IP address of the server or peer to which the local system is synchronised.
|
||||
[203.0.113.15]
|
||||
. The stratum of the local system. [3]
|
||||
. The stratum of the local system. [2]
|
||||
. The local system frequency (in ppm, positive means the local system runs fast
|
||||
of UTC). [340.529]
|
||||
. The error bounds on the frequency (in ppm). [1.606]
|
||||
. The estimated local offset at the epoch (which is rapidly corrected by
|
||||
slewing the local clock. (In seconds, positive indicates the local system
|
||||
is fast of UTC). [1.046e-3]
|
||||
of UTC). [-3.541]
|
||||
. The error bounds on the frequency (in ppm). [0.075]
|
||||
. The estimated local offset at the epoch, which is normally corrected by
|
||||
slewing the local clock (in seconds, positive indicates the clock is fast of
|
||||
UTC). [-8.621e-06]
|
||||
. Leap status (_N_ means normal, _+_ means that the last minute of this month
|
||||
has 61 seconds, _-_ means that the last minute of the month has 59 seconds,
|
||||
_?_ means the clock is not currently synchronised.) [N]
|
||||
. The number of combined sources. [4]
|
||||
. The number of combined sources. [2]
|
||||
. The estimated standard deviation of the combined offset (in seconds).
|
||||
[6.849e-03]
|
||||
[2.940e-03]
|
||||
. The remaining offset correction from the previous update (in seconds,
|
||||
positive means the system clock is slow of UTC). [-4.670e-04]
|
||||
positive means the system clock is slow of UTC). [-2.084e-04]
|
||||
. The total of the network path delays to the reference clock to which
|
||||
the local clock is ultimately synchronised (in seconds). [1.534e-02]
|
||||
. The total dispersion accumulated through all the servers back to the
|
||||
reference clock to which the local clock is ultimately synchronised
|
||||
(in seconds). [3.472e-04]
|
||||
. The maximum estimated error of the system clock in the interval since the
|
||||
previous update (in seconds). It includes the offset, remaining offset
|
||||
correction, root delay, and dispersion from the previous update with the
|
||||
dispersion which accumulated in the interval. [8.304e-03]
|
||||
+
|
||||
*rtc*:::
|
||||
This option logs information about the system's real-time clock. An example
|
||||
@@ -1782,7 +1914,7 @@ sendmail binary.
|
||||
|
||||
=== Miscellaneous
|
||||
|
||||
[[hwtimestamp]]*hwtimestamp* _interface_::
|
||||
[[hwtimestamp]]*hwtimestamp* _interface_ [_option_]...::
|
||||
This directive enables hardware timestamping of NTP packets sent to and
|
||||
received from the specified network interface. The network interface controller
|
||||
(NIC) uses its own clock to accurately timestamp the actual transmissions and
|
||||
@@ -1798,25 +1930,75 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
|
||||
This directive is supported on Linux 3.19 and newer. The NIC must support HW
|
||||
timestamping, which can be verified with the *ethtool -T* command. The list of
|
||||
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
|
||||
_SOF_TIMESTAMPING_TX_HARDWARE_, _SOF_TIMESTAMPING_RX_HARDWARE_, and the filter
|
||||
modes should have _HWTSTAMP_FILTER_ALL_. When *chronyd* is running, no other
|
||||
process should be working with the clock on the NIC. If no *hwtimestamp*
|
||||
directive is specified, *chronyd* will try to use software (kernel)
|
||||
timestamping. With both hardware and software timestamping there are
|
||||
some limitations on which packets can be actually timestamped, e.g. transmit
|
||||
timestamping does not currently work with IPv6 packets using IP options and
|
||||
hardware receive timestamping does not work with packets from bridged
|
||||
interfaces. The timestamping used in measurements is indicated in the
|
||||
_measurements.log_ file if enabled by the <<log,*log measurements*>> directive,
|
||||
and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in *chronyc*.
|
||||
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
|
||||
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
|
||||
timestamping of received packets. Timestamping of packets received from bridged
|
||||
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
|
||||
running, no other process (e.g. a PTP daemon) should be working with the NIC
|
||||
clock.
|
||||
+
|
||||
If the kernel supports software timestamping, it will be enabled for all
|
||||
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
|
||||
indicated in the _measurements.log_ file if enabled by the <<log,*log
|
||||
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
|
||||
*chronyc*.
|
||||
+
|
||||
If the specified interface is _*_, *chronyd* will try to enable HW timestamping
|
||||
on all available interfaces.
|
||||
+
|
||||
An example of the directive is:
|
||||
The *hwtimestamp* directive has the following options:
|
||||
+
|
||||
*minpoll* _poll_:::
|
||||
This option specifies the minimum interval between readings of the NIC clock.
|
||||
It's defined as a power of two. It should correspond to the minimum polling
|
||||
interval of all NTP sources and the minimum expected polling interval of NTP
|
||||
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
|
||||
of a second).
|
||||
*minsamples* _samples_:::
|
||||
This option specifies the minimum number of readings kept for tracking of the
|
||||
NIC clock. The default value is 2.
|
||||
*maxsamples* _samples_:::
|
||||
This option specifies the maximum number of readings kept for tracking of the
|
||||
NIC clock. The default value is 16.
|
||||
*precision* _precision_:::
|
||||
This option specifies the assumed precision of reading of the NIC clock. The
|
||||
default value is 100e-9 (100 nanoseconds).
|
||||
*txcomp* _compensation_:::
|
||||
This option specifies the difference in seconds between the actual transmission
|
||||
time at the physical layer and the reported transmit timestamp. This value will
|
||||
be added to transmit timestamps obtained from the NIC. The default value is 0.
|
||||
*rxcomp* _compensation_:::
|
||||
This option specifies the difference in seconds between the reported receive
|
||||
timestamp and the actual reception time at the physical layer. This value will
|
||||
be subtracted from receive timestamps obtained from the NIC. The default value
|
||||
is 0.
|
||||
*nocrossts*:::
|
||||
Some hardware can precisely cross timestamp the NIC clock with the system
|
||||
clock. This option disables the use of the cross timestamping.
|
||||
*rxfilter* _filter_:::
|
||||
This option selects the receive timestamping filter. The _filter_ can be one of
|
||||
the following:
|
||||
_all_::::
|
||||
Enables timestamping of all received packets.
|
||||
_ntp_::::
|
||||
Enables timestamping of received NTP packets.
|
||||
_none_::::
|
||||
Disables timestamping of received packets.
|
||||
:::
|
||||
The most specific filter for timestamping NTP packets which is supported by the
|
||||
NIC is selected by default. Some NICs can timestamp only PTP packets, which
|
||||
limits the selection to the _none_ filter. Forcing timestamping of all packets
|
||||
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
|
||||
useful when packets are received from or on a non-standard UDP port (e.g.
|
||||
specified by the *port* directive).
|
||||
::
|
||||
+
|
||||
Examples of the directive are:
|
||||
+
|
||||
----
|
||||
hwtimestamp eth0
|
||||
hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
|
||||
hwtimestamp *
|
||||
----
|
||||
|
||||
[[include]]*include* _pattern_::
|
||||
@@ -1853,12 +2035,18 @@ format of the file is shown below:
|
||||
+
|
||||
Each line consists of an ID, name of an authentication hash function (optional),
|
||||
and a password. The ID can be any unsigned integer in the range 1 through
|
||||
2^32-1. The default hash function is *MD5*. Depending on how *chronyd*
|
||||
was compiled, other supported functions might be *SHA1*, *SHA256*, *SHA384*,
|
||||
*SHA512*, *RMD128*, *RMD160*, *RMD256*, *RMD320*, *TIGER*, and *WHIRLPOOL*. The
|
||||
password can be specified as a string of characters not containing white space
|
||||
with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:*
|
||||
prefix. The maximum length of the line is 2047 characters.
|
||||
2^32-1. The default hash function is *MD5*, which is always supported.
|
||||
+
|
||||
If *chronyd* was built with enabled support for hashing using a crypto library
|
||||
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
|
||||
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
|
||||
*chronyd* using, some or all of the following functions may also be available:
|
||||
*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*,
|
||||
*RMD320*, *TIGER*, *WHIRLPOOL*.
|
||||
+
|
||||
The password can be specified as a string of characters not containing white
|
||||
space with an optional *ASCII:* prefix, or as a hexadecimal number with the
|
||||
*HEX:* prefix. The maximum length of the line is 2047 characters.
|
||||
+
|
||||
The password is used with the hash function to generate and verify a message
|
||||
authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
|
||||
@@ -1870,46 +2058,51 @@ that has password shorter than 80 bits.
|
||||
The <<chronyc.adoc#keygen,*keygen*>> command of *chronyc* can be used to
|
||||
generate random keys for the key file. By default, it generates 160-bit MD5 or
|
||||
SHA1 keys.
|
||||
+
|
||||
For security reasons, the file should be readable only by root and the user
|
||||
under which *chronyd* is normally running (to allow *chronyd* to re-read the
|
||||
file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
|
||||
|
||||
[[lock_all]]*lock_all*::
|
||||
The *lock_all* directive will lock chronyd into RAM so that it will never be
|
||||
paged out. This mode is only supported on Linux. This directive uses the Linux
|
||||
*mlockall()* system call to prevent *chronyd* from ever being swapped out. This
|
||||
should result in lower and more consistent latency. It should not have
|
||||
significant impact on performance as *chronyd's* memory usage is modest. The
|
||||
*mlockall(2)* man page has more details.
|
||||
paged out. This mode is supported on Linux, FreeBSD, NetBSD, and Solaris. This
|
||||
directive uses the POSIX *mlockall()* system call to prevent *chronyd* from
|
||||
ever being swapped out. This should result in lower and more consistent
|
||||
latency. It should not have significant impact on performance as *chronyd's*
|
||||
memory usage is modest. The *mlockall(2)* man page has more details.
|
||||
|
||||
[[pidfile]]*pidfile* _file_::
|
||||
*chronyd* always writes its process ID (PID) to a file, and checks this file on
|
||||
startup to see if another *chronyd* might already be running on the system. By
|
||||
default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
|
||||
allows the name to be changed, e.g.:
|
||||
Unless *chronyd* is started with the *-Q* option, it writes its process ID
|
||||
(PID) to a file, and checks this file on startup to see if another *chronyd*
|
||||
might already be running on the system. By default, the file used is
|
||||
_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
|
||||
e.g.:
|
||||
+
|
||||
----
|
||||
pidfile /run/chronyd.pid
|
||||
----
|
||||
|
||||
[[sched_priority]]*sched_priority* _priority_::
|
||||
On Linux, the *sched_priority* directive will select the SCHED_FIFO real-time
|
||||
scheduler at the specified priority (which must be between 0 and 100). On
|
||||
macOS, this option must have either a value of 0 (the default) to disable the
|
||||
thread time constraint policy or 1 for the policy to be enabled. Other systems
|
||||
do not support this option.
|
||||
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
|
||||
select the SCHED_FIFO real-time scheduler at the specified priority (which must
|
||||
be between 0 and 100). On macOS, this option must have either a value of 0 (the
|
||||
default) to disable the thread time constraint policy or 1 for the policy to be
|
||||
enabled.
|
||||
+
|
||||
On Linux, this directive uses the *sched_setscheduler()* system call to
|
||||
instruct the kernel to use the SCHED_FIFO first-in, first-out real-time
|
||||
scheduling policy for *chronyd* with the specified priority. This means that
|
||||
whenever *chronyd* is ready to run it will run, interrupting whatever else is
|
||||
running unless it is a higher priority real-time process. This should not
|
||||
impact performance as *chronyd* resource requirements are modest, but it should
|
||||
result in lower and more consistent latency since *chronyd* will not need to
|
||||
wait for the scheduler to get around to running it. You should not use this
|
||||
unless you really need it. The *sched_setscheduler(2)* man page has more
|
||||
details.
|
||||
On systems other than macOS, this directive uses the *pthread_setschedparam()*
|
||||
system call to instruct the kernel to use the SCHED_FIFO first-in, first-out
|
||||
real-time scheduling policy for *chronyd* with the specified priority. This
|
||||
means that whenever *chronyd* is ready to run it will run, interrupting
|
||||
whatever else is running unless it is a higher priority real-time process. This
|
||||
should not impact performance as *chronyd* resource requirements are modest,
|
||||
but it should result in lower and more consistent latency since *chronyd* will
|
||||
not need to wait for the scheduler to get around to running it. You should not
|
||||
use this unless you really need it. The *pthread_setschedparam(3)* man page has
|
||||
more details.
|
||||
+
|
||||
On macOS, this directive uses the *thread_policy_set()* kernel call to
|
||||
specify real-time scheduling. As noted for Linux, you should not use this
|
||||
directive unless you really need it.
|
||||
specify real-time scheduling. As noted above, you should not use this directive
|
||||
unless you really need it.
|
||||
|
||||
[[user]]*user* _user_::
|
||||
The *user* directive sets the name of the system user to which *chronyd* will
|
||||
@@ -2136,8 +2329,7 @@ is made of the RTC error at a particular RTC second, and the rate at which the
|
||||
RTC gains or loses time relative to true time.
|
||||
|
||||
When the computer is powered down, the measurement histories for all the NTP
|
||||
servers are saved to files (if the <<dumponexit,*dumponexit*>> directive is
|
||||
specified in the configuration file), and the RTC tracking information is also
|
||||
servers are saved to files, and the RTC tracking information is also
|
||||
saved to a file (if the <<rtcfile,*rtcfile*>> directive has been specified).
|
||||
These pieces of information are also saved if the <<chronyc.adoc#dump,*dump*>>
|
||||
and <<chronyc.adoc#writertc,*writertc*>> commands respectively are issued
|
||||
@@ -2190,7 +2382,6 @@ log statistics measurements tracking
|
||||
driftfile @CHRONYVARDIR@/drift
|
||||
makestep 1.0 3
|
||||
maxupdateskew 100.0
|
||||
dumponexit
|
||||
dumpdir @CHRONYVARDIR@
|
||||
rtcfile @CHRONYVARDIR@/rtc
|
||||
----
|
||||
@@ -2225,27 +2416,36 @@ information to be saved.
|
||||
*chronyd* can be configured to operate as a public NTP server, e.g. to join the
|
||||
http://www.pool.ntp.org/en/join.html[pool.ntp.org] project. The configuration
|
||||
is similar to the NTP client with permanent connection, except it needs to
|
||||
allow client access from all addresses. It is recommended to handpick at least
|
||||
few good servers, and possibly combine them with a random selection of other
|
||||
servers in the pool. The rate limiting interval can be increased to save more
|
||||
bandwidth on misconfigured and broken NTP clients. The *-r* option with the
|
||||
*dumpdir* directive shortens the time for which *chronyd* will not serve time
|
||||
to its clients when it needs to be restarted for any reason.
|
||||
allow client access from all addresses. It is recommended to find at least four
|
||||
good servers (e.g. from the pool, or on the NTP homepage). If the server has a
|
||||
hardware reference clock (e.g. a GPS receiver), it can be specified by the
|
||||
<<refclock,*refclock*>> directive.
|
||||
|
||||
The configuration file might be:
|
||||
The amount of memory used for logging client accesses can be increased in order
|
||||
to enable clients to use the interleaved mode even when the server has a large
|
||||
number of clients, and better support rate limiting if it is enabled by the
|
||||
<<ratelimit,*ratelimit*>> directive. The system timezone database, if it is
|
||||
kept up to date and includes the _right/UTC_ timezone, can be used as a
|
||||
reliable source to determine when a leap second will be applied to UTC. The
|
||||
*-r* option with the <<dumpdir,*dumpdir*>> directive shortens the time in which
|
||||
*chronyd* will not be able to serve time to its clients when it needs to be
|
||||
restarted (e.g. after upgrading to a newer version, or a change in the
|
||||
configuration).
|
||||
|
||||
The configuration file could look like:
|
||||
|
||||
----
|
||||
server foo.example.net iburst
|
||||
server bar.example.net iburst
|
||||
server baz.example.net iburst
|
||||
pool pool.ntp.org iburst
|
||||
server qux.example.net iburst
|
||||
makestep 1.0 3
|
||||
rtcsync
|
||||
allow
|
||||
ratelimit interval 1
|
||||
clientloglimit 100000000
|
||||
leapsectz right/UTC
|
||||
driftfile @CHRONYVARDIR@/drift
|
||||
dumpdir @CHRONYRUNDIR@
|
||||
dumponexit
|
||||
----
|
||||
|
||||
== SEE ALSO
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// This file is part of chrony
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Miroslav Lichvar 2009-2016
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -129,15 +130,15 @@ performance. An example of the output is shown below.
|
||||
----
|
||||
Reference ID : CB00710F (foo.example.net)
|
||||
Stratum : 3
|
||||
Ref time (UTC) : Fri Feb 3 15:00:29 2012
|
||||
System time : 0.000001501 seconds slow of NTP time
|
||||
Last offset : -0.000001632 seconds
|
||||
RMS offset : 0.000002360 seconds
|
||||
Frequency : 331.898 ppm fast
|
||||
Residual freq : 0.004 ppm
|
||||
Skew : 0.154 ppm
|
||||
Root delay : 0.373169 seconds
|
||||
Root dispersion : 0.024780 seconds
|
||||
Ref time (UTC) : Fri Jan 27 09:49:17 2017
|
||||
System time : 0.000006523 seconds slow of NTP time
|
||||
Last offset : -0.000006747 seconds
|
||||
RMS offset : 0.000035822 seconds
|
||||
Frequency : 3.225 ppm slow
|
||||
Residual freq : -0.000 ppm
|
||||
Skew : 0.129 ppm
|
||||
Root delay : 0.013639022 seconds
|
||||
Root dispersion : 0.001100737 seconds
|
||||
Update interval : 64.2 seconds
|
||||
Leap status : Normal
|
||||
----
|
||||
@@ -186,9 +187,6 @@ The '`frequency`' is the rate by which the system's clock would be wrong if
|
||||
For example, a value of 1 ppm would mean that when the system's clock thinks it
|
||||
has advanced 1 second, it has actually advanced by 1.000001 seconds relative to
|
||||
true time.
|
||||
+
|
||||
As you can see in the example, the clock in the computer is not a very
|
||||
good one; it would gain about 30 seconds per day if it was not corrected!
|
||||
*Residual freq*:::
|
||||
This shows the '`residual frequency`' for the currently selected reference
|
||||
source. This reflects any difference between what the measurements from the
|
||||
@@ -218,7 +216,7 @@ An absolute bound on the computer's clock accuracy (assuming the stratum-1
|
||||
computer is correct) is given by:
|
||||
+
|
||||
----
|
||||
clock_error <= root_dispersion + (0.5 * |root_delay|)
|
||||
clock_error <= |system_time_offset| + root_dispersion + (0.5 * root_delay)
|
||||
----
|
||||
*Update interval*:::
|
||||
This is the interval between the last two clock updates.
|
||||
@@ -340,8 +338,9 @@ register has 8 bits and is updated on every received or missed packet from
|
||||
the source. A value of 377 indicates that a valid reply was received for all
|
||||
from the last eight transmissions.
|
||||
*LastRx*:::
|
||||
This column shows how long ago the last sample was received from the source.
|
||||
This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
|
||||
This column shows how long ago the last good sample (which is shown in the next
|
||||
column) was received from the source. Measurements that failed some tests are
|
||||
ignored. This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
|
||||
minutes, hours, days, or years.
|
||||
*Last sample*:::
|
||||
This column shows the offset between the local clock and the source at the
|
||||
@@ -458,7 +457,7 @@ Poll interval : 10 (1024 seconds)
|
||||
Precision : -24 (0.000000060 seconds)
|
||||
Root delay : 0.000015 seconds
|
||||
Root dispersion : 0.000015 seconds
|
||||
Reference ID : 50505331
|
||||
Reference ID : 47505300 (GPS)
|
||||
Reference time : Fri Nov 25 15:22:12 2016
|
||||
Offset : -0.000060878 seconds
|
||||
Peer delay : 0.000175634 seconds
|
||||
@@ -724,6 +723,13 @@ particular source or sources has been restored.
|
||||
+
|
||||
The syntax is identical to that of the <<offline,*offline*>> command.
|
||||
|
||||
[[onoffline]]
|
||||
*onoffline*::
|
||||
The *onoffline* command tells *chronyd* to switch all sources to the online or
|
||||
offline status according to the current network configuration. A source is
|
||||
considered online if it is possible to send requests to it, i.e. a route to the
|
||||
network is present.
|
||||
|
||||
[[polltarget]]*polltarget* _address_ _polltarget_::
|
||||
The *polltarget* command is used to modify the poll target for one of the
|
||||
current set of sources. It is equivalent to the *polltarget* option in the
|
||||
@@ -1120,17 +1126,19 @@ purged. An example of how to do this is shown below.
|
||||
|
||||
[[dump]]*dump*::
|
||||
The *dump* command causes *chronyd* to write its current history of
|
||||
measurements for each of its sources to dump files, either for inspection or to
|
||||
support the *-r* option when *chronyd* is restarted.
|
||||
+
|
||||
The *dump* command is somewhat equivalent to the
|
||||
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration
|
||||
file.
|
||||
+
|
||||
To use the *dump* command, you might want to configure the name of the
|
||||
directory into which the dump files will be written. This can only be
|
||||
done in the configuration file with the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
|
||||
directive.
|
||||
measurements for each of its sources to dump files in the directory specified
|
||||
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
|
||||
directive. Note that *chronyd* does this automatically when it exits. This
|
||||
command is mainly useful for inspection of the history whilst *chronyd* is
|
||||
running.
|
||||
|
||||
[[rekey]]*rekey*::
|
||||
The *rekey* command causes *chronyd* to re-read the key file specified in the
|
||||
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
|
||||
|
||||
[[rekey]]*shutdown*::
|
||||
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
|
||||
the process the SIGTERM signal.
|
||||
|
||||
=== Client commands
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// This file is part of chrony
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Miroslav Lichvar 2009-2016
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -62,28 +62,33 @@ When run in this mode, the program will not detach itself from the terminal.
|
||||
|
||||
*-d*::
|
||||
When run in this mode, the program will not detach itself from the terminal,
|
||||
and all messages will be sent to the terminal instead of to syslog. When
|
||||
and all messages will be written to the terminal instead of syslog. When
|
||||
*chronyd* was compiled with debugging support, this option can be used twice to
|
||||
print also debugging messages.
|
||||
|
||||
*-l* _file_::
|
||||
This option specifies a file which should be used for logging instead of syslog
|
||||
or terminal.
|
||||
|
||||
*-q*::
|
||||
When run in this mode, *chronyd* will set the system clock once and exit. It
|
||||
will not detach from the terminal.
|
||||
|
||||
*-Q*::
|
||||
This option is similar to *-q*, but it will only print the offset without any
|
||||
corrections of the clock.
|
||||
This option is similar to the *-q* option, except it only prints the offset
|
||||
without making any corrections of the clock and it allows *chronyd* to be
|
||||
started without root privileges.
|
||||
|
||||
*-r*::
|
||||
This option will try to reload and then delete files containing sample
|
||||
histories for each of the servers and reference clocks being used. These
|
||||
histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
|
||||
*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
|
||||
histories for each of the servers and reference clocks being used. The
|
||||
files are expected to be in the directory specified by the
|
||||
<<chrony.conf.adoc#dumpdir,*dumpdir*>>
|
||||
directive in the configuration file. This option is useful if you want to stop
|
||||
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
||||
However, it should be used only on systems where the kernel can maintain clock
|
||||
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD
|
||||
and Solaris).
|
||||
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
||||
Solaris, and macOS 10.13 or later).
|
||||
|
||||
*-R*::
|
||||
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
||||
@@ -130,7 +135,7 @@ range of privileged system calls on behalf of the parent.
|
||||
*-F* _level_::
|
||||
This option configures a system call filter when *chronyd* is compiled with
|
||||
support for the Linux secure computing (seccomp) facility. In level 1 the
|
||||
process is killed when a forbidden system call is made, in level -1 the SYSSIG
|
||||
process is killed when a forbidden system call is made, in level -1 the SIGSYS
|
||||
signal is thrown instead and in level 0 the filter is disabled (default 0).
|
||||
+
|
||||
It's recommended to enable the filter only when it's known to work on the
|
||||
@@ -151,6 +156,15 @@ support this option.
|
||||
This option will lock *chronyd* into RAM so that it will never be paged out.
|
||||
This mode is only supported on Linux.
|
||||
|
||||
*-x*::
|
||||
This option disables the control of the system clock. *chronyd* will not try to
|
||||
make any adjustments of the clock. It will assume the clock is free running and
|
||||
still track its offset and frequency relative to the estimated true time. This
|
||||
option allows *chronyd* to run without the capability to adjust or set the
|
||||
system clock (e.g. in some containers) in order to operate as an NTP server. It
|
||||
is not recommended to run *chronyd* (with or without *-x*) when another process
|
||||
is controlling the system clock.
|
||||
|
||||
*-v*::
|
||||
With this option *chronyd* will print version number to the terminal and exit.
|
||||
|
||||
|
||||
194
doc/faq.adoc
194
doc/faq.adoc
@@ -50,7 +50,7 @@ directive can be used for names that resolve to multiple addresses. For good
|
||||
reliability the client should have at least three servers. The `iburst` option
|
||||
speeds up the initial synchronisation.
|
||||
|
||||
To stabilize the initial synchronisation on the next start, the estimated drift
|
||||
To stabilise the initial synchronisation on the next start, the estimated drift
|
||||
of the system clock is saved to a file specified by the `driftfile` directive.
|
||||
|
||||
If the system clock can be far from the true time after boot for any reason,
|
||||
@@ -59,7 +59,7 @@ slewing, which would take a very long time. The `makestep` directive does
|
||||
that.
|
||||
|
||||
In order to keep the real-time clock (RTC) close to the true time, so the
|
||||
system time is reasonably close to the true time when it's initialized on the
|
||||
system time is reasonably close to the true time when it's initialised on the
|
||||
next boot from the RTC, the `rtcsync` directive enables a mode in which the
|
||||
system time is periodically copied to the RTC. It is supported on Linux and
|
||||
macOS.
|
||||
@@ -79,7 +79,7 @@ rtcsync
|
||||
|
||||
You need to add an `allow` directive to the _chrony.conf_ file in order to open
|
||||
the NTP port and allow `chronyd` to reply to client requests. `allow` with no
|
||||
specified subnet allows all IPv4 and IPv6 addresses.
|
||||
specified subnet allows access from all IPv4 and IPv6 addresses.
|
||||
|
||||
=== I have several computers on a LAN. Should be all clients of an external server?
|
||||
|
||||
@@ -97,7 +97,7 @@ _chrony.conf_ file. This configuration will be better because
|
||||
No. Starting from version 1.25, `chronyd` will keep trying to resolve
|
||||
the names specified by the `server`, `pool`, and `peer` directives in an
|
||||
increasing interval until it succeeds. The `online` command can be issued from
|
||||
`chronyc` to try to resolve them immediately.
|
||||
`chronyc` to force `chronyd` to try to resolve the names immediately.
|
||||
|
||||
=== How can I make `chronyd` more secure?
|
||||
|
||||
@@ -161,7 +161,7 @@ The first three options set the minimum and maximum allowed polling interval,
|
||||
and how should be the actual interval adjusted in the specified range. Their
|
||||
default values are 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
|
||||
`maxpoll` and 8 (samples) for `polltarget`. The default values should be used
|
||||
for general servers on the Internet. With your own NTP servers or if have
|
||||
for general servers on the Internet. With your own NTP servers, or if you have
|
||||
permission to poll some servers more frequently, setting these options for
|
||||
shorter polling intervals may significantly improve the accuracy of the system
|
||||
clock.
|
||||
@@ -171,6 +171,11 @@ network latency and stability of the system clock (which mainly depends on the
|
||||
temperature sensitivity of the crystal oscillator and the maximum rate of the
|
||||
temperature change).
|
||||
|
||||
Generally, if the `sourcestats` command usually reports a small number of
|
||||
samples retained for a source (e.g. fewer than 16), a shorter polling interval
|
||||
should be considered. If the number of samples is usually at the maximum of 64,
|
||||
a longer polling interval may work better.
|
||||
|
||||
An example of the directive for an NTP server on the Internet that you are
|
||||
allowed to poll frequently could be
|
||||
|
||||
@@ -178,15 +183,15 @@ allowed to poll frequently could be
|
||||
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
|
||||
----
|
||||
|
||||
An example using very short polling intervals for a server located in the same
|
||||
An example using shorter polling intervals with a server located in the same
|
||||
LAN could be
|
||||
|
||||
----
|
||||
server ntp.local minpoll 2 maxpoll 4 polltarget 30
|
||||
----
|
||||
|
||||
The maxdelay options are useful to ignore measurements with larger delay (e.g.
|
||||
due to congestion in the network) and improve the stability of the
|
||||
The maxdelay options are useful to ignore measurements with an unusally large
|
||||
delay (e.g. due to congestion in the network) and improve the stability of the
|
||||
synchronisation. The `maxdelaydevratio` option could be added to the example
|
||||
with local NTP server
|
||||
|
||||
@@ -194,16 +199,74 @@ with local NTP server
|
||||
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
|
||||
----
|
||||
|
||||
If your server supports the interleaved mode, the `xleave` option should be
|
||||
added to the `server` directive in order to receive server's more accurate
|
||||
hardware or kernel transmit timestamps. When combined with local hardware
|
||||
timestamping, a sub-microsecond accuracy may be possible. An example could be
|
||||
If your server supports the interleaved mode (e.g. it is running `chronyd`),
|
||||
the `xleave` option should be added to the `server` directive in order to allow
|
||||
the server to send the client more accurate transmit timestamps (kernel or
|
||||
preferably hardware). For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll 2 maxpoll 2 xleave
|
||||
server ntp.local minpoll 2 maxpoll 4 xleave
|
||||
----
|
||||
|
||||
When combined with local hardware timestamping, good network switches, and even
|
||||
shorter polling intervals, a sub-microsecond accuracy and stability of a few
|
||||
tens of nanoseconds may be possible. For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll 0 maxpoll 0 xleave
|
||||
hwtimestamp eth0
|
||||
----
|
||||
|
||||
For best stability, the CPU should be running at a constant frequency (i.e.
|
||||
disabled power saving and performance boosting). Energy-Efficient Ethernet
|
||||
(EEE) should be disabled in the network. The switches should be configured to
|
||||
prioritize NTP packets, especially if the network is expected to be heavily
|
||||
loaded.
|
||||
|
||||
If it is acceptable for NTP clients in the network to send requests at an
|
||||
excessive rate, a sub-second polling interval may be specified. A median filter
|
||||
can be enabled in order to update the clock at a reduced rate with more stable
|
||||
measurements. For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
|
||||
hwtimestamp eth0 minpoll -6
|
||||
----
|
||||
|
||||
=== Does `chronyd` have an ntpdate mode?
|
||||
|
||||
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
|
||||
With the `-Q` option it will print the measured offset without setting the
|
||||
clock. If you don't want to use a configuration file, NTP servers can be
|
||||
specified on the command line. For example:
|
||||
|
||||
----
|
||||
# chronyd -q 'pool pool.ntp.org iburst'
|
||||
----
|
||||
|
||||
=== Can `chronyd` be configured to control the clock like `ntpd`?
|
||||
|
||||
It is not possible to perfectly emulate `ntpd`, but there are some options that
|
||||
can configure `chronyd` to behave more like `ntpd`.
|
||||
|
||||
In the following example the `minsamples` directive slows down the response to
|
||||
changes in the frequency and offset of the clock. The `maxslewrate` and
|
||||
`corrtimeratio` directives reduce the maximum frequency error due to an offset
|
||||
correction and the `maxdrift` directive reduces the maximum assumed frequency
|
||||
error of the clock. The `makestep` directive enables a step threshold and the
|
||||
`maxchange` directive enables a panic threshold. The `maxclockerror` directive
|
||||
increases the minimum dispersion rate.
|
||||
|
||||
----
|
||||
minsamples 32
|
||||
maxslewrate 500
|
||||
corrtimeratio 100
|
||||
maxdrift 500
|
||||
makestep 0.128 -1
|
||||
maxchange 1000 1 1
|
||||
maxclockerror 15
|
||||
----
|
||||
|
||||
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
||||
|
||||
They were removed in version 2.2. Authentication is no longer supported in the
|
||||
@@ -242,8 +305,17 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||
=== Are NTP servers specified with the `offline` option?
|
||||
|
||||
Check that you're using ``chronyc``'s `online` and `offline` commands
|
||||
appropriately. Again, check in _measurements.log_ to see if you're getting any
|
||||
data back from the server.
|
||||
appropriately. The `activity` command prints the number of sources that are
|
||||
currently online and offline. For example:
|
||||
|
||||
----
|
||||
200 OK
|
||||
3 sources online
|
||||
0 sources offline
|
||||
0 sources doing burst (return to online)
|
||||
0 sources doing burst (return to offline)
|
||||
0 sources with unknown address
|
||||
----
|
||||
|
||||
=== Is `chronyd` allowed to step the system clock?
|
||||
|
||||
@@ -271,6 +343,49 @@ to
|
||||
makestep 1 -1
|
||||
----
|
||||
|
||||
=== Using a Windows NTP server?
|
||||
|
||||
A common issue with Windows NTP servers is that they report a very large root
|
||||
dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
|
||||
server for being too inaccurate. The `sources` command may show a valid
|
||||
measurement, but the server is not selected for synchronisation. You can check
|
||||
the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
|
||||
|
||||
The `maxdistance` value needs to be increased in _chrony.conf_ to enable
|
||||
synchronisation to such a server. For example:
|
||||
|
||||
----
|
||||
maxdistance 16.0
|
||||
----
|
||||
|
||||
=== Using a PPS reference clock?
|
||||
|
||||
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
||||
determine which second of UTC corresponds to each pulse. If it is another
|
||||
reference clock specified with the `lock` option in the `refclock` directive,
|
||||
the offset between the two reference clocks must be smaller than 0.2 seconds in
|
||||
order for the PPS reference clock to work. With NMEA reference clocks it is
|
||||
common to have a larger offset. It needs to be corrected with the `offset`
|
||||
option.
|
||||
|
||||
One approach to find out a good value of the `offset` option is to configure
|
||||
the reference clocks with the `noselect` option and compare them to an NTP
|
||||
server. For example, if the `sourcestats` command showed
|
||||
|
||||
----
|
||||
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
||||
==============================================================================
|
||||
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
|
||||
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
|
||||
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
|
||||
----
|
||||
|
||||
the offset of the NMEA source would need to be increased by about 0.504
|
||||
seconds. It does not have to be very accurate. As long as the offset of the
|
||||
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
|
||||
able to determine the seconds corresponding to the pulses and allow the samples
|
||||
to be used for synchronisation.
|
||||
|
||||
== Issues with `chronyc`
|
||||
|
||||
=== I keep getting the error `506 Cannot talk to daemon`
|
||||
@@ -326,14 +441,14 @@ Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
||||
=== What is the real-time clock (RTC)?
|
||||
|
||||
This is the clock which keeps the time even when your computer is turned off.
|
||||
It is used to initialize the system clock on boot. It normally doesn't drift
|
||||
It is used to initialise the system clock on boot. It normally doesn't drift
|
||||
more than few seconds per day.
|
||||
|
||||
There are two approaches how `chronyd` can work with it. One is to use the
|
||||
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
|
||||
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch
|
||||
the RTC. If the computer is not turned off for a long time, the RTC should
|
||||
still be close to the true time when the system clock will be initialized from
|
||||
still be close to the true time when the system clock will be initialised from
|
||||
it on the next boot.
|
||||
|
||||
The other option is to use the `rtcfile` directive, which tells `chronyd` to
|
||||
@@ -372,16 +487,31 @@ things
|
||||
|
||||
Some other program running on the system may be using the device.
|
||||
|
||||
=== What if my computer does not have an RTC or backup battery?
|
||||
|
||||
In this case you can still use the `-s` option to set the system clock to the
|
||||
last modification time of the drift file, which should correspond to the system
|
||||
time when `chronyd` was previously stopped. The initial system time will be
|
||||
increasing across reboots and applications started after `chronyd` will not
|
||||
observe backward steps.
|
||||
|
||||
== NTP-specific issues
|
||||
|
||||
=== Can `chronyd` be driven from broadcast NTP servers?
|
||||
=== Can `chronyd` be driven from broadcast/multicast NTP servers?
|
||||
|
||||
No, the broadcast client mode is not supported and there is currently no plan
|
||||
to implement it. The broadcast and multicast modes are inherently less
|
||||
accurate and less secure (even with authentication) than the ordinary
|
||||
server/client mode and they are not as useful as they used to be. Even with
|
||||
very modest hardware a single NTP server can serve time to hundreds of
|
||||
thousands of clients using the ordinary mode.
|
||||
No, the broadcast/multicast client mode is not supported and there is currently
|
||||
no plan to implement it. While the mode may be useful to simplify configuration
|
||||
of clients in large networks, it is inherently less accurate and less secure
|
||||
(even with authentication) than the ordinary client/server mode.
|
||||
|
||||
When configuring a large number of clients in a network, it is recommended to
|
||||
use the `pool` directive with a DNS name which resolves to addresses of
|
||||
multiple NTP servers. The clients will automatically replace the servers when
|
||||
they become unreachable, or otherwise unsuitable for synchronisation, with new
|
||||
servers from the pool.
|
||||
|
||||
Even with very modest hardware, an NTP server can serve time to hundreds of
|
||||
thousands of clients using the ordinary client/server mode.
|
||||
|
||||
=== Can `chronyd` transmit broadcast NTP packets?
|
||||
|
||||
@@ -396,15 +526,15 @@ option for all time sources in the _chrony.conf_ file.
|
||||
|
||||
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
|
||||
|
||||
`chronyd` will keep trying to access the server(s) that it thinks are online.
|
||||
When the network is connected again, it will take some time (on average half of
|
||||
the maximum polling interval) before new measurements are made and the clock is
|
||||
corrected. If the servers were set to offline and the `online` command was
|
||||
issued when the network was connected, `chronyd` would make new measurements
|
||||
immediately.
|
||||
`chronyd` will keep trying to access the sources that it thinks are online, and
|
||||
it will take longer before new measurements are actually made and the clock is
|
||||
corrected when the network is connected again. If the sources were set to
|
||||
offline, `chronyd` would make new measurements immediately after issuing the
|
||||
`online` command.
|
||||
|
||||
The `auto_offline` option to the `server` entry in the _chrony.conf_ file may
|
||||
be useful to switch the servers to the offline state automatically.
|
||||
Unless the network connection lasts only few minutes (less than the maximum
|
||||
polling interval), the delay is usually not a problem, and it may be acceptable
|
||||
to keep all sources online all the time.
|
||||
|
||||
== Operating systems
|
||||
|
||||
|
||||
@@ -22,6 +22,19 @@ The software is distributed as source code which has to be compiled. The source
|
||||
code is supplied in the form of a gzipped tar file, which unpacks to a
|
||||
subdirectory identifying the name and version of the program.
|
||||
|
||||
The following programs and libraries with their development files are needed to
|
||||
build `chrony`:
|
||||
|
||||
* C compiler (gcc or clang recommended)
|
||||
* GNU Make
|
||||
* Nettle, NSS, or LibTomCrypt (optional)
|
||||
* Editline (optional)
|
||||
* libcap (Linux only, optional)
|
||||
* libseccomp (Linux only, optional)
|
||||
* timepps.h header (optional)
|
||||
* Asciidoctor (for HTML documentation)
|
||||
* Bash (for testing)
|
||||
|
||||
After unpacking the source code, change directory into it, and type
|
||||
|
||||
----
|
||||
@@ -29,8 +42,8 @@ After unpacking the source code, change directory into it, and type
|
||||
----
|
||||
|
||||
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,
|
||||
an optional parameter `--prefix`, which indicates the directory tree where the
|
||||
software should be installed. For example,
|
||||
|
||||
----
|
||||
./configure --prefix=/opt/free
|
||||
@@ -40,11 +53,11 @@ 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:
|
||||
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
|
||||
CC=cc ./configure --prefix=/opt/free
|
||||
----
|
||||
|
||||
for Bourne-family shells, or
|
||||
@@ -63,11 +76,26 @@ shown. Otherwise, `Makefile` will be generated.
|
||||
On Linux, if development files for the libcap library are available, `chronyd`
|
||||
will be built with support for dropping root privileges. On other systems no
|
||||
extra library is needed. The default user which `chronyd` should run as can be
|
||||
specified with the `--with-user` option of the configure script.
|
||||
specified with the `--with-user` option of the `configure` script.
|
||||
|
||||
If development files for the POSIX threads library are available, `chronyd`
|
||||
will be built with support for asynchronous resolving of hostnames specified in
|
||||
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
|
||||
a server to respond to client requests when resolving a hostname. If you don't
|
||||
want to enable the support, specify the `--disable-asyncdns` flag to
|
||||
`configure`.
|
||||
|
||||
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
|
||||
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
|
||||
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
|
||||
`chronyd` will be built with support for other cryptographic hash functions
|
||||
than MD5, which can be used for NTP authentication with a symmetric key. If you
|
||||
don't want to enable the support, specify the `--disable-sechash` flag to
|
||||
`configure`.
|
||||
|
||||
If development files for the editline or readline library are available,
|
||||
`chronyc` will be built with line editing support. If you don't want this,
|
||||
specify the `--disable-readline` flag to configure.
|
||||
specify the `--disable-readline` flag to `configure`.
|
||||
|
||||
If a `timepps.h` header is available (e.g. from the
|
||||
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
|
||||
@@ -75,6 +103,9 @@ 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 the `CPPFLAGS` variable to `-I/path/to/timepps`.
|
||||
|
||||
The `--help` option can be specified to `configure` to print all options
|
||||
supported by the script.
|
||||
|
||||
Now type
|
||||
|
||||
----
|
||||
@@ -122,6 +153,16 @@ unprivileged user for `chronyd` and specify it with the `-u` command-line
|
||||
option or the `user` directive in the configuration file, or set the default
|
||||
user with the `--with-user` configure option before building.
|
||||
|
||||
== Support for system call filtering
|
||||
|
||||
`chronyd` can be built with support for the Linux secure computing (seccomp)
|
||||
facility. This requires development files for the
|
||||
https://github.com/seccomp/libseccomp[libseccomp] library and the
|
||||
`--enable-scfilter` option specified to `configure`. The `-F` option of
|
||||
`chronyd` will enable a system call filter, which should significantly reduce
|
||||
the kernel attack surface and possibly prevent kernel exploits from `chronyd`
|
||||
if it is compromised.
|
||||
|
||||
== Support for line editing libraries
|
||||
|
||||
`chronyc` can be built with support for line editing, this allows you to use
|
||||
@@ -132,12 +173,12 @@ Please note that readline since version 6.0 is licensed under GPLv3+ which is
|
||||
incompatible with chrony's license GPLv2. You should use editline instead if
|
||||
you don't want to use older readline versions.
|
||||
|
||||
The configure script will automatically enable the line editing support if one
|
||||
of the supported libraries is available. If they are both available, the
|
||||
The `configure` script will automatically enable the line editing support if
|
||||
one of the supported libraries is available. If they are both available, the
|
||||
editline library will be used.
|
||||
|
||||
If you don't want to use it (in which case chronyc will use a minimal command
|
||||
line interface), invoke configure like this:
|
||||
If you don't want to use it (in which case `chronyc` will use a minimal command
|
||||
line interface), invoke `configure` like this:
|
||||
|
||||
----
|
||||
./configure --disable-readline other-options...
|
||||
@@ -161,12 +202,12 @@ normally searched by the compiler and linker, you need to use extra options:
|
||||
|
||||
== Extra options for package builders
|
||||
|
||||
The configure and make procedures have some extra options that may be useful if
|
||||
you are building a distribution package for chrony.
|
||||
The `configure` and `make` procedures have some extra options that may be
|
||||
useful if you are building a distribution package for `chrony`.
|
||||
|
||||
The `--mandir=DIR` option to configure specifies an install directory for the
|
||||
man pages. This overrides the `man` subdirectory of the argument to the
|
||||
--prefix option.
|
||||
The `--mandir=DIR` option to `configure` specifies an installation directory
|
||||
for the man pages. This overrides the `man` subdirectory of the argument to the
|
||||
`--prefix` option.
|
||||
|
||||
----
|
||||
./configure --prefix=/usr --mandir=/usr/share/man
|
||||
@@ -174,8 +215,8 @@ man pages. This overrides the `man` subdirectory of the argument to the
|
||||
|
||||
to set both options together.
|
||||
|
||||
The final option is the `DESTDIR` option to the make command. For example, you
|
||||
could use the commands
|
||||
The final option is the `DESTDIR` option to the `make` command. For example,
|
||||
you could use the commands
|
||||
|
||||
----
|
||||
./configure --prefix=/usr --mandir=/usr/share/man
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[Unit]
|
||||
Description=Wait for chrony to synchronize system clock
|
||||
Documentation=man:chronyc(1)
|
||||
After=chronyd.service
|
||||
Requires=chronyd.service
|
||||
Before=time-sync.target
|
||||
|
||||
@@ -4,8 +4,8 @@ pool pool.ntp.org iburst
|
||||
# Record the rate at which the system clock gains/losses time.
|
||||
driftfile /var/lib/chrony/drift
|
||||
|
||||
# In first three updates step the system clock instead of slew
|
||||
# if the adjustment is larger than 1 second.
|
||||
# Allow the system clock to be stepped in the first three updates
|
||||
# if its offset is larger than 1 second.
|
||||
makestep 1.0 3
|
||||
|
||||
# Enable kernel synchronization of the real-time clock (RTC).
|
||||
|
||||
@@ -5,22 +5,32 @@ pool pool.ntp.org iburst
|
||||
# Record the rate at which the system clock gains/losses time.
|
||||
driftfile /var/lib/chrony/drift
|
||||
|
||||
# In first three updates step the system clock instead of slew
|
||||
# if the adjustment is larger than 1 second.
|
||||
# Allow the system clock to be stepped in the first three updates
|
||||
# if its offset is larger than 1 second.
|
||||
makestep 1.0 3
|
||||
|
||||
# Enable kernel synchronization of the real-time clock (RTC).
|
||||
rtcsync
|
||||
|
||||
# Allow NTP client access from local network.
|
||||
#allow 192.168/16
|
||||
# Enable hardware timestamping on all interfaces that support it.
|
||||
#hwtimestamp *
|
||||
|
||||
# Serve time even if not synchronized to any NTP server.
|
||||
# Increase the minimum number of selectable sources required to adjust
|
||||
# the system clock.
|
||||
#minsources 2
|
||||
|
||||
# Allow NTP client access from local network.
|
||||
#allow 192.168.0.0/16
|
||||
|
||||
# Serve time even if not synchronized to a time source.
|
||||
#local stratum 10
|
||||
|
||||
# Specify file containing keys for NTP authentication.
|
||||
#keyfile /etc/chrony.keys
|
||||
|
||||
# Get TAI-UTC offset and leap seconds from the system tz database.
|
||||
#leapsectz right/UTC
|
||||
|
||||
# Specify directory for log files.
|
||||
logdir /var/log/chrony
|
||||
|
||||
|
||||
@@ -33,42 +33,30 @@
|
||||
|
||||
! pool pool.ntp.org iburst
|
||||
|
||||
# However, for dial-up use you probably want these instead. The word
|
||||
# 'offline' means that the server is not visible at boot time. Use
|
||||
# chronyc's 'online' command to tell chronyd that these servers have
|
||||
# become visible after you go on-line.
|
||||
|
||||
! server foo.example.net offline
|
||||
! server bar.example.net offline
|
||||
! server baz.example.net offline
|
||||
|
||||
! pool pool.ntp.org offline
|
||||
|
||||
# You may want to specify NTP 'peers' instead. If you run a network
|
||||
# with a lot of computers and want several computers running chrony to
|
||||
# have the 'front-line' interface to the public NTP servers, you can
|
||||
# 'peer' these machines together to increase robustness.
|
||||
|
||||
! peer foo.example.net
|
||||
|
||||
# There are other options to the 'server' and 'peer' directives that you
|
||||
# might want to use. For example, you can ignore measurements whose
|
||||
# round-trip-time is too large (indicating that the measurement is
|
||||
# probably useless, because you don't know which way the measurement
|
||||
# message got held up.) Consult the full documentation for details.
|
||||
|
||||
#######################################################################
|
||||
### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
|
||||
#
|
||||
# To avoid changes being made to your computer's gain/loss compensation
|
||||
# when the measurement history is too erratic, you might want to enable
|
||||
# one of the following lines. The first seems good for dial-up (or
|
||||
# other high-latency connections like slow leased lines), the second
|
||||
# seems OK for a LAN environment.
|
||||
# one of the following lines. The first seems good with servers on the
|
||||
# Internet, the second seems OK for a LAN environment.
|
||||
|
||||
! maxupdateskew 100
|
||||
! maxupdateskew 5
|
||||
|
||||
# If you want to increase the minimum number of selectable sources
|
||||
# required to update the system clock in order to make the
|
||||
# synchronisation more reliable, uncomment (and edit) the following
|
||||
# line.
|
||||
|
||||
! minsources 2
|
||||
|
||||
# If your computer has a good stable clock (e.g. it is not a virtual
|
||||
# machine), you might also want to reduce the maximum assumed drift
|
||||
# (frequency error) of the clock (the value is specified in ppm).
|
||||
|
||||
! maxdrift 100
|
||||
|
||||
#######################################################################
|
||||
### FILENAMES ETC
|
||||
# Chrony likes to keep information about your computer's clock in files.
|
||||
@@ -107,7 +95,13 @@ driftfile /var/lib/chrony/drift
|
||||
# still running and bail out. If you want to change the path to the PID
|
||||
# file, uncomment this line and edit it. The default path is shown.
|
||||
|
||||
! pidfile /var/run/chronyd.pid
|
||||
! pidfile /var/run/chrony/chronyd.pid
|
||||
|
||||
# If the system timezone database is kept up to date and includes the
|
||||
# right/UTC timezone, chronyd can use it to determine the current
|
||||
# TAI-UTC offset and when will the next leap second occur.
|
||||
|
||||
! leapsectz right/UTC
|
||||
|
||||
#######################################################################
|
||||
### INITIAL CLOCK CORRECTION
|
||||
@@ -181,13 +175,12 @@ driftfile /var/lib/chrony/drift
|
||||
# machine accesses it. The information can be accessed by the 'clients'
|
||||
# command of chronyc. You can disable this facility by uncommenting the
|
||||
# following line. This will save a bit of memory if you have many
|
||||
# clients.
|
||||
# clients and it will also disable support for the interleaved mode.
|
||||
|
||||
! noclientlog
|
||||
|
||||
# The clientlog size is limited to 512KB by default. If you have many
|
||||
# clients, especially in many different subnets, you might want to
|
||||
# increase the limit.
|
||||
# clients, you might want to increase the limit.
|
||||
|
||||
! clientloglimit 4194304
|
||||
|
||||
@@ -196,7 +189,7 @@ driftfile /var/lib/chrony/drift
|
||||
# clients that are sending requests too frequently, uncomment and edit
|
||||
# the following line.
|
||||
|
||||
! limitrate interval 3 burst 8
|
||||
! ratelimit interval 3 burst 8
|
||||
|
||||
#######################################################################
|
||||
### REPORTING BIG CLOCK CHANGES
|
||||
@@ -243,7 +236,17 @@ driftfile /var/lib/chrony/drift
|
||||
# Rate limiting can be enabled also for command packets. (Note,
|
||||
# commands from localhost are never limited.)
|
||||
|
||||
! cmdratelimit interval 1 burst 16
|
||||
! cmdratelimit interval -4 burst 16
|
||||
|
||||
#######################################################################
|
||||
### HARDWARE TIMESTAMPING
|
||||
# On Linux, if the network interface controller and its driver support
|
||||
# hardware timestamping, it can significantly improve the accuracy of
|
||||
# synchronisation. It can be enabled on specified interfaces only, or it
|
||||
# can be enabled on all interfaces that support it.
|
||||
|
||||
! hwtimestamp eth0
|
||||
! hwtimestamp *
|
||||
|
||||
#######################################################################
|
||||
### REAL TIME CLOCK
|
||||
@@ -274,6 +277,12 @@ driftfile /var/lib/chrony/drift
|
||||
|
||||
! rtcdevice /dev/misc/rtc
|
||||
|
||||
# Alternatively, if not using the -s option, this directive can be used
|
||||
# to enable a mode in which the RTC is periodically set to the system
|
||||
# time, with no tracking of its drift.
|
||||
|
||||
! rtcsync
|
||||
|
||||
#######################################################################
|
||||
### REAL TIME SCHEDULER
|
||||
# This directive tells chronyd to use the real-time FIFO scheduler with the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# This is an example chrony keys file. It is used for NTP authentication with
|
||||
# symmetric keys. It should be readable only by root or the user to which
|
||||
# chronyd is configured to switch to after start.
|
||||
# This is an example chrony keys file. It enables authentication of NTP
|
||||
# packets with symmetric keys when its location is specified by the keyfile
|
||||
# directive in chrony.conf(5). It should be readable only by root and the
|
||||
# user under which chronyd is running.
|
||||
#
|
||||
# Don't use the example keys! It's recommended to generate random keys using
|
||||
# the chronyc keygen command.
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#!/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.
|
||||
# This is a NetworkManager dispatcher / networkd-dispatcher script for
|
||||
# chronyd to set its NTP sources online or offline when a network interface
|
||||
# is configured or removed
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$2" = "up" ]; then
|
||||
/sbin/ip route list dev "$1" | grep -q '^default' &&
|
||||
/usr/bin/chronyc online > /dev/null 2>&1
|
||||
fi
|
||||
# For NetworkManager consider only up/down events
|
||||
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
|
||||
|
||||
if [ "$2" = "down" ]; then
|
||||
/sbin/ip route list | grep -q '^default' ||
|
||||
/usr/bin/chronyc offline > /dev/null 2>&1
|
||||
fi
|
||||
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
||||
|
||||
chronyc onoffline > /dev/null 2>&1
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
%global chrony_version @@VERSION@@
|
||||
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
|
||||
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
|
||||
%endif
|
||||
Summary: An NTP client/server
|
||||
Name: chrony
|
||||
Version: %(echo %{chrony_version} | sed 's/-.*//')
|
||||
Release: %{!?prerelease:1}%{?prerelease:0.1.%{prerelease}}
|
||||
Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
|
||||
License: GPLv2
|
||||
Group: Applications/Utilities
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
|
||||
|
||||
%description
|
||||
chrony is a client and server for the Network Time Protocol (NTP).
|
||||
This program keeps your computer's clock accurate. It was specially
|
||||
designed to support systems with intermittent Internet connections,
|
||||
but it also works well in permanently connected environments. It can
|
||||
also use hardware reference clocks, the system real-time clock, or
|
||||
manual input as time references.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}%{?prerelease:-%{prerelease}}
|
||||
|
||||
%build
|
||||
./configure \
|
||||
--prefix=%{_prefix} \
|
||||
--bindir=%{_bindir} \
|
||||
--sbindir=%{_sbindir} \
|
||||
--mandir=%{_mandir}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%{_sbindir}/chronyd
|
||||
%{_bindir}/chronyc
|
||||
%{_mandir}/man1/chronyc.1.gz
|
||||
%{_mandir}/man5/chrony.conf.5.gz
|
||||
%{_mandir}/man8/chronyd.8.gz
|
||||
%doc README FAQ NEWS COPYING
|
||||
%doc examples/chrony.conf.example*
|
||||
%doc examples/chrony.keys.example
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
[Unit]
|
||||
Description=NTP client/server
|
||||
Documentation=man:chronyd(8) man:chrony.conf(5)
|
||||
After=ntpdate.service sntp.service ntpd.service
|
||||
Conflicts=ntpd.service systemd-timesyncd.service
|
||||
ConditionCapability=CAP_SYS_TIME
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile=/var/run/chronyd.pid
|
||||
PIDFile=/var/run/chrony/chronyd.pid
|
||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||
ExecStart=/usr/sbin/chronyd $OPTIONS
|
||||
PrivateTmp=yes
|
||||
ProtectHome=yes
|
||||
ProtectSystem=full
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "sysincl.h"
|
||||
#include "hash.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "md5.c"
|
||||
|
||||
@@ -49,18 +50,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
|
||||
const unsigned char *in2, unsigned int in2_len,
|
||||
unsigned char *out, unsigned int out_len)
|
||||
{
|
||||
if (out_len < 16)
|
||||
return 0;
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, in1, in1_len);
|
||||
if (in2)
|
||||
MD5Update(&ctx, in2, in2_len);
|
||||
MD5Final(&ctx);
|
||||
|
||||
memcpy(out, ctx.digest, 16);
|
||||
out_len = MIN(out_len, 16);
|
||||
|
||||
return 16;
|
||||
memcpy(out, ctx.digest, out_len);
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
120
hash_nettle.c
Normal file
120
hash_nettle.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Routines implementing crypto hashing using the nettle library.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <nettle/nettle-meta.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "memory.h"
|
||||
|
||||
struct hash {
|
||||
const char *name;
|
||||
const char *int_name;
|
||||
const struct nettle_hash *nettle_hash;
|
||||
void *context;
|
||||
};
|
||||
|
||||
static struct hash hashes[] = {
|
||||
{ "MD5", "md5", NULL, NULL },
|
||||
{ "RMD160", "ripemd160", NULL, NULL },
|
||||
{ "SHA1", "sha1", NULL, NULL },
|
||||
{ "SHA256", "sha256", NULL, NULL },
|
||||
{ "SHA384", "sha384", NULL, NULL },
|
||||
{ "SHA512", "sha512", NULL, NULL },
|
||||
{ "SHA3-224", "sha3_224", NULL, NULL },
|
||||
{ "SHA3-256", "sha3_256", NULL, NULL },
|
||||
{ "SHA3-384", "sha3_384", NULL, NULL },
|
||||
{ "SHA3-512", "sha3_512", NULL, NULL },
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int
|
||||
HSH_GetHashId(const char *name)
|
||||
{
|
||||
int id, nid;
|
||||
|
||||
for (id = 0; hashes[id].name; id++) {
|
||||
if (!strcmp(name, hashes[id].name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hashes[id].name)
|
||||
return -1;
|
||||
|
||||
if (hashes[id].context)
|
||||
return id;
|
||||
|
||||
for (nid = 0; nettle_hashes[nid]; nid++) {
|
||||
if (!strcmp(hashes[id].int_name, nettle_hashes[nid]->name))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nettle_hashes[nid] || !nettle_hashes[nid]->context_size || !nettle_hashes[nid]->init)
|
||||
return -1;
|
||||
|
||||
hashes[id].nettle_hash = nettle_hashes[nid];
|
||||
hashes[id].context = Malloc(hashes[id].nettle_hash->context_size);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
|
||||
const unsigned char *in2, unsigned int in2_len,
|
||||
unsigned char *out, unsigned int out_len)
|
||||
{
|
||||
const struct nettle_hash *hash;
|
||||
void *context;
|
||||
|
||||
hash = hashes[id].nettle_hash;
|
||||
context = hashes[id].context;
|
||||
|
||||
if (out_len > hash->digest_size)
|
||||
out_len = hash->digest_size;
|
||||
|
||||
hash->init(context);
|
||||
hash->update(context, in1_len, in1);
|
||||
if (in2)
|
||||
hash->update(context, in2_len, in2);
|
||||
hash->digest(context, out_len, out);
|
||||
|
||||
return out_len;
|
||||
}
|
||||
|
||||
void
|
||||
HSH_Finalise(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; hashes[i].name; i++) {
|
||||
if (hashes[i].context)
|
||||
Free(hashes[i].context);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <nsslowhash.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
static NSSLOWInitContext *ictx;
|
||||
|
||||
@@ -78,13 +79,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
|
||||
const unsigned char *in2, unsigned int in2_len,
|
||||
unsigned char *out, unsigned int out_len)
|
||||
{
|
||||
unsigned int ret;
|
||||
unsigned char buf[MAX_HASH_LENGTH];
|
||||
unsigned int ret = 0;
|
||||
|
||||
NSSLOWHASH_Begin(hashes[id].context);
|
||||
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
|
||||
if (in2)
|
||||
NSSLOWHASH_Update(hashes[id].context, in2, in2_len);
|
||||
NSSLOWHASH_End(hashes[id].context, out, &ret, out_len);
|
||||
NSSLOWHASH_End(hashes[id].context, buf, &ret, sizeof (buf));
|
||||
|
||||
ret = MIN(ret, out_len);
|
||||
memcpy(out, buf, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2012
|
||||
* Copyright (C) Miroslav Lichvar 2012, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
struct hash {
|
||||
const char *name;
|
||||
@@ -62,6 +63,12 @@ static const struct hash hashes[] = {
|
||||
#ifdef LTC_SHA512
|
||||
{ "SHA512", "sha512", &sha512_desc },
|
||||
#endif
|
||||
#ifdef LTC_SHA3
|
||||
{ "SHA3-224", "sha3-224", &sha3_224_desc },
|
||||
{ "SHA3-256", "sha3-256", &sha3_256_desc },
|
||||
{ "SHA3-384", "sha3-384", &sha3_384_desc },
|
||||
{ "SHA3-512", "sha3-512", &sha3_512_desc },
|
||||
#endif
|
||||
#ifdef LTC_TIGER
|
||||
{ "TIGER", "tiger", &tiger_desc },
|
||||
#endif
|
||||
@@ -99,19 +106,24 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
|
||||
const unsigned char *in2, unsigned int in2_len,
|
||||
unsigned char *out, unsigned int out_len)
|
||||
{
|
||||
unsigned char buf[MAX_HASH_LENGTH];
|
||||
unsigned long len;
|
||||
int r;
|
||||
|
||||
len = out_len;
|
||||
len = sizeof (buf);
|
||||
if (in2)
|
||||
r = hash_memory_multi(id, out, &len,
|
||||
in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
|
||||
r = hash_memory_multi(id, buf, &len,
|
||||
in1, (unsigned long)in1_len,
|
||||
in2, (unsigned long)in2_len, NULL, 0);
|
||||
else
|
||||
r = hash_memory(id, in1, in1_len, out, &len);
|
||||
r = hash_memory(id, in1, in1_len, buf, &len);
|
||||
|
||||
if (r != CRYPT_OK)
|
||||
return 0;
|
||||
|
||||
len = MIN(len, out_len);
|
||||
memcpy(out, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
87
hwclock.c
87
hwclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -36,11 +36,12 @@
|
||||
#include "regress.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Maximum number of samples per clock */
|
||||
#define MAX_SAMPLES 16
|
||||
/* Minimum and maximum number of samples per clock */
|
||||
#define MIN_SAMPLES 2
|
||||
#define MAX_SAMPLES 64
|
||||
|
||||
/* Minimum interval between samples (in seconds) */
|
||||
#define MIN_SAMPLE_SEPARATION 1.0
|
||||
/* Maximum acceptable frequency offset of the clock */
|
||||
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
||||
|
||||
struct HCL_Instance_Record {
|
||||
/* HW and local reference timestamp */
|
||||
@@ -49,12 +50,20 @@ struct HCL_Instance_Record {
|
||||
|
||||
/* Samples stored as intervals (uncorrected for frequency error)
|
||||
relative to local_ref and hw_ref */
|
||||
double x_data[MAX_SAMPLES];
|
||||
double y_data[MAX_SAMPLES];
|
||||
double *x_data;
|
||||
double *y_data;
|
||||
|
||||
/* Number of samples */
|
||||
/* Minimum, maximum and current number of samples */
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int n_samples;
|
||||
|
||||
/* Maximum error of the last sample */
|
||||
double last_err;
|
||||
|
||||
/* Minimum interval between samples */
|
||||
double min_separation;
|
||||
|
||||
/* Flag indicating the offset and frequency values are valid */
|
||||
int valid_coefs;
|
||||
|
||||
@@ -83,15 +92,24 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
/* ================================================== */
|
||||
|
||||
HCL_Instance
|
||||
HCL_CreateInstance(void)
|
||||
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
||||
{
|
||||
HCL_Instance clock;
|
||||
|
||||
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
|
||||
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
|
||||
max_samples = MAX(min_samples, max_samples);
|
||||
|
||||
clock = MallocNew(struct HCL_Instance_Record);
|
||||
clock->x_data[MAX_SAMPLES - 1] = 0.0;
|
||||
clock->y_data[MAX_SAMPLES - 1] = 0.0;
|
||||
clock->x_data = MallocArray(double, max_samples);
|
||||
clock->y_data = MallocArray(double, max_samples);
|
||||
clock->x_data[max_samples - 1] = 0.0;
|
||||
clock->y_data[max_samples - 1] = 0.0;
|
||||
clock->min_samples = min_samples;
|
||||
clock->max_samples = max_samples;
|
||||
clock->n_samples = 0;
|
||||
clock->valid_coefs = 0;
|
||||
clock->min_separation = min_separation;
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_slew, clock);
|
||||
|
||||
@@ -103,6 +121,8 @@ HCL_CreateInstance(void)
|
||||
void HCL_DestroyInstance(HCL_Instance clock)
|
||||
{
|
||||
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
||||
Free(clock->y_data);
|
||||
Free(clock->x_data);
|
||||
Free(clock);
|
||||
}
|
||||
|
||||
@@ -112,7 +132,7 @@ int
|
||||
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
|
||||
{
|
||||
if (!clock->n_samples ||
|
||||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= MIN_SAMPLE_SEPARATION)
|
||||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -131,18 +151,18 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||
|
||||
/* Shift old samples */
|
||||
if (clock->n_samples) {
|
||||
if (clock->n_samples >= MAX_SAMPLES)
|
||||
if (clock->n_samples >= clock->max_samples)
|
||||
clock->n_samples--;
|
||||
|
||||
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
|
||||
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
|
||||
|
||||
if (hw_delta <= 0.0 || local_delta < MIN_SAMPLE_SEPARATION / 2.0) {
|
||||
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
|
||||
clock->n_samples = 0;
|
||||
DEBUG_LOG(LOGF_HwClocks, "HW clock reset interval=%f", local_delta);
|
||||
DEBUG_LOG("HW clock reset interval=%f", local_delta);
|
||||
}
|
||||
|
||||
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
|
||||
for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
|
||||
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
|
||||
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
|
||||
}
|
||||
@@ -151,34 +171,37 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||
clock->n_samples++;
|
||||
clock->hw_ref = *hw_ts;
|
||||
clock->local_ref = *local_ts;
|
||||
clock->last_err = err;
|
||||
|
||||
/* Get new coefficients */
|
||||
clock->valid_coefs =
|
||||
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
|
||||
clock->y_data + MAX_SAMPLES - clock->n_samples,
|
||||
clock->n_samples, 1.0e-9, &clock->offset, &raw_freq,
|
||||
RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
|
||||
clock->y_data + clock->max_samples - clock->n_samples,
|
||||
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
|
||||
&n_runs, &best_start);
|
||||
|
||||
if (!clock->valid_coefs) {
|
||||
DEBUG_LOG(LOGF_HwClocks, "HW clock needs more samples");
|
||||
DEBUG_LOG("HW clock needs more samples");
|
||||
return;
|
||||
}
|
||||
|
||||
clock->frequency = raw_freq / local_freq;
|
||||
|
||||
/* Drop unneeded samples */
|
||||
clock->n_samples -= best_start;
|
||||
if (clock->n_samples > clock->min_samples)
|
||||
clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
|
||||
|
||||
/* If the fit doesn't cross the error interval of the last sample, throw away
|
||||
all previous samples and keep only the frequency estimate */
|
||||
if (fabs(clock->offset) > err) {
|
||||
DEBUG_LOG(LOGF_HwClocks, "HW clock reset offset=%e", clock->offset);
|
||||
clock->offset = 0.0;
|
||||
clock->n_samples = 1;
|
||||
/* If the fit doesn't cross the error interval of the last sample,
|
||||
or the frequency is not sane, drop all samples and start again */
|
||||
if (fabs(clock->offset) > err ||
|
||||
fabs(clock->frequency - 1.0) > MAX_FREQ_OFFSET) {
|
||||
DEBUG_LOG("HW clock reset");
|
||||
clock->n_samples = 0;
|
||||
clock->valid_coefs = 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_HwClocks, "HW clock samples=%d offset=%e freq=%.9e raw_freq=%.9e err=%e ref_diff=%e",
|
||||
clock->n_samples, clock->offset, clock->frequency, raw_freq, err,
|
||||
DEBUG_LOG("HW clock samples=%d offset=%e freq=%e raw_freq=%e err=%e ref_diff=%e",
|
||||
clock->n_samples, clock->offset, clock->frequency - 1.0, raw_freq - 1.0, err,
|
||||
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
|
||||
}
|
||||
|
||||
@@ -193,12 +216,12 @@ HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
|
||||
return 0;
|
||||
|
||||
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
|
||||
offset = clock->offset + elapsed / clock->frequency;
|
||||
offset = elapsed / clock->frequency - clock->offset;
|
||||
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
|
||||
|
||||
/* Estimation of the error is not implemented yet */
|
||||
/* Fow now, just return the error of the last sample */
|
||||
if (err)
|
||||
*err = 0.0;
|
||||
*err = clock->last_err;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
typedef struct HCL_Instance_Record *HCL_Instance;
|
||||
|
||||
/* Create a new HW clock instance */
|
||||
extern HCL_Instance HCL_CreateInstance(void);
|
||||
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
||||
double min_separation);
|
||||
|
||||
/* Destroy a HW clock instance */
|
||||
extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||
|
||||
14
keys.c
14
keys.c
@@ -107,6 +107,8 @@ determine_hash_delay(uint32_t key_id)
|
||||
double diff, min_diff;
|
||||
int i, nsecs;
|
||||
|
||||
memset(&pkt, 0, sizeof (pkt));
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
LCL_ReadRawTime(&before);
|
||||
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
|
||||
@@ -122,7 +124,7 @@ determine_hash_delay(uint32_t key_id)
|
||||
/* Add on a bit extra to allow for copying, conversions etc */
|
||||
nsecs = 1.0625e9 * min_diff;
|
||||
|
||||
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
|
||||
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
|
||||
|
||||
return nsecs;
|
||||
}
|
||||
@@ -200,7 +202,7 @@ KEY_Reload(void)
|
||||
|
||||
in = fopen(key_file, "r");
|
||||
if (!in) {
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file);
|
||||
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -212,19 +214,19 @@ KEY_Reload(void)
|
||||
continue;
|
||||
|
||||
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file);
|
||||
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
|
||||
continue;
|
||||
}
|
||||
|
||||
key.hash_id = HSH_GetHashId(hashname);
|
||||
if (key.hash_id < 0) {
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id);
|
||||
LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
key.len = decode_password(keyval);
|
||||
if (!key.len) {
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id);
|
||||
LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -244,7 +246,7 @@ KEY_Reload(void)
|
||||
/* Check for duplicates */
|
||||
for (i = 1; i < ARR_GetSize(keys); i++) {
|
||||
if (get_key(i - 1)->id == get_key(i)->id)
|
||||
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
|
||||
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
|
||||
}
|
||||
|
||||
/* Erase any passwords from stack */
|
||||
|
||||
24
local.c
24
local.c
@@ -144,7 +144,9 @@ calculate_sys_precision(void)
|
||||
best *= 2;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
|
||||
assert(precision_log >= -30);
|
||||
|
||||
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -356,12 +358,12 @@ LCL_ReadRawTime(struct timespec *ts)
|
||||
{
|
||||
#if HAVE_CLOCK_GETTIME
|
||||
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
|
||||
LOG_FATAL(LOGF_Local, "clock_gettime() failed : %s", strerror(errno));
|
||||
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
|
||||
#else
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday(&tv, NULL) < 0)
|
||||
LOG_FATAL(LOGF_Local, "gettimeofday() failed : %s", strerror(errno));
|
||||
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
|
||||
|
||||
UTI_TimevalToTimespec(&tv, ts);
|
||||
#endif
|
||||
@@ -424,7 +426,7 @@ clamp_freq(double freq)
|
||||
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
|
||||
return freq;
|
||||
|
||||
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
|
||||
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
|
||||
|
||||
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
|
||||
}
|
||||
@@ -438,7 +440,7 @@ check_offset(struct timespec *now, double offset)
|
||||
if (UTI_IsTimeOffsetSane(now, -offset))
|
||||
return 1;
|
||||
|
||||
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
|
||||
LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -544,7 +546,7 @@ LCL_ApplyStepOffset(double offset)
|
||||
return 0;
|
||||
|
||||
if (!(*drv_apply_step_offset)(offset)) {
|
||||
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
|
||||
LOG(LOGS_ERR, "Could not step system clock");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -611,7 +613,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
||||
|
||||
current_freq_ppm = clamp_freq(current_freq_ppm);
|
||||
|
||||
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
|
||||
DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
|
||||
old_freq_ppm, current_freq_ppm, doffset);
|
||||
|
||||
/* Call the system-specific driver for setting the frequency */
|
||||
@@ -658,7 +660,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
||||
|
||||
current_freq_ppm = (*drv_read_freq)();
|
||||
|
||||
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
|
||||
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -682,7 +684,7 @@ LCL_MakeStep(void)
|
||||
if (!LCL_ApplyStepOffset(-correction))
|
||||
return 0;
|
||||
|
||||
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
|
||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -698,10 +700,10 @@ LCL_CanSystemLeap(void)
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LCL_SetSystemLeap(int leap)
|
||||
LCL_SetSystemLeap(int leap, int tai_offset)
|
||||
{
|
||||
if (drv_set_leap) {
|
||||
(drv_set_leap)(leap);
|
||||
(drv_set_leap)(leap, tai_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
local.h
9
local.h
@@ -201,10 +201,11 @@ extern int LCL_MakeStep(void);
|
||||
does something */
|
||||
extern int LCL_CanSystemLeap(void);
|
||||
|
||||
/* Routine to set the system clock to correct itself for a leap second if
|
||||
supported. Leap second will be inserted at the end of the day if the
|
||||
argument is positive, deleted if negative, and zero resets the setting. */
|
||||
extern void LCL_SetSystemLeap(int leap);
|
||||
/* Routine to set the system clock to correct itself for a leap second and also
|
||||
set its TAI-UTC offset. If supported, leap second will be inserted at the
|
||||
end of the day if the argument is positive, deleted if negative, and zero
|
||||
resets the setting. */
|
||||
extern void LCL_SetSystemLeap(int leap, int tai_offset);
|
||||
|
||||
/* Routine to set a frequency correction (in ppm) that should be applied
|
||||
to local clock to compensate for temperature changes. A positive
|
||||
|
||||
4
localp.h
4
localp.h
@@ -54,8 +54,8 @@ typedef int (*lcl_ApplyStepOffsetDriver)(double offset);
|
||||
raw time to get the corrected time */
|
||||
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err);
|
||||
|
||||
/* System driver to schedule leap second */
|
||||
typedef void (*lcl_SetLeapDriver)(int leap);
|
||||
/* System driver to schedule leap seconds and set TAI-UTC offset */
|
||||
typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset);
|
||||
|
||||
/* System driver to set the synchronisation status */
|
||||
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error);
|
||||
|
||||
80
logging.c
80
logging.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014, 2018
|
||||
*
|
||||
* This program 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 +40,7 @@ int log_debug_enabled = 0;
|
||||
/* Flag indicating we have initialised */
|
||||
static int initialised = 0;
|
||||
|
||||
static FILE *file_log;
|
||||
static int system_log = 0;
|
||||
|
||||
static int parent_fd = 0;
|
||||
@@ -69,6 +70,7 @@ void
|
||||
LOG_Initialise(void)
|
||||
{
|
||||
initialised = 1;
|
||||
file_log = stderr;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -77,9 +79,11 @@ LOG_Initialise(void)
|
||||
void
|
||||
LOG_Finalise(void)
|
||||
{
|
||||
if (system_log) {
|
||||
if (system_log)
|
||||
closelog();
|
||||
}
|
||||
|
||||
if (file_log)
|
||||
fclose(file_log);
|
||||
|
||||
LOG_CycleLogFiles();
|
||||
|
||||
@@ -112,8 +116,8 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
|
||||
assert(0);
|
||||
}
|
||||
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
|
||||
} else {
|
||||
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
|
||||
} else if (file_log) {
|
||||
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,25 +125,26 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
|
||||
|
||||
void LOG_Message(LOG_Severity severity,
|
||||
#if DEBUG > 0
|
||||
LOG_Facility facility, int line_number,
|
||||
const char *filename, const char *function_name,
|
||||
int line_number, const char *filename, const char *function_name,
|
||||
#endif
|
||||
const char *format, ...)
|
||||
{
|
||||
char buf[2048];
|
||||
va_list other_args;
|
||||
time_t t;
|
||||
struct tm stm;
|
||||
struct tm *tm;
|
||||
|
||||
if (!system_log) {
|
||||
if (!system_log && file_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);
|
||||
tm = gmtime(&t);
|
||||
if (tm) {
|
||||
strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
|
||||
fprintf(file_log, "%s ", buf);
|
||||
}
|
||||
#if DEBUG > 0
|
||||
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
|
||||
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
|
||||
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -157,16 +162,14 @@ void LOG_Message(LOG_Severity severity,
|
||||
case LOGS_FATAL:
|
||||
log_message(1, severity, buf);
|
||||
|
||||
/* With syslog, send the message also to the grandparent
|
||||
process or write it to stderr if not detached */
|
||||
if (system_log) {
|
||||
if (parent_fd > 0) {
|
||||
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
|
||||
; /* Not much we can do here */
|
||||
} else if (parent_fd == 0) {
|
||||
system_log = 0;
|
||||
log_message(1, severity, buf);
|
||||
}
|
||||
/* Send the message also to the foreground process if it is
|
||||
still running, or stderr if it is still open */
|
||||
if (parent_fd > 0) {
|
||||
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
|
||||
; /* Not much we can do here */
|
||||
} else if (system_log && parent_fd == 0) {
|
||||
system_log = 0;
|
||||
log_message(1, severity, buf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -174,6 +177,31 @@ void LOG_Message(LOG_Severity severity,
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_OpenFileLog(const char *log_file)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if (log_file) {
|
||||
f = fopen(log_file, "a");
|
||||
if (!f)
|
||||
LOG_FATAL("Could not open log file %s", log_file);
|
||||
} else {
|
||||
f = stderr;
|
||||
}
|
||||
|
||||
/* Enable line buffering */
|
||||
setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
||||
|
||||
if (file_log && file_log != stderr)
|
||||
fclose(file_log);
|
||||
|
||||
file_log = f;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
@@ -199,6 +227,8 @@ void
|
||||
LOG_SetParentFd(int fd)
|
||||
{
|
||||
parent_fd = fd;
|
||||
if (file_log == stderr)
|
||||
file_log = NULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -241,7 +271,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
|
||||
char filename[512], *logdir = CNF_GetLogDir();
|
||||
|
||||
if (logdir[0] == '\0') {
|
||||
LOG(LOGS_WARN, LOGF_Logging, "logdir not specified");
|
||||
LOG(LOGS_WARN, "logdir not specified");
|
||||
logfiles[id].name = NULL;
|
||||
return;
|
||||
}
|
||||
@@ -249,7 +279,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
|
||||
if (snprintf(filename, sizeof(filename), "%s/%s.log",
|
||||
logdir, logfiles[id].name) >= sizeof (filename) ||
|
||||
!(logfiles[id].file = fopen(filename, "a"))) {
|
||||
LOG(LOGS_WARN, LOGF_Logging, "Could not open log file %s", filename);
|
||||
LOG(LOGS_WARN, "Could not open log file %s", filename);
|
||||
logfiles[id].name = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
70
logging.h
70
logging.h
@@ -46,26 +46,26 @@ extern int log_debug_enabled;
|
||||
#endif
|
||||
|
||||
#if DEBUG > 0
|
||||
#define LOG_MESSAGE(severity, facility, ...) \
|
||||
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
|
||||
#define LOG_MESSAGE(severity, ...) \
|
||||
LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_MESSAGE(severity, facility, ...) \
|
||||
#define LOG_MESSAGE(severity, ...) \
|
||||
LOG_Message(severity, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define DEBUG_LOG(facility, ...) \
|
||||
#define DEBUG_LOG(...) \
|
||||
do { \
|
||||
if (DEBUG && log_debug_enabled) \
|
||||
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \
|
||||
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define LOG_FATAL(facility, ...) \
|
||||
#define LOG_FATAL(...) \
|
||||
do { \
|
||||
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \
|
||||
LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__)
|
||||
#define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
|
||||
|
||||
/* Definition of severity */
|
||||
typedef enum {
|
||||
@@ -76,50 +76,6 @@ typedef enum {
|
||||
LOGS_DEBUG
|
||||
} LOG_Severity;
|
||||
|
||||
/* Definition of facility. Each message is tagged with who generated
|
||||
it, so that the user can customise what level of reporting he gets
|
||||
for each area of the software */
|
||||
typedef enum {
|
||||
LOGF_Reference,
|
||||
LOGF_NtpIO,
|
||||
LOGF_NtpIOLinux,
|
||||
LOGF_NtpCore,
|
||||
LOGF_NtpSignd,
|
||||
LOGF_NtpSources,
|
||||
LOGF_Scheduler,
|
||||
LOGF_SourceStats,
|
||||
LOGF_Sources,
|
||||
LOGF_Local,
|
||||
LOGF_Util,
|
||||
LOGF_Main,
|
||||
LOGF_Memory,
|
||||
LOGF_Client,
|
||||
LOGF_ClientLog,
|
||||
LOGF_Configure,
|
||||
LOGF_CmdMon,
|
||||
LOGF_Acquire,
|
||||
LOGF_Manual,
|
||||
LOGF_Keys,
|
||||
LOGF_Logging,
|
||||
LOGF_Nameserv,
|
||||
LOGF_PrivOps,
|
||||
LOGF_Rtc,
|
||||
LOGF_Regress,
|
||||
LOGF_Sys,
|
||||
LOGF_SysGeneric,
|
||||
LOGF_SysLinux,
|
||||
LOGF_SysMacOSX,
|
||||
LOGF_SysNetBSD,
|
||||
LOGF_SysSolaris,
|
||||
LOGF_SysTimex,
|
||||
LOGF_SysWinnt,
|
||||
LOGF_TempComp,
|
||||
LOGF_RtcLinux,
|
||||
LOGF_Refclock,
|
||||
LOGF_HwClocks,
|
||||
LOGF_Smooth,
|
||||
} LOG_Facility;
|
||||
|
||||
/* Init function */
|
||||
extern void LOG_Initialise(void);
|
||||
|
||||
@@ -128,9 +84,8 @@ extern void LOG_Finalise(void);
|
||||
|
||||
/* Line logging function */
|
||||
#if DEBUG > 0
|
||||
FORMAT_ATTRIBUTE_PRINTF(6, 7)
|
||||
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
|
||||
int line_number, const char *filename,
|
||||
FORMAT_ATTRIBUTE_PRINTF(5, 6)
|
||||
extern void LOG_Message(LOG_Severity severity, int line_number, const char *filename,
|
||||
const char *function_name, const char *format, ...);
|
||||
#else
|
||||
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||
@@ -144,10 +99,13 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
|
||||
*/
|
||||
extern void LOG_SetDebugLevel(int level);
|
||||
|
||||
/* Log messages to a file instead of stderr, or stderr again if NULL */
|
||||
extern void LOG_OpenFileLog(const char *log_file);
|
||||
|
||||
/* Log messages to syslog instead of stderr */
|
||||
extern void LOG_OpenSystemLog(void);
|
||||
|
||||
/* Send fatal message also to the foreground process */
|
||||
/* Stop using stderr and send fatal message to the foreground process */
|
||||
extern void LOG_SetParentFd(int fd);
|
||||
|
||||
/* Close the pipe to the foreground process so it can exit */
|
||||
|
||||
319
main.c
319
main.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2012-2015
|
||||
* Copyright (C) Miroslav Lichvar 2012-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -86,6 +86,10 @@ static void
|
||||
delete_pidfile(void)
|
||||
{
|
||||
const char *pidfile = CNF_GetPidFile();
|
||||
|
||||
if (!pidfile[0])
|
||||
return;
|
||||
|
||||
/* Don't care if this fails, there's not a lot we can do */
|
||||
unlink(pidfile);
|
||||
}
|
||||
@@ -97,7 +101,7 @@ MAI_CleanupAndExit(void)
|
||||
{
|
||||
if (!initialised) exit(exit_status);
|
||||
|
||||
if (CNF_GetDumpOnExit()) {
|
||||
if (CNF_GetDumpDir()[0] != '\0') {
|
||||
SRC_DumpSources();
|
||||
}
|
||||
|
||||
@@ -127,9 +131,8 @@ MAI_CleanupAndExit(void)
|
||||
delete_pidfile();
|
||||
|
||||
CNF_Finalise();
|
||||
LOG_Finalise();
|
||||
|
||||
HSH_Finalise();
|
||||
LOG_Finalise();
|
||||
|
||||
exit(exit_status);
|
||||
}
|
||||
@@ -242,55 +245,45 @@ post_init_rtc_hook(void *anything)
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Return 1 if the process exists on the system. */
|
||||
|
||||
static int
|
||||
does_process_exist(int pid)
|
||||
{
|
||||
int status;
|
||||
status = getsid(pid);
|
||||
if (status >= 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
maybe_another_chronyd_running(int *other_pid)
|
||||
static void
|
||||
check_pidfile(void)
|
||||
{
|
||||
const char *pidfile = CNF_GetPidFile();
|
||||
FILE *in;
|
||||
int pid, count;
|
||||
|
||||
*other_pid = 0;
|
||||
|
||||
in = fopen(pidfile, "r");
|
||||
if (!in) return 0;
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
count = fscanf(in, "%d", &pid);
|
||||
fclose(in);
|
||||
|
||||
if (count != 1) return 0;
|
||||
if (count != 1)
|
||||
return;
|
||||
|
||||
*other_pid = pid;
|
||||
return does_process_exist(pid);
|
||||
|
||||
if (getsid(pid) < 0)
|
||||
return;
|
||||
|
||||
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
|
||||
pid, pidfile);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
write_lockfile(void)
|
||||
write_pidfile(void)
|
||||
{
|
||||
const char *pidfile = CNF_GetPidFile();
|
||||
FILE *out;
|
||||
|
||||
if (!pidfile[0])
|
||||
return;
|
||||
|
||||
out = fopen(pidfile, "w");
|
||||
if (!out) {
|
||||
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
|
||||
LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
|
||||
} else {
|
||||
fprintf(out, "%d\n", (int)getpid());
|
||||
fclose(out);
|
||||
@@ -299,6 +292,8 @@ write_lockfile(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#define DEV_NULL "/dev/null"
|
||||
|
||||
static void
|
||||
go_daemon(void)
|
||||
{
|
||||
@@ -307,14 +302,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_FATAL(LOGF_Main, "Could not detach, pipe failed : %s", strerror(errno));
|
||||
LOG_FATAL("pipe() failed : %s", strerror(errno));
|
||||
}
|
||||
|
||||
/* Does this preserve existing signal handlers? */
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
|
||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||
} else if (pid > 0) {
|
||||
/* In the 'grandparent' */
|
||||
char message[1024];
|
||||
@@ -325,7 +320,8 @@ go_daemon(void)
|
||||
if (r) {
|
||||
if (r > 0) {
|
||||
/* Print the error message from the child */
|
||||
fprintf(stderr, "%.1024s\n", message);
|
||||
message[sizeof (message) - 1] = '\0';
|
||||
fprintf(stderr, "%s\n", message);
|
||||
}
|
||||
exit(1);
|
||||
} else
|
||||
@@ -339,7 +335,7 @@ go_daemon(void)
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
LOG_FATAL(LOGF_Main, "Could not detach, fork failed : %s", strerror(errno));
|
||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||
} else if (pid > 0) {
|
||||
exit(0); /* In the 'parent' */
|
||||
} else {
|
||||
@@ -347,7 +343,7 @@ go_daemon(void)
|
||||
|
||||
/* Change current directory to / */
|
||||
if (chdir("/") < 0) {
|
||||
LOG_FATAL(LOGF_Main, "Could not chdir to / : %s", strerror(errno));
|
||||
LOG_FATAL("chdir() failed : %s", strerror(errno));
|
||||
}
|
||||
|
||||
/* Don't keep stdin/out/err from before. But don't close
|
||||
@@ -358,148 +354,199 @@ go_daemon(void)
|
||||
}
|
||||
|
||||
LOG_SetParentFd(pipefd[1]);
|
||||
|
||||
/* Open /dev/null as new stdin/out/err */
|
||||
errno = 0;
|
||||
if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
|
||||
open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
|
||||
open(DEV_NULL, O_RDWR) != STDERR_FILENO)
|
||||
LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_help(const char *progname)
|
||||
{
|
||||
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
print_version(void)
|
||||
{
|
||||
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
parse_int_arg(const char *arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sscanf(arg, "%d", &i) != 1)
|
||||
LOG_FATAL("Invalid argument %s", arg);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int main
|
||||
(int argc, char **argv)
|
||||
{
|
||||
const char *conf_file = DEFAULT_CONF_FILE;
|
||||
const char *progname = argv[0];
|
||||
char *user = NULL;
|
||||
char *user = NULL, *log_file = NULL;
|
||||
struct passwd *pw;
|
||||
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
|
||||
int do_init_rtc = 0, restarted = 0, timeout = 0;
|
||||
int other_pid;
|
||||
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
|
||||
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
|
||||
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
|
||||
int system_log = 1;
|
||||
int clock_control = 1, system_log = 1;
|
||||
int config_args = 0;
|
||||
|
||||
do_platform_checks();
|
||||
|
||||
LOG_Initialise();
|
||||
|
||||
/* Parse command line options */
|
||||
while (++argv, (--argc)>0) {
|
||||
|
||||
if (!strcmp("-f", *argv)) {
|
||||
++argv, --argc;
|
||||
conf_file = *argv;
|
||||
} else if (!strcmp("-P", *argv)) {
|
||||
++argv, --argc;
|
||||
if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) {
|
||||
LOG_FATAL(LOGF_Main, "Bad scheduler priority");
|
||||
}
|
||||
} else if (!strcmp("-m", *argv)) {
|
||||
lock_memory = 1;
|
||||
} else if (!strcmp("-r", *argv)) {
|
||||
reload = 1;
|
||||
} else if (!strcmp("-R", *argv)) {
|
||||
restarted = 1;
|
||||
} else if (!strcmp("-u", *argv)) {
|
||||
++argv, --argc;
|
||||
if (argc == 0) {
|
||||
LOG_FATAL(LOGF_Main, "Missing user name");
|
||||
} else {
|
||||
user = *argv;
|
||||
}
|
||||
} else if (!strcmp("-F", *argv)) {
|
||||
++argv, --argc;
|
||||
if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1)
|
||||
LOG_FATAL(LOGF_Main, "Bad syscall filter level");
|
||||
} else if (!strcmp("-s", *argv)) {
|
||||
do_init_rtc = 1;
|
||||
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
|
||||
/* This write to the terminal is OK, it comes before we turn into a daemon */
|
||||
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
|
||||
/* Parse (undocumented) long command-line options */
|
||||
for (optind = 1; optind < argc; optind++) {
|
||||
if (!strcmp("--help", argv[optind])) {
|
||||
print_help(progname);
|
||||
return 0;
|
||||
} else if (!strcmp("-n", *argv)) {
|
||||
nofork = 1;
|
||||
} else if (!strcmp("-d", *argv)) {
|
||||
debug++;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-q", *argv)) {
|
||||
ref_mode = REF_ModeUpdateOnce;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-Q", *argv)) {
|
||||
ref_mode = REF_ModePrintOnce;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
} else if (!strcmp("-t", *argv)) {
|
||||
++argv, --argc;
|
||||
if (argc == 0 || sscanf(*argv, "%d", &timeout) != 1 || timeout <= 0)
|
||||
LOG_FATAL(LOGF_Main, "Bad timeout");
|
||||
} else if (!strcmp("-4", *argv)) {
|
||||
address_family = IPADDR_INET4;
|
||||
} else if (!strcmp("-6", *argv)) {
|
||||
address_family = IPADDR_INET6;
|
||||
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
|
||||
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
|
||||
progname);
|
||||
} else if (!strcmp("--version", argv[optind])) {
|
||||
print_version();
|
||||
return 0;
|
||||
} else if (*argv[0] == '-') {
|
||||
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
|
||||
} else {
|
||||
/* Process remaining arguments and configuration lines */
|
||||
config_args = argc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (getuid() != 0) {
|
||||
/* This write to the terminal is OK, it comes before we turn into a daemon */
|
||||
fprintf(stderr,"Not superuser\n");
|
||||
return 1;
|
||||
optind = 1;
|
||||
|
||||
/* Parse short command-line options */
|
||||
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
|
||||
switch (opt) {
|
||||
case '4':
|
||||
case '6':
|
||||
address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
nofork = 1;
|
||||
system_log = 0;
|
||||
break;
|
||||
case 'f':
|
||||
conf_file = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
scfilter_level = parse_int_arg(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
log_file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
lock_memory = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nofork = 1;
|
||||
break;
|
||||
case 'P':
|
||||
sched_priority = parse_int_arg(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
ref_mode = REF_ModeUpdateOnce;
|
||||
nofork = 1;
|
||||
client_only = 0;
|
||||
system_log = 0;
|
||||
break;
|
||||
case 'Q':
|
||||
ref_mode = REF_ModePrintOnce;
|
||||
nofork = 1;
|
||||
client_only = 1;
|
||||
clock_control = 0;
|
||||
system_log = 0;
|
||||
break;
|
||||
case 'r':
|
||||
reload = 1;
|
||||
break;
|
||||
case 'R':
|
||||
restarted = 1;
|
||||
break;
|
||||
case 's':
|
||||
do_init_rtc = 1;
|
||||
break;
|
||||
case 't':
|
||||
timeout = parse_int_arg(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
print_version();
|
||||
return 0;
|
||||
case 'x':
|
||||
clock_control = 0;
|
||||
break;
|
||||
default:
|
||||
print_help(progname);
|
||||
return opt != 'h';
|
||||
}
|
||||
}
|
||||
|
||||
if (getuid() && !client_only)
|
||||
LOG_FATAL("Not superuser");
|
||||
|
||||
/* Turn into a daemon */
|
||||
if (!nofork) {
|
||||
go_daemon();
|
||||
}
|
||||
|
||||
if (system_log) {
|
||||
if (log_file) {
|
||||
LOG_OpenFileLog(log_file);
|
||||
} else if (system_log) {
|
||||
LOG_OpenSystemLog();
|
||||
}
|
||||
|
||||
LOG_SetDebugLevel(debug);
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)",
|
||||
CHRONY_VERSION, CHRONYD_FEATURES);
|
||||
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
|
||||
|
||||
DNS_SetAddressFamily(address_family);
|
||||
|
||||
CNF_Initialise(restarted);
|
||||
CNF_Initialise(restarted, client_only);
|
||||
|
||||
/* Parse the config file or the remaining command line arguments */
|
||||
config_args = argc - optind;
|
||||
if (!config_args) {
|
||||
CNF_ReadFile(conf_file);
|
||||
} else {
|
||||
do {
|
||||
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
|
||||
} while (++argv, --argc);
|
||||
for (; optind < argc; optind++)
|
||||
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
|
||||
}
|
||||
|
||||
/* Check whether another chronyd may already be running. Do this after
|
||||
* forking, so that message logging goes to the right place (i.e. syslog), in
|
||||
* case this chronyd is being run from a boot script. */
|
||||
if (maybe_another_chronyd_running(&other_pid)) {
|
||||
LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
|
||||
other_pid, CNF_GetPidFile());
|
||||
}
|
||||
/* Check whether another chronyd may already be running */
|
||||
check_pidfile();
|
||||
|
||||
/* Write our lockfile to prevent other chronyds running. This has *GOT* to
|
||||
* be done *AFTER* the daemon-creation fork() */
|
||||
write_lockfile();
|
||||
if (!user)
|
||||
user = CNF_GetUser();
|
||||
|
||||
pw = getpwnam(user);
|
||||
if (!pw)
|
||||
LOG_FATAL("Could not get user/group ID of %s", user);
|
||||
|
||||
/* Create directories for sockets, log files, and dump files */
|
||||
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
|
||||
|
||||
/* Write our pidfile to prevent other instances from running */
|
||||
write_pidfile();
|
||||
|
||||
PRV_Initialise();
|
||||
LCL_Initialise();
|
||||
SCH_Initialise();
|
||||
SYS_Initialise();
|
||||
SYS_Initialise(clock_control);
|
||||
RTC_Initialise(do_init_rtc);
|
||||
SRC_Initialise();
|
||||
RCL_Initialise();
|
||||
@@ -523,18 +570,8 @@ int main
|
||||
SYS_LockMemory();
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
user = CNF_GetUser();
|
||||
}
|
||||
|
||||
if ((pw = getpwnam(user)) == NULL)
|
||||
LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user);
|
||||
|
||||
/* Create all directories before dropping root */
|
||||
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
|
||||
|
||||
/* Drop root privileges if the user has non-zero uid or gid */
|
||||
if (pw->pw_uid || pw->pw_gid)
|
||||
/* Drop root privileges if the specified user has a non-zero UID */
|
||||
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
|
||||
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
|
||||
|
||||
REF_Initialise();
|
||||
@@ -549,7 +586,7 @@ int main
|
||||
/* From now on, it is safe to do finalisation on exit */
|
||||
initialised = 1;
|
||||
|
||||
UTI_SetQuitSignalsHandler(signal_cleanup);
|
||||
UTI_SetQuitSignalsHandler(signal_cleanup, 1);
|
||||
|
||||
CAM_OpenUnixSocket();
|
||||
|
||||
@@ -563,7 +600,7 @@ int main
|
||||
REF_SetModeEndHandler(reference_mode_end);
|
||||
REF_SetMode(ref_mode);
|
||||
|
||||
if (timeout)
|
||||
if (timeout > 0)
|
||||
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
|
||||
|
||||
if (do_init_rtc) {
|
||||
@@ -576,7 +613,7 @@ int main
|
||||
the scheduler. */
|
||||
SCH_MainLoop();
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Main, "chronyd exiting");
|
||||
LOG(LOGS_INFO, "chronyd exiting");
|
||||
|
||||
MAI_CleanupAndExit();
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ cd RELEASES/$subdir || exit 1
|
||||
|
||||
echo $version > version.txt
|
||||
|
||||
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
|
||||
|
||||
./configure && make -C doc man txt || exit 1
|
||||
|
||||
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL
|
||||
|
||||
46
manual.c
46
manual.c
@@ -97,7 +97,8 @@ MNL_Finalise(void)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
estimate_and_set_system(struct timespec *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
|
||||
estimate_and_set_system(struct timespec *now, int offset_provided, double offset,
|
||||
double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
|
||||
{
|
||||
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
|
||||
double b0, b1;
|
||||
@@ -108,32 +109,26 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
|
||||
int found_freq;
|
||||
double slew_by;
|
||||
|
||||
b0 = offset_provided ? offset : 0.0;
|
||||
b1 = freq = 0.0;
|
||||
found_freq = 0;
|
||||
|
||||
if (n_samples > 1) {
|
||||
for (i=0; i<n_samples; i++) {
|
||||
agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when);
|
||||
offsets[i] = samples[i].offset;
|
||||
}
|
||||
|
||||
RGR_FindBestRobustRegression(agos, offsets, n_samples,
|
||||
1.0e-8, /* 0.01ppm easily good enough for this! */
|
||||
&b0, &b1, &n_runs, &best_start);
|
||||
|
||||
|
||||
/* Ignore b0 from regression; treat offset as being the most
|
||||
recently entered value. (If the administrator knows he's put
|
||||
an outlier in, he will rerun the settime operation.) However,
|
||||
the frequency estimate comes from the regression. */
|
||||
|
||||
freq = -b1;
|
||||
found_freq = 1;
|
||||
} else {
|
||||
if (offset_provided) {
|
||||
b0 = offset;
|
||||
} else {
|
||||
b0 = 0.0;
|
||||
if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8,
|
||||
&b0, &b1, &n_runs, &best_start)) {
|
||||
/* Ignore b0 from regression; treat offset as being the most
|
||||
recently entered value. (If the administrator knows he's put
|
||||
an outlier in, he will rerun the settime operation.) However,
|
||||
the frequency estimate comes from the regression. */
|
||||
freq = -b1;
|
||||
found_freq = 1;
|
||||
}
|
||||
b1 = freq = 0.0;
|
||||
found_freq = 0;
|
||||
} else {
|
||||
agos[0] = 0.0;
|
||||
offsets[0] = b0;
|
||||
}
|
||||
@@ -145,21 +140,20 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
|
||||
}
|
||||
|
||||
if (found_freq) {
|
||||
LOG(LOGS_INFO, LOGF_Manual,
|
||||
"Making a frequency change of %.3f ppm and a slew of %.6f",
|
||||
LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f",
|
||||
1.0e6 * freq, slew_by);
|
||||
|
||||
REF_SetManualReference(now,
|
||||
slew_by,
|
||||
freq, skew);
|
||||
} else {
|
||||
LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
|
||||
LOG(LOGS_INFO, "Making a slew of %.6f", slew_by);
|
||||
REF_SetManualReference(now,
|
||||
slew_by,
|
||||
0.0, skew);
|
||||
}
|
||||
|
||||
if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
|
||||
if (reg_offset) *reg_offset = b0;
|
||||
if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
|
||||
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
|
||||
|
||||
@@ -173,7 +167,7 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
|
||||
MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
|
||||
{
|
||||
struct timespec now;
|
||||
double offset, diff;
|
||||
@@ -210,7 +204,7 @@ MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, dou
|
||||
samples[n_samples].orig_offset = offset;
|
||||
++n_samples;
|
||||
|
||||
estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
|
||||
estimate_and_set_system(&now, 1, offset, reg_offset, dfreq_ppm, new_afreq_ppm);
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
2
manual.h
2
manual.h
@@ -33,7 +33,7 @@
|
||||
|
||||
extern void MNL_Initialise(void);
|
||||
extern void MNL_Finalise(void);
|
||||
extern int MNL_AcceptTimestamp(struct timespec *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
|
||||
extern int MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm);
|
||||
|
||||
extern void MNL_Enable(void);
|
||||
extern void MNL_Disable(void);
|
||||
|
||||
34
memory.c
34
memory.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2014
|
||||
* Copyright (C) Miroslav Lichvar 2014, 2017
|
||||
*
|
||||
* This program 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 @@ Malloc(size_t size)
|
||||
|
||||
r = malloc(size);
|
||||
if (!r && size)
|
||||
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
|
||||
LOG_FATAL("Could not allocate memory");
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -49,11 +49,37 @@ Realloc(void *ptr, size_t size)
|
||||
|
||||
r = realloc(ptr, size);
|
||||
if (!r && size)
|
||||
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
|
||||
LOG_FATAL("Could not allocate memory");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_array_size(size_t nmemb, size_t size)
|
||||
{
|
||||
size_t array_size;
|
||||
|
||||
array_size = nmemb * size;
|
||||
|
||||
/* Check for overflow */
|
||||
if (nmemb > 0 && array_size / nmemb != size)
|
||||
LOG_FATAL("Could not allocate memory");
|
||||
|
||||
return array_size;
|
||||
}
|
||||
|
||||
void *
|
||||
Malloc2(size_t nmemb, size_t size)
|
||||
{
|
||||
return Malloc(get_array_size(nmemb, size));
|
||||
}
|
||||
|
||||
void *
|
||||
Realloc2(void *ptr, size_t nmemb, size_t size)
|
||||
{
|
||||
return Realloc(ptr, get_array_size(nmemb, size));
|
||||
}
|
||||
|
||||
char *
|
||||
Strdup(const char *s)
|
||||
{
|
||||
@@ -61,7 +87,7 @@ Strdup(const char *s)
|
||||
|
||||
r = strdup(s);
|
||||
if (!r)
|
||||
LOG_FATAL(LOGF_Memory, "Could not allocate memory");
|
||||
LOG_FATAL("Could not allocate memory");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
8
memory.h
8
memory.h
@@ -27,15 +27,19 @@
|
||||
#ifndef GOT_MEMORY_H
|
||||
#define GOT_MEMORY_H
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
/* Wrappers checking for errors */
|
||||
extern void *Malloc(size_t size);
|
||||
extern void *Realloc(void *ptr, size_t size);
|
||||
extern void *Malloc2(size_t nmemb, size_t size);
|
||||
extern void *Realloc2(void *ptr, size_t nmemb, size_t size);
|
||||
extern char *Strdup(const char *s);
|
||||
|
||||
/* Convenient macros */
|
||||
#define MallocNew(T) ((T *) Malloc(sizeof(T)))
|
||||
#define MallocArray(T, n) ((T *) Malloc((n) * sizeof(T)))
|
||||
#define ReallocArray(T,n,x) ((T *) Realloc((void *)(x), (n)*sizeof(T)))
|
||||
#define MallocArray(T, n) ((T *) Malloc2(n, sizeof(T)))
|
||||
#define ReallocArray(T, n, x) ((T *) Realloc2((void *)(x), n, sizeof(T)))
|
||||
#define Free(x) free(x)
|
||||
|
||||
#endif /* GOT_MEMORY_H */
|
||||
|
||||
17
nameserv.c
17
nameserv.c
@@ -30,6 +30,9 @@
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include "nameserv.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -53,7 +56,19 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
|
||||
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
|
||||
|
||||
memset(&hints, 0, sizeof (hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
switch (address_family) {
|
||||
case IPADDR_INET4:
|
||||
hints.ai_family = AF_INET;
|
||||
break;
|
||||
#ifdef FEAT_IPV6
|
||||
case IPADDR_INET6:
|
||||
hints.ai_family = AF_INET6;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
result = getaddrinfo(name, NULL, &hints, &res);
|
||||
|
||||
@@ -78,7 +78,7 @@ end_resolving(int fd, int event, void *anything)
|
||||
int i;
|
||||
|
||||
if (pthread_join(inst->thread, NULL)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
|
||||
LOG_FATAL("pthread_join() failed");
|
||||
}
|
||||
|
||||
resolving_threads--;
|
||||
@@ -110,7 +110,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
|
||||
inst->status = DNS_Failure;
|
||||
|
||||
if (pipe(inst->pipe)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
|
||||
LOG_FATAL("pipe() failed");
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(inst->pipe[0]);
|
||||
@@ -120,7 +120,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
|
||||
assert(resolving_threads <= 1);
|
||||
|
||||
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
|
||||
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
|
||||
LOG_FATAL("pthread_create() failed");
|
||||
}
|
||||
|
||||
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst);
|
||||
|
||||
18
ntp.h
18
ntp.h
@@ -38,6 +38,9 @@ typedef struct {
|
||||
|
||||
typedef uint32_t NTP_int32;
|
||||
|
||||
/* The UDP port number used by NTP */
|
||||
#define NTP_PORT 123
|
||||
|
||||
/* The NTP protocol version that we support */
|
||||
#define NTP_VERSION 4
|
||||
|
||||
@@ -118,4 +121,19 @@ typedef struct {
|
||||
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
|
||||
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
|
||||
|
||||
/* Structure used to save NTP measurements. time is the local time at which
|
||||
the sample is to be considered to have been made and offset is the offset at
|
||||
the time (positive indicates that the local clock is slow relative to the
|
||||
source). root_delay/root_dispersion include peer_delay/peer_dispersion. */
|
||||
typedef struct {
|
||||
struct timespec time;
|
||||
double offset;
|
||||
double peer_delay;
|
||||
double peer_dispersion;
|
||||
double root_delay;
|
||||
double root_dispersion;
|
||||
int stratum;
|
||||
NTP_Leap leap;
|
||||
} NTP_Sample;
|
||||
|
||||
#endif /* GOT_NTP_H */
|
||||
|
||||
1076
ntp_core.c
1076
ntp_core.c
File diff suppressed because it is too large
Load Diff
@@ -99,12 +99,9 @@ extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Addr
|
||||
/* Slew receive and transmit times in instance records */
|
||||
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
|
||||
|
||||
/* Take a particular source online (i.e. start sampling it) */
|
||||
extern void NCR_TakeSourceOnline(NCR_Instance inst);
|
||||
|
||||
/* Take a particular source offline (i.e. stop sampling it, without
|
||||
marking it unreachable in the source selection stuff) */
|
||||
extern void NCR_TakeSourceOffline(NCR_Instance inst);
|
||||
/* Take a particular source online (i.e. start sampling it) or offline
|
||||
(i.e. stop sampling it) */
|
||||
extern void NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity);
|
||||
|
||||
extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);
|
||||
|
||||
|
||||
170
ntp_io.c
170
ntp_io.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Timo Teras 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2015
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -105,6 +105,9 @@ static int separate_client_sockets;
|
||||
disabled */
|
||||
static int permanent_server_sockets;
|
||||
|
||||
/* Flag indicating the server IPv4 socket is bound to an address */
|
||||
static int bound_server_sock_fd4;
|
||||
|
||||
/* Flag indicating that we have been initialised */
|
||||
static int initialised=0;
|
||||
|
||||
@@ -130,10 +133,10 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
|
||||
if (sock_fd < 0) {
|
||||
if (!client_only) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
|
||||
LOG(LOGS_ERR, "Could not open %s NTP socket : %s",
|
||||
UTI_SockaddrFamilyToString(family), strerror(errno));
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
|
||||
DEBUG_LOG("Could not open %s NTP socket : %s",
|
||||
UTI_SockaddrFamilyToString(family), strerror(errno));
|
||||
}
|
||||
return INVALID_SOCK_FD;
|
||||
@@ -142,6 +145,10 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
/* Close on exec */
|
||||
UTI_FdSetCloexec(sock_fd);
|
||||
|
||||
/* Enable non-blocking mode on server sockets */
|
||||
if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
|
||||
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
|
||||
|
||||
/* Prepare local address */
|
||||
memset(&my_addr, 0, sizeof (my_addr));
|
||||
my_addr_len = 0;
|
||||
@@ -164,6 +171,9 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
my_addr.in4.sin_port = htons(port_number);
|
||||
my_addr_len = sizeof (my_addr.in4);
|
||||
|
||||
if (!client_only)
|
||||
bound_server_sock_fd4 = my_addr.in4.sin_addr.s_addr != htonl(INADDR_ANY);
|
||||
|
||||
break;
|
||||
#ifdef FEAT_IPV6
|
||||
case AF_INET6:
|
||||
@@ -193,48 +203,45 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
/* 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 %s socket option", "SO_REUSEADDR");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
|
||||
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
|
||||
if (!client_only &&
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_BROADCAST");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
|
||||
#ifdef SO_TIMESTAMP
|
||||
/* Enable receiving of timestamp control messages */
|
||||
#ifdef SO_TIMESTAMPNS
|
||||
/* Try nanosecond resolution first */
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
|
||||
#endif
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Enable kernel/HW timestamping of packets */
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events);
|
||||
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
|
||||
#endif
|
||||
#ifdef SO_TIMESTAMPNS
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
|
||||
#endif
|
||||
#ifdef SO_TIMESTAMP
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
|
||||
#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 %s socket option", "IP_FREEBIND");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (family == AF_INET) {
|
||||
#ifdef HAVE_IN_PKTINFO
|
||||
/* We want the local IP info on server sockets */
|
||||
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_PKTINFO");
|
||||
/* Don't quit - we might survive anyway */
|
||||
}
|
||||
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0)
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
|
||||
#elif defined(IP_RECVDSTADDR)
|
||||
if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0)
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR");
|
||||
#endif
|
||||
}
|
||||
#ifdef FEAT_IPV6
|
||||
@@ -242,18 +249,18 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
#ifdef IPV6_V6ONLY
|
||||
/* Receive IPv6 packets only */
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_V6ONLY");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IN6_PKTINFO
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_RECVPKTINFO");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO");
|
||||
}
|
||||
#else
|
||||
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_PKTINFO");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -262,7 +269,7 @@ prepare_socket(int family, int port_number, int client_only)
|
||||
|
||||
/* Bind the socket if a port or address was specified */
|
||||
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
|
||||
LOG(LOGS_ERR, "Could not bind %s NTP socket : %s",
|
||||
UTI_SockaddrFamilyToString(family), strerror(errno));
|
||||
close(sock_fd);
|
||||
return INVALID_SOCK_FD;
|
||||
@@ -304,7 +311,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
|
||||
assert(addr_len);
|
||||
|
||||
if (connect(sock_fd, &addr.u, addr_len) < 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
|
||||
DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
@@ -321,6 +328,9 @@ close_socket(int sock_fd)
|
||||
if (sock_fd == INVALID_SOCK_FD)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
NIO_Linux_NotifySocketClosing(sock_fd);
|
||||
#endif
|
||||
SCH_RemoveFileHandler(sock_fd);
|
||||
close(sock_fd);
|
||||
}
|
||||
@@ -364,8 +374,11 @@ NIO_Initialise(int family)
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
NIO_Linux_Initialise();
|
||||
#else
|
||||
if (ARR_GetSize(CNF_GetHwTsInterfaces()))
|
||||
LOG_FATAL(LOGF_NtpIO, "HW timestamping not supported");
|
||||
if (1) {
|
||||
CNF_HwTsInterface *conf_iface;
|
||||
if (CNF_GetHwTsInterface(0, &conf_iface))
|
||||
LOG_FATAL("HW timestamping not supported");
|
||||
}
|
||||
#endif
|
||||
|
||||
recv_messages = ARR_CreateInstance(sizeof (struct Message));
|
||||
@@ -427,7 +440,7 @@ NIO_Initialise(int family)
|
||||
&& client_sock_fd6 == INVALID_SOCK_FD
|
||||
#endif
|
||||
)) {
|
||||
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
|
||||
LOG_FATAL("Could not open NTP sockets");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,6 +580,23 @@ NIO_IsServerSocket(int sock_fd)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
|
||||
{
|
||||
int sock_fd, r;
|
||||
|
||||
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
|
||||
if (sock_fd == INVALID_SOCK_FD)
|
||||
return 0;
|
||||
|
||||
r = connect_socket(sock_fd, remote_addr);
|
||||
close_socket(sock_fd);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
{
|
||||
@@ -575,14 +605,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
NTP_Local_Timestamp local_ts;
|
||||
struct timespec sched_ts;
|
||||
struct cmsghdr *cmsg;
|
||||
int if_index;
|
||||
|
||||
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
|
||||
local_ts.source = NTP_TS_DAEMON;
|
||||
sched_ts = local_ts.ts;
|
||||
|
||||
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Truncated source address");
|
||||
DEBUG_LOG("Truncated source address");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -595,17 +624,17 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
}
|
||||
|
||||
local_addr.ip_addr.family = IPADDR_UNSPEC;
|
||||
local_addr.if_index = INVALID_IF_INDEX;
|
||||
local_addr.sock_fd = sock_fd;
|
||||
if_index = -1;
|
||||
|
||||
if (hdr->msg_flags & MSG_TRUNC) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Received truncated message from %s:%d",
|
||||
DEBUG_LOG("Received truncated message from %s:%d",
|
||||
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdr->msg_flags & MSG_CTRUNC) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Truncated control message");
|
||||
DEBUG_LOG("Truncated control message");
|
||||
/* Continue */
|
||||
}
|
||||
|
||||
@@ -617,7 +646,15 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
|
||||
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
|
||||
local_addr.ip_addr.family = IPADDR_INET4;
|
||||
if_index = ipi.ipi_ifindex;
|
||||
local_addr.if_index = ipi.ipi_ifindex;
|
||||
}
|
||||
#elif defined(IP_RECVDSTADDR)
|
||||
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
|
||||
struct in_addr addr;
|
||||
|
||||
memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
|
||||
local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr);
|
||||
local_addr.ip_addr.family = IPADDR_INET4;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -629,7 +666,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
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;
|
||||
if_index = ipi.ipi6_ifindex;
|
||||
local_addr.if_index = ipi.ipi6_ifindex;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -657,14 +694,13 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts,
|
||||
hdr, length, sock_fd, if_index))
|
||||
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length))
|
||||
return;
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
|
||||
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f",
|
||||
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
|
||||
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, if_index,
|
||||
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
|
||||
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
|
||||
|
||||
/* Just ignore the packet if it's not of a recognized length */
|
||||
@@ -687,6 +723,11 @@ read_from_socket(int sock_fd, int event, void *anything)
|
||||
unsigned int i, n;
|
||||
int status, flags = 0;
|
||||
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
if (NIO_Linux_ProcessEvent(sock_fd, event))
|
||||
return;
|
||||
#endif
|
||||
|
||||
hdr = ARR_GetElements(recv_headers);
|
||||
n = ARR_GetSize(recv_headers);
|
||||
assert(n >= 1);
|
||||
@@ -711,7 +752,21 @@ read_from_socket(int sock_fd, int event, void *anything)
|
||||
#endif
|
||||
|
||||
if (status < 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not receive from fd %d : %s", sock_fd,
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
/* If reading from the error queue failed, the exception should be
|
||||
for a socket error. Clear the error to avoid a busy loop. */
|
||||
if (flags & MSG_ERRQUEUE) {
|
||||
int error = 0;
|
||||
socklen_t len = sizeof (error);
|
||||
|
||||
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
|
||||
DEBUG_LOG("Could not get SO_ERROR");
|
||||
if (error)
|
||||
errno = error;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
@@ -742,7 +797,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
assert(initialised);
|
||||
|
||||
if (local_addr->sock_fd == INVALID_SOCK_FD) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
|
||||
DEBUG_LOG("No socket to send to %s:%d",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
|
||||
return 0;
|
||||
}
|
||||
@@ -786,6 +841,25 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
|
||||
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
|
||||
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
|
||||
if (local_addr->if_index != INVALID_IF_INDEX)
|
||||
ipi->ipi_ifindex = local_addr->if_index;
|
||||
}
|
||||
#elif defined(IP_SENDSRCADDR)
|
||||
/* Specify the IPv4 source address only if the socket is not bound */
|
||||
if (local_addr->ip_addr.family == IPADDR_INET4 &&
|
||||
local_addr->sock_fd == server_sock_fd4 && !bound_server_sock_fd4) {
|
||||
struct in_addr *addr;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
|
||||
cmsglen += CMSG_SPACE(sizeof (struct in_addr));
|
||||
|
||||
cmsg->cmsg_level = IPPROTO_IP;
|
||||
cmsg->cmsg_type = IP_SENDSRCADDR;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
|
||||
|
||||
addr = (struct in_addr *)CMSG_DATA(cmsg);
|
||||
addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -804,6 +878,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
|
||||
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
|
||||
sizeof(ipi->ipi6_addr.s6_addr));
|
||||
if (local_addr->if_index != INVALID_IF_INDEX)
|
||||
ipi->ipi6_ifindex = local_addr->if_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -818,14 +894,14 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
msg.msg_control = NULL;
|
||||
|
||||
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
|
||||
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
|
||||
DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s",
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", length,
|
||||
DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
|
||||
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
|
||||
|
||||
|
||||
3
ntp_io.h
3
ntp_io.h
@@ -53,6 +53,9 @@ extern void NIO_CloseServerSocket(int sock_fd);
|
||||
/* Function to check if socket is a server socket */
|
||||
extern int NIO_IsServerSocket(int sock_fd);
|
||||
|
||||
/* Function to check if client packets can be sent to a server */
|
||||
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
|
||||
|
||||
/* Function to transmit a packet */
|
||||
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
NTP_Local_Address *local_addr, int length, int process_tx);
|
||||
|
||||
520
ntp_io_linux.c
520
ntp_io_linux.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/ptp_clock.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <net/if.h>
|
||||
|
||||
@@ -61,17 +60,30 @@ struct Interface {
|
||||
char name[IF_NAMESIZE];
|
||||
int if_index;
|
||||
int phc_fd;
|
||||
int phc_mode;
|
||||
int phc_nocrossts;
|
||||
/* Link speed in mbit/s */
|
||||
int link_speed;
|
||||
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
||||
int l2_udp4_ntp_start;
|
||||
int l2_udp6_ntp_start;
|
||||
/* Precision of PHC readings */
|
||||
double precision;
|
||||
/* Compensation of errors in TX and RX timestamping */
|
||||
double tx_comp;
|
||||
double rx_comp;
|
||||
HCL_Instance clock;
|
||||
};
|
||||
|
||||
/* Number of PHC readings per HW clock sample */
|
||||
#define PHC_READINGS 10
|
||||
|
||||
/* Minimum interval between PHC readings */
|
||||
#define MIN_PHC_POLL -6
|
||||
|
||||
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
|
||||
#define MAX_TS_DELAY 1.0
|
||||
|
||||
/* Array of Interfaces */
|
||||
static ARR_Instance interfaces;
|
||||
|
||||
@@ -82,22 +94,42 @@ static int ts_tx_flags;
|
||||
/* Flag indicating the socket options can't be changed in control messages */
|
||||
static int permanent_ts_options;
|
||||
|
||||
/* When sending client requests to a close and fast server, it is possible that
|
||||
a response will be received before the HW transmit timestamp of the request
|
||||
itself. To avoid processing of the response without the HW timestamp, we
|
||||
monitor events returned by select() and suspend reading of packets from the
|
||||
receive queue for up to 200 microseconds. As the requests are normally
|
||||
separated by at least 200 milliseconds, it is sufficient to monitor and
|
||||
suspend one socket at a time. */
|
||||
static int monitored_socket;
|
||||
static int suspended_socket;
|
||||
static SCH_TimeoutID resume_timeout_id;
|
||||
|
||||
#define RESUME_TIMEOUT 200.0e-6
|
||||
|
||||
/* Unbound socket keeping the kernel RX timestamping permanently enabled
|
||||
in order to avoid a race condition between receiving a server response
|
||||
and the kernel actually starting to timestamp received packets after
|
||||
enabling the timestamping and sending a request */
|
||||
static int dummy_rxts_socket;
|
||||
|
||||
#define INVALID_SOCK_FD -3
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
add_interface(const char *name)
|
||||
add_interface(CNF_HwTsInterface *conf_iface)
|
||||
{
|
||||
struct ethtool_ts_info ts_info;
|
||||
struct hwtstamp_config ts_config;
|
||||
struct ifreq req;
|
||||
int sock_fd, if_index, phc_index, phc_fd;
|
||||
int sock_fd, if_index, phc_fd, req_hwts_flags;
|
||||
unsigned int i;
|
||||
struct Interface *iface;
|
||||
char phc_path[64];
|
||||
|
||||
/* Check if the interface was not already added */
|
||||
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
||||
if (!strcmp(name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
|
||||
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -108,13 +140,14 @@ add_interface(const char *name)
|
||||
memset(&req, 0, sizeof (req));
|
||||
memset(&ts_info, 0, sizeof (ts_info));
|
||||
|
||||
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", name) >= sizeof (req.ifr_name)) {
|
||||
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
|
||||
sizeof (req.ifr_name)) {
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -125,50 +158,89 @@ add_interface(const char *name)
|
||||
req.ifr_data = (char *)&ts_info;
|
||||
|
||||
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
req_hwts_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
|
||||
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ts_info.phc_index < 0) {
|
||||
DEBUG_LOG("PHC missing on %s", req.ifr_name);
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ts_config.flags = 0;
|
||||
ts_config.tx_type = HWTSTAMP_TX_ON;
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
|
||||
switch (conf_iface->rxfilter) {
|
||||
case CNF_HWTS_RXFILTER_ANY:
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
|
||||
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
else
|
||||
#endif
|
||||
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
else
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
break;
|
||||
case CNF_HWTS_RXFILTER_NONE:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
|
||||
break;
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
|
||||
case CNF_HWTS_RXFILTER_NTP:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
break;
|
||||
}
|
||||
|
||||
req.ifr_data = (char *)&ts_config;
|
||||
|
||||
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
phc_index = ts_info.phc_index;
|
||||
|
||||
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
|
||||
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
|
||||
if (phc_fd < 0)
|
||||
return 0;
|
||||
|
||||
phc_fd = open(phc_path, O_RDONLY);
|
||||
if (phc_fd < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not open %s : %s", phc_path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
|
||||
iface = ARR_GetNewElement(interfaces);
|
||||
|
||||
snprintf(iface->name, sizeof (iface->name), "%s", name);
|
||||
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
|
||||
iface->if_index = if_index;
|
||||
iface->phc_fd = phc_fd;
|
||||
iface->phc_mode = 0;
|
||||
iface->phc_nocrossts = conf_iface->nocrossts;
|
||||
|
||||
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
|
||||
iface->link_speed = 1000;
|
||||
iface->l2_udp4_ntp_start = 42;
|
||||
iface->l2_udp6_ntp_start = 62;
|
||||
|
||||
iface->clock = HCL_CreateInstance();
|
||||
iface->precision = conf_iface->precision;
|
||||
iface->tx_comp = conf_iface->tx_comp;
|
||||
iface->rx_comp = conf_iface->rx_comp;
|
||||
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "Enabled HW timestamping on %s", name);
|
||||
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
||||
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
|
||||
|
||||
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
||||
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -176,18 +248,22 @@ add_interface(const char *name)
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
add_all_interfaces(void)
|
||||
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
|
||||
{
|
||||
CNF_HwTsInterface conf_iface;
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
int r;
|
||||
|
||||
conf_iface = *conf_iface_all;
|
||||
|
||||
if (getifaddrs(&ifaddr)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "getifaddrs() failed : %s", strerror(errno));
|
||||
DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
||||
if (add_interface(ifa->ifa_name))
|
||||
conf_iface.name = ifa->ifa_name;
|
||||
if (add_interface(&conf_iface))
|
||||
r = 1;
|
||||
}
|
||||
|
||||
@@ -204,7 +280,7 @@ update_interface_speed(struct Interface *iface)
|
||||
{
|
||||
struct ethtool_cmd cmd;
|
||||
struct ifreq req;
|
||||
int sock_fd;
|
||||
int sock_fd, link_speed;
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock_fd < 0)
|
||||
@@ -218,14 +294,65 @@ update_interface_speed(struct Interface *iface)
|
||||
req.ifr_data = (char *)&cmd;
|
||||
|
||||
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
|
||||
close(sock_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
|
||||
iface->link_speed = ethtool_cmd_speed(&cmd);
|
||||
link_speed = ethtool_cmd_speed(&cmd);
|
||||
|
||||
if (iface->link_speed != link_speed) {
|
||||
iface->link_speed = link_speed;
|
||||
DEBUG_LOG("Updated speed of %s to %d Mb/s", iface->name, link_speed);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW)
|
||||
static int
|
||||
check_timestamping_option(int option)
|
||||
{
|
||||
int sock_fd;
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock_fd < 0)
|
||||
return 0;
|
||||
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
|
||||
DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
|
||||
close(sock_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
open_dummy_socket(void)
|
||||
{
|
||||
int sock_fd, events = 0;
|
||||
|
||||
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
|
||||
#ifdef FEAT_IPV6
|
||||
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
|
||||
#endif
|
||||
)
|
||||
return INVALID_SOCK_FD;
|
||||
|
||||
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
|
||||
close(sock_fd);
|
||||
return INVALID_SOCK_FD;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(sock_fd);
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -233,42 +360,45 @@ update_interface_speed(struct Interface *iface)
|
||||
void
|
||||
NIO_Linux_Initialise(void)
|
||||
{
|
||||
ARR_Instance config_hwts_ifaces;
|
||||
char *if_name;
|
||||
CNF_HwTsInterface *conf_iface;
|
||||
unsigned int i;
|
||||
int wildcard, hwts;
|
||||
int hwts;
|
||||
|
||||
interfaces = ARR_CreateInstance(sizeof (struct Interface));
|
||||
|
||||
config_hwts_ifaces = CNF_GetHwTsInterfaces();
|
||||
|
||||
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
|
||||
all interfaces. If no interface was specified, enable SW timestamping. */
|
||||
|
||||
for (i = wildcard = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
|
||||
if (!strcmp("*", *(char **)ARR_GetElement(config_hwts_ifaces, i)))
|
||||
wildcard = 1;
|
||||
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
|
||||
if (!strcmp("*", conf_iface->name))
|
||||
continue;
|
||||
if (!add_interface(conf_iface))
|
||||
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
|
||||
hwts = 1;
|
||||
}
|
||||
|
||||
if (!wildcard && ARR_GetSize(config_hwts_ifaces)) {
|
||||
for (i = 0; i < ARR_GetSize(config_hwts_ifaces); i++) {
|
||||
if_name = *(char **)ARR_GetElement(config_hwts_ifaces, i);
|
||||
if (!add_interface(if_name))
|
||||
LOG_FATAL(LOGF_NtpIO, "Could not enable HW timestamping on %s", if_name);
|
||||
}
|
||||
hwts = 1;
|
||||
} else if (wildcard && add_all_interfaces()) {
|
||||
hwts = 1;
|
||||
} else {
|
||||
hwts = 0;
|
||||
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
|
||||
if (strcmp("*", conf_iface->name))
|
||||
continue;
|
||||
if (add_all_interfaces(conf_iface))
|
||||
hwts = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
|
||||
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
|
||||
|
||||
if (hwts) {
|
||||
ts_flags = SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
|
||||
ts_tx_flags = SOF_TIMESTAMPING_TX_HARDWARE;
|
||||
} else {
|
||||
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
|
||||
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
|
||||
ts_flags |= SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
|
||||
ts_tx_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
|
||||
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_PKTINFO))
|
||||
ts_flags |= SOF_TIMESTAMPING_OPT_PKTINFO;
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW
|
||||
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_TX_SWHW))
|
||||
ts_flags |= SOF_TIMESTAMPING_OPT_TX_SWHW;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Enable IP_PKTINFO in messages looped back to the error queue */
|
||||
@@ -276,6 +406,10 @@ NIO_Linux_Initialise(void)
|
||||
|
||||
/* Kernels before 4.7 ignore timestamping flags set in control messages */
|
||||
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
|
||||
|
||||
monitored_socket = INVALID_SOCK_FD;
|
||||
suspended_socket = INVALID_SOCK_FD;
|
||||
dummy_rxts_socket = INVALID_SOCK_FD;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -286,6 +420,9 @@ NIO_Linux_Finalise(void)
|
||||
struct Interface *iface;
|
||||
unsigned int i;
|
||||
|
||||
if (dummy_rxts_socket != INVALID_SOCK_FD)
|
||||
close(dummy_rxts_socket);
|
||||
|
||||
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
||||
iface = ARR_GetElement(interfaces, i);
|
||||
HCL_DestroyInstance(iface->clock);
|
||||
@@ -315,13 +452,13 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
||||
flags |= ts_tx_flags;
|
||||
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
|
||||
ts_flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_NtpIOLinux, "Could not set %s socket option", "SO_TIMESTAMPING");
|
||||
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
|
||||
ts_flags = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -332,64 +469,69 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_phc_sample(int phc_fd, struct timespec *phc_ts, struct timespec *local_ts, double *p_delay)
|
||||
static void
|
||||
resume_socket(int sock_fd)
|
||||
{
|
||||
struct ptp_sys_offset sys_off;
|
||||
struct timespec ts1, ts2, ts3, phc_tss[PHC_READINGS], sys_tss[PHC_READINGS];
|
||||
double min_delay = 0.0, delays[PHC_READINGS], phc_sum, local_sum, local_prec;
|
||||
int i, n;
|
||||
if (monitored_socket == sock_fd)
|
||||
monitored_socket = INVALID_SOCK_FD;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
|
||||
return;
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
suspended_socket = INVALID_SOCK_FD;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
|
||||
|
||||
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
|
||||
resume_timeout_id ? "before" : "on", sock_fd);
|
||||
|
||||
if (resume_timeout_id) {
|
||||
SCH_RemoveTimeout(resume_timeout_id);
|
||||
resume_timeout_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
resume_timeout(void *arg)
|
||||
{
|
||||
resume_timeout_id = 0;
|
||||
resume_socket(suspended_socket);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
suspend_socket(int sock_fd)
|
||||
{
|
||||
resume_socket(suspended_socket);
|
||||
|
||||
suspended_socket = sock_fd;
|
||||
|
||||
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
|
||||
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
|
||||
|
||||
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NIO_Linux_ProcessEvent(int sock_fd, int event)
|
||||
{
|
||||
if (sock_fd != monitored_socket)
|
||||
return 0;
|
||||
|
||||
if (event == SCH_FILE_INPUT) {
|
||||
suspend_socket(monitored_socket);
|
||||
monitored_socket = INVALID_SOCK_FD;
|
||||
|
||||
/* Don't process the message yet */
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts1.tv_sec = sys_off.ts[i * 2].sec;
|
||||
ts1.tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
ts2.tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
ts2.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
|
||||
ts3.tv_sec = sys_off.ts[i * 2 + 2].sec;
|
||||
ts3.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
|
||||
sys_tss[i] = ts1;
|
||||
phc_tss[i] = ts2;
|
||||
delays[i] = UTI_DiffTimespecsToDouble(&ts3, &ts1);
|
||||
|
||||
if (delays[i] <= 0.0)
|
||||
/* Step in the middle of a PHC reading? */
|
||||
return 0;
|
||||
|
||||
if (!i || delays[i] < min_delay)
|
||||
min_delay = delays[i];
|
||||
}
|
||||
|
||||
local_prec = LCL_GetSysPrecisionAsQuantum();
|
||||
|
||||
/* Combine best readings */
|
||||
for (i = n = 0, phc_sum = local_sum = 0.0; i < PHC_READINGS; i++) {
|
||||
if (delays[i] > min_delay + local_prec)
|
||||
continue;
|
||||
|
||||
phc_sum += UTI_DiffTimespecsToDouble(&phc_tss[i], &phc_tss[0]);
|
||||
local_sum += UTI_DiffTimespecsToDouble(&sys_tss[i], &sys_tss[0]) + delays[i] / 2.0;
|
||||
n++;
|
||||
}
|
||||
|
||||
assert(n);
|
||||
|
||||
UTI_AddDoubleToTimespec(&phc_tss[0], phc_sum / n, phc_ts);
|
||||
UTI_AddDoubleToTimespec(&sys_tss[0], local_sum / n, &ts1);
|
||||
LCL_CookTime(&ts1, local_ts, NULL);
|
||||
*p_delay = min_delay;
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -415,38 +557,60 @@ get_interface(int if_index)
|
||||
|
||||
static void
|
||||
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
||||
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family)
|
||||
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
|
||||
int l2_length)
|
||||
{
|
||||
struct timespec sample_phc_ts, sample_local_ts;
|
||||
double sample_delay, rx_correction;
|
||||
int l2_length;
|
||||
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
|
||||
double rx_correction, ts_delay, phc_err, local_err;
|
||||
|
||||
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
|
||||
if (!get_phc_sample(iface->phc_fd, &sample_phc_ts, &sample_local_ts, &sample_delay))
|
||||
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
|
||||
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
|
||||
&phc_err))
|
||||
return;
|
||||
|
||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
|
||||
sample_delay / 2.0);
|
||||
phc_err + local_err);
|
||||
|
||||
update_interface_speed(iface);
|
||||
}
|
||||
|
||||
/* We need to transpose RX timestamps as hardware timestamps are normally
|
||||
preamble timestamps and RX timestamps in NTP are supposed to be trailer
|
||||
timestamps. Without raw sockets we don't know the length of the packet
|
||||
at layer 2, so we make an assumption that UDP data start at the same
|
||||
position as in the last transmitted packet which had a HW TX timestamp. */
|
||||
timestamps. If we don't know the length of the packet at layer 2, we
|
||||
make an assumption that UDP data start at the same position as in the
|
||||
last transmitted packet which had a HW TX timestamp. */
|
||||
if (rx_ntp_length && iface->link_speed) {
|
||||
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
|
||||
iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
|
||||
if (!l2_length)
|
||||
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
|
||||
iface->l2_udp6_ntp_start) + rx_ntp_length;
|
||||
|
||||
/* Include the frame check sequence (FCS) */
|
||||
l2_length += 4;
|
||||
|
||||
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
|
||||
|
||||
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
|
||||
}
|
||||
|
||||
if (!HCL_CookTime(iface->clock, hw_ts, &local_ts->ts, &local_ts->err))
|
||||
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
|
||||
return;
|
||||
|
||||
if (!rx_ntp_length && iface->tx_comp)
|
||||
UTI_AddDoubleToTimespec(&ts, iface->tx_comp, &ts);
|
||||
else if (rx_ntp_length && iface->rx_comp)
|
||||
UTI_AddDoubleToTimespec(&ts, -iface->rx_comp, &ts);
|
||||
|
||||
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
|
||||
|
||||
if (fabs(ts_delay) > MAX_TS_DELAY) {
|
||||
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
|
||||
return;
|
||||
}
|
||||
|
||||
local_ts->ts = ts;
|
||||
local_ts->err = local_err;
|
||||
local_ts->source = NTP_TS_HARDWARE;
|
||||
}
|
||||
|
||||
@@ -491,14 +655,43 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
|
||||
len -= ihl + 8, msg += ihl + 8;
|
||||
#ifdef FEAT_IPV6
|
||||
} else if (len >= 48 && msg[0] >> 4 == 6) {
|
||||
/* IPv6 extension headers are not supported */
|
||||
if (msg[6] != 17)
|
||||
return 0;
|
||||
int eh_len, next_header = msg[6];
|
||||
|
||||
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
|
||||
addr.in6.sin6_port = *(uint16_t *)(msg + 40 + 2);
|
||||
len -= 40, msg += 40;
|
||||
|
||||
/* Skip IPv6 extension headers if present */
|
||||
while (next_header != 17) {
|
||||
switch (next_header) {
|
||||
case 44: /* Fragment Header */
|
||||
/* Process only the first fragment */
|
||||
if (ntohs(*(uint16_t *)(msg + 2)) >> 3 != 0)
|
||||
return 0;
|
||||
eh_len = 8;
|
||||
break;
|
||||
case 0: /* Hop-by-Hop Options */
|
||||
case 43: /* Routing Header */
|
||||
case 60: /* Destination Options */
|
||||
case 135: /* Mobility Header */
|
||||
eh_len = 8 * (msg[1] + 1);
|
||||
break;
|
||||
case 51: /* Authentication Header */
|
||||
eh_len = 4 * (msg[1] + 2);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (eh_len < 8 || len < eh_len + 8)
|
||||
return 0;
|
||||
|
||||
next_header = msg[0];
|
||||
len -= eh_len, msg += eh_len;
|
||||
}
|
||||
|
||||
addr.in6.sin6_port = *(uint16_t *)(msg + 2);
|
||||
addr.in6.sin6_family = AF_INET6;
|
||||
len -= 48, msg += 48;
|
||||
len -= 8, msg += 8;
|
||||
#endif
|
||||
} else {
|
||||
return 0;
|
||||
@@ -517,33 +710,55 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
|
||||
|
||||
int
|
||||
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
NTP_Local_Timestamp *local_ts, struct msghdr *hdr,
|
||||
int length, int sock_fd, int if_index)
|
||||
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
|
||||
{
|
||||
struct Interface *iface;
|
||||
struct cmsghdr *cmsg;
|
||||
int is_tx, l2_length;
|
||||
int is_tx, ts_if_index, l2_length;
|
||||
|
||||
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
|
||||
iface = NULL;
|
||||
ts_if_index = local_addr->if_index;
|
||||
l2_length = 0;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
|
||||
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
|
||||
struct scm_ts_pktinfo ts_pktinfo;
|
||||
|
||||
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
|
||||
|
||||
ts_if_index = ts_pktinfo.if_index;
|
||||
l2_length = ts_pktinfo.pkt_length;
|
||||
|
||||
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
|
||||
struct scm_timestamping ts3;
|
||||
|
||||
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
|
||||
|
||||
if (!UTI_IsZeroTimespec(&ts3.ts[0])) {
|
||||
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
|
||||
local_ts->source = NTP_TS_KERNEL;
|
||||
} else {
|
||||
iface = get_interface(if_index);
|
||||
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
|
||||
iface = get_interface(ts_if_index);
|
||||
if (iface) {
|
||||
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
|
||||
remote_addr->ip_addr.family);
|
||||
remote_addr->ip_addr.family, l2_length);
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "HW clock not found for interface %d", if_index);
|
||||
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
|
||||
}
|
||||
|
||||
/* If a HW transmit timestamp was received, resume processing
|
||||
of non-error messages on this socket */
|
||||
if (is_tx)
|
||||
resume_socket(local_addr->sock_fd);
|
||||
}
|
||||
|
||||
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
|
||||
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
|
||||
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
|
||||
local_ts->source = NTP_TS_KERNEL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,13 +770,21 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
|
||||
|
||||
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
|
||||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "Unknown extended error");
|
||||
DEBUG_LOG("Unknown extended error");
|
||||
/* Drop the message */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the kernel is slow with enabling RX timestamping, open a dummy
|
||||
socket to keep the kernel RX timestamping permanently enabled */
|
||||
if (!is_tx && local_ts->source == NTP_TS_DAEMON && ts_flags) {
|
||||
DEBUG_LOG("Missing kernel RX timestamp");
|
||||
if (dummy_rxts_socket == INVALID_SOCK_FD)
|
||||
dummy_rxts_socket = open_dummy_socket();
|
||||
}
|
||||
|
||||
/* Return the message if it's not received from the error queue */
|
||||
if (!is_tx)
|
||||
return 0;
|
||||
@@ -572,9 +795,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
|
||||
l2_length = length;
|
||||
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
|
||||
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
|
||||
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u",
|
||||
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
|
||||
sock_fd, if_index, local_ts->source);
|
||||
local_addr->sock_fd, local_addr->if_index, local_ts->source);
|
||||
|
||||
/* Update assumed position of UDP data at layer 2 for next received packet */
|
||||
if (iface && length) {
|
||||
@@ -584,9 +807,9 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
|
||||
iface->l2_udp6_ntp_start = l2_length - length;
|
||||
}
|
||||
|
||||
/* Drop the message if HW timestamp is missing or its processing failed */
|
||||
if ((ts_flags & SOF_TIMESTAMPING_RAW_HARDWARE) && local_ts->source != NTP_TS_HARDWARE) {
|
||||
DEBUG_LOG(LOGF_NtpIOLinux, "Missing HW timestamp");
|
||||
/* Drop the message if it has no timestamp or its processing failed */
|
||||
if (local_ts->source == NTP_TS_DAEMON) {
|
||||
DEBUG_LOG("Missing TX timestamp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -606,6 +829,15 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
|
||||
{
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
if (!ts_flags)
|
||||
return cmsglen;
|
||||
|
||||
/* If a HW transmit timestamp is requested on a client socket, monitor
|
||||
events on the socket in order to avoid processing of a fast response
|
||||
without the HW timestamp of the request */
|
||||
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
|
||||
monitored_socket = sock_fd;
|
||||
|
||||
/* Check if TX timestamping is disabled on this socket */
|
||||
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
|
||||
return cmsglen;
|
||||
@@ -625,3 +857,11 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
|
||||
|
||||
return cmsglen;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
NIO_Linux_NotifySocketClosing(int sock_fd)
|
||||
{
|
||||
resume_socket(sock_fd);
|
||||
}
|
||||
|
||||
@@ -24,14 +24,22 @@
|
||||
This is the header file for the Linux-specific NTP socket I/O bits.
|
||||
*/
|
||||
|
||||
#ifndef GOT_NTP_IO_LINUX_H
|
||||
#define GOT_NTP_IO_LINUX_H
|
||||
|
||||
extern void NIO_Linux_Initialise(void);
|
||||
|
||||
extern void NIO_Linux_Finalise(void);
|
||||
|
||||
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
|
||||
|
||||
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
|
||||
|
||||
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length,
|
||||
int sock_fd, int if_index);
|
||||
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
|
||||
|
||||
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
|
||||
|
||||
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
|
||||
|
||||
#endif
|
||||
|
||||
47
ntp_signd.c
47
ntp_signd.c
@@ -135,7 +135,7 @@ open_socket(void)
|
||||
|
||||
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Could not open signd socket : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -145,18 +145,18 @@ open_socket(void)
|
||||
s.sun_family = AF_UNIX;
|
||||
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
|
||||
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd socket path too long");
|
||||
DEBUG_LOG("signd socket path too long");
|
||||
close_socket();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Could not connect to signd : %s", strerror(errno));
|
||||
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
|
||||
close_socket();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Connected to signd");
|
||||
DEBUG_LOG("Connected to signd");
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -170,25 +170,25 @@ process_response(SignInstance *inst)
|
||||
double delay;
|
||||
|
||||
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Invalid response ID");
|
||||
DEBUG_LOG("Invalid response ID");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Signing failed");
|
||||
DEBUG_LOG("Signing failed");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the file descriptor is still valid */
|
||||
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Invalid NTP socket");
|
||||
DEBUG_LOG("Invalid NTP socket");
|
||||
return;
|
||||
}
|
||||
|
||||
SCH_GetLastEventTime(NULL, NULL, &ts);
|
||||
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Signing succeeded (delay %f)", delay);
|
||||
DEBUG_LOG("Signing succeeded (delay %f)", delay);
|
||||
|
||||
/* Send the signed NTP packet */
|
||||
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
|
||||
@@ -222,12 +222,12 @@ read_write_socket(int sock_fd, int event, void *anything)
|
||||
inst->request_length - inst->sent, 0);
|
||||
|
||||
if (s < 0) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
|
||||
DEBUG_LOG("signd socket error: %s", strerror(errno));
|
||||
close_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Sent %d bytes to signd", s);
|
||||
DEBUG_LOG("Sent %d bytes to signd", s);
|
||||
inst->sent += s;
|
||||
|
||||
/* Try again later if the request is not complete yet */
|
||||
@@ -235,12 +235,12 @@ read_write_socket(int sock_fd, int event, void *anything)
|
||||
return;
|
||||
|
||||
/* Disable output and wait for a response */
|
||||
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
|
||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
|
||||
}
|
||||
|
||||
if (event == SCH_FILE_INPUT) {
|
||||
if (IS_QUEUE_EMPTY()) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Unexpected signd response");
|
||||
DEBUG_LOG("Unexpected signd response");
|
||||
close_socket();
|
||||
return;
|
||||
}
|
||||
@@ -251,15 +251,15 @@ read_write_socket(int sock_fd, int event, void *anything)
|
||||
|
||||
if (s <= 0) {
|
||||
if (s < 0)
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd socket error: %s", strerror(errno));
|
||||
DEBUG_LOG("signd socket error: %s", strerror(errno));
|
||||
else
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd socket closed");
|
||||
DEBUG_LOG("signd socket closed");
|
||||
|
||||
close_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Received %d bytes from signd", s);
|
||||
DEBUG_LOG("Received %d bytes from signd", s);
|
||||
inst->received += s;
|
||||
|
||||
if (inst->received < sizeof (inst->response.length))
|
||||
@@ -269,7 +269,7 @@ read_write_socket(int sock_fd, int event, void *anything)
|
||||
|
||||
if (response_length < offsetof(SigndResponse, signed_packet) ||
|
||||
response_length > sizeof (SigndResponse)) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Invalid response length");
|
||||
DEBUG_LOG("Invalid response length");
|
||||
close_socket();
|
||||
return;
|
||||
}
|
||||
@@ -283,7 +283,7 @@ read_write_socket(int sock_fd, int event, void *anything)
|
||||
/* Move the head and enable output for the next packet */
|
||||
queue_head = NEXT_QUEUE_INDEX(queue_head);
|
||||
if (!IS_QUEUE_EMPTY())
|
||||
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
|
||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ NSD_Initialise()
|
||||
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
|
||||
queue_head = queue_tail = 0;
|
||||
|
||||
LOG(LOGS_INFO, LOGF_NtpSignd, "MS-SNTP authentication enabled");
|
||||
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -333,17 +333,17 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
|
||||
SignInstance *inst;
|
||||
|
||||
if (!enabled) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd disabled");
|
||||
DEBUG_LOG("signd disabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "signd queue full");
|
||||
DEBUG_LOG("signd queue full");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (length != NTP_NORMAL_PACKET_LENGTH) {
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Invalid packet length");
|
||||
DEBUG_LOG("Invalid packet length");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -369,12 +369,11 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
|
||||
|
||||
/* Enable output if there was no pending request */
|
||||
if (IS_QUEUE_EMPTY())
|
||||
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
|
||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
|
||||
|
||||
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSignd, "Packet added to signd queue (%u:%u)",
|
||||
queue_head, queue_tail);
|
||||
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "local.h"
|
||||
#include "memory.h"
|
||||
#include "nameserv_async.h"
|
||||
#include "privops.h"
|
||||
#include "sched.h"
|
||||
|
||||
/* ================================================== */
|
||||
@@ -205,12 +206,13 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
|
||||
unsigned short port;
|
||||
|
||||
size = ARR_GetSize(records);
|
||||
|
||||
*slot = 0;
|
||||
*found = 0;
|
||||
|
||||
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
|
||||
remote_addr->ip_addr.family != IPADDR_INET6) {
|
||||
*found = *slot = 0;
|
||||
remote_addr->ip_addr.family != IPADDR_INET6)
|
||||
return;
|
||||
}
|
||||
|
||||
hash = UTI_IPToHash(&remote_addr->ip_addr);
|
||||
port = remote_addr->port;
|
||||
@@ -229,8 +231,6 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*found = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -354,7 +354,7 @@ replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
|
||||
/* The hash table must be rebuilt for the new address */
|
||||
rehash_records();
|
||||
|
||||
LOG(LOGS_INFO, LOGF_NtpSources, "Source %s replaced with %s",
|
||||
LOG(LOGS_INFO, "Source %s replaced with %s",
|
||||
UTI_IPToString(&old_addr->ip_addr),
|
||||
UTI_IPToString(&new_addr->ip_addr));
|
||||
|
||||
@@ -377,7 +377,7 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
|
||||
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
|
||||
address.port = us->port;
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
|
||||
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
|
||||
|
||||
if (us->replacement) {
|
||||
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
|
||||
@@ -404,7 +404,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
|
||||
|
||||
assert(us == resolving_source);
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs);
|
||||
DEBUG_LOG("%s resolved to %d addrs", us->name, n_addrs);
|
||||
|
||||
switch (status) {
|
||||
case DNS_TryAgain:
|
||||
@@ -413,7 +413,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
|
||||
process_resolved_name(us, ip_addrs, n_addrs);
|
||||
break;
|
||||
case DNS_Failure:
|
||||
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
|
||||
LOG(LOGS_WARN, "Invalid host %s", us->name);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
@@ -438,7 +438,7 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
|
||||
|
||||
if (next) {
|
||||
/* Continue with the next source in the list */
|
||||
DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name);
|
||||
DEBUG_LOG("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
|
||||
@@ -469,14 +469,14 @@ resolve_sources(void *arg)
|
||||
|
||||
assert(!resolving_source);
|
||||
|
||||
DNS_Reload();
|
||||
PRV_ReloadDNS();
|
||||
|
||||
/* Start with the first source in the list, name_resolve_handler
|
||||
will iterate over the rest */
|
||||
us = unresolved_sources;
|
||||
|
||||
resolving_source = us;
|
||||
DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name);
|
||||
DEBUG_LOG("resolving %s", us->name);
|
||||
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
|
||||
}
|
||||
|
||||
@@ -657,8 +657,7 @@ resolve_source_replacement(SourceRecord *record)
|
||||
{
|
||||
struct UnresolvedSource *us;
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s",
|
||||
UTI_IPToString(&record->remote_addr->ip_addr));
|
||||
DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr));
|
||||
|
||||
us = MallocNew(struct UnresolvedSource);
|
||||
us->name = Strdup(record->name);
|
||||
@@ -704,7 +703,7 @@ NSR_HandleBadSource(IPAddr *address)
|
||||
SCH_GetLastEventTime(NULL, NULL, &now);
|
||||
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
|
||||
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
|
||||
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
|
||||
DEBUG_LOG("replacement postponed");
|
||||
return;
|
||||
}
|
||||
last_replacement = now;
|
||||
@@ -742,7 +741,7 @@ static void remove_tentative_pool_sources(int pool)
|
||||
if (!record->remote_addr || record->pool != pool || !record->tentative)
|
||||
continue;
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s",
|
||||
DEBUG_LOG("removing tentative source %s",
|
||||
UTI_IPToString(&record->remote_addr->ip_addr));
|
||||
|
||||
clean_source_record(record);
|
||||
@@ -800,8 +799,7 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
pool = ARR_GetElement(pools, record->pool);
|
||||
pool->sources++;
|
||||
|
||||
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
|
||||
record->name, pool->sources);
|
||||
DEBUG_LOG("pool %s has %d confirmed sources", record->name, pool->sources);
|
||||
|
||||
/* If the number of sources from the pool reached the configured
|
||||
maximum, remove the remaining tentative sources */
|
||||
@@ -862,48 +860,14 @@ slew_sources(struct timespec *raw,
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
|
||||
{
|
||||
SourceRecord *record;
|
||||
unsigned int i;
|
||||
int any;
|
||||
|
||||
NSR_ResolveSources();
|
||||
|
||||
any = 0;
|
||||
for (i = 0; i < ARR_GetSize(records); i++) {
|
||||
record = get_record(i);
|
||||
if (record->remote_addr) {
|
||||
if (address->family == IPADDR_UNSPEC ||
|
||||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
|
||||
any = 1;
|
||||
NCR_TakeSourceOnline(record->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (address->family == IPADDR_UNSPEC) {
|
||||
struct UnresolvedSource *us;
|
||||
|
||||
for (us = unresolved_sources; us; us = us->next) {
|
||||
if (us->replacement)
|
||||
continue;
|
||||
any = 1;
|
||||
us->new_source.params.online = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
|
||||
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
|
||||
{
|
||||
SourceRecord *record, *syncpeer;
|
||||
unsigned int i, any;
|
||||
|
||||
if (connectivity != SRC_OFFLINE)
|
||||
NSR_ResolveSources();
|
||||
|
||||
any = 0;
|
||||
syncpeer = NULL;
|
||||
for (i = 0; i < ARR_GetSize(records); i++) {
|
||||
@@ -916,15 +880,14 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
|
||||
syncpeer = record;
|
||||
continue;
|
||||
}
|
||||
NCR_TakeSourceOffline(record->data);
|
||||
NCR_SetConnectivity(record->data, connectivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Take sync peer offline as last to avoid reference switching */
|
||||
if (syncpeer) {
|
||||
NCR_TakeSourceOffline(syncpeer->data);
|
||||
}
|
||||
/* Set the sync peer last to avoid unnecessary reference switching */
|
||||
if (syncpeer)
|
||||
NCR_SetConnectivity(syncpeer->data, connectivity);
|
||||
|
||||
if (address->family == IPADDR_UNSPEC) {
|
||||
struct UnresolvedSource *us;
|
||||
@@ -933,7 +896,7 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
|
||||
if (us->replacement)
|
||||
continue;
|
||||
any = 1;
|
||||
us->new_source.params.online = 0;
|
||||
us->new_source.params.connectivity = connectivity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,14 +102,9 @@ extern void NSR_Initialise(void);
|
||||
extern void NSR_Finalise(void);
|
||||
|
||||
/* This routine is used to indicate that sources whose IP addresses
|
||||
match a particular subnet should be set online again. Returns a
|
||||
flag indicating whether any hosts matched the address */
|
||||
extern int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address);
|
||||
|
||||
/* This routine is used to indicate that sources whose IP addresses
|
||||
match a particular subnet should be set offline. Returns a flag
|
||||
indicating whether any hosts matched the address */
|
||||
extern int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address);
|
||||
match a particular subnet should be set online or offline. It returns
|
||||
a flag indicating whether any hosts matched the address. */
|
||||
extern int NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity);
|
||||
|
||||
extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll);
|
||||
|
||||
|
||||
29
pktlength.c
29
pktlength.c
@@ -114,8 +114,12 @@ static const struct request_length request_lengths[] = {
|
||||
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
|
||||
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
|
||||
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
|
||||
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER2 */
|
||||
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER2 */
|
||||
{ 0, 0 }, /* ADD_SERVER2 */
|
||||
{ 0, 0 }, /* ADD_PEER2 */
|
||||
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
|
||||
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
|
||||
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
|
||||
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
|
||||
};
|
||||
|
||||
static const uint16_t reply_lengths[] = {
|
||||
@@ -123,19 +127,21 @@ static const uint16_t reply_lengths[] = {
|
||||
RPY_LENGTH_ENTRY(null), /* NULL */
|
||||
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
|
||||
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */
|
||||
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP */
|
||||
0, /* MANUAL_TIMESTAMP */
|
||||
RPY_LENGTH_ENTRY(tracking), /* TRACKING */
|
||||
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
|
||||
RPY_LENGTH_ENTRY(rtc), /* RTC */
|
||||
0, /* SUBNETS_ACCESSED - not supported */
|
||||
0, /* CLIENT_ACCESSES - not supported */
|
||||
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
|
||||
0, /* MANUAL_LIST - variable length */
|
||||
0, /* MANUAL_LIST - not supported */
|
||||
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
|
||||
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
|
||||
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
|
||||
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
|
||||
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
|
||||
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
|
||||
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
@@ -192,21 +198,6 @@ PKL_ReplyLength(CMD_Reply *r)
|
||||
if (type < 1 || type >= N_REPLY_TYPES)
|
||||
return 0;
|
||||
|
||||
/* Length of MANUAL_LIST depends on number of samples stored in it */
|
||||
if (type == RPY_MANUAL_LIST) {
|
||||
uint32_t ns;
|
||||
|
||||
if (r->status != htons(STT_SUCCESS))
|
||||
return offsetof(CMD_Reply, data);
|
||||
|
||||
ns = ntohl(r->data.manual_list.n_samples);
|
||||
if (ns > MAX_MANUAL_LIST_SAMPLES)
|
||||
return 0;
|
||||
|
||||
return offsetof(CMD_Reply, data.manual_list.samples) +
|
||||
ns * sizeof (RPY_ManualListSample);
|
||||
}
|
||||
|
||||
return reply_lengths[type];
|
||||
}
|
||||
|
||||
|
||||
74
privops.c
74
privops.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Bryan Christianson 2015
|
||||
* Copyright (C) Miroslav Lichvar 2016
|
||||
* Copyright (C) Miroslav Lichvar 2017
|
||||
*
|
||||
* This program 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 +40,7 @@
|
||||
#define OP_SETTIME 1026
|
||||
#define OP_BINDSOCKET 1027
|
||||
#define OP_NAME2IPADDRESS 1028
|
||||
#define OP_RELOADDNS 1029
|
||||
#define OP_QUIT 1099
|
||||
|
||||
union sockaddr_in46 {
|
||||
@@ -267,7 +268,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
|
||||
sock_fd = req->sock;
|
||||
|
||||
UTI_SockaddrToIPAndPort(sa, &ip, &port);
|
||||
if (port && port != CNF_GetNTPPort()) {
|
||||
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
|
||||
close(sock_fd);
|
||||
res_fatal(res, "Invalid port %d", port);
|
||||
return;
|
||||
@@ -293,8 +294,6 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
|
||||
/* make sure the string is terminated */
|
||||
req->name[sizeof (req->name) - 1] = '\0';
|
||||
|
||||
DNS_Reload();
|
||||
|
||||
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
|
||||
DNS_MAX_ADDRESSES);
|
||||
}
|
||||
@@ -302,6 +301,19 @@ do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
/* HELPER - perform DNS_Reload() */
|
||||
|
||||
#ifdef PRIVOPS_RELOADDNS
|
||||
static void
|
||||
do_reload_dns(PrvResponse *res)
|
||||
{
|
||||
DNS_Reload();
|
||||
res->rc = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
/* HELPER - main loop - action requests from the daemon */
|
||||
|
||||
static void
|
||||
@@ -343,6 +355,11 @@ helper_main(int fd)
|
||||
case OP_NAME2IPADDRESS:
|
||||
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
|
||||
break;
|
||||
#endif
|
||||
#ifdef PRIVOPS_RELOADDNS
|
||||
case OP_RELOADDNS:
|
||||
do_reload_dns(&res);
|
||||
break;
|
||||
#endif
|
||||
case OP_QUIT:
|
||||
quit = 1;
|
||||
@@ -371,14 +388,14 @@ receive_response(PrvResponse *res)
|
||||
|
||||
resp_len = recv(helper_fd, res, sizeof (*res), 0);
|
||||
if (resp_len < 0)
|
||||
LOG_FATAL(LOGF_PrivOps, "Could not read from helper : %s", strerror(errno));
|
||||
LOG_FATAL("Could not read from helper : %s", strerror(errno));
|
||||
if (resp_len != sizeof (*res))
|
||||
LOG_FATAL(LOGF_PrivOps, "Invalid helper response");
|
||||
LOG_FATAL("Invalid helper response");
|
||||
|
||||
if (res->fatal_error)
|
||||
LOG_FATAL(LOGF_PrivOps, "Error in helper : %s", res->data.fatal_msg.msg);
|
||||
LOG_FATAL("Error in helper : %s", res->data.fatal_msg.msg);
|
||||
|
||||
DEBUG_LOG(LOGF_PrivOps, "Received response rc=%d", res->rc);
|
||||
DEBUG_LOG("Received response rc=%d", res->rc);
|
||||
|
||||
/* if operation failed in the helper, set errno so daemon can print log message */
|
||||
if (res->res_errno)
|
||||
@@ -429,10 +446,10 @@ send_request(PrvRequest *req)
|
||||
if (sendmsg(helper_fd, &msg, 0) < 0) {
|
||||
/* don't try to send another request from exit() */
|
||||
helper_fd = -1;
|
||||
LOG_FATAL(LOGF_PrivOps, "Could not send to helper : %s", strerror(errno));
|
||||
LOG_FATAL("Could not send to helper : %s", strerror(errno));
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_PrivOps, "Sent request op=%d", req->op);
|
||||
DEBUG_LOG("Sent request op=%d", req->op);
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
@@ -562,7 +579,8 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
|
||||
unsigned short port;
|
||||
|
||||
UTI_SockaddrToIPAndPort(address, &ip, &port);
|
||||
assert(!port || port == CNF_GetNTPPort());
|
||||
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
|
||||
assert(0);
|
||||
|
||||
if (!have_helper())
|
||||
return bind(sock, address, address_len);
|
||||
@@ -598,7 +616,7 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
|
||||
req.op = OP_NAME2IPADDRESS;
|
||||
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
|
||||
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
|
||||
DEBUG_LOG(LOGF_PrivOps, "Name too long");
|
||||
DEBUG_LOG("Name too long");
|
||||
return DNS_Failure;
|
||||
}
|
||||
|
||||
@@ -613,6 +631,30 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
/* DAEMON - request res_init() */
|
||||
|
||||
#ifdef PRIVOPS_RELOADDNS
|
||||
void
|
||||
PRV_ReloadDNS(void)
|
||||
{
|
||||
PrvRequest req;
|
||||
PrvResponse res;
|
||||
|
||||
if (!have_helper()) {
|
||||
DNS_Reload();
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&req, 0, sizeof (req));
|
||||
req.op = OP_RELOADDNS;
|
||||
|
||||
submit_request(&req, &res);
|
||||
assert(!res.rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
void
|
||||
PRV_Initialise(void)
|
||||
{
|
||||
@@ -631,21 +673,21 @@ PRV_StartHelper(void)
|
||||
int fd, sock_pair[2];
|
||||
|
||||
if (have_helper())
|
||||
LOG_FATAL(LOGF_PrivOps, "Helper already running");
|
||||
LOG_FATAL("Helper already running");
|
||||
|
||||
if (
|
||||
#ifdef SOCK_SEQPACKET
|
||||
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
|
||||
#endif
|
||||
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
|
||||
LOG_FATAL(LOGF_PrivOps, "socketpair() failed : %s", strerror(errno));
|
||||
LOG_FATAL("socketpair() failed : %s", strerror(errno));
|
||||
|
||||
UTI_FdSetCloexec(sock_pair[0]);
|
||||
UTI_FdSetCloexec(sock_pair[1]);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
LOG_FATAL(LOGF_PrivOps, "fork() failed : %s", strerror(errno));
|
||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||
|
||||
if (pid == 0) {
|
||||
/* child process */
|
||||
@@ -658,7 +700,7 @@ PRV_StartHelper(void)
|
||||
}
|
||||
|
||||
/* ignore signals, the process will exit on OP_QUIT request */
|
||||
UTI_SetQuitSignalsHandler(SIG_IGN);
|
||||
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
|
||||
|
||||
helper_main(sock_pair[1]);
|
||||
|
||||
|
||||
@@ -58,6 +58,12 @@ int PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
|
||||
#define PRV_Name2IPAddress DNS_Name2IPAddress
|
||||
#endif
|
||||
|
||||
#ifdef PRIVOPS_RELOADDNS
|
||||
void PRV_ReloadDNS(void);
|
||||
#else
|
||||
#define PRV_ReloadDNS DNS_Reload
|
||||
#endif
|
||||
|
||||
#ifdef PRIVOPS_HELPER
|
||||
void PRV_Initialise(void);
|
||||
void PRV_StartHelper(void);
|
||||
|
||||
684
refclock.c
684
refclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2018
|
||||
*
|
||||
* This program 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,6 +37,7 @@
|
||||
#include "sources.h"
|
||||
#include "logging.h"
|
||||
#include "regress.h"
|
||||
#include "samplefilt.h"
|
||||
#include "sched.h"
|
||||
|
||||
/* list of refclock drivers */
|
||||
@@ -75,15 +76,19 @@ struct RCL_Instance_Record {
|
||||
int driver_polled;
|
||||
int poll;
|
||||
int leap_status;
|
||||
int pps_forced;
|
||||
int pps_rate;
|
||||
int pps_active;
|
||||
int max_lock_age;
|
||||
struct MedianFilter filter;
|
||||
int stratum;
|
||||
int tai;
|
||||
uint32_t ref_id;
|
||||
uint32_t lock_ref;
|
||||
double offset;
|
||||
double delay;
|
||||
double precision;
|
||||
double pulse_width;
|
||||
SPF_Instance filter;
|
||||
SCH_TimeoutID timeout_id;
|
||||
SRC_Instance source;
|
||||
};
|
||||
@@ -93,7 +98,7 @@ static ARR_Instance refclocks;
|
||||
|
||||
static LOG_FileID logfileid;
|
||||
|
||||
static int valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked);
|
||||
static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time);
|
||||
static int pps_stratum(RCL_Instance instance, struct timespec *ts);
|
||||
static void poll_timeout(void *arg);
|
||||
static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
@@ -101,18 +106,6 @@ static void slew_samples(struct timespec *raw, struct timespec *cooked, double d
|
||||
static void add_dispersion(double dispersion, void *anything);
|
||||
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
|
||||
|
||||
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
|
||||
static void filter_fini(struct MedianFilter *filter);
|
||||
static void filter_reset(struct MedianFilter *filter);
|
||||
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
|
||||
static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
|
||||
static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
|
||||
static int filter_get_samples(struct MedianFilter *filter);
|
||||
static int filter_select_samples(struct MedianFilter *filter);
|
||||
static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
|
||||
static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
|
||||
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
|
||||
|
||||
static RCL_Instance
|
||||
get_refclock(unsigned int index)
|
||||
{
|
||||
@@ -147,7 +140,7 @@ RCL_Finalise(void)
|
||||
if (inst->driver->fini)
|
||||
inst->driver->fini(inst);
|
||||
|
||||
filter_fini(&inst->filter);
|
||||
SPF_DestroyInstance(inst->filter);
|
||||
Free(inst->driver_parameter);
|
||||
SRC_DestroyInstance(inst->source);
|
||||
Free(inst);
|
||||
@@ -164,7 +157,6 @@ RCL_Finalise(void)
|
||||
int
|
||||
RCL_AddRefclock(RefclockParameters *params)
|
||||
{
|
||||
int pps_source = 0;
|
||||
RCL_Instance inst;
|
||||
|
||||
inst = MallocNew(struct RCL_Instance_Record);
|
||||
@@ -172,27 +164,21 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
|
||||
if (strcmp(params->driver_name, "SHM") == 0) {
|
||||
inst->driver = &RCL_SHM_driver;
|
||||
inst->precision = 1e-6;
|
||||
} else if (strcmp(params->driver_name, "SOCK") == 0) {
|
||||
inst->driver = &RCL_SOCK_driver;
|
||||
inst->precision = 1e-9;
|
||||
pps_source = 1;
|
||||
} else if (strcmp(params->driver_name, "PPS") == 0) {
|
||||
inst->driver = &RCL_PPS_driver;
|
||||
inst->precision = 1e-9;
|
||||
pps_source = 1;
|
||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||
inst->driver = &RCL_PHC_driver;
|
||||
inst->precision = 1e-9;
|
||||
} else {
|
||||
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
|
||||
return 0;
|
||||
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
||||
}
|
||||
|
||||
if (!inst->driver->init && !inst->driver->poll) {
|
||||
LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name);
|
||||
return 0;
|
||||
}
|
||||
if (!inst->driver->init && !inst->driver->poll)
|
||||
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
|
||||
|
||||
if (params->tai && !CNF_GetLeapSecTimezone())
|
||||
LOG_FATAL("refclock tai option requires leapsectz");
|
||||
|
||||
inst->data = NULL;
|
||||
inst->driver_parameter = params->driver_parameter;
|
||||
@@ -201,14 +187,18 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->poll = params->poll;
|
||||
inst->driver_polled = 0;
|
||||
inst->leap_status = LEAP_Normal;
|
||||
inst->pps_forced = params->pps_forced;
|
||||
inst->pps_rate = params->pps_rate;
|
||||
inst->pps_active = 0;
|
||||
inst->max_lock_age = params->max_lock_age;
|
||||
inst->stratum = params->stratum;
|
||||
inst->tai = params->tai;
|
||||
inst->lock_ref = params->lock_ref_id;
|
||||
inst->offset = params->offset;
|
||||
inst->delay = params->delay;
|
||||
if (params->precision > 0.0)
|
||||
inst->precision = params->precision;
|
||||
inst->precision = LCL_GetSysPrecisionAsQuantum();
|
||||
inst->precision = MAX(inst->precision, params->precision);
|
||||
inst->pulse_width = params->pulse_width;
|
||||
inst->timeout_id = -1;
|
||||
inst->source = NULL;
|
||||
|
||||
@@ -221,12 +211,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->driver_parameter[i] = '\0';
|
||||
}
|
||||
|
||||
if (pps_source) {
|
||||
if (inst->pps_rate < 1)
|
||||
inst->pps_rate = 1;
|
||||
} else {
|
||||
inst->pps_rate = 0;
|
||||
}
|
||||
if (inst->pps_rate < 1)
|
||||
inst->pps_rate = 1;
|
||||
|
||||
if (params->ref_id)
|
||||
inst->ref_id = params->ref_id;
|
||||
@@ -251,25 +237,26 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
max_samples = 1 << (inst->poll - inst->driver_poll);
|
||||
if (max_samples < params->filter_length) {
|
||||
if (max_samples < 4) {
|
||||
LOG(LOGS_WARN, LOGF_Refclock, "Setting filter length for %s to %d",
|
||||
LOG(LOGS_WARN, "Setting filter length for %s to %d",
|
||||
UTI_RefidToString(inst->ref_id), max_samples);
|
||||
}
|
||||
params->filter_length = max_samples;
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->driver->init)
|
||||
if (!inst->driver->init(inst)) {
|
||||
LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name);
|
||||
return 0;
|
||||
}
|
||||
if (inst->driver->init && !inst->driver->init(inst))
|
||||
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
|
||||
|
||||
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
|
||||
/* Require the filter to have at least 4 samples to produce a filtered
|
||||
sample, or be full for shorter lengths, and combine 60% of samples
|
||||
closest to the median */
|
||||
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
|
||||
params->max_dispersion, 0.6);
|
||||
|
||||
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
|
||||
params->min_samples, params->max_samples);
|
||||
params->min_samples, params->max_samples, 0.0, 0.0);
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d",
|
||||
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
|
||||
params->driver_name, UTI_RefidToString(inst->ref_id),
|
||||
inst->poll, inst->driver_poll, params->filter_length);
|
||||
|
||||
@@ -338,44 +325,123 @@ RCL_GetDriverParameter(RCL_Instance instance)
|
||||
return instance->driver_parameter;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_next_driver_option(RCL_Instance instance, char *option)
|
||||
{
|
||||
if (option == NULL)
|
||||
option = instance->driver_parameter;
|
||||
|
||||
option += strlen(option) + 1;
|
||||
|
||||
if (option >= instance->driver_parameter + instance->driver_parameter_length)
|
||||
return NULL;
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
void
|
||||
RCL_CheckDriverOptions(RCL_Instance instance, const char **options)
|
||||
{
|
||||
char *option;
|
||||
int i, len;
|
||||
|
||||
for (option = get_next_driver_option(instance, NULL);
|
||||
option;
|
||||
option = get_next_driver_option(instance, option)) {
|
||||
for (i = 0; options && options[i]; i++) {
|
||||
len = strlen(options[i]);
|
||||
if (!strncmp(options[i], option, strlen(options[i])) &&
|
||||
(option[len] == '=' || option[len] == '\0'))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!options || !options[i])
|
||||
LOG_FATAL("Invalid refclock driver option %s", option);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
RCL_GetDriverOption(RCL_Instance instance, char *name)
|
||||
{
|
||||
char *s, *e;
|
||||
int n;
|
||||
char *option;
|
||||
int len;
|
||||
|
||||
s = instance->driver_parameter;
|
||||
e = s + instance->driver_parameter_length;
|
||||
n = strlen(name);
|
||||
len = strlen(name);
|
||||
|
||||
while (1) {
|
||||
s += strlen(s) + 1;
|
||||
if (s >= e)
|
||||
break;
|
||||
if (!strncmp(name, s, n)) {
|
||||
if (s[n] == '=')
|
||||
return s + n + 1;
|
||||
if (s[n] == '\0')
|
||||
return s + n;
|
||||
for (option = get_next_driver_option(instance, NULL);
|
||||
option;
|
||||
option = get_next_driver_option(instance, option)) {
|
||||
if (!strncmp(name, option, len)) {
|
||||
if (option[len] == '=')
|
||||
return option + len + 1;
|
||||
if (option[len] == '\0')
|
||||
return option + len;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
convert_tai_offset(struct timespec *sample_time, double *offset)
|
||||
{
|
||||
struct timespec tai_ts, utc_ts;
|
||||
int tai_offset;
|
||||
|
||||
/* Get approximate TAI-UTC offset for the reference time in TAI */
|
||||
UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
|
||||
tai_offset = REF_GetTaiOffset(&tai_ts);
|
||||
|
||||
/* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
|
||||
UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
|
||||
tai_offset = REF_GetTaiOffset(&utc_ts);
|
||||
|
||||
if (!tai_offset)
|
||||
return 0;
|
||||
|
||||
*offset -= tai_offset;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
|
||||
{
|
||||
NTP_Sample sample;
|
||||
|
||||
sample.time = *sample_time;
|
||||
sample.offset = offset;
|
||||
sample.peer_delay = instance->delay;
|
||||
sample.root_delay = instance->delay;
|
||||
sample.peer_dispersion = dispersion;
|
||||
sample.root_dispersion = dispersion;
|
||||
sample.leap = instance->leap_status;
|
||||
|
||||
/* Handle special case when PPS is used with the local reference */
|
||||
if (instance->pps_active && instance->lock_ref == -1)
|
||||
sample.stratum = pps_stratum(instance, &sample.time);
|
||||
else
|
||||
sample.stratum = instance->stratum;
|
||||
|
||||
return SPF_AccumulateSample(instance->filter, &sample);
|
||||
}
|
||||
|
||||
int
|
||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
|
||||
{
|
||||
double correction, dispersion;
|
||||
struct timespec cooked_time;
|
||||
|
||||
if (instance->pps_forced)
|
||||
return RCL_AddPulse(instance, sample_time, -offset);
|
||||
|
||||
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
||||
dispersion += instance->precision;
|
||||
|
||||
/* Make sure the timestamp and offset provided by the driver are sane */
|
||||
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
|
||||
!valid_sample_time(instance, sample_time, &cooked_time))
|
||||
!valid_sample_time(instance, &cooked_time))
|
||||
return 0;
|
||||
|
||||
switch (leap) {
|
||||
@@ -385,11 +451,19 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
||||
instance->leap_status = leap;
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap);
|
||||
DEBUG_LOG("refclock sample ignored bad leap %d", leap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
|
||||
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
||||
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!accumulate_sample(instance, &cooked_time,
|
||||
offset - correction + instance->offset, dispersion))
|
||||
return 0;
|
||||
|
||||
instance->pps_active = 0;
|
||||
|
||||
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
|
||||
@@ -404,24 +478,57 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
||||
int
|
||||
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
{
|
||||
double correction, dispersion, offset;
|
||||
double correction, dispersion;
|
||||
struct timespec cooked_time;
|
||||
|
||||
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
|
||||
second += correction;
|
||||
|
||||
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
|
||||
return 0;
|
||||
|
||||
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
|
||||
}
|
||||
|
||||
static int
|
||||
check_pulse_edge(RCL_Instance instance, double offset, double distance)
|
||||
{
|
||||
double max_error;
|
||||
|
||||
if (instance->pulse_width <= 0.0)
|
||||
return 1;
|
||||
|
||||
max_error = 1.0 / instance->pps_rate - instance->pulse_width;
|
||||
max_error = MIN(instance->pulse_width, max_error);
|
||||
max_error *= 0.5;
|
||||
|
||||
if (fabs(offset) > max_error || distance > max_error) {
|
||||
DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
|
||||
offset, distance, max_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
double second, double dispersion, double raw_correction)
|
||||
{
|
||||
double offset;
|
||||
int rate;
|
||||
NTP_Leap leap;
|
||||
|
||||
leap = LEAP_Normal;
|
||||
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
|
||||
dispersion += instance->precision;
|
||||
|
||||
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
|
||||
!valid_sample_time(instance, pulse_time, &cooked_time))
|
||||
if (!UTI_IsTimeOffsetSane(cooked_time, second) ||
|
||||
!valid_sample_time(instance, cooked_time))
|
||||
return 0;
|
||||
|
||||
leap = LEAP_Normal;
|
||||
dispersion += instance->precision;
|
||||
rate = instance->pps_rate;
|
||||
assert(rate > 0);
|
||||
|
||||
offset = -second - correction + instance->offset;
|
||||
offset = -second + instance->offset;
|
||||
|
||||
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
|
||||
offset -= (long)(offset * rate) / (double)rate;
|
||||
@@ -432,44 +539,47 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
|
||||
if (instance->lock_ref != -1) {
|
||||
RCL_Instance lock_refclock;
|
||||
struct timespec ref_sample_time;
|
||||
double sample_diff, ref_offset, ref_dispersion, shift;
|
||||
NTP_Sample ref_sample;
|
||||
double sample_diff, shift;
|
||||
|
||||
lock_refclock = get_refclock(instance->lock_ref);
|
||||
|
||||
if (!filter_get_last_sample(&lock_refclock->filter,
|
||||
&ref_sample_time, &ref_offset, &ref_dispersion)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
|
||||
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
||||
DEBUG_LOG("refclock pulse ignored no ref sample");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
|
||||
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
||||
|
||||
sample_diff = UTI_DiffTimespecsToDouble(&cooked_time, &ref_sample_time);
|
||||
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
||||
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
|
||||
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
|
||||
sample_diff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Align the offset to the reference sample */
|
||||
if ((ref_offset - offset) >= 0.0)
|
||||
shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
|
||||
if ((ref_sample.offset - offset) >= 0.0)
|
||||
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
|
||||
else
|
||||
shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
|
||||
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
|
||||
|
||||
offset += shift;
|
||||
|
||||
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
|
||||
ref_offset - offset, ref_dispersion, dispersion);
|
||||
if (fabs(ref_sample.offset - offset) +
|
||||
ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
|
||||
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
|
||||
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!check_pulse_edge(instance, ref_sample.offset - offset, 0.0))
|
||||
return 0;
|
||||
|
||||
leap = lock_refclock->leap_status;
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
|
||||
second, offset, ref_offset - offset, sample_diff);
|
||||
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
|
||||
offset, ref_sample.offset - offset, sample_diff);
|
||||
} else {
|
||||
struct timespec ref_time;
|
||||
int is_synchronised, stratum;
|
||||
@@ -479,24 +589,30 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
/* Ignore the pulse if we are not well synchronized and the local
|
||||
reference is not active */
|
||||
|
||||
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
|
||||
REF_GetReferenceParams(cooked_time, &is_synchronised, &leap, &stratum,
|
||||
&ref_id, &ref_time, &root_delay, &root_dispersion);
|
||||
distance = fabs(root_delay) / 2 + root_dispersion;
|
||||
|
||||
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
|
||||
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
|
||||
second, leap != LEAP_Unsynchronised, distance);
|
||||
DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
|
||||
offset, leap != LEAP_Unsynchronised, distance);
|
||||
/* Drop also all stored samples */
|
||||
filter_reset(&instance->filter);
|
||||
SPF_DropSamples(instance->filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!check_pulse_edge(instance, offset, distance))
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
|
||||
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
|
||||
return 0;
|
||||
|
||||
instance->leap_status = leap;
|
||||
instance->pps_active = 1;
|
||||
|
||||
log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
|
||||
log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset,
|
||||
offset, dispersion);
|
||||
|
||||
/* for logging purposes */
|
||||
if (!instance->driver->poll)
|
||||
@@ -505,23 +621,31 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
valid_sample_time(RCL_Instance instance, struct timespec *raw, struct timespec *cooked)
|
||||
double
|
||||
RCL_GetPrecision(RCL_Instance instance)
|
||||
{
|
||||
struct timespec now_raw, last_sample_time;
|
||||
double diff, last_offset, last_dispersion;
|
||||
return instance->precision;
|
||||
}
|
||||
|
||||
LCL_ReadRawTime(&now_raw);
|
||||
diff = UTI_DiffTimespecsToDouble(&now_raw, raw);
|
||||
int
|
||||
RCL_GetDriverPoll(RCL_Instance instance)
|
||||
{
|
||||
return instance->driver_poll;
|
||||
}
|
||||
|
||||
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
|
||||
(filter_get_samples(&instance->filter) > 0 &&
|
||||
filter_get_last_sample(&instance->filter, &last_sample_time,
|
||||
&last_offset, &last_dispersion) &&
|
||||
UTI_CompareTimespecs(&last_sample_time, cooked) >= 0)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
|
||||
UTI_RefidToString(instance->ref_id), diff,
|
||||
UTI_TimespecToString(raw), UTI_TimespecToString(cooked));
|
||||
static int
|
||||
valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
|
||||
{
|
||||
struct timespec now;
|
||||
double diff;
|
||||
|
||||
LCL_ReadCookedTime(&now, NULL);
|
||||
diff = UTI_DiffTimespecsToDouble(&now, sample_time);
|
||||
|
||||
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
|
||||
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
|
||||
UTI_RefidToString(instance->ref_id),
|
||||
UTI_TimespecToString(sample_time), diff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -562,6 +686,7 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
|
||||
static void
|
||||
poll_timeout(void *arg)
|
||||
{
|
||||
NTP_Sample sample;
|
||||
int poll;
|
||||
|
||||
RCL_Instance inst = (RCL_Instance)arg;
|
||||
@@ -575,26 +700,14 @@ poll_timeout(void *arg)
|
||||
}
|
||||
|
||||
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
|
||||
double offset, dispersion;
|
||||
struct timespec sample_time;
|
||||
int sample_ok, stratum;
|
||||
|
||||
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
|
||||
inst->driver_polled = 0;
|
||||
|
||||
if (sample_ok) {
|
||||
if (inst->pps_active && inst->lock_ref == -1)
|
||||
/* Handle special case when PPS is used with local stratum */
|
||||
stratum = pps_stratum(inst, &sample_time);
|
||||
else
|
||||
stratum = 0;
|
||||
|
||||
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
||||
SRC_UpdateReachability(inst->source, 1);
|
||||
SRC_AccumulateSample(inst->source, &sample_time, offset,
|
||||
inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
|
||||
SRC_AccumulateSample(inst->source, &sample);
|
||||
SRC_SelectSource(inst->source);
|
||||
|
||||
log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
|
||||
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
||||
} else {
|
||||
SRC_UpdateReachability(inst->source, 0);
|
||||
}
|
||||
@@ -611,9 +724,9 @@ slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
|
||||
for (i = 0; i < ARR_GetSize(refclocks); i++) {
|
||||
if (change_type == LCL_ChangeUnknownStep)
|
||||
filter_reset(&get_refclock(i)->filter);
|
||||
SPF_DropSamples(get_refclock(i)->filter);
|
||||
else
|
||||
filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
|
||||
SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +736,7 @@ add_dispersion(double dispersion, void *anything)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARR_GetSize(refclocks); i++)
|
||||
filter_add_dispersion(&get_refclock(i)->filter, dispersion);
|
||||
SPF_AddDispersion(get_refclock(i)->filter, dispersion);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -655,320 +768,3 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
|
||||
dispersion);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
|
||||
{
|
||||
if (length < 1)
|
||||
length = 1;
|
||||
|
||||
filter->length = length;
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
filter->last = -1;
|
||||
/* set first estimate to system precision */
|
||||
filter->avg_var_n = 0;
|
||||
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
|
||||
filter->max_var = max_dispersion * max_dispersion;
|
||||
filter->samples = MallocArray(struct FilterSample, filter->length);
|
||||
filter->selected = MallocArray(int, filter->length);
|
||||
filter->x_data = MallocArray(double, filter->length);
|
||||
filter->y_data = MallocArray(double, filter->length);
|
||||
filter->w_data = MallocArray(double, filter->length);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_fini(struct MedianFilter *filter)
|
||||
{
|
||||
Free(filter->samples);
|
||||
Free(filter->selected);
|
||||
Free(filter->x_data);
|
||||
Free(filter->y_data);
|
||||
Free(filter->w_data);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_reset(struct MedianFilter *filter)
|
||||
{
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
}
|
||||
|
||||
static double
|
||||
filter_get_avg_sample_dispersion(struct MedianFilter *filter)
|
||||
{
|
||||
return sqrt(filter->avg_var);
|
||||
}
|
||||
|
||||
static void
|
||||
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
|
||||
{
|
||||
filter->index++;
|
||||
filter->index %= filter->length;
|
||||
filter->last = filter->index;
|
||||
if (filter->used < filter->length)
|
||||
filter->used++;
|
||||
|
||||
filter->samples[filter->index].sample_time = *sample_time;
|
||||
filter->samples[filter->index].offset = offset;
|
||||
filter->samples[filter->index].dispersion = dispersion;
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
|
||||
filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
|
||||
}
|
||||
|
||||
static int
|
||||
filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
|
||||
{
|
||||
if (filter->last < 0)
|
||||
return 0;
|
||||
|
||||
*sample_time = filter->samples[filter->last].sample_time;
|
||||
*offset = filter->samples[filter->last].offset;
|
||||
*dispersion = filter->samples[filter->last].dispersion;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
filter_get_samples(struct MedianFilter *filter)
|
||||
{
|
||||
return filter->used;
|
||||
}
|
||||
|
||||
static const struct FilterSample *tmp_sorted_array;
|
||||
|
||||
static int
|
||||
sample_compare(const void *a, const void *b)
|
||||
{
|
||||
const struct FilterSample *s1, *s2;
|
||||
|
||||
s1 = &tmp_sorted_array[*(int *)a];
|
||||
s2 = &tmp_sorted_array[*(int *)b];
|
||||
|
||||
if (s1->offset < s2->offset)
|
||||
return -1;
|
||||
else if (s1->offset > s2->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
filter_select_samples(struct MedianFilter *filter)
|
||||
{
|
||||
int i, j, k, o, from, to, *selected;
|
||||
double min_dispersion;
|
||||
|
||||
if (filter->used < 1)
|
||||
return 0;
|
||||
|
||||
/* for lengths below 4 require full filter,
|
||||
for 4 and above require at least 4 samples */
|
||||
if ((filter->length < 4 && filter->used != filter->length) ||
|
||||
(filter->length >= 4 && filter->used < 4))
|
||||
return 0;
|
||||
|
||||
selected = filter->selected;
|
||||
|
||||
if (filter->used > 4) {
|
||||
/* select samples with dispersion better than 1.5 * minimum */
|
||||
|
||||
for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
|
||||
if (min_dispersion > filter->samples[i].dispersion)
|
||||
min_dispersion = filter->samples[i].dispersion;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < filter->used; i++) {
|
||||
if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
|
||||
selected[j++] = i;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
if (j < 4) {
|
||||
/* select all samples */
|
||||
|
||||
for (j = 0; j < filter->used; j++)
|
||||
selected[j] = j;
|
||||
}
|
||||
|
||||
/* and sort their indices by offset */
|
||||
tmp_sorted_array = filter->samples;
|
||||
qsort(selected, j, sizeof (int), sample_compare);
|
||||
|
||||
/* select 60 percent of the samples closest to the median */
|
||||
if (j > 2) {
|
||||
from = j / 5;
|
||||
if (from < 1)
|
||||
from = 1;
|
||||
to = j - from;
|
||||
} else {
|
||||
from = 0;
|
||||
to = j;
|
||||
}
|
||||
|
||||
/* mark unused samples and sort the rest from oldest to newest */
|
||||
|
||||
o = filter->used - filter->index - 1;
|
||||
|
||||
for (i = 0; i < from; i++)
|
||||
selected[i] = -1;
|
||||
for (; i < to; i++)
|
||||
selected[i] = (selected[i] + o) % filter->used;
|
||||
for (; i < filter->used; i++)
|
||||
selected[i] = -1;
|
||||
|
||||
for (i = from; i < to; i++) {
|
||||
j = selected[i];
|
||||
selected[i] = -1;
|
||||
while (j != -1 && selected[j] != j) {
|
||||
k = selected[j];
|
||||
selected[j] = j;
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = j = 0, k = -1; i < filter->used; i++) {
|
||||
if (selected[i] != -1)
|
||||
selected[j++] = (selected[i] + filter->used - o) % filter->used;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
static int
|
||||
filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
|
||||
{
|
||||
struct FilterSample *s, *ls;
|
||||
int i, n, dof;
|
||||
double x, y, d, e, var, prev_avg_var;
|
||||
|
||||
n = filter_select_samples(filter);
|
||||
|
||||
if (n < 1)
|
||||
return 0;
|
||||
|
||||
ls = &filter->samples[filter->selected[n - 1]];
|
||||
|
||||
/* prepare data */
|
||||
for (i = 0; i < n; i++) {
|
||||
s = &filter->samples[filter->selected[i]];
|
||||
|
||||
filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
|
||||
filter->y_data[i] = s->offset;
|
||||
filter->w_data[i] = s->dispersion;
|
||||
}
|
||||
|
||||
/* mean offset, sample time and sample dispersion */
|
||||
for (i = 0, x = y = e = 0.0; i < n; i++) {
|
||||
x += filter->x_data[i];
|
||||
y += filter->y_data[i];
|
||||
e += filter->w_data[i];
|
||||
}
|
||||
x /= n;
|
||||
y /= n;
|
||||
e /= n;
|
||||
|
||||
if (n >= 4) {
|
||||
double b0, b1, s2, sb0, sb1;
|
||||
|
||||
/* set y axis to the mean sample time */
|
||||
for (i = 0; i < n; i++)
|
||||
filter->x_data[i] -= x;
|
||||
|
||||
/* make a linear fit and use the estimated standard deviation of intercept
|
||||
as dispersion */
|
||||
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
|
||||
&b0, &b1, &s2, &sb0, &sb1);
|
||||
var = s2;
|
||||
d = sb0;
|
||||
dof = n - 2;
|
||||
} else if (n >= 2) {
|
||||
for (i = 0, d = 0.0; i < n; i++)
|
||||
d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
|
||||
var = d / (n - 1);
|
||||
d = sqrt(var);
|
||||
dof = n - 1;
|
||||
} else {
|
||||
var = filter->avg_var;
|
||||
d = sqrt(var);
|
||||
dof = 1;
|
||||
}
|
||||
|
||||
/* avoid having zero dispersion */
|
||||
if (var < 1e-20) {
|
||||
var = 1e-20;
|
||||
d = sqrt(var);
|
||||
}
|
||||
|
||||
/* drop the sample if variance is larger than allowed maximum */
|
||||
if (filter->max_var > 0.0 && var > filter->max_var) {
|
||||
DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
|
||||
sqrt(var), sqrt(filter->max_var));
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev_avg_var = filter->avg_var;
|
||||
|
||||
/* update exponential moving average of the variance */
|
||||
if (filter->avg_var_n > 50) {
|
||||
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
|
||||
} else {
|
||||
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
|
||||
(dof + filter->avg_var_n);
|
||||
if (filter->avg_var_n == 0)
|
||||
prev_avg_var = filter->avg_var;
|
||||
filter->avg_var_n += dof;
|
||||
}
|
||||
|
||||
/* reduce noise in sourcestats weights by using the long-term average
|
||||
instead of the estimated variance if it's not significantly lower */
|
||||
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
|
||||
d = sqrt(filter->avg_var) * d / sqrt(var);
|
||||
|
||||
if (d < e)
|
||||
d = e;
|
||||
|
||||
UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
|
||||
*offset = y;
|
||||
*dispersion = d;
|
||||
|
||||
filter_reset(filter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
|
||||
{
|
||||
int i, first, last;
|
||||
double delta_time;
|
||||
struct timespec *sample;
|
||||
|
||||
if (filter->last < 0)
|
||||
return;
|
||||
|
||||
/* always slew the last sample as it may be needed by PPS refclocks */
|
||||
if (filter->used > 0) {
|
||||
first = 0;
|
||||
last = filter->used - 1;
|
||||
} else {
|
||||
first = last = filter->last;
|
||||
}
|
||||
|
||||
for (i = first; i <= last; i++) {
|
||||
sample = &filter->samples[i].sample_time;
|
||||
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
|
||||
filter->samples[i].offset -= delta_time;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
filter_add_dispersion(struct MedianFilter *filter, double dispersion)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < filter->used; i++) {
|
||||
filter->samples[i].dispersion += dispersion;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,17 +37,21 @@ typedef struct {
|
||||
int driver_poll;
|
||||
int poll;
|
||||
int filter_length;
|
||||
int pps_forced;
|
||||
int pps_rate;
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int sel_options;
|
||||
int max_lock_age;
|
||||
int stratum;
|
||||
int tai;
|
||||
uint32_t ref_id;
|
||||
uint32_t lock_ref_id;
|
||||
double offset;
|
||||
double delay;
|
||||
double precision;
|
||||
double max_dispersion;
|
||||
double pulse_width;
|
||||
} RefclockParameters;
|
||||
|
||||
typedef struct RCL_Instance_Record *RCL_Instance;
|
||||
@@ -68,8 +72,13 @@ extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
|
||||
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
|
||||
extern void *RCL_GetDriverData(RCL_Instance instance);
|
||||
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
|
||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
||||
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
double second, double dispersion, double raw_correction);
|
||||
extern double RCL_GetPrecision(RCL_Instance instance);
|
||||
extern int RCL_GetDriverPoll(RCL_Instance instance);
|
||||
|
||||
#endif
|
||||
|
||||
207
refclock_phc.c
207
refclock_phc.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2013
|
||||
* Copyright (C) Miroslav Lichvar 2013, 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -33,144 +33,137 @@
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <linux/ptp_clock.h>
|
||||
|
||||
#include "refclock.h"
|
||||
#include "hwclock.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
#include "sched.h"
|
||||
#include "sys_linux.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;
|
||||
struct phc_instance {
|
||||
int fd;
|
||||
int mode;
|
||||
int nocrossts;
|
||||
int extpps;
|
||||
int pin;
|
||||
int channel;
|
||||
HCL_Instance clock;
|
||||
};
|
||||
|
||||
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 void read_ext_pulse(int sockfd, int event, void *anything);
|
||||
|
||||
static int phc_initialise(RCL_Instance instance)
|
||||
{
|
||||
struct ptp_clock_caps caps;
|
||||
int phc_fd;
|
||||
char *path;
|
||||
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
||||
struct phc_instance *phc;
|
||||
int phc_fd, rising_edge;
|
||||
char *path, *s;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
phc_fd = open(path, O_RDONLY);
|
||||
phc_fd = SYS_Linux_OpenPHC(path, 0);
|
||||
if (phc_fd < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
|
||||
LOG_FATAL("Could not open PHC");
|
||||
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;
|
||||
phc = MallocNew(struct phc_instance);
|
||||
phc->fd = phc_fd;
|
||||
phc->mode = 0;
|
||||
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
||||
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
||||
|
||||
if (phc->extpps) {
|
||||
s = RCL_GetDriverOption(instance, "pin");
|
||||
phc->pin = s ? atoi(s) : 0;
|
||||
s = RCL_GetDriverOption(instance, "channel");
|
||||
phc->channel = s ? atoi(s) : 0;
|
||||
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
||||
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
|
||||
|
||||
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
||||
rising_edge, !rising_edge, 1))
|
||||
LOG_FATAL("Could not enable external PHC timestamping");
|
||||
|
||||
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
||||
} else {
|
||||
phc->pin = phc->channel = 0;
|
||||
phc->clock = NULL;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
|
||||
RCL_SetDriverData(instance, (void *)(long)phc_fd);
|
||||
RCL_SetDriverData(instance, phc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void phc_finalise(RCL_Instance instance)
|
||||
{
|
||||
close((long)RCL_GetDriverData(instance));
|
||||
struct phc_instance *phc;
|
||||
|
||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||
|
||||
if (phc->extpps) {
|
||||
SCH_RemoveFileHandler(phc->fd);
|
||||
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
||||
HCL_DestroyInstance(phc->clock);
|
||||
}
|
||||
|
||||
close(phc->fd);
|
||||
Free(phc);
|
||||
}
|
||||
|
||||
static void read_ext_pulse(int fd, int event, void *anything)
|
||||
{
|
||||
RCL_Instance instance;
|
||||
struct phc_instance *phc;
|
||||
struct timespec phc_ts, local_ts;
|
||||
double local_err;
|
||||
int channel;
|
||||
|
||||
instance = anything;
|
||||
phc = RCL_GetDriverData(instance);
|
||||
|
||||
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
|
||||
return;
|
||||
|
||||
if (channel != phc->channel) {
|
||||
DEBUG_LOG("Unexpected extts channel %d\n", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
|
||||
return;
|
||||
|
||||
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
||||
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
|
||||
}
|
||||
|
||||
static int phc_poll(RCL_Instance instance)
|
||||
{
|
||||
struct phc_reading readings[NUM_READINGS];
|
||||
double offset = 0.0, delay, best_delay = 0.0;
|
||||
int i, phc_fd, best;
|
||||
|
||||
phc_fd = (long)RCL_GetDriverData(instance);
|
||||
struct phc_instance *phc;
|
||||
struct timespec phc_ts, sys_ts, local_ts;
|
||||
double offset, phc_err, local_err;
|
||||
|
||||
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;
|
||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||
|
||||
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
|
||||
&phc->mode, &phc_ts, &sys_ts, &phc_err))
|
||||
return 0;
|
||||
|
||||
if (phc->extpps) {
|
||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the fastest reading */
|
||||
for (i = 0; i < NUM_READINGS; i++) {
|
||||
delay = UTI_DiffTimespecsToDouble(&readings[i].sys_ts2, &readings[i].sys_ts1);
|
||||
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
|
||||
|
||||
if (!i || best_delay > delay) {
|
||||
best = i;
|
||||
best_delay = delay;
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
|
||||
|
||||
offset = UTI_DiffTimespecsToDouble(&readings[best].phc_ts, &readings[best].sys_ts2) +
|
||||
best_delay / 2.0;
|
||||
|
||||
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
|
||||
|
||||
return RCL_AddSample(instance, &readings[best].sys_ts2, offset, LEAP_Normal);
|
||||
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_PHC_driver = {
|
||||
|
||||
@@ -48,48 +48,51 @@ struct pps_instance {
|
||||
};
|
||||
|
||||
static int pps_initialise(RCL_Instance instance) {
|
||||
const char *options[] = {"clear", NULL};
|
||||
pps_handle_t handle;
|
||||
pps_params_t params;
|
||||
struct pps_instance *pps;
|
||||
int fd, edge_clear, mode;
|
||||
char *path;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
|
||||
LOG_FATAL("Could not open %s : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(fd);
|
||||
|
||||
if (time_pps_create(fd, &handle) < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path);
|
||||
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_pps_getcap(handle, &mode) < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path);
|
||||
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_pps_getparams(handle, ¶ms) < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path);
|
||||
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!edge_clear) {
|
||||
if (!(mode & PPS_CAPTUREASSERT)) {
|
||||
LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path);
|
||||
LOG_FATAL("CAPTUREASSERT not supported on %s", path);
|
||||
return 0;
|
||||
}
|
||||
params.mode |= PPS_CAPTUREASSERT;
|
||||
params.mode &= ~PPS_CAPTURECLEAR;
|
||||
} else {
|
||||
if (!(mode & PPS_CAPTURECLEAR)) {
|
||||
LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path);
|
||||
LOG_FATAL("CAPTURECLEAR not supported on %s", path);
|
||||
return 0;
|
||||
}
|
||||
params.mode |= PPS_CAPTURECLEAR;
|
||||
@@ -97,7 +100,7 @@ static int pps_initialise(RCL_Instance instance) {
|
||||
}
|
||||
|
||||
if (time_pps_setparams(handle, ¶ms) < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path);
|
||||
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -133,7 +136,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) {
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
|
||||
LOG(LOGS_ERR, "time_pps_fetch() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -146,8 +149,8 @@ static int pps_poll(RCL_Instance instance)
|
||||
}
|
||||
|
||||
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
|
||||
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%s",
|
||||
seq, UTI_TimespecToString(&ts));
|
||||
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
|
||||
(unsigned long)seq, UTI_TimespecToString(&ts));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,23 +59,26 @@ struct shmTime {
|
||||
};
|
||||
|
||||
static int shm_initialise(RCL_Instance instance) {
|
||||
const char *options[] = {"perm", NULL};
|
||||
int id, param, perm;
|
||||
char *s;
|
||||
struct shmTime *shm;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
param = atoi(RCL_GetDriverParameter(instance));
|
||||
s = RCL_GetDriverOption(instance, "perm");
|
||||
perm = s ? strtol(s, NULL, 8) & 0777 : 0600;
|
||||
|
||||
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
|
||||
if (id == -1) {
|
||||
LOG_FATAL(LOGF_Refclock, "shmget() failed");
|
||||
LOG_FATAL("shmget() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
shm = (struct shmTime *)shmat(id, 0, 0);
|
||||
if ((long)shm == -1) {
|
||||
LOG_FATAL(LOGF_Refclock, "shmat() failed");
|
||||
LOG_FATAL("shmat() failed : %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -100,7 +103,7 @@ static int shm_poll(RCL_Instance instance)
|
||||
|
||||
if ((t.mode == 1 && t.count != shm->count) ||
|
||||
!(t.mode == 0 || t.mode == 1) || !t.valid) {
|
||||
DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
|
||||
DEBUG_LOG("SHM sample ignored mode=%d count=%d valid=%d",
|
||||
t.mode, t.count, t.valid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -69,20 +69,19 @@ static void read_sample(int sockfd, int event, void *anything)
|
||||
s = recv(sockfd, &sample, sizeof (sample), 0);
|
||||
|
||||
if (s < 0) {
|
||||
LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
|
||||
strerror(errno));
|
||||
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (s != sizeof (sample)) {
|
||||
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
|
||||
s, (long)sizeof (sample));
|
||||
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
|
||||
s, (long)sizeof (sample));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sample.magic != SOCK_MAGIC) {
|
||||
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
|
||||
sample.magic, SOCK_MAGIC);
|
||||
DEBUG_LOG("Unexpected magic number in SOCK sample : %x != %x",
|
||||
(unsigned int)sample.magic, (unsigned int)SOCK_MAGIC);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,17 +101,19 @@ static int sock_initialise(RCL_Instance instance)
|
||||
int sockfd;
|
||||
char *path;
|
||||
|
||||
RCL_CheckDriverOptions(instance, NULL);
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
s.sun_family = AF_UNIX;
|
||||
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
|
||||
LOG_FATAL(LOGF_Refclock, "path %s is too long", path);
|
||||
LOG_FATAL("Path %s too long", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "socket() failed");
|
||||
LOG_FATAL("socket() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ static int sock_initialise(RCL_Instance instance)
|
||||
|
||||
unlink(path);
|
||||
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
|
||||
LOG_FATAL(LOGF_Refclock, "bind() failed");
|
||||
LOG_FATAL("bind(%s) failed : %s", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
462
reference.c
462
reference.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -49,6 +49,7 @@ static int local_orphan;
|
||||
static double local_distance;
|
||||
static NTP_Leap our_leap_status;
|
||||
static int our_leap_sec;
|
||||
static int our_tai_offset;
|
||||
static int our_stratum;
|
||||
static uint32_t our_ref_id;
|
||||
static IPAddr our_ref_ip;
|
||||
@@ -111,8 +112,6 @@ static SCH_TimeoutID leap_timeout_id;
|
||||
|
||||
/* Name of a system timezone containing leap seconds occuring at midnight */
|
||||
static char *leap_tzname;
|
||||
static time_t last_tz_leap_check;
|
||||
static NTP_Leap tz_leap;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -141,7 +140,7 @@ static double last_ref_update_interval;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static NTP_Leap get_tz_leap(time_t when);
|
||||
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
||||
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
|
||||
|
||||
/* ================================================== */
|
||||
@@ -157,7 +156,8 @@ handle_slew(struct timespec *raw,
|
||||
double delta;
|
||||
struct timespec now;
|
||||
|
||||
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
|
||||
if (!UTI_IsZeroTimespec(&our_ref_time))
|
||||
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
|
||||
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
UTI_ZeroTimespec(&last_ref_update);
|
||||
@@ -181,11 +181,13 @@ REF_Initialise(void)
|
||||
FILE *in;
|
||||
double file_freq_ppm, file_skew_ppm;
|
||||
double our_frequency_ppm;
|
||||
int tai_offset;
|
||||
|
||||
mode = REF_ModeNormal;
|
||||
are_we_synchronised = 0;
|
||||
our_leap_status = LEAP_Unsynchronised;
|
||||
our_leap_sec = 0;
|
||||
our_tai_offset = 0;
|
||||
initialised = 1;
|
||||
our_root_dispersion = 1.0;
|
||||
our_root_delay = 1.0;
|
||||
@@ -205,11 +207,11 @@ REF_Initialise(void)
|
||||
our_skew = 1.0e-6 * file_skew_ppm;
|
||||
if (our_skew < MIN_SKEW)
|
||||
our_skew = MIN_SKEW;
|
||||
LOG(LOGS_INFO, LOGF_Reference, "Frequency %.3f +/- %.3f ppm read from %s",
|
||||
LOG(LOGS_INFO, "Frequency %.3f +/- %.3f ppm read from %s",
|
||||
file_freq_ppm, file_skew_ppm, drift_file);
|
||||
LCL_SetAbsoluteFrequency(our_frequency_ppm);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Could not read valid frequency and skew from driftfile %s",
|
||||
LOG(LOGS_WARN, "Could not read valid frequency and skew from driftfile %s",
|
||||
drift_file);
|
||||
}
|
||||
fclose(in);
|
||||
@@ -219,12 +221,12 @@ REF_Initialise(void)
|
||||
if (our_frequency_ppm == 0.0) {
|
||||
our_frequency_ppm = LCL_ReadAbsoluteFrequency();
|
||||
if (our_frequency_ppm != 0.0) {
|
||||
LOG(LOGS_INFO, LOGF_Reference, "Initial frequency %.3f ppm", our_frequency_ppm);
|
||||
LOG(LOGS_INFO, "Initial frequency %.3f ppm", our_frequency_ppm);
|
||||
}
|
||||
}
|
||||
|
||||
logfileid = CNF_GetLogTracking() ? LOG_FileOpen("tracking",
|
||||
" Date (UTC) Time IP Address St Freq ppm Skew ppm Offset L Co Offset sd Rem. corr.")
|
||||
" Date (UTC) Time IP Address St Freq ppm Skew ppm Offset L Co Offset sd Rem. corr. Root delay Root disp. Max. error")
|
||||
: -1;
|
||||
|
||||
max_update_skew = fabs(CNF_GetMaxUpdateSkew()) * 1.0e-6;
|
||||
@@ -243,11 +245,11 @@ REF_Initialise(void)
|
||||
leap_tzname = CNF_GetLeapSecTimezone();
|
||||
if (leap_tzname) {
|
||||
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
|
||||
if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
|
||||
get_tz_leap(1356912000) == LEAP_Normal) {
|
||||
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
|
||||
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
|
||||
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
|
||||
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
||||
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
||||
leap_tzname = NULL;
|
||||
}
|
||||
}
|
||||
@@ -266,6 +268,7 @@ REF_Initialise(void)
|
||||
fb_drift_timeout_id = 0;
|
||||
}
|
||||
|
||||
UTI_ZeroTimespec(&our_ref_time);
|
||||
UTI_ZeroTimespec(&last_ref_update);
|
||||
last_ref_update_interval = 0.0;
|
||||
|
||||
@@ -322,23 +325,6 @@ REF_GetLeapMode(void)
|
||||
return leap_mode;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
Sqr(double x)
|
||||
{
|
||||
return x*x;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
#if 0
|
||||
static double
|
||||
Cube(double x)
|
||||
{
|
||||
return x*x*x;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
/* Update the drift coefficients to the file. */
|
||||
|
||||
@@ -364,7 +350,7 @@ update_drift_file(double freq_ppm, double skew)
|
||||
out = fopen(temp_drift_file, "w");
|
||||
if (!out) {
|
||||
Free(temp_drift_file);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Could not open temporary driftfile %s.tmp for writing",
|
||||
LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing",
|
||||
drift_file);
|
||||
return;
|
||||
}
|
||||
@@ -374,7 +360,7 @@ update_drift_file(double freq_ppm, double 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",
|
||||
LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp",
|
||||
drift_file);
|
||||
return;
|
||||
}
|
||||
@@ -384,8 +370,7 @@ update_drift_file(double freq_ppm, double skew)
|
||||
if (!stat(drift_file,&buf)) {
|
||||
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
|
||||
chmod(temp_drift_file,buf.st_mode & 0777)) {
|
||||
LOG(LOGS_WARN, LOGF_Reference,
|
||||
"Could not change ownership or permissions of temporary driftfile %s.tmp",
|
||||
LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
|
||||
drift_file);
|
||||
}
|
||||
}
|
||||
@@ -395,7 +380,7 @@ update_drift_file(double freq_ppm, double skew)
|
||||
if (rename(temp_drift_file,drift_file)) {
|
||||
unlink(temp_drift_file);
|
||||
Free(temp_drift_file);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Could not replace old driftfile %s with new one %s.tmp",
|
||||
LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp",
|
||||
drift_file,drift_file);
|
||||
return;
|
||||
}
|
||||
@@ -443,8 +428,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
|
||||
(freq_ppm - fb_drifts[i].freq);
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
|
||||
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
|
||||
DEBUG_LOG("Fallback drift %d updated: %f ppm %f seconds",
|
||||
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +442,7 @@ fb_drift_timeout(void *arg)
|
||||
|
||||
fb_drift_timeout_id = 0;
|
||||
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
|
||||
DEBUG_LOG("Fallback drift %d active: %f ppm",
|
||||
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
|
||||
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
|
||||
REF_SetUnsynchronised();
|
||||
@@ -492,14 +477,14 @@ schedule_fb_drift(struct timespec *now)
|
||||
if (c > next_fb_drift) {
|
||||
LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq);
|
||||
next_fb_drift = c;
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c);
|
||||
DEBUG_LOG("Fallback drift %d set", c);
|
||||
}
|
||||
|
||||
if (i <= fb_drift_max) {
|
||||
next_fb_drift = i;
|
||||
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
|
||||
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
|
||||
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
|
||||
DEBUG_LOG("Fallback drift %d scheduled", i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,29 +511,36 @@ maybe_log_offset(double offset, time_t now)
|
||||
double abs_offset;
|
||||
FILE *p;
|
||||
char buffer[BUFLEN], host[BUFLEN];
|
||||
struct tm stm;
|
||||
struct tm *tm;
|
||||
|
||||
abs_offset = fabs(offset);
|
||||
|
||||
if (abs_offset > log_change_threshold) {
|
||||
LOG(LOGS_WARN, LOGF_Reference,
|
||||
"System clock wrong by %.6f seconds, adjustment started",
|
||||
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
|
||||
-offset);
|
||||
}
|
||||
|
||||
if (do_mail_change &&
|
||||
(abs_offset > mail_change_threshold)) {
|
||||
snprintf(buffer, sizeof(buffer), "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
|
||||
snprintf(buffer, sizeof (buffer), "%s -t", MAIL_PROGRAM);
|
||||
p = popen(buffer, "w");
|
||||
if (p) {
|
||||
if (gethostname(host, sizeof(host)) < 0) {
|
||||
strcpy(host, "<UNKNOWN>");
|
||||
}
|
||||
host[sizeof (host) - 1] = '\0';
|
||||
|
||||
fprintf(p, "To: %s\n", mail_change_user);
|
||||
fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
|
||||
fputs("\n", p);
|
||||
stm = *localtime(&now);
|
||||
strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", &stm);
|
||||
fputs(buffer, p);
|
||||
|
||||
tm = localtime(&now);
|
||||
if (tm) {
|
||||
strftime(buffer, sizeof (buffer),
|
||||
"On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", tm);
|
||||
fputs(buffer, p);
|
||||
}
|
||||
|
||||
/* If offset < 0 the local clock is slow, so we are applying a
|
||||
positive change to it to bring it into line, hence the
|
||||
negation of 'offset' in the next statement (and earlier) */
|
||||
@@ -558,8 +550,7 @@ maybe_log_offset(double offset, time_t now)
|
||||
-offset, mail_change_threshold);
|
||||
pclose(p);
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Reference,
|
||||
"Could not send mail notification to user %s\n",
|
||||
LOG(LOGS_ERR, "Could not send mail notification to user %s\n",
|
||||
mail_change_user);
|
||||
}
|
||||
}
|
||||
@@ -594,7 +585,7 @@ is_offset_ok(double offset)
|
||||
|
||||
offset = fabs(offset);
|
||||
if (offset > max_offset) {
|
||||
LOG(LOGS_WARN, LOGF_Reference,
|
||||
LOG(LOGS_WARN,
|
||||
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
|
||||
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
|
||||
if (!max_offset_ignore)
|
||||
@@ -609,7 +600,14 @@ is_offset_ok(double offset)
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
is_leap_second_day(struct tm *stm) {
|
||||
is_leap_second_day(time_t when)
|
||||
{
|
||||
struct tm *stm;
|
||||
|
||||
stm = gmtime(&when);
|
||||
if (!stm)
|
||||
return 0;
|
||||
|
||||
/* 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);
|
||||
@@ -618,12 +616,18 @@ is_leap_second_day(struct tm *stm) {
|
||||
/* ================================================== */
|
||||
|
||||
static NTP_Leap
|
||||
get_tz_leap(time_t when)
|
||||
get_tz_leap(time_t when, int *tai_offset)
|
||||
{
|
||||
struct tm stm;
|
||||
static time_t last_tz_leap_check;
|
||||
static NTP_Leap tz_leap;
|
||||
static int tz_tai_offset;
|
||||
|
||||
struct tm stm, *tm;
|
||||
time_t t;
|
||||
char *tz_env, tz_orig[128];
|
||||
|
||||
*tai_offset = tz_tai_offset;
|
||||
|
||||
/* Do this check at most twice a day */
|
||||
when = when / (12 * 3600) * (12 * 3600);
|
||||
if (last_tz_leap_check == when)
|
||||
@@ -631,12 +635,14 @@ get_tz_leap(time_t when)
|
||||
|
||||
last_tz_leap_check = when;
|
||||
tz_leap = LEAP_Normal;
|
||||
tz_tai_offset = 0;
|
||||
|
||||
stm = *gmtime(&when);
|
||||
|
||||
if (!is_leap_second_day(&stm))
|
||||
tm = gmtime(&when);
|
||||
if (!tm)
|
||||
return tz_leap;
|
||||
|
||||
stm = *tm;
|
||||
|
||||
/* Temporarily switch to the timezone containing leap seconds */
|
||||
tz_env = getenv("TZ");
|
||||
if (tz_env) {
|
||||
@@ -647,6 +653,11 @@ get_tz_leap(time_t when)
|
||||
setenv("TZ", leap_tzname, 1);
|
||||
tzset();
|
||||
|
||||
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
|
||||
t = mktime(&stm);
|
||||
if (t != -1)
|
||||
tz_tai_offset = t - when + 10;
|
||||
|
||||
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
||||
stm.tm_sec = 60;
|
||||
stm.tm_min = 59;
|
||||
@@ -668,6 +679,8 @@ get_tz_leap(time_t when)
|
||||
else if (stm.tm_sec == 1)
|
||||
tz_leap = LEAP_DeleteSecond;
|
||||
|
||||
*tai_offset = tz_tai_offset;
|
||||
|
||||
return tz_leap;
|
||||
}
|
||||
|
||||
@@ -678,10 +691,13 @@ leap_end_timeout(void *arg)
|
||||
{
|
||||
leap_timeout_id = 0;
|
||||
leap_in_progress = 0;
|
||||
|
||||
if (our_tai_offset)
|
||||
our_tai_offset += our_leap_sec;
|
||||
our_leap_sec = 0;
|
||||
|
||||
if (leap_mode == REF_LeapModeSystem)
|
||||
LCL_SetSystemLeap(0);
|
||||
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
|
||||
|
||||
if (our_leap_status == LEAP_InsertSecond ||
|
||||
our_leap_status == LEAP_DeleteSecond)
|
||||
@@ -697,20 +713,20 @@ leap_start_timeout(void *arg)
|
||||
|
||||
switch (leap_mode) {
|
||||
case REF_LeapModeSystem:
|
||||
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction");
|
||||
DEBUG_LOG("Waiting for system clock leap second correction");
|
||||
break;
|
||||
case REF_LeapModeSlew:
|
||||
LCL_NotifyLeap(our_leap_sec);
|
||||
LCL_AccumulateOffset(our_leap_sec, 0.0);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
|
||||
LOG(LOGS_WARN, "Adjusting system clock for leap second");
|
||||
break;
|
||||
case REF_LeapModeStep:
|
||||
LCL_NotifyLeap(our_leap_sec);
|
||||
LCL_ApplyStepOffset(our_leap_sec);
|
||||
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
|
||||
LOG(LOGS_WARN, "System clock was stepped for leap second");
|
||||
break;
|
||||
case REF_LeapModeIgnore:
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
|
||||
LOG(LOGS_WARN, "Ignoring leap second");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -755,17 +771,22 @@ set_leap_timeout(time_t now)
|
||||
static void
|
||||
update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||
{
|
||||
int leap_sec;
|
||||
NTP_Leap tz_leap;
|
||||
int leap_sec, tai_offset;
|
||||
|
||||
leap_sec = 0;
|
||||
tai_offset = 0;
|
||||
|
||||
if (leap_tzname && now && leap == LEAP_Normal)
|
||||
leap = get_tz_leap(now);
|
||||
if (leap_tzname && now) {
|
||||
tz_leap = get_tz_leap(now, &tai_offset);
|
||||
if (leap == LEAP_Normal)
|
||||
leap = tz_leap;
|
||||
}
|
||||
|
||||
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
||||
/* Check that leap second is allowed today */
|
||||
|
||||
if (is_leap_second_day(gmtime(&now))) {
|
||||
if (is_leap_second_day(now)) {
|
||||
if (leap == LEAP_InsertSecond) {
|
||||
leap_sec = 1;
|
||||
} else {
|
||||
@@ -776,12 +797,14 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||
}
|
||||
}
|
||||
|
||||
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
|
||||
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
|
||||
&& !REF_IsLeapSecondClose()) {
|
||||
our_leap_sec = leap_sec;
|
||||
our_tai_offset = tai_offset;
|
||||
|
||||
switch (leap_mode) {
|
||||
case REF_LeapModeSystem:
|
||||
LCL_SetSystemLeap(our_leap_sec);
|
||||
LCL_SetSystemLeap(our_leap_sec, our_tai_offset);
|
||||
/* Fall through */
|
||||
case REF_LeapModeSlew:
|
||||
case REF_LeapModeStep:
|
||||
@@ -801,18 +824,43 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
get_root_dispersion(struct timespec *ts)
|
||||
{
|
||||
if (UTI_IsZeroTimespec(&our_ref_time))
|
||||
return 1.0;
|
||||
|
||||
return our_root_dispersion +
|
||||
fabs(UTI_DiffTimespecsToDouble(ts, &our_ref_time)) *
|
||||
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError());
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
write_log(struct timespec *ref_time, char *ref, int stratum, NTP_Leap leap,
|
||||
double freq, double skew, double offset, int combined_sources,
|
||||
double offset_sd, double uncorrected_offset)
|
||||
write_log(struct timespec *now, int combined_sources, double freq,
|
||||
double offset, double offset_sd, double uncorrected_offset,
|
||||
double orig_root_distance)
|
||||
{
|
||||
const char leap_codes[4] = {'N', '+', '-', '?'};
|
||||
if (logfileid != -1) {
|
||||
LOG_FileWrite(logfileid, "%s %-15s %2d %10.3f %10.3f %10.3e %1c %2d %10.3e %10.3e",
|
||||
UTI_TimeToLogForm(ref_time->tv_sec), ref, stratum, freq, skew,
|
||||
offset, leap_codes[leap], combined_sources, offset_sd,
|
||||
uncorrected_offset);
|
||||
}
|
||||
double root_dispersion, max_error;
|
||||
static double last_sys_offset = 0.0;
|
||||
|
||||
if (logfileid == -1)
|
||||
return;
|
||||
|
||||
max_error = orig_root_distance + fabs(last_sys_offset);
|
||||
root_dispersion = get_root_dispersion(now);
|
||||
last_sys_offset = offset - uncorrected_offset;
|
||||
|
||||
LOG_FileWrite(logfileid,
|
||||
"%s %-15s %2d %10.3f %10.3f %10.3e %1c %2d %10.3e %10.3e %10.3e %10.3e %10.3e",
|
||||
UTI_TimeToLogForm(now->tv_sec),
|
||||
our_ref_ip.family != IPADDR_UNSPEC ?
|
||||
UTI_IPToString(&our_ref_ip) : UTI_RefidToString(our_ref_id),
|
||||
our_stratum, freq, 1.0e6 * our_skew, offset,
|
||||
leap_codes[our_leap_status], combined_sources, offset_sd,
|
||||
uncorrected_offset, our_root_delay, root_dispersion, max_error);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -825,15 +873,14 @@ special_mode_sync(int valid, double offset)
|
||||
switch (mode) {
|
||||
case REF_ModeInitStepSlew:
|
||||
if (!valid) {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
|
||||
LOG(LOGS_WARN, "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)",
|
||||
LOG(LOGS_INFO, "System's initial offset : %.6f seconds %s of true (%s)",
|
||||
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
|
||||
|
||||
if (step)
|
||||
@@ -847,14 +894,14 @@ special_mode_sync(int valid, double offset)
|
||||
case REF_ModeUpdateOnce:
|
||||
case REF_ModePrintOnce:
|
||||
if (!valid) {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation");
|
||||
LOG(LOGS_WARN, "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)",
|
||||
LOG(LOGS_INFO, "System clock wrong by %.6f seconds (%s)",
|
||||
-offset, step ? "step" : "ignored");
|
||||
|
||||
if (step)
|
||||
@@ -873,35 +920,58 @@ special_mode_sync(int valid, double offset)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_SetReference(int stratum,
|
||||
NTP_Leap leap,
|
||||
int combined_sources,
|
||||
uint32_t ref_id,
|
||||
IPAddr *ref_ip,
|
||||
struct timespec *ref_time,
|
||||
double offset,
|
||||
double offset_sd,
|
||||
double frequency,
|
||||
double skew,
|
||||
double root_delay,
|
||||
double root_dispersion
|
||||
)
|
||||
static void
|
||||
get_clock_estimates(int manual,
|
||||
double measured_freq, double measured_skew,
|
||||
double *estimated_freq, double *estimated_skew,
|
||||
double *residual_freq)
|
||||
{
|
||||
double gain, expected_freq, expected_skew, extra_skew;
|
||||
|
||||
/* We assume that the local clock is running according to our previously
|
||||
determined value */
|
||||
expected_freq = 0.0;
|
||||
expected_skew = our_skew;
|
||||
|
||||
/* Set new frequency based on weighted average of the expected and measured
|
||||
skew. Disable updates that are based on totally unreliable frequency
|
||||
information unless it is a manual reference. */
|
||||
if (manual) {
|
||||
gain = 1.0;
|
||||
} else if (fabs(measured_skew) > max_update_skew) {
|
||||
DEBUG_LOG("Skew %f too large to track", measured_skew);
|
||||
gain = 0.0;
|
||||
} else {
|
||||
gain = 3.0 * SQUARE(expected_skew) /
|
||||
(3.0 * SQUARE(expected_skew) + SQUARE(measured_skew));
|
||||
}
|
||||
|
||||
gain = CLAMP(0.0, gain, 1.0);
|
||||
|
||||
*estimated_freq = expected_freq + gain * (measured_freq - expected_freq);
|
||||
*residual_freq = measured_freq - *estimated_freq;
|
||||
|
||||
extra_skew = sqrt(SQUARE(expected_freq - *estimated_freq) * (1.0 - gain) +
|
||||
SQUARE(measured_freq - *estimated_freq) * gain);
|
||||
|
||||
*estimated_skew = expected_skew + gain * (measured_skew - expected_skew) + extra_skew;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
||||
double offset, double offset_sd,
|
||||
double frequency, double frequency_sd, double skew,
|
||||
double root_delay, double root_dispersion)
|
||||
{
|
||||
double previous_skew, new_skew;
|
||||
double previous_freq, new_freq;
|
||||
double old_weight, new_weight, sum_weight;
|
||||
double delta_freq1, delta_freq2;
|
||||
double skew1, skew2;
|
||||
double our_offset;
|
||||
double our_frequency;
|
||||
double abs_freq_ppm;
|
||||
double update_interval;
|
||||
double elapsed;
|
||||
double correction_rate;
|
||||
double uncorrected_offset, accumulate_offset, step_offset;
|
||||
double residual_frequency, local_abs_frequency;
|
||||
double elapsed, update_interval, correction_rate, orig_root_distance;
|
||||
struct timespec now, raw_now;
|
||||
NTP_int64 ref_fuzz;
|
||||
int manual;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
@@ -911,39 +981,33 @@ REF_SetReference(int stratum,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Guard against dividing by zero */
|
||||
if (skew < MIN_SKEW)
|
||||
skew = MIN_SKEW;
|
||||
manual = leap == LEAP_Unsynchronised;
|
||||
|
||||
/* If we get a serious rounding error in the source stats regression
|
||||
processing, there is a remote chance that the skew argument is a
|
||||
'not a number'. If such a quantity gets propagated into the
|
||||
machine's kernel clock variables, nasty things will happen ..
|
||||
|
||||
To guard against this we need to check whether the skew argument
|
||||
is a reasonable real number. I don't think isnan, isinf etc are
|
||||
platform independent, so the following algorithm is used. */
|
||||
|
||||
{
|
||||
double t;
|
||||
t = (skew + skew) / skew; /* Skew shouldn't be zero either */
|
||||
if ((t < 1.9) || (t > 2.1)) {
|
||||
LOG(LOGS_WARN, LOGF_Reference, "Bogus skew value encountered");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LCL_ReadRawTime(&raw_now);
|
||||
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
|
||||
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
|
||||
|
||||
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
|
||||
our_offset = offset + elapsed * frequency;
|
||||
offset += elapsed * frequency;
|
||||
offset_sd += elapsed * frequency_sd;
|
||||
|
||||
if (!is_offset_ok(our_offset))
|
||||
if (last_ref_update.tv_sec) {
|
||||
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
|
||||
update_interval = MAX(update_interval, 0.0);
|
||||
} else {
|
||||
update_interval = 0.0;
|
||||
}
|
||||
|
||||
/* Get new estimates of the frequency and skew including the new data */
|
||||
get_clock_estimates(manual, frequency, skew,
|
||||
&frequency, &skew, &residual_frequency);
|
||||
|
||||
if (!is_offset_ok(offset))
|
||||
return;
|
||||
|
||||
are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0;
|
||||
orig_root_distance = our_root_delay / 2.0 + get_root_dispersion(&now);
|
||||
|
||||
are_we_synchronised = leap != LEAP_Unsynchronised;
|
||||
our_stratum = stratum + 1;
|
||||
our_ref_id = ref_id;
|
||||
if (ref_ip)
|
||||
@@ -951,17 +1015,13 @@ REF_SetReference(int stratum,
|
||||
else
|
||||
our_ref_ip.family = IPADDR_UNSPEC;
|
||||
our_ref_time = *ref_time;
|
||||
our_skew = skew;
|
||||
our_residual_freq = residual_frequency;
|
||||
our_root_delay = root_delay;
|
||||
our_root_dispersion = root_dispersion;
|
||||
|
||||
if (last_ref_update.tv_sec) {
|
||||
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
|
||||
if (update_interval < 0.0)
|
||||
update_interval = 0.0;
|
||||
} else {
|
||||
update_interval = 0.0;
|
||||
}
|
||||
last_ref_update = now;
|
||||
last_ref_update_interval = update_interval;
|
||||
last_offset = offset;
|
||||
|
||||
/* We want to correct the offset quickly, but we also want to keep the
|
||||
frequency error caused by the correction itself low.
|
||||
@@ -979,68 +1039,28 @@ 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)) {
|
||||
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
||||
/* Cancel the uncorrected offset and correct the total offset by step */
|
||||
accumulate_offset = uncorrected_offset;
|
||||
step_offset = our_offset - uncorrected_offset;
|
||||
step_offset = offset - uncorrected_offset;
|
||||
} else {
|
||||
accumulate_offset = our_offset;
|
||||
accumulate_offset = offset;
|
||||
step_offset = 0.0;
|
||||
}
|
||||
|
||||
/* Eliminate updates that are based on totally unreliable frequency
|
||||
information. Ignore this limit with manual reference. */
|
||||
|
||||
if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) {
|
||||
|
||||
previous_skew = our_skew;
|
||||
new_skew = skew;
|
||||
|
||||
previous_freq = 0.0; /* We assume that the local clock is running
|
||||
according to our previously determined
|
||||
value; note that this is a delta frequency
|
||||
--- absolute frequencies are only known in
|
||||
the local module. */
|
||||
new_freq = frequency;
|
||||
|
||||
/* Set new frequency based on weighted average of old and new skew. With
|
||||
manual reference the old frequency has no weight. */
|
||||
|
||||
old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0;
|
||||
new_weight = 3.0 / Sqr(new_skew);
|
||||
|
||||
sum_weight = old_weight + new_weight;
|
||||
|
||||
our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
|
||||
|
||||
delta_freq1 = previous_freq - our_frequency;
|
||||
delta_freq2 = new_freq - our_frequency;
|
||||
|
||||
skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
|
||||
skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
|
||||
our_skew = skew1 + skew2;
|
||||
|
||||
our_residual_freq = new_freq - our_frequency;
|
||||
|
||||
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
|
||||
/* Adjust the clock */
|
||||
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
|
||||
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset);
|
||||
|
||||
LCL_AccumulateOffset(accumulate_offset, correction_rate);
|
||||
|
||||
our_residual_freq = frequency;
|
||||
}
|
||||
|
||||
update_leap_status(leap, raw_now.tv_sec, 0);
|
||||
maybe_log_offset(our_offset, raw_now.tv_sec);
|
||||
maybe_log_offset(offset, raw_now.tv_sec);
|
||||
|
||||
if (step_offset != 0.0) {
|
||||
if (LCL_ApplyStepOffset(step_offset))
|
||||
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
|
||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
|
||||
}
|
||||
|
||||
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
|
||||
LCL_SetSyncStatus(are_we_synchronised, offset_sd,
|
||||
root_delay / 2.0 + get_root_dispersion(&now));
|
||||
|
||||
/* Add a random error of up to one second to the reference time to make it
|
||||
less useful when disclosed to NTP and cmdmon clients for estimating
|
||||
@@ -1051,44 +1071,33 @@ REF_SetReference(int stratum,
|
||||
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
|
||||
our_ref_time.tv_sec--;
|
||||
|
||||
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
|
||||
local_abs_frequency = LCL_ReadAbsoluteFrequency();
|
||||
|
||||
write_log(&now,
|
||||
our_ref_ip.family != IPADDR_UNSPEC ? UTI_IPToString(&our_ref_ip) : UTI_RefidToString(our_ref_id),
|
||||
our_stratum,
|
||||
our_leap_status,
|
||||
abs_freq_ppm,
|
||||
1.0e6*our_skew,
|
||||
our_offset,
|
||||
combined_sources,
|
||||
offset_sd,
|
||||
uncorrected_offset);
|
||||
write_log(&now, combined_sources, local_abs_frequency,
|
||||
offset, offset_sd, uncorrected_offset, orig_root_distance);
|
||||
|
||||
if (drift_file) {
|
||||
/* Update drift file at most once per hour */
|
||||
drift_file_age += update_interval;
|
||||
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
|
||||
update_drift_file(abs_freq_ppm, our_skew);
|
||||
update_drift_file(local_abs_frequency, our_skew);
|
||||
drift_file_age = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update fallback drifts */
|
||||
if (fb_drifts) {
|
||||
update_fb_drifts(abs_freq_ppm, update_interval);
|
||||
if (fb_drifts && are_we_synchronised) {
|
||||
update_fb_drifts(local_abs_frequency, update_interval);
|
||||
schedule_fb_drift(&now);
|
||||
}
|
||||
|
||||
last_ref_update_interval = update_interval;
|
||||
last_offset = our_offset;
|
||||
|
||||
/* Update the moving average of squares of offset, quickly on start */
|
||||
if (avg2_moving) {
|
||||
avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset);
|
||||
avg2_offset += 0.1 * (SQUARE(offset) - avg2_offset);
|
||||
} else {
|
||||
if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset)
|
||||
if (avg2_offset > 0.0 && avg2_offset < SQUARE(offset))
|
||||
avg2_moving = 1;
|
||||
avg2_offset = our_offset * our_offset;
|
||||
avg2_offset = SQUARE(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1107,7 +1116,7 @@ REF_SetManualReference
|
||||
only supposed to be used with the local source option, really.
|
||||
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
|
||||
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
|
||||
ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
|
||||
ref_time, offset, 0.0, frequency, skew, skew, 0.0, 0.0);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1136,20 +1145,15 @@ REF_SetUnsynchronised(void)
|
||||
}
|
||||
|
||||
update_leap_status(LEAP_Unsynchronised, 0, 0);
|
||||
our_ref_ip.family = IPADDR_INET4;
|
||||
our_ref_ip.addr.in4 = 0;
|
||||
our_stratum = 0;
|
||||
are_we_synchronised = 0;
|
||||
|
||||
LCL_SetSyncStatus(0, 0.0, 0.0);
|
||||
|
||||
write_log(&now,
|
||||
"0.0.0.0",
|
||||
0,
|
||||
our_leap_status,
|
||||
LCL_ReadAbsoluteFrequency(),
|
||||
1.0e6*our_skew,
|
||||
0.0,
|
||||
0,
|
||||
0.0,
|
||||
uncorrected_offset);
|
||||
write_log(&now, 0, LCL_ReadAbsoluteFrequency(), 0.0, 0.0, uncorrected_offset,
|
||||
our_root_delay / 2.0 + get_root_dispersion(&now));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1167,14 +1171,12 @@ REF_GetReferenceParams
|
||||
double *root_dispersion
|
||||
)
|
||||
{
|
||||
double elapsed, dispersion;
|
||||
double dispersion;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
if (are_we_synchronised) {
|
||||
elapsed = UTI_DiffTimespecsToDouble(local_time, &our_ref_time);
|
||||
dispersion = our_root_dispersion +
|
||||
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
|
||||
dispersion = get_root_dispersion(local_time);
|
||||
} else {
|
||||
dispersion = 0.0;
|
||||
}
|
||||
@@ -1214,7 +1216,7 @@ REF_GetReferenceParams
|
||||
*leap_status = LEAP_Normal;
|
||||
|
||||
*root_delay = 0.0;
|
||||
*root_dispersion = LCL_GetSysPrecisionAsQuantum();
|
||||
*root_dispersion = 0.0;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -1332,6 +1334,18 @@ int REF_IsLeapSecondClose(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
REF_GetTaiOffset(struct timespec *ts)
|
||||
{
|
||||
int tai_offset;
|
||||
|
||||
get_tz_leap(ts->tv_sec, &tai_offset);
|
||||
|
||||
return tai_offset;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_GetTrackingReport(RPT_TrackingReport *rep)
|
||||
{
|
||||
@@ -1348,7 +1362,7 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
|
||||
&rep->ref_id, &rep->ref_time,
|
||||
&rep->root_delay, &rep->root_dispersion);
|
||||
|
||||
if (rep->stratum == NTP_MAX_STRATUM)
|
||||
if (rep->stratum == NTP_MAX_STRATUM && !synchronised)
|
||||
rep->stratum = 0;
|
||||
|
||||
rep->ip_addr.family = IPADDR_UNSPEC;
|
||||
|
||||
@@ -144,6 +144,7 @@ extern void REF_SetReference
|
||||
double offset,
|
||||
double offset_sd,
|
||||
double frequency,
|
||||
double frequency_sd,
|
||||
double skew,
|
||||
double root_delay,
|
||||
double root_dispersion
|
||||
@@ -184,6 +185,9 @@ extern void REF_DisableLocal(void);
|
||||
and is better to discard any measurements */
|
||||
extern int REF_IsLeapSecondClose(void);
|
||||
|
||||
/* Return TAI-UTC offset corresponding to a time in UTC if available */
|
||||
extern int REF_GetTaiOffset(struct timespec *ts);
|
||||
|
||||
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
|
||||
|
||||
#endif /* GOT_REFERENCE_H */
|
||||
|
||||
59
regress.c
59
regress.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2016-2017
|
||||
*
|
||||
* This program 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,7 +34,7 @@
|
||||
#include "logging.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MAX_POINTS 128
|
||||
#define MAX_POINTS 64
|
||||
|
||||
void
|
||||
RGR_WeightedRegression
|
||||
@@ -285,7 +285,7 @@ RGR_FindBestRegression
|
||||
n - start <= min_samples) {
|
||||
if (start != resid_start) {
|
||||
/* Ignore extra samples in returned nruns */
|
||||
nruns = n_runs_from_residuals(resid - resid_start + start, n - start);
|
||||
nruns = n_runs_from_residuals(resid + (start - resid_start), n - start);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
@@ -340,7 +340,7 @@ RGR_FindBestRegression
|
||||
0-521-43108-5). */
|
||||
|
||||
static double
|
||||
find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
|
||||
find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
|
||||
{
|
||||
int u, v, l, r;
|
||||
double temp;
|
||||
@@ -403,9 +403,9 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
|
||||
static double
|
||||
find_ordered_entry(double *x, int n, int index)
|
||||
{
|
||||
int flags[MAX_POINTS];
|
||||
char flags[MAX_POINTS];
|
||||
|
||||
memset(flags, 0, n * sizeof(int));
|
||||
memset(flags, 0, n * sizeof (flags[0]));
|
||||
return find_ordered_entry_with_flags(x, n, index, flags);
|
||||
}
|
||||
#endif
|
||||
@@ -417,9 +417,9 @@ static double
|
||||
find_median(double *x, int n)
|
||||
{
|
||||
int k;
|
||||
int flags[MAX_POINTS];
|
||||
char flags[MAX_POINTS];
|
||||
|
||||
memset(flags, 0, n*sizeof(int));
|
||||
memset(flags, 0, n * sizeof (flags[0]));
|
||||
k = n>>1;
|
||||
if (n&1) {
|
||||
return find_ordered_entry_with_flags(x, n, k, flags);
|
||||
@@ -429,6 +429,19 @@ find_median(double *x, int n)
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
RGR_FindMedian(double *x, int n)
|
||||
{
|
||||
double tmp[MAX_POINTS];
|
||||
|
||||
assert(n > 0 && n <= MAX_POINTS);
|
||||
memcpy(tmp, x, n * sizeof (tmp[0]));
|
||||
|
||||
return find_median(tmp, n);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* This function evaluates the equation
|
||||
|
||||
@@ -509,7 +522,7 @@ RGR_FindBestRobustRegression
|
||||
double mx, dx, my, dy;
|
||||
int nruns = 0;
|
||||
|
||||
assert(n < MAX_POINTS);
|
||||
assert(n <= MAX_POINTS);
|
||||
|
||||
if (n < 2) {
|
||||
return 0;
|
||||
@@ -566,24 +579,18 @@ RGR_FindBestRobustRegression
|
||||
Estimate standard deviation of b and expand range about b based
|
||||
on that. */
|
||||
sb = sqrt(s2 * W/V);
|
||||
if (sb > tol) {
|
||||
incr = 3.0 * sb;
|
||||
} else {
|
||||
incr = 3.0 * tol;
|
||||
}
|
||||
incr = MAX(sb, tol);
|
||||
|
||||
blo = b;
|
||||
bhi = b;
|
||||
|
||||
do {
|
||||
/* Make sure incr is significant to blo and bhi */
|
||||
while (bhi + incr == bhi || blo - incr == blo) {
|
||||
incr *= 2;
|
||||
}
|
||||
incr *= 2.0;
|
||||
|
||||
/* Give up if the interval is too large */
|
||||
if (incr > 100.0)
|
||||
return 0;
|
||||
|
||||
blo = b - incr;
|
||||
bhi = b + incr;
|
||||
|
||||
blo -= incr;
|
||||
bhi += incr;
|
||||
|
||||
/* We don't want 'a' yet */
|
||||
eval_robust_residual(x + start, y + start, n_points, blo, &a, &rlo);
|
||||
eval_robust_residual(x + start, y + start, n_points, bhi, &a, &rhi);
|
||||
@@ -594,6 +601,8 @@ RGR_FindBestRobustRegression
|
||||
/* OK, so the root for b lies in (blo, bhi). Start bisecting */
|
||||
do {
|
||||
bmid = 0.5 * (blo + bhi);
|
||||
if (!(blo < bmid && bmid < bhi))
|
||||
break;
|
||||
eval_robust_residual(x + start, y + start, n_points, bmid, &a, &rmid);
|
||||
if (rmid == 0.0) {
|
||||
break;
|
||||
@@ -606,7 +615,7 @@ RGR_FindBestRobustRegression
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
} while ((bhi - blo) > tol && (bmid - blo) * (bhi - bmid) > 0.0);
|
||||
} while (bhi - blo > tol);
|
||||
|
||||
*b0 = a;
|
||||
*b1 = bmid;
|
||||
|
||||
@@ -131,4 +131,7 @@ RGR_MultipleRegress
|
||||
double *b2 /* estimated second slope */
|
||||
);
|
||||
|
||||
/* Return the median value from an array */
|
||||
extern double RGR_FindMedian(double *x, int n);
|
||||
|
||||
#endif /* GOT_REGRESS_H */
|
||||
|
||||
6
rtc.c
6
rtc.c
@@ -104,7 +104,7 @@ apply_driftfile_time(time_t t)
|
||||
|
||||
if (now.tv_sec < t) {
|
||||
if (LCL_ApplyStepOffset(now.tv_sec - t))
|
||||
LOG(LOGS_INFO, LOGF_Rtc, "System time restored from driftfile");
|
||||
LOG(LOGS_INFO, "System time restored from driftfile");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ RTC_Initialise(int initial_set)
|
||||
|
||||
if (file_name) {
|
||||
if (CNF_GetRtcSync()) {
|
||||
LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync");
|
||||
LOG_FATAL("rtcfile directive cannot be used with rtcsync");
|
||||
}
|
||||
|
||||
if (driver.init) {
|
||||
@@ -150,7 +150,7 @@ RTC_Initialise(int initial_set)
|
||||
driver_initialised = 1;
|
||||
}
|
||||
} else {
|
||||
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
|
||||
LOG(LOGS_ERR, "RTC not supported on this operating system");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
rtc_linux.c
69
rtc_linux.c
@@ -187,6 +187,11 @@ accumulate_sample(time_t rtc, struct timespec *sys)
|
||||
discard_samples(NEW_FIRST_WHEN_FULL);
|
||||
}
|
||||
|
||||
/* Discard all samples if the RTC was stepped back (not our trim) */
|
||||
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
|
||||
DEBUG_LOG("RTC samples discarded");
|
||||
n_samples = 0;
|
||||
}
|
||||
|
||||
/* Always use most recent sample as reference */
|
||||
/* use sample only if n_sample is not negative*/
|
||||
@@ -289,7 +294,7 @@ slew_samples
|
||||
coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
|
||||
DEBUG_LOG("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);
|
||||
@@ -347,7 +352,7 @@ rtc_from_t(const time_t *t)
|
||||
|
||||
static time_t
|
||||
t_from_rtc(struct tm *stm) {
|
||||
struct tm temp1, temp2;
|
||||
struct tm temp1, temp2, *tm;
|
||||
long diff;
|
||||
time_t t1, t2;
|
||||
|
||||
@@ -355,18 +360,20 @@ t_from_rtc(struct tm *stm) {
|
||||
temp1.tm_isdst = 0;
|
||||
|
||||
t1 = mktime(&temp1);
|
||||
if (rtc_on_utc) {
|
||||
temp2 = *gmtime(&t1);
|
||||
} else {
|
||||
temp2 = *localtime(&t1);
|
||||
|
||||
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
|
||||
if (!tm) {
|
||||
DEBUG_LOG("gmtime()/localtime() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
temp2 = *tm;
|
||||
temp2.tm_isdst = 0;
|
||||
t2 = mktime(&temp2);
|
||||
diff = t2 - t1;
|
||||
|
||||
if (t1 - diff == -1)
|
||||
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time");
|
||||
DEBUG_LOG("Could not convert RTC time");
|
||||
|
||||
return t1 - diff;
|
||||
}
|
||||
@@ -385,7 +392,7 @@ read_hwclock_file(const char *hwclock_file)
|
||||
|
||||
in = fopen(hwclock_file, "r");
|
||||
if (!in) {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s",
|
||||
LOG(LOGS_WARN, "Could not open %s : %s",
|
||||
hwclock_file, strerror(errno));
|
||||
return;
|
||||
}
|
||||
@@ -403,8 +410,7 @@ read_hwclock_file(const char *hwclock_file)
|
||||
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
|
||||
rtc_on_utc = 1;
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s",
|
||||
hwclock_file);
|
||||
LOG(LOGS_WARN, "Could not read RTC LOCAL/UTC setting from %s", hwclock_file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,8 +452,7 @@ read_coefs_from_file(void)
|
||||
&file_ref_offset,
|
||||
&file_rate_ppm) == 4) {
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s",
|
||||
coefs_file_name);
|
||||
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
@@ -480,7 +485,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
out = fopen(temp_coefs_file_name, "w");
|
||||
if (!out) {
|
||||
Free(temp_coefs_file_name);
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open temporary RTC file %s.tmp for writing",
|
||||
LOG(LOGS_WARN, "Could not open temporary RTC file %s.tmp for writing",
|
||||
coefs_file_name);
|
||||
return RTC_ST_BADFILE;
|
||||
}
|
||||
@@ -491,7 +496,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double 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",
|
||||
LOG(LOGS_WARN, "Could not write to temporary RTC file %s.tmp",
|
||||
coefs_file_name);
|
||||
return RTC_ST_BADFILE;
|
||||
}
|
||||
@@ -501,7 +506,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
if (!stat(coefs_file_name,&buf)) {
|
||||
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,
|
||||
LOG(LOGS_WARN,
|
||||
"Could not change ownership or permissions of temporary RTC file %s.tmp",
|
||||
coefs_file_name);
|
||||
}
|
||||
@@ -512,7 +517,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
if (rename(temp_coefs_file_name,coefs_file_name)) {
|
||||
unlink(temp_coefs_file_name);
|
||||
Free(temp_coefs_file_name);
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not replace old RTC file %s.tmp with new one %s",
|
||||
LOG(LOGS_WARN, "Could not replace old RTC file %s.tmp with new one %s",
|
||||
coefs_file_name, coefs_file_name);
|
||||
return RTC_ST_BADFILE;
|
||||
}
|
||||
@@ -545,7 +550,7 @@ RTC_Linux_Initialise(void)
|
||||
|
||||
fd = open (CNF_GetRtcDevice(), O_RDWR);
|
||||
if (fd < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
|
||||
LOG(LOGS_ERR, "Could not open RTC device %s : %s",
|
||||
CNF_GetRtcDevice(), strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
@@ -606,16 +611,14 @@ switch_interrupts(int onoff)
|
||||
if (onoff) {
|
||||
status = ioctl(fd, RTC_UIE_ON, 0);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
|
||||
"enable", strerror(errno));
|
||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "enable", strerror(errno));
|
||||
return;
|
||||
}
|
||||
skip_interrupts = 1;
|
||||
} else {
|
||||
status = ioctl(fd, RTC_UIE_OFF, 0);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
|
||||
"disable", strerror(errno));
|
||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s", "disable", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +656,7 @@ set_rtc(time_t new_rtc_time)
|
||||
|
||||
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not set RTC time");
|
||||
LOG(LOGS_ERR, "Could not set RTC time");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -696,10 +699,10 @@ handle_initial_trim(void)
|
||||
sys_error_now = rtc_error_now - coef_seconds_fast;
|
||||
|
||||
LCL_AccumulateOffset(sys_error_now, 0.0);
|
||||
LOG(LOGS_INFO, LOGF_RtcLinux, "System clock off from RTC by %f seconds (slew)",
|
||||
LOG(LOGS_INFO, "System clock off from RTC by %f seconds (slew)",
|
||||
sys_error_now);
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients");
|
||||
LOG(LOGS_WARN, "No valid rtcfile coefficients");
|
||||
}
|
||||
|
||||
coefs_valid = 0;
|
||||
@@ -724,7 +727,7 @@ handle_relock_after_trim(void)
|
||||
if (valid) {
|
||||
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
|
||||
} else {
|
||||
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim");
|
||||
DEBUG_LOG("Could not do regression after trim");
|
||||
}
|
||||
|
||||
coefs_valid = 0;
|
||||
@@ -819,7 +822,7 @@ read_from_device(int fd_, int event, void *any)
|
||||
if (status < 0) {
|
||||
/* This looks like a bad error : the file descriptor was indicating it was
|
||||
* ready to read but we couldn't read anything. Give up. */
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
SCH_RemoveFileHandler(fd);
|
||||
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
|
||||
close(fd);
|
||||
@@ -843,7 +846,7 @@ read_from_device(int fd_, int event, void *any)
|
||||
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
error = 1;
|
||||
goto turn_off_interrupt;
|
||||
}
|
||||
@@ -884,7 +887,7 @@ turn_off_interrupt:
|
||||
switch (operating_mode) {
|
||||
case OM_INITIAL:
|
||||
if (error) {
|
||||
DEBUG_LOG(LOGF_RtcLinux, "Could not complete initial step due to errors");
|
||||
DEBUG_LOG("Could not complete initial step due to errors");
|
||||
operating_mode = OM_NORMAL;
|
||||
(after_init_hook)(after_init_hook_arg);
|
||||
|
||||
@@ -897,7 +900,7 @@ turn_off_interrupt:
|
||||
|
||||
case OM_AFTERTRIM:
|
||||
if (error) {
|
||||
DEBUG_LOG(LOGF_RtcLinux, "Could not complete after trim relock due to errors");
|
||||
DEBUG_LOG("Could not complete after trim relock due to errors");
|
||||
operating_mode = OM_NORMAL;
|
||||
|
||||
switch_interrupts(0);
|
||||
@@ -1036,7 +1039,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
|
||||
|
||||
if (new_sys_time.tv_sec < driftfile_time) {
|
||||
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
|
||||
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1048,7 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||
/* Set system time only if the step is larger than 1 second */
|
||||
if (fabs(sys_offset) >= 1.0) {
|
||||
if (LCL_ApplyStepOffset(sys_offset))
|
||||
LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC");
|
||||
LOG(LOGS_INFO, "System time set from RTC");
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
@@ -1090,7 +1093,7 @@ RTC_Linux_Trim(void)
|
||||
|
||||
if (fabs(coef_seconds_fast) > 1.0) {
|
||||
|
||||
LOG(LOGS_INFO, LOGF_RtcLinux, "RTC wrong by %.3f seconds (step)",
|
||||
LOG(LOGS_INFO, "RTC wrong by %.3f seconds (step)",
|
||||
coef_seconds_fast);
|
||||
|
||||
/* Do processing to set clock. Let R be the value we set the
|
||||
|
||||
453
samplefilt.c
Normal file
453
samplefilt.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2014, 2016, 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Routines implementing a median sample filter.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "regress.h"
|
||||
#include "samplefilt.h"
|
||||
#include "util.h"
|
||||
|
||||
#define MIN_SAMPLES 1
|
||||
#define MAX_SAMPLES 256
|
||||
|
||||
struct SPF_Instance_Record {
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int index;
|
||||
int used;
|
||||
int last;
|
||||
int avg_var_n;
|
||||
double avg_var;
|
||||
double max_var;
|
||||
double combine_ratio;
|
||||
NTP_Sample *samples;
|
||||
int *selected;
|
||||
double *x_data;
|
||||
double *y_data;
|
||||
double *w_data;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
SPF_Instance
|
||||
SPF_CreateInstance(int min_samples, int max_samples, double max_dispersion, double combine_ratio)
|
||||
{
|
||||
SPF_Instance filter;
|
||||
|
||||
filter = MallocNew(struct SPF_Instance_Record);
|
||||
|
||||
min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
|
||||
max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
|
||||
max_samples = MAX(min_samples, max_samples);
|
||||
combine_ratio = CLAMP(0.0, combine_ratio, 1.0);
|
||||
|
||||
filter->min_samples = min_samples;
|
||||
filter->max_samples = max_samples;
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
filter->last = -1;
|
||||
/* Set the first estimate to the system precision */
|
||||
filter->avg_var_n = 0;
|
||||
filter->avg_var = SQUARE(LCL_GetSysPrecisionAsQuantum());
|
||||
filter->max_var = SQUARE(max_dispersion);
|
||||
filter->combine_ratio = combine_ratio;
|
||||
filter->samples = MallocArray(NTP_Sample, filter->max_samples);
|
||||
filter->selected = MallocArray(int, filter->max_samples);
|
||||
filter->x_data = MallocArray(double, filter->max_samples);
|
||||
filter->y_data = MallocArray(double, filter->max_samples);
|
||||
filter->w_data = MallocArray(double, filter->max_samples);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DestroyInstance(SPF_Instance filter)
|
||||
{
|
||||
Free(filter->samples);
|
||||
Free(filter->selected);
|
||||
Free(filter->x_data);
|
||||
Free(filter->y_data);
|
||||
Free(filter->w_data);
|
||||
Free(filter);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Check that samples times are strictly increasing */
|
||||
|
||||
static int
|
||||
check_sample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (filter->used <= 0)
|
||||
return 1;
|
||||
|
||||
if (UTI_CompareTimespecs(&filter->samples[filter->last].time, &sample->time) >= 0) {
|
||||
DEBUG_LOG("filter non-increasing sample time %s", UTI_TimespecToString(&sample->time));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (!check_sample(filter, sample))
|
||||
return 0;
|
||||
|
||||
filter->index++;
|
||||
filter->index %= filter->max_samples;
|
||||
filter->last = filter->index;
|
||||
if (filter->used < filter->max_samples)
|
||||
filter->used++;
|
||||
|
||||
filter->samples[filter->index] = *sample;
|
||||
|
||||
DEBUG_LOG("filter sample %d t=%s offset=%.9f peer_disp=%.9f",
|
||||
filter->index, UTI_TimespecToString(&sample->time),
|
||||
sample->offset, sample->peer_dispersion);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
if (filter->last < 0)
|
||||
return 0;
|
||||
|
||||
*sample = filter->samples[filter->last];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetNumberOfSamples(SPF_Instance filter)
|
||||
{
|
||||
return filter->used;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||
{
|
||||
return sqrt(filter->avg_var);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DropSamples(SPF_Instance filter)
|
||||
{
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static const NTP_Sample *tmp_sort_samples;
|
||||
|
||||
static int
|
||||
compare_samples(const void *a, const void *b)
|
||||
{
|
||||
const NTP_Sample *s1, *s2;
|
||||
|
||||
s1 = &tmp_sort_samples[*(int *)a];
|
||||
s2 = &tmp_sort_samples[*(int *)b];
|
||||
|
||||
if (s1->offset < s2->offset)
|
||||
return -1;
|
||||
else if (s1->offset > s2->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
select_samples(SPF_Instance filter)
|
||||
{
|
||||
int i, j, k, o, from, to, *selected;
|
||||
double min_dispersion;
|
||||
|
||||
if (filter->used < filter->min_samples)
|
||||
return 0;
|
||||
|
||||
selected = filter->selected;
|
||||
|
||||
/* With 4 or more samples, select those that have peer dispersion smaller
|
||||
than 1.5x of the minimum dispersion */
|
||||
if (filter->used > 4) {
|
||||
for (i = 1, min_dispersion = filter->samples[0].peer_dispersion; i < filter->used; i++) {
|
||||
if (min_dispersion > filter->samples[i].peer_dispersion)
|
||||
min_dispersion = filter->samples[i].peer_dispersion;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < filter->used; i++) {
|
||||
if (filter->samples[i].peer_dispersion <= 1.5 * min_dispersion)
|
||||
selected[j++] = i;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
if (j < 4) {
|
||||
/* Select all samples */
|
||||
|
||||
for (j = 0; j < filter->used; j++)
|
||||
selected[j] = j;
|
||||
}
|
||||
|
||||
/* And sort their indices by offset */
|
||||
tmp_sort_samples = filter->samples;
|
||||
qsort(selected, j, sizeof (int), compare_samples);
|
||||
|
||||
/* Select samples closest to the median */
|
||||
if (j > 2) {
|
||||
from = j * (1.0 - filter->combine_ratio) / 2.0;
|
||||
from = CLAMP(1, from, (j - 1) / 2);
|
||||
} else {
|
||||
from = 0;
|
||||
}
|
||||
|
||||
to = j - from;
|
||||
|
||||
/* Mark unused samples and sort the rest by their time */
|
||||
|
||||
o = filter->used - filter->index - 1;
|
||||
|
||||
for (i = 0; i < from; i++)
|
||||
selected[i] = -1;
|
||||
for (; i < to; i++)
|
||||
selected[i] = (selected[i] + o) % filter->used;
|
||||
for (; i < filter->used; i++)
|
||||
selected[i] = -1;
|
||||
|
||||
for (i = from; i < to; i++) {
|
||||
j = selected[i];
|
||||
selected[i] = -1;
|
||||
while (j != -1 && selected[j] != j) {
|
||||
k = selected[j];
|
||||
selected[j] = j;
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = j = 0, k = -1; i < filter->used; i++) {
|
||||
if (selected[i] != -1)
|
||||
selected[j++] = (selected[i] + filter->used - o) % filter->used;
|
||||
}
|
||||
|
||||
assert(j > 0 && j <= filter->max_samples);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
|
||||
{
|
||||
double mean_peer_dispersion, mean_root_dispersion, mean_peer_delay, mean_root_delay;
|
||||
double mean_x, mean_y, disp, var, prev_avg_var;
|
||||
NTP_Sample *sample, *last_sample;
|
||||
int i, dof;
|
||||
|
||||
last_sample = &filter->samples[filter->selected[n - 1]];
|
||||
|
||||
/* Prepare data */
|
||||
for (i = 0; i < n; i++) {
|
||||
sample = &filter->samples[filter->selected[i]];
|
||||
|
||||
filter->x_data[i] = UTI_DiffTimespecsToDouble(&sample->time, &last_sample->time);
|
||||
filter->y_data[i] = sample->offset;
|
||||
filter->w_data[i] = sample->peer_dispersion;
|
||||
}
|
||||
|
||||
/* Calculate mean offset and interval since the last sample */
|
||||
for (i = 0, mean_x = mean_y = 0.0; i < n; i++) {
|
||||
mean_x += filter->x_data[i];
|
||||
mean_y += filter->y_data[i];
|
||||
}
|
||||
mean_x /= n;
|
||||
mean_y /= n;
|
||||
|
||||
if (n >= 4) {
|
||||
double b0, b1, s2, sb0, sb1;
|
||||
|
||||
/* Set y axis to the mean sample time */
|
||||
for (i = 0; i < n; i++)
|
||||
filter->x_data[i] -= mean_x;
|
||||
|
||||
/* Make a linear fit and use the estimated standard deviation of the
|
||||
intercept as dispersion */
|
||||
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
|
||||
&b0, &b1, &s2, &sb0, &sb1);
|
||||
var = s2;
|
||||
disp = sb0;
|
||||
dof = n - 2;
|
||||
} else if (n >= 2) {
|
||||
for (i = 0, disp = 0.0; i < n; i++)
|
||||
disp += (filter->y_data[i] - mean_y) * (filter->y_data[i] - mean_y);
|
||||
var = disp / (n - 1);
|
||||
disp = sqrt(var);
|
||||
dof = n - 1;
|
||||
} else {
|
||||
var = filter->avg_var;
|
||||
disp = sqrt(var);
|
||||
dof = 1;
|
||||
}
|
||||
|
||||
/* Avoid working with zero dispersion */
|
||||
if (var < 1e-20) {
|
||||
var = 1e-20;
|
||||
disp = sqrt(var);
|
||||
}
|
||||
|
||||
/* Drop the sample if the variance is larger than the maximum */
|
||||
if (filter->max_var > 0.0 && var > filter->max_var) {
|
||||
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
|
||||
sqrt(var), sqrt(filter->max_var));
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev_avg_var = filter->avg_var;
|
||||
|
||||
/* Update the exponential moving average of the variance */
|
||||
if (filter->avg_var_n > 50) {
|
||||
filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
|
||||
} else {
|
||||
filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
|
||||
(dof + filter->avg_var_n);
|
||||
if (filter->avg_var_n == 0)
|
||||
prev_avg_var = filter->avg_var;
|
||||
filter->avg_var_n += dof;
|
||||
}
|
||||
|
||||
/* Use the long-term average of variance instead of the estimated value
|
||||
unless it is significantly smaller in order to reduce the noise in
|
||||
sourcestats weights */
|
||||
if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
|
||||
disp = sqrt(filter->avg_var) * disp / sqrt(var);
|
||||
|
||||
mean_peer_dispersion = mean_root_dispersion = mean_peer_delay = mean_root_delay = 0.0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
sample = &filter->samples[filter->selected[i]];
|
||||
|
||||
mean_peer_dispersion += sample->peer_dispersion;
|
||||
mean_root_dispersion += sample->root_dispersion;
|
||||
mean_peer_delay += sample->peer_delay;
|
||||
mean_root_delay += sample->root_delay;
|
||||
}
|
||||
|
||||
mean_peer_dispersion /= n;
|
||||
mean_root_dispersion /= n;
|
||||
mean_peer_delay /= n;
|
||||
mean_root_delay /= n;
|
||||
|
||||
UTI_AddDoubleToTimespec(&last_sample->time, mean_x, &result->time);
|
||||
result->offset = mean_y;
|
||||
result->peer_dispersion = MAX(disp, mean_peer_dispersion);
|
||||
result->root_dispersion = MAX(disp, mean_root_dispersion);
|
||||
result->peer_delay = mean_peer_delay;
|
||||
result->root_delay = mean_root_delay;
|
||||
result->stratum = last_sample->stratum;
|
||||
result->leap = last_sample->leap;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = select_samples(filter);
|
||||
|
||||
if (n < 1)
|
||||
return 0;
|
||||
|
||||
if (!combine_selected_samples(filter, n, sample))
|
||||
return 0;
|
||||
|
||||
SPF_DropSamples(filter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double doffset)
|
||||
{
|
||||
int i, first, last;
|
||||
double delta_time;
|
||||
|
||||
if (filter->last < 0)
|
||||
return;
|
||||
|
||||
/* Always slew the last sample as it may be returned even if no new
|
||||
samples were accumulated */
|
||||
if (filter->used > 0) {
|
||||
first = 0;
|
||||
last = filter->used - 1;
|
||||
} else {
|
||||
first = last = filter->last;
|
||||
}
|
||||
|
||||
for (i = first; i <= last; i++) {
|
||||
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
||||
&delta_time, dfreq, doffset);
|
||||
filter->samples[i].offset -= delta_time;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < filter->used; i++) {
|
||||
filter->samples[i].peer_dispersion += dispersion;
|
||||
filter->samples[i].root_dispersion += dispersion;
|
||||
}
|
||||
}
|
||||
49
samplefilt.h
Normal file
49
samplefilt.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You 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 sample filter.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef GOT_SAMPLEFILT_H
|
||||
#define GOT_SAMPLEFILT_H
|
||||
|
||||
#include "ntp.h"
|
||||
|
||||
typedef struct SPF_Instance_Record *SPF_Instance;
|
||||
|
||||
extern SPF_Instance SPF_CreateInstance(int min_samples, int max_samples,
|
||||
double max_dispersion, double combine_ratio);
|
||||
extern void SPF_DestroyInstance(SPF_Instance filter);
|
||||
|
||||
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
||||
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
||||
extern void SPF_DropSamples(SPF_Instance filter);
|
||||
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
||||
double dfreq, double doffset);
|
||||
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
||||
|
||||
#endif
|
||||
34
sched.c
34
sched.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2013-2015
|
||||
* Copyright (C) Miroslav Lichvar 2011, 2013-2016
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -163,7 +163,7 @@ SCH_AddFileHandler
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fd >= FD_SETSIZE)
|
||||
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
|
||||
LOG_FATAL("Too many file descriptors");
|
||||
|
||||
/* Resize the array if the descriptor is highest so far */
|
||||
while (ARR_GetSize(file_handlers) <= fd) {
|
||||
@@ -219,13 +219,16 @@ SCH_RemoveFileHandler(int fd)
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SCH_SetFileHandlerEvents(int fd, int events)
|
||||
SCH_SetFileHandlerEvent(int fd, int event, int enable)
|
||||
{
|
||||
FileHandlerEntry *ptr;
|
||||
|
||||
assert(events);
|
||||
ptr = ARR_GetElement(file_handlers, fd);
|
||||
ptr->events = events;
|
||||
|
||||
if (enable)
|
||||
ptr->events |= event;
|
||||
else
|
||||
ptr->events &= ~event;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -350,7 +353,7 @@ SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArg
|
||||
LCL_ReadRawTime(&now);
|
||||
UTI_AddDoubleToTimespec(&now, delay, &then);
|
||||
if (UTI_CompareTimespecs(&now, &then) > 0) {
|
||||
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
|
||||
LOG_FATAL("Timeout overflow");
|
||||
}
|
||||
|
||||
return SCH_AddTimeout(&then, handler, arg);
|
||||
@@ -512,7 +515,7 @@ dispatch_timeouts(struct timespec *now) {
|
||||
more time than was delay of a scheduled timeout. */
|
||||
if (n_done > n_timer_queue_entries * 4 &&
|
||||
n_done > n_entries_on_start * 4) {
|
||||
LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling");
|
||||
LOG_FATAL("Possible infinite loop in scheduling");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,7 +534,8 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
|
||||
if (except_fds && FD_ISSET(fd, except_fds)) {
|
||||
/* This descriptor has an exception, dispatch its handler */
|
||||
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
|
||||
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
|
||||
if (ptr->handler)
|
||||
(ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
|
||||
nfd--;
|
||||
|
||||
/* Don't try to read from it now */
|
||||
@@ -544,14 +548,16 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
|
||||
if (read_fds && FD_ISSET(fd, read_fds)) {
|
||||
/* This descriptor can be read from, dispatch its handler */
|
||||
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
|
||||
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
|
||||
if (ptr->handler)
|
||||
(ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
|
||||
nfd--;
|
||||
}
|
||||
|
||||
if (write_fds && FD_ISSET(fd, write_fds)) {
|
||||
/* This descriptor can be written to, dispatch its handler */
|
||||
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
|
||||
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
|
||||
if (ptr->handler)
|
||||
(ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
|
||||
nfd--;
|
||||
}
|
||||
}
|
||||
@@ -680,10 +686,10 @@ check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
|
||||
|
||||
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!");
|
||||
LOG(LOGS_WARN, "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!");
|
||||
LOG(LOGS_WARN, "Forward time jump detected!");
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
@@ -742,7 +748,7 @@ SCH_MainLoop(void)
|
||||
/* if there are no file descriptors being waited on and no
|
||||
timeout set, this is clearly ridiculous, so stop the run */
|
||||
if (!ptv && !p_read_fds && !p_write_fds)
|
||||
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
|
||||
LOG_FATAL("Nothing to do");
|
||||
|
||||
status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
|
||||
errsv = errno;
|
||||
@@ -762,7 +768,7 @@ SCH_MainLoop(void)
|
||||
|
||||
if (status < 0) {
|
||||
if (!need_to_exit && errsv != EINTR) {
|
||||
LOG_FATAL(LOGF_Scheduler, "select() failed : %s", strerror(errsv));
|
||||
LOG_FATAL("select() failed : %s", strerror(errsv));
|
||||
}
|
||||
} else if (status > 0) {
|
||||
/* A file descriptor is ready for input or output */
|
||||
|
||||
5
sched.h
5
sched.h
@@ -34,7 +34,8 @@ typedef unsigned int SCH_TimeoutID;
|
||||
|
||||
typedef enum {
|
||||
SCH_ReservedTimeoutValue = 0,
|
||||
SCH_NtpSamplingClass,
|
||||
SCH_NtpClientClass,
|
||||
SCH_NtpPeerClass,
|
||||
SCH_NtpBroadcastClass,
|
||||
SCH_NumberOfClasses /* needs to be last */
|
||||
} SCH_TimeoutClass;
|
||||
@@ -59,7 +60,7 @@ extern void SCH_Finalise(void);
|
||||
/* Register a handler for when select goes true on a file descriptor */
|
||||
extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
|
||||
extern void SCH_RemoveFileHandler(int fd);
|
||||
extern void SCH_SetFileHandlerEvents(int fd, int events);
|
||||
extern void SCH_SetFileHandlerEvent(int fd, int event, int enable);
|
||||
|
||||
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
|
||||
extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
|
||||
|
||||
13
smooth.c
13
smooth.c
@@ -144,7 +144,7 @@ update_stages(void)
|
||||
is equal to the offset that should be smoothed out */
|
||||
|
||||
s1 = smooth_offset / max_wander;
|
||||
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
|
||||
s2 = SQUARE(smooth_freq) / (2.0 * SQUARE(max_wander));
|
||||
|
||||
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
|
||||
frequency limit. The direction of the 1st stage is selected so that
|
||||
@@ -208,7 +208,7 @@ update_stages(void)
|
||||
stages[2].length = l3;
|
||||
|
||||
for (i = 0; i < NUM_STAGES; i++) {
|
||||
DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f",
|
||||
DEBUG_LOG("Smooth stage %d wander %e length %f",
|
||||
i + 1, stages[i].wander, stages[i].length);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ update_smoothing(struct timespec *now, double offset, double freq)
|
||||
|
||||
update_stages();
|
||||
|
||||
DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq);
|
||||
DEBUG_LOG("Smooth offset %e freq %e", smooth_offset, smooth_freq);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -246,7 +246,8 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
update_smoothing(cooked, doffset, dfreq);
|
||||
}
|
||||
|
||||
UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
|
||||
if (!UTI_IsZeroTimespec(&last_update))
|
||||
UTI_AdjustTimespec(&last_update, cooked, &last_update, &delta, dfreq, doffset);
|
||||
}
|
||||
|
||||
void SMT_Initialise(void)
|
||||
@@ -264,6 +265,8 @@ void SMT_Initialise(void)
|
||||
max_freq *= 1e-6;
|
||||
max_wander *= 1e-6;
|
||||
|
||||
UTI_ZeroTimespec(&last_update);
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
||||
}
|
||||
|
||||
@@ -295,7 +298,7 @@ SMT_Activate(struct timespec *now)
|
||||
if (!enabled || !locked)
|
||||
return;
|
||||
|
||||
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ?
|
||||
LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
|
||||
" (leap seconds only)" : "");
|
||||
locked = 0;
|
||||
last_update = *now;
|
||||
|
||||
147
sources.c
147
sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018
|
||||
*
|
||||
* This program 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,6 +54,7 @@ static int initialised = 0;
|
||||
/* ================================================== */
|
||||
/* Structure used to hold info for selecting between sources */
|
||||
struct SelectInfo {
|
||||
NTP_Leap leap;
|
||||
int stratum;
|
||||
int select_ok;
|
||||
double std_dev;
|
||||
@@ -91,7 +92,6 @@ typedef enum {
|
||||
source */
|
||||
struct SRC_Instance_Record {
|
||||
SST_Stats stats;
|
||||
NTP_Leap leap_status; /* Leap status */
|
||||
int index; /* Index back into the array of source */
|
||||
uint32_t ref_id; /* The reference ID of this source
|
||||
(i.e. from its IP address, NOT the
|
||||
@@ -213,7 +213,9 @@ void SRC_Finalise(void)
|
||||
/* Function to create a new instance. This would be called by one of
|
||||
the individual source-type instance creation routines. */
|
||||
|
||||
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples)
|
||||
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
|
||||
IPAddr *addr, int min_samples, int max_samples,
|
||||
double min_delay, double asymmetry)
|
||||
{
|
||||
SRC_Instance result;
|
||||
|
||||
@@ -225,7 +227,8 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_optio
|
||||
max_samples = CNF_GetMaxSamples();
|
||||
|
||||
result = MallocNew(struct SRC_Instance_Record);
|
||||
result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples);
|
||||
result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples,
|
||||
min_delay, asymmetry);
|
||||
|
||||
if (n_sources == max_n_sources) {
|
||||
/* Reallocate memory */
|
||||
@@ -288,7 +291,6 @@ void SRC_DestroyInstance(SRC_Instance instance)
|
||||
void
|
||||
SRC_ResetInstance(SRC_Instance instance)
|
||||
{
|
||||
instance->leap_status = LEAP_Normal;
|
||||
instance->active = 0;
|
||||
instance->updates = 0;
|
||||
instance->reachability = 0;
|
||||
@@ -327,39 +329,24 @@ SRC_GetSourcestats(SRC_Instance instance)
|
||||
This function causes the frequency estimation to be re-run for the
|
||||
designated source, and the clock selection procedure to be re-run
|
||||
afterwards.
|
||||
|
||||
Parameters are described in sources.h
|
||||
|
||||
*/
|
||||
|
||||
void SRC_AccumulateSample
|
||||
(SRC_Instance inst,
|
||||
struct timespec *sample_time,
|
||||
double offset,
|
||||
double peer_delay,
|
||||
double peer_dispersion,
|
||||
double root_delay,
|
||||
double root_dispersion,
|
||||
int stratum,
|
||||
NTP_Leap leap_status)
|
||||
void
|
||||
SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
|
||||
{
|
||||
|
||||
assert(initialised);
|
||||
|
||||
inst->leap_status = leap_status;
|
||||
|
||||
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
|
||||
source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
|
||||
root_delay, root_dispersion, stratum);
|
||||
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
|
||||
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
|
||||
sample->root_delay, sample->root_dispersion, sample->stratum);
|
||||
|
||||
if (REF_IsLeapSecondClose()) {
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
|
||||
LOG(LOGS_INFO, "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_AccumulateSample(inst->stats, sample);
|
||||
SST_DoNewRegression(inst->stats);
|
||||
}
|
||||
|
||||
@@ -425,10 +412,11 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
||||
}
|
||||
|
||||
/* Try to replace NTP sources that are unreachable, falsetickers, or
|
||||
have root distance larger than the allowed maximum */
|
||||
have root distance or jitter larger than the allowed maximums */
|
||||
if (inst->type == SRC_NTP &&
|
||||
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
|
||||
inst->status == SRC_FALSETICKER || inst->status == SRC_BAD_DISTANCE)) {
|
||||
inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
|
||||
inst->status == SRC_FALSETICKER)) {
|
||||
NSR_HandleBadSource(inst->ip_addr);
|
||||
}
|
||||
}
|
||||
@@ -450,7 +438,7 @@ log_selection_message(char *format, char *arg)
|
||||
{
|
||||
if (REF_GetMode() != REF_ModeNormal)
|
||||
return;
|
||||
LOG(LOGS_INFO, LOGF_Sources, format, arg);
|
||||
LOG(LOGS_INFO, format, arg);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -508,20 +496,21 @@ mark_ok_sources(SRC_Status status)
|
||||
|
||||
static int
|
||||
combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
||||
double *offset_sd, double *frequency, double *skew)
|
||||
double *offset_sd, double *frequency, double *frequency_sd, double *skew)
|
||||
{
|
||||
struct timespec src_ref_time;
|
||||
double src_offset, src_offset_sd, src_frequency, src_skew;
|
||||
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
|
||||
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
|
||||
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
|
||||
double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
|
||||
double frequency_weight, sum_frequency_weight, sum_frequency;
|
||||
double inv_sum2_frequency_sd, inv_sum2_skew;
|
||||
int i, index, combined;
|
||||
|
||||
if (n_sel_sources == 1)
|
||||
return 1;
|
||||
|
||||
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
|
||||
sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
|
||||
sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0;
|
||||
|
||||
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
|
||||
if (sources[selected_source_index]->type == SRC_NTP)
|
||||
@@ -531,7 +520,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
||||
index = sel_sources[i];
|
||||
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
|
||||
&src_offset, &src_offset_sd,
|
||||
&src_frequency, &src_skew,
|
||||
&src_frequency, &src_frequency_sd, &src_skew,
|
||||
&src_root_delay, &src_root_dispersion);
|
||||
|
||||
/* Don't include this source if its distance is longer than the distance of
|
||||
@@ -559,20 +548,23 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
||||
|
||||
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
|
||||
src_offset += elapsed * src_frequency;
|
||||
src_offset_sd += elapsed * src_frequency_sd;
|
||||
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
|
||||
frequency_weight = 1.0 / src_skew;
|
||||
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
|
||||
|
||||
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);
|
||||
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
|
||||
index, offset_weight, src_offset, src_offset_sd,
|
||||
frequency_weight, src_frequency, src_frequency_sd, src_skew);
|
||||
|
||||
sum_offset_weight += offset_weight;
|
||||
sum_offset += offset_weight * src_offset;
|
||||
sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd +
|
||||
(src_offset - *offset) * (src_offset - *offset));
|
||||
sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) +
|
||||
SQUARE(src_offset - *offset));
|
||||
|
||||
sum_frequency_weight += frequency_weight;
|
||||
sum_frequency += frequency_weight * src_frequency;
|
||||
inv_sum2_skew += 1.0 / (src_skew * src_skew);
|
||||
inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd);
|
||||
inv_sum2_skew += 1.0 / SQUARE(src_skew);
|
||||
|
||||
combined++;
|
||||
}
|
||||
@@ -581,10 +573,11 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
||||
*offset = sum_offset / sum_offset_weight;
|
||||
*offset_sd = sqrt(sum2_offset_sd / sum_offset_weight);
|
||||
*frequency = sum_frequency / sum_frequency_weight;
|
||||
*frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd);
|
||||
*skew = 1.0 / sqrt(inv_sum2_skew);
|
||||
|
||||
DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
|
||||
*offset, *offset_sd, *frequency, *skew);
|
||||
DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e",
|
||||
*offset, *offset_sd, *frequency, *frequency_sd, *skew);
|
||||
|
||||
return combined;
|
||||
}
|
||||
@@ -598,12 +591,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
{
|
||||
struct SelectInfo *si;
|
||||
struct timespec now, ref_time;
|
||||
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
|
||||
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
|
||||
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
|
||||
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
|
||||
int depth, best_depth, trust_depth, best_trust_depth;
|
||||
int combined, stratum, min_stratum, max_score_index;
|
||||
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
|
||||
double src_offset, src_offset_sd, src_frequency, src_skew;
|
||||
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
|
||||
double src_root_delay, src_root_dispersion;
|
||||
double best_lo, best_hi, distance, sel_src_distance, max_score;
|
||||
double first_sample_ago, max_reach_sample_ago;
|
||||
@@ -631,6 +624,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
n_badstats_sources = 0;
|
||||
sel_req_source = 0;
|
||||
max_sel_reach = max_badstat_reach = 0;
|
||||
max_sel_reach_size = 0;
|
||||
max_reach_sample_ago = 0.0;
|
||||
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
@@ -648,7 +642,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
}
|
||||
|
||||
si = &sources[i]->sel_info;
|
||||
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
|
||||
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap,
|
||||
&si->lo_limit, &si->hi_limit, &si->root_distance,
|
||||
&si->std_dev, &first_sample_ago,
|
||||
&si->last_sample_ago, &si->select_ok);
|
||||
@@ -661,6 +655,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Include extra dispersion in the root distance of sources that don't
|
||||
have new samples (the last sample is older than span of all samples) */
|
||||
if (first_sample_ago < 2.0 * si->last_sample_ago) {
|
||||
double extra_disp = LCL_GetMaxClockError() *
|
||||
(2.0 * si->last_sample_ago - first_sample_ago);
|
||||
si->root_distance += extra_disp;
|
||||
si->lo_limit -= extra_disp;
|
||||
si->hi_limit += extra_disp;
|
||||
}
|
||||
|
||||
/* Require the root distance to be below the allowed maximum */
|
||||
if (si->root_distance > max_distance) {
|
||||
sources[i]->status = SRC_BAD_DISTANCE;
|
||||
@@ -680,6 +684,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
|
||||
if (max_sel_reach < sources[i]->reachability)
|
||||
max_sel_reach = sources[i]->reachability;
|
||||
|
||||
if (max_sel_reach_size < sources[i]->reachability_size)
|
||||
max_sel_reach_size = sources[i]->reachability_size;
|
||||
}
|
||||
|
||||
orphan_stratum = REF_GetOrphanStratum();
|
||||
@@ -735,12 +742,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr);
|
||||
|
||||
if (!local_ref_id) {
|
||||
LOG(LOGS_ERR, LOGF_Sources, "Unknown local refid in orphan mode");
|
||||
LOG(LOGS_ERR, "Unknown local refid in orphan mode");
|
||||
} else if (sources[orphan_source]->ref_id < local_ref_id) {
|
||||
sources[orphan_source]->status = SRC_OK;
|
||||
n_sel_sources = 1;
|
||||
DEBUG_LOG(LOGF_Sources, "selecting orphan refid=%"PRIx32,
|
||||
sources[orphan_source]->ref_id);
|
||||
DEBUG_LOG("selecting orphan refid=%"PRIx32, sources[orphan_source]->ref_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,18 +770,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
n_endpoints += 2;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Sources, "badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
|
||||
n_badstats_sources, n_sel_sources, max_badstat_reach,
|
||||
max_sel_reach, max_reach_sample_ago);
|
||||
DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f",
|
||||
n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
|
||||
(unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
|
||||
|
||||
/* Wait for the next call if we have no source selected and there is
|
||||
a source with bad stats (has less than 3 samples) with reachability
|
||||
equal to shifted maximum reachability of sources with valid stats.
|
||||
This delays selecting source on start with servers using the same
|
||||
polling interval until they all have valid stats. */
|
||||
if (n_badstats_sources && n_sel_sources &&
|
||||
selected_source_index == INVALID_SOURCE &&
|
||||
max_sel_reach >> 1 == max_badstat_reach) {
|
||||
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
|
||||
max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
|
||||
mark_ok_sources(SRC_WAITS_STATS);
|
||||
return;
|
||||
}
|
||||
@@ -916,9 +921,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
|
||||
continue;
|
||||
leap_votes++;
|
||||
if (sources[index]->leap_status == LEAP_InsertSecond)
|
||||
if (sources[index]->sel_info.leap == LEAP_InsertSecond)
|
||||
leap_ins++;
|
||||
else if (sources[index]->leap_status == LEAP_DeleteSecond)
|
||||
else if (sources[index]->sel_info.leap == LEAP_DeleteSecond)
|
||||
leap_del++;
|
||||
}
|
||||
|
||||
@@ -1002,7 +1007,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
sources[i]->sel_score = 1.0 / distance;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_Sources, "select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
|
||||
DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f",
|
||||
sources[i]->sel_score, sources[i]->ref_id,
|
||||
updated_inst ? updated_inst->ref_id : 0,
|
||||
sources[i]->status, distance);
|
||||
@@ -1026,7 +1031,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
if (sources[max_score_index]->updates == 0) {
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
mark_ok_sources(SRC_WAITS_UPDATE);
|
||||
DEBUG_LOG(LOGF_Sources, "best source has no updates");
|
||||
DEBUG_LOG("best source has no updates");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1065,18 +1070,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
|
||||
SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time,
|
||||
&src_offset, &src_offset_sd,
|
||||
&src_frequency, &src_skew,
|
||||
&src_frequency, &src_frequency_sd, &src_skew,
|
||||
&src_root_delay, &src_root_dispersion);
|
||||
|
||||
combined = combine_sources(n_sel_sources, &ref_time, &src_offset,
|
||||
&src_offset_sd, &src_frequency, &src_skew);
|
||||
combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
|
||||
&src_frequency, &src_frequency_sd, &src_skew);
|
||||
|
||||
REF_SetReference(sources[selected_source_index]->sel_info.stratum,
|
||||
leap_status, combined,
|
||||
sources[selected_source_index]->ref_id,
|
||||
sources[selected_source_index]->ip_addr,
|
||||
&ref_time, src_offset, src_offset_sd,
|
||||
src_frequency, src_skew,
|
||||
src_frequency, src_frequency_sd, src_skew,
|
||||
src_root_delay, src_root_dispersion);
|
||||
}
|
||||
|
||||
@@ -1097,7 +1102,7 @@ SRC_SetReselectDistance(double distance)
|
||||
{
|
||||
if (reselect_distance != distance) {
|
||||
reselect_distance = distance;
|
||||
LOG(LOGS_INFO, LOGF_Sources, "New reselect distance %f", distance);
|
||||
LOG(LOGS_INFO, "New reselect distance %f", distance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1152,7 +1157,7 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
|
||||
|
||||
dumpdir = CNF_GetDumpDir();
|
||||
if (dumpdir[0] == '\0') {
|
||||
LOG(LOGS_WARN, LOGF_Sources, "dumpdir not specified");
|
||||
LOG(LOGS_WARN, "dumpdir not specified");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1163,13 +1168,13 @@ FILE *open_dumpfile(SRC_Instance inst, const char *mode)
|
||||
(inst->type != SRC_NTP &&
|
||||
snprintf(filename, sizeof (filename), "%s/refid:%08"PRIx32".dat",
|
||||
dumpdir, inst->ref_id) >= sizeof (filename))) {
|
||||
LOG(LOGS_WARN, LOGF_Sources, "dumpdir too long");
|
||||
LOG(LOGS_WARN, "dumpdir too long");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = fopen(filename, mode);
|
||||
if (!f && mode[0] != 'r')
|
||||
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file for %s",
|
||||
LOG(LOGS_WARN, "Could not open dump file for %s",
|
||||
source_to_string(inst));
|
||||
|
||||
return f;
|
||||
@@ -1206,10 +1211,10 @@ SRC_ReloadSources(void)
|
||||
if (!in)
|
||||
continue;
|
||||
if (!SST_LoadFromFile(sources[i]->stats, in))
|
||||
LOG(LOGS_WARN, LOGF_Sources, "Could not load dump file for %s",
|
||||
LOG(LOGS_WARN, "Could not load dump file for %s",
|
||||
source_to_string(sources[i]));
|
||||
else
|
||||
LOG(LOGS_INFO, LOGF_Sources, "Loaded dump file for %s",
|
||||
LOG(LOGS_INFO, "Loaded dump file for %s",
|
||||
source_to_string(sources[i]));
|
||||
fclose(in);
|
||||
}
|
||||
@@ -1247,7 +1252,7 @@ SRC_RemoveDumpFiles(void)
|
||||
if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr))
|
||||
continue;
|
||||
|
||||
DEBUG_LOG(LOGF_Sources, "Removing %s", gl.gl_pathv[i]);
|
||||
DEBUG_LOG("Removing %s", gl.gl_pathv[i]);
|
||||
unlink(gl.gl_pathv[i]);
|
||||
}
|
||||
|
||||
|
||||
34
sources.h
34
sources.h
@@ -59,7 +59,9 @@ typedef enum {
|
||||
/* Function to create a new instance. This would be called by one of
|
||||
the individual source-type instance creation routines. */
|
||||
|
||||
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples);
|
||||
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options,
|
||||
IPAddr *addr, int min_samples, int max_samples,
|
||||
double min_delay, double asymmetry);
|
||||
|
||||
/* Function to get rid of a source when it is being unconfigured.
|
||||
This may cause the current reference source to be reselected, if this
|
||||
@@ -78,34 +80,8 @@ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
|
||||
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
|
||||
|
||||
/* This function is called by one of the source drivers when it has
|
||||
a new sample that is to be accumulated.
|
||||
|
||||
This function causes the frequency estimation to be re-run for the
|
||||
designated source, and the clock selection procedure to be re-run
|
||||
afterwards.
|
||||
|
||||
sample_time is the local time at which the sample is to be
|
||||
considered to have been made, in terms of doing a regression fit of
|
||||
offset against local time.
|
||||
|
||||
offset is the offset at the time, in seconds. Positive indicates
|
||||
that the local clock is SLOW relative to the source, negative
|
||||
indicates that the local clock is FAST relative to it.
|
||||
|
||||
root_delay and root_dispersion are in seconds, and are as per
|
||||
RFC 5905. root_dispersion only includes the peer's root dispersion
|
||||
+ local sampling precision + skew dispersion accrued during the
|
||||
measurement. It is the job of the source statistics algorithms +
|
||||
track.c to add on the extra dispersion due to the residual standard
|
||||
deviation of the offsets from this source after regression, to form
|
||||
the root_dispersion field in the packets transmitted to clients or
|
||||
peers.
|
||||
|
||||
stratum is the stratum of the source that supplied the sample.
|
||||
|
||||
*/
|
||||
|
||||
extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
|
||||
a new sample that is to be accumulated */
|
||||
extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);
|
||||
|
||||
/* This routine sets the source as receiving reachability updates */
|
||||
extern void SRC_SetActive(SRC_Instance inst);
|
||||
|
||||
239
sourcestats.c
239
sourcestats.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014
|
||||
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -51,8 +51,8 @@
|
||||
#define MIN_SKEW 1.0e-12
|
||||
#define MAX_SKEW 1.0e+02
|
||||
|
||||
/* The minimum assumed std dev for weighting */
|
||||
#define MIN_WEIGHT_SD 1.0e-9
|
||||
/* The minimum standard deviation */
|
||||
#define MIN_STDDEV 1.0e-9
|
||||
|
||||
/* The asymmetry of network jitter when all jitter is in one direction */
|
||||
#define MAX_ASYMMETRY 0.5
|
||||
@@ -85,6 +85,12 @@ struct SST_Stats_Record {
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
|
||||
/* User defined minimum delay */
|
||||
double fixed_min_delay;
|
||||
|
||||
/* User defined asymmetry of network jitter */
|
||||
double fixed_asymmetry;
|
||||
|
||||
/* Number of samples currently stored. The samples are stored in circular
|
||||
buffer. */
|
||||
int n_samples;
|
||||
@@ -126,6 +132,7 @@ struct SST_Stats_Record {
|
||||
source per unit local time. (Positive => local clock fast,
|
||||
negative => local clock slow) */
|
||||
double estimated_frequency;
|
||||
double estimated_frequency_sd;
|
||||
|
||||
/* This is the assumed worst case bounds on the estimated frequency.
|
||||
We assume that the true frequency lies within +/- half this much
|
||||
@@ -168,10 +175,11 @@ struct SST_Stats_Record {
|
||||
time of the measurements */
|
||||
double root_dispersions[MAX_SAMPLES];
|
||||
|
||||
/* This array contains the strata that were associated with the sources
|
||||
at the times the samples were generated */
|
||||
int strata[MAX_SAMPLES];
|
||||
/* The stratum from the last accumulated sample */
|
||||
int stratum;
|
||||
|
||||
/* The leap status from the last accumulated sample */
|
||||
NTP_Leap leap;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
@@ -200,13 +208,16 @@ SST_Finalise(void)
|
||||
/* This function creates a new instance of the statistics handler */
|
||||
|
||||
SST_Stats
|
||||
SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples)
|
||||
SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples,
|
||||
double min_delay, double asymmetry)
|
||||
{
|
||||
SST_Stats inst;
|
||||
inst = MallocNew(struct SST_Stats_Record);
|
||||
|
||||
inst->min_samples = min_samples;
|
||||
inst->max_samples = max_samples;
|
||||
inst->fixed_min_delay = min_delay;
|
||||
inst->fixed_asymmetry = asymmetry;
|
||||
|
||||
SST_SetRefid(inst, refid, addr);
|
||||
SST_ResetInstance(inst);
|
||||
@@ -235,7 +246,8 @@ SST_ResetInstance(SST_Stats inst)
|
||||
inst->best_single_sample = 0;
|
||||
inst->min_delay_sample = 0;
|
||||
inst->estimated_frequency = 0;
|
||||
inst->skew = 2000.0e-6;
|
||||
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
|
||||
inst->skew = WORST_CASE_FREQ_BOUND;
|
||||
inst->estimated_offset = 0.0;
|
||||
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
|
||||
UTI_ZeroTimespec(&inst->offset_time);
|
||||
@@ -243,6 +255,7 @@ SST_ResetInstance(SST_Stats inst)
|
||||
inst->nruns = 0;
|
||||
inst->asymmetry_run = 0;
|
||||
inst->asymmetry = 0.0;
|
||||
inst->leap = LEAP_Unsynchronised;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -278,11 +291,7 @@ prune_register(SST_Stats inst, int new_oldest)
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
|
||||
double offset,
|
||||
double peer_delay, double peer_dispersion,
|
||||
double root_delay, double root_dispersion,
|
||||
int stratum)
|
||||
SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
|
||||
{
|
||||
int n, m;
|
||||
|
||||
@@ -294,8 +303,8 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
|
||||
|
||||
/* Make sure it's newer than the last sample */
|
||||
if (inst->n_samples &&
|
||||
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
|
||||
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
|
||||
UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], &sample->time) >= 0) {
|
||||
LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
|
||||
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
|
||||
SST_ResetInstance(inst);
|
||||
}
|
||||
@@ -304,15 +313,21 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
|
||||
(MAX_SAMPLES * REGRESS_RUNS_RATIO);
|
||||
m = n % MAX_SAMPLES;
|
||||
|
||||
inst->sample_times[n] = *sample_time;
|
||||
inst->offsets[n] = offset;
|
||||
inst->orig_offsets[m] = offset;
|
||||
inst->peer_delays[n] = peer_delay;
|
||||
inst->peer_dispersions[m] = peer_dispersion;
|
||||
inst->root_delays[m] = root_delay;
|
||||
inst->root_dispersions[m] = root_dispersion;
|
||||
inst->strata[m] = stratum;
|
||||
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
|
||||
IS FLIPPED */
|
||||
inst->sample_times[n] = sample->time;
|
||||
inst->offsets[n] = -sample->offset;
|
||||
inst->orig_offsets[m] = -sample->offset;
|
||||
inst->peer_delays[n] = sample->peer_delay;
|
||||
inst->peer_dispersions[m] = sample->peer_dispersion;
|
||||
inst->root_delays[m] = sample->root_delay;
|
||||
inst->root_dispersions[m] = sample->root_dispersion;
|
||||
inst->stratum = sample->stratum;
|
||||
inst->leap = sample->leap;
|
||||
|
||||
if (inst->peer_delays[n] < inst->fixed_min_delay)
|
||||
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
|
||||
|
||||
if (!inst->n_samples || inst->peer_delays[n] < inst->peer_delays[inst->min_delay_sample])
|
||||
inst->min_delay_sample = n;
|
||||
|
||||
@@ -418,45 +433,63 @@ find_min_delay_sample(SST_Stats inst)
|
||||
minimum network delay. This can significantly improve the accuracy and
|
||||
stability of the estimated offset and frequency. */
|
||||
|
||||
static int
|
||||
estimate_asymmetry(double *times_back, double *offsets, double *delays, int n,
|
||||
double *asymmetry, int *asymmetry_run)
|
||||
{
|
||||
double a;
|
||||
|
||||
/* Reset the counter when the regression fails or the sign changes */
|
||||
if (!RGR_MultipleRegress(times_back, delays, offsets, n, &a) ||
|
||||
a * *asymmetry_run < 0.0) {
|
||||
*asymmetry = 0;
|
||||
*asymmetry_run = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a <= -MIN_ASYMMETRY && *asymmetry_run > -MAX_ASYMMETRY_RUN)
|
||||
(*asymmetry_run)--;
|
||||
else if (a >= MIN_ASYMMETRY && *asymmetry_run < MAX_ASYMMETRY_RUN)
|
||||
(*asymmetry_run)++;
|
||||
|
||||
if (abs(*asymmetry_run) < MIN_ASYMMETRY_RUN)
|
||||
return 0;
|
||||
|
||||
*asymmetry = CLAMP(-MAX_ASYMMETRY, a, MAX_ASYMMETRY);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
|
||||
{
|
||||
double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
|
||||
double min_delay, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
|
||||
int i, n;
|
||||
|
||||
/* Don't try to estimate the asymmetry with reference clocks */
|
||||
if (!inst->ip_addr)
|
||||
/* Check if the asymmetry was not specified to be zero */
|
||||
if (inst->fixed_asymmetry == 0.0)
|
||||
return;
|
||||
|
||||
min_delay = SST_MinRoundTripDelay(inst);
|
||||
n = inst->runs_samples + inst->n_samples;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
delays[i] = inst->peer_delays[get_runsbuf_index(inst, i - inst->runs_samples)] -
|
||||
inst->peer_delays[inst->min_delay_sample];
|
||||
min_delay;
|
||||
|
||||
/* Reset the counter when the regression fails or the sign changes */
|
||||
if (!RGR_MultipleRegress(times_back, delays, offsets, n, &asymmetry) ||
|
||||
asymmetry * inst->asymmetry_run < 0.0) {
|
||||
inst->asymmetry_run = 0;
|
||||
inst->asymmetry = 0.0;
|
||||
return;
|
||||
if (fabs(inst->fixed_asymmetry) <= MAX_ASYMMETRY) {
|
||||
inst->asymmetry = inst->fixed_asymmetry;
|
||||
} else {
|
||||
if (!estimate_asymmetry(times_back, offsets, delays, n,
|
||||
&inst->asymmetry, &inst->asymmetry_run))
|
||||
return;
|
||||
}
|
||||
|
||||
asymmetry = CLAMP(-MAX_ASYMMETRY, asymmetry, MAX_ASYMMETRY);
|
||||
|
||||
if (asymmetry <= -MIN_ASYMMETRY && inst->asymmetry_run > -MAX_ASYMMETRY_RUN)
|
||||
inst->asymmetry_run--;
|
||||
else if (asymmetry >= MIN_ASYMMETRY && inst->asymmetry_run < MAX_ASYMMETRY_RUN)
|
||||
inst->asymmetry_run++;
|
||||
|
||||
if (abs(inst->asymmetry_run) < MIN_ASYMMETRY_RUN)
|
||||
return;
|
||||
|
||||
/* Correct the offsets */
|
||||
for (i = 0; i < n; i++)
|
||||
offsets[i] -= asymmetry * delays[i];
|
||||
|
||||
inst->asymmetry = asymmetry;
|
||||
offsets[i] -= inst->asymmetry * delays[i];
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -466,7 +499,7 @@ correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
|
||||
time. E.g. a value of 4 means that we think the standard deviation
|
||||
is four times the fluctuation of the peer distance */
|
||||
|
||||
#define SD_TO_DIST_RATIO 1.0
|
||||
#define SD_TO_DIST_RATIO 0.7
|
||||
|
||||
/* ================================================== */
|
||||
/* This function runs the linear regression operation on the data. It
|
||||
@@ -486,9 +519,10 @@ SST_DoNewRegression(SST_Stats inst)
|
||||
int best_start, times_back_start;
|
||||
double est_intercept, est_slope, est_var, est_intercept_sd, est_slope_sd;
|
||||
int i, j, nruns;
|
||||
double min_distance, mean_distance;
|
||||
double min_distance, median_distance;
|
||||
double sd_weight, sd;
|
||||
double old_skew, old_freq, stress;
|
||||
double precision;
|
||||
|
||||
convert_to_intervals(inst, times_back + inst->runs_samples);
|
||||
|
||||
@@ -497,25 +531,29 @@ SST_DoNewRegression(SST_Stats inst)
|
||||
offsets[i + inst->runs_samples] = inst->offsets[get_runsbuf_index(inst, i)];
|
||||
}
|
||||
|
||||
for (i = 0, mean_distance = 0.0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
|
||||
for (i = 0, min_distance = DBL_MAX; i < inst->n_samples; i++) {
|
||||
j = get_buf_index(inst, i);
|
||||
peer_distances[i] = 0.5 * inst->peer_delays[get_runsbuf_index(inst, i)] +
|
||||
inst->peer_dispersions[j];
|
||||
mean_distance += peer_distances[i];
|
||||
if (peer_distances[i] < min_distance) {
|
||||
min_distance = peer_distances[i];
|
||||
}
|
||||
}
|
||||
mean_distance /= inst->n_samples;
|
||||
|
||||
/* And now, work out the weight vector */
|
||||
|
||||
sd = mean_distance - min_distance;
|
||||
sd = CLAMP(MIN_WEIGHT_SD, sd, min_distance);
|
||||
precision = LCL_GetSysPrecisionAsQuantum();
|
||||
median_distance = RGR_FindMedian(peer_distances, inst->n_samples);
|
||||
|
||||
sd = (median_distance - min_distance) / SD_TO_DIST_RATIO;
|
||||
sd = CLAMP(precision, sd, min_distance);
|
||||
min_distance += precision;
|
||||
|
||||
for (i=0; i<inst->n_samples; i++) {
|
||||
sd_weight = 1.0 + SD_TO_DIST_RATIO * (peer_distances[i] - min_distance) / sd;
|
||||
weights[i] = sd_weight * sd_weight;
|
||||
sd_weight = 1.0;
|
||||
if (peer_distances[i] > min_distance)
|
||||
sd_weight += (peer_distances[i] - min_distance) / sd;
|
||||
weights[i] = SQUARE(sd_weight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,17 +573,18 @@ SST_DoNewRegression(SST_Stats inst)
|
||||
old_freq = inst->estimated_frequency;
|
||||
|
||||
inst->estimated_frequency = est_slope;
|
||||
inst->estimated_frequency_sd = CLAMP(MIN_SKEW, est_slope_sd, MAX_SKEW);
|
||||
inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
|
||||
inst->estimated_offset = est_intercept;
|
||||
inst->offset_time = inst->sample_times[inst->last_sample];
|
||||
inst->estimated_offset_sd = est_intercept_sd;
|
||||
inst->std_dev = sqrt(est_var);
|
||||
inst->std_dev = MAX(MIN_STDDEV, sqrt(est_var));
|
||||
inst->nruns = nruns;
|
||||
|
||||
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
|
||||
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
|
||||
|
||||
DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
|
||||
DEBUG_LOG("off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
|
||||
inst->estimated_offset, inst->estimated_frequency, inst->skew,
|
||||
inst->n_samples, best_start, inst->nruns,
|
||||
inst->asymmetry, inst->asymmetry_run);
|
||||
@@ -565,6 +604,7 @@ SST_DoNewRegression(SST_Stats inst)
|
||||
prune_register(inst, best_start);
|
||||
} else {
|
||||
inst->estimated_frequency = 0.0;
|
||||
inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
|
||||
inst->skew = WORST_CASE_FREQ_BOUND;
|
||||
times_back_start = 0;
|
||||
}
|
||||
@@ -601,7 +641,7 @@ SST_GetFrequencyRange(SST_Stats inst,
|
||||
|
||||
void
|
||||
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
int *stratum,
|
||||
int *stratum, NTP_Leap *leap,
|
||||
double *offset_lo_limit,
|
||||
double *offset_hi_limit,
|
||||
double *root_distance,
|
||||
@@ -621,10 +661,11 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
i = get_runsbuf_index(inst, inst->best_single_sample);
|
||||
j = get_buf_index(inst, inst->best_single_sample);
|
||||
|
||||
*stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
|
||||
*stratum = inst->stratum;
|
||||
*leap = inst->leap;
|
||||
*std_dev = inst->std_dev;
|
||||
|
||||
sample_elapsed = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
|
||||
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
|
||||
offset = inst->offsets[i] + sample_elapsed * inst->estimated_frequency;
|
||||
*root_distance = 0.5 * inst->root_delays[j] +
|
||||
inst->root_dispersions[j] + sample_elapsed * inst->skew;
|
||||
@@ -653,7 +694,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
|
||||
*select_ok = inst->regression_ok;
|
||||
|
||||
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
|
||||
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
|
||||
inst->n_samples, offset, *root_distance, *std_dev,
|
||||
*first_sample_ago, *last_sample_ago, *select_ok);
|
||||
}
|
||||
@@ -663,7 +704,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
void
|
||||
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
|
||||
double *average_offset, double *offset_sd,
|
||||
double *frequency, double *skew,
|
||||
double *frequency, double *frequency_sd, double *skew,
|
||||
double *root_delay, double *root_dispersion)
|
||||
{
|
||||
int i, j;
|
||||
@@ -678,15 +719,16 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
|
||||
*average_offset = inst->estimated_offset;
|
||||
*offset_sd = inst->estimated_offset_sd;
|
||||
*frequency = inst->estimated_frequency;
|
||||
*frequency_sd = inst->estimated_frequency_sd;
|
||||
*skew = inst->skew;
|
||||
*root_delay = inst->root_delays[j];
|
||||
|
||||
elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
|
||||
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
|
||||
|
||||
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
|
||||
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion);
|
||||
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample + *offset_sd;
|
||||
|
||||
DEBUG_LOG("n=%d off=%f offsd=%f freq=%e freqsd=%e skew=%e delay=%f disp=%f",
|
||||
inst->n_samples, *average_offset, *offset_sd,
|
||||
*frequency, *frequency_sd, *skew, *root_delay, *root_dispersion);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -719,7 +761,7 @@ SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doff
|
||||
inst->estimated_offset += delta_time;
|
||||
inst->estimated_frequency = (inst->estimated_frequency - dfreq) / (1.0 - dfreq);
|
||||
|
||||
DEBUG_LOG(LOGF_SourceStats, "n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
|
||||
DEBUG_LOG("n=%d m=%d old_off_time=%s new=%s old_off=%f new_off=%f old_freq=%.3f new_freq=%.3f",
|
||||
inst->n_samples, inst->runs_samples,
|
||||
UTI_TimespecToString(&prev), UTI_TimespecToString(&inst->offset_time),
|
||||
prev_offset, inst->estimated_offset,
|
||||
@@ -768,47 +810,33 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
|
||||
double
|
||||
SST_MinRoundTripDelay(SST_Stats inst)
|
||||
{
|
||||
if (inst->fixed_min_delay > 0.0)
|
||||
return inst->fixed_min_delay;
|
||||
|
||||
if (!inst->n_samples)
|
||||
return DBL_MAX;
|
||||
|
||||
return inst->peer_delays[inst->min_delay_sample];
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SST_IsGoodSample(SST_Stats inst, double offset, double delay,
|
||||
double max_delay_dev_ratio, double clock_error, struct timespec *when)
|
||||
SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
|
||||
double *last_sample_ago, double *predicted_offset,
|
||||
double *min_delay, double *skew, double *std_dev)
|
||||
{
|
||||
double elapsed, allowed_increase, delay_increase;
|
||||
if (inst->n_samples < 6)
|
||||
return 0;
|
||||
|
||||
if (inst->n_samples < 3)
|
||||
return 1;
|
||||
*last_sample_ago = UTI_DiffTimespecsToDouble(sample_time, &inst->offset_time);
|
||||
*predicted_offset = inst->estimated_offset +
|
||||
*last_sample_ago * inst->estimated_frequency;
|
||||
*min_delay = SST_MinRoundTripDelay(inst);
|
||||
*skew = inst->skew;
|
||||
*std_dev = inst->std_dev;
|
||||
|
||||
elapsed = UTI_DiffTimespecsToDouble(when, &inst->offset_time);
|
||||
|
||||
/* Require that the ratio of the increase in delay from the minimum to the
|
||||
standard deviation is less than max_delay_dev_ratio. In the allowed
|
||||
increase in delay include also skew and clock_error. */
|
||||
|
||||
allowed_increase = inst->std_dev * max_delay_dev_ratio +
|
||||
elapsed * (inst->skew + clock_error);
|
||||
delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0;
|
||||
|
||||
if (delay_increase < allowed_increase)
|
||||
return 1;
|
||||
|
||||
offset -= inst->estimated_offset + elapsed * inst->estimated_frequency;
|
||||
|
||||
/* Before we decide to drop the sample, make sure the difference between
|
||||
measured offset and predicted offset is not significantly larger than
|
||||
the increase in delay */
|
||||
if (fabs(offset) - delay_increase > allowed_increase)
|
||||
return 1;
|
||||
|
||||
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
|
||||
offset, delay, allowed_increase, delay_increase);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -842,7 +870,7 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
|
||||
inst->root_delays[j],
|
||||
inst->root_dispersions[j],
|
||||
1.0, /* used to be inst->weights[i] */
|
||||
inst->strata[j]);
|
||||
inst->stratum /* used to be an array */);
|
||||
|
||||
}
|
||||
|
||||
@@ -865,7 +893,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
||||
char line[1024];
|
||||
double weight;
|
||||
|
||||
assert(!inst->n_samples);
|
||||
SST_ResetInstance(inst);
|
||||
|
||||
if (fgets(line, sizeof(line), in) &&
|
||||
sscanf(line, "%d", &inst->n_samples) == 1 &&
|
||||
@@ -887,7 +915,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
||||
&(inst->root_delays[i]),
|
||||
&(inst->root_dispersions[i]),
|
||||
&weight, /* not used anymore */
|
||||
&(inst->strata[i])) != 10)) {
|
||||
&inst->stratum) != 10)) {
|
||||
|
||||
/* This is the branch taken if the read FAILED */
|
||||
|
||||
@@ -914,7 +942,6 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
||||
return 1;
|
||||
|
||||
inst->last_sample = inst->n_samples - 1;
|
||||
inst->runs_samples = 0;
|
||||
|
||||
find_min_delay_sample(inst);
|
||||
SST_DoNewRegression(inst);
|
||||
@@ -928,7 +955,7 @@ void
|
||||
SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *now)
|
||||
{
|
||||
int i, j;
|
||||
struct timespec ago;
|
||||
struct timespec last_sample_time;
|
||||
|
||||
if (inst->n_samples > 0) {
|
||||
i = get_runsbuf_index(inst, inst->n_samples - 1);
|
||||
@@ -936,10 +963,12 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
|
||||
report->orig_latest_meas = inst->orig_offsets[j];
|
||||
report->latest_meas = inst->offsets[i];
|
||||
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
|
||||
report->stratum = inst->strata[j];
|
||||
report->stratum = inst->stratum;
|
||||
|
||||
UTI_DiffTimespecs(&ago, now, &inst->sample_times[i]);
|
||||
report->latest_meas_ago = ago.tv_sec;
|
||||
/* Align the sample time to reduce the leak of the receive timestamp */
|
||||
last_sample_time = inst->sample_times[i];
|
||||
last_sample_time.tv_nsec = 0;
|
||||
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
|
||||
} else {
|
||||
report->latest_meas_ago = (uint32_t)-1;
|
||||
report->orig_latest_meas = 0;
|
||||
|
||||
@@ -38,7 +38,9 @@ extern void SST_Initialise(void);
|
||||
extern void SST_Finalise(void);
|
||||
|
||||
/* This function creates a new instance of the statistics handler */
|
||||
extern SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_samples);
|
||||
extern SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr,
|
||||
int min_samples, int max_samples,
|
||||
double min_delay, double asymmetry);
|
||||
|
||||
/* This function deletes an instance of the statistics handler. */
|
||||
extern void SST_DeleteInstance(SST_Stats inst);
|
||||
@@ -49,19 +51,8 @@ extern void SST_ResetInstance(SST_Stats inst);
|
||||
/* This function changes the reference ID and IP address */
|
||||
extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
|
||||
|
||||
/* This function accumulates a single sample into the statistics handler
|
||||
|
||||
sample_time is the epoch at which the sample is to be considered to
|
||||
have been made.
|
||||
|
||||
offset is the offset of the local clock relative to the source in
|
||||
seconds. Positive indicates that the local clock if FAST (contrary
|
||||
to the NTP parts of the software)
|
||||
|
||||
stratum is the stratum of the source from which the sample came.
|
||||
*/
|
||||
|
||||
extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
|
||||
/* This function accumulates a single sample into the statistics handler */
|
||||
extern void SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample);
|
||||
|
||||
/* This function runs the linear regression operation on the data. It
|
||||
finds the set of most recent samples that give the tightest
|
||||
@@ -78,7 +69,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
|
||||
/* Get data needed for selection */
|
||||
extern void
|
||||
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
int *stratum,
|
||||
int *stratum, NTP_Leap *leap,
|
||||
double *offset_lo_limit,
|
||||
double *offset_hi_limit,
|
||||
double *root_distance,
|
||||
@@ -91,7 +82,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
extern void
|
||||
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
|
||||
double *average_offset, double *offset_sd,
|
||||
double *frequency, double *skew,
|
||||
double *frequency, double *frequency_sd, double *skew,
|
||||
double *root_delay, double *root_dispersion);
|
||||
|
||||
/* This routine is called when the local machine clock parameters are
|
||||
@@ -124,10 +115,10 @@ extern double SST_PredictOffset(SST_Stats inst, struct timespec *when);
|
||||
/* Find the minimum round trip delay in the register */
|
||||
extern double SST_MinRoundTripDelay(SST_Stats inst);
|
||||
|
||||
/* This routine determines if a new sample is good enough that it should be
|
||||
accumulated */
|
||||
extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay,
|
||||
double max_delay_dev_ratio, double clock_error, struct timespec *when);
|
||||
/* Get data needed for testing NTP delay */
|
||||
extern int SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
|
||||
double *last_sample_ago, double *predicted_offset,
|
||||
double *min_delay, double *skew, double *std_dev);
|
||||
|
||||
extern void SST_SaveToFile(SST_Stats inst, FILE *out);
|
||||
|
||||
|
||||
13
srcparams.h
13
srcparams.h
@@ -29,12 +29,19 @@
|
||||
|
||||
#include "sources.h"
|
||||
|
||||
typedef enum {
|
||||
SRC_OFFLINE,
|
||||
SRC_ONLINE,
|
||||
SRC_MAYBE_ONLINE,
|
||||
} SRC_Connectivity;
|
||||
|
||||
typedef struct {
|
||||
int minpoll;
|
||||
int maxpoll;
|
||||
int online;
|
||||
SRC_Connectivity connectivity;
|
||||
int auto_offline;
|
||||
int presend_minpoll;
|
||||
int burst;
|
||||
int iburst;
|
||||
int min_stratum;
|
||||
int poll_target;
|
||||
@@ -42,12 +49,15 @@ typedef struct {
|
||||
int max_sources;
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int filter_length;
|
||||
int interleaved;
|
||||
int sel_options;
|
||||
uint32_t authkey;
|
||||
double max_delay;
|
||||
double max_delay_ratio;
|
||||
double max_delay_dev_ratio;
|
||||
double min_delay;
|
||||
double asymmetry;
|
||||
double offset;
|
||||
} SourceParameters;
|
||||
|
||||
@@ -63,6 +73,7 @@ typedef struct {
|
||||
#define SRC_DEFAULT_MAXSOURCES 4
|
||||
#define SRC_DEFAULT_MINSAMPLES (-1)
|
||||
#define SRC_DEFAULT_MAXSAMPLES (-1)
|
||||
#define SRC_DEFAULT_ASYMMETRY 1.0
|
||||
#define INACTIVE_AUTHKEY 0
|
||||
|
||||
/* Flags for source selection */
|
||||
|
||||
12
stubs.c
12
stubs.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2014-2015
|
||||
* Copyright (C) Miroslav Lichvar 2014-2016
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -91,7 +91,7 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
|
||||
inst->arg = anything;
|
||||
|
||||
if (pipe(inst->pipe))
|
||||
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
|
||||
LOG_FATAL("pipe() failed");
|
||||
|
||||
UTI_FdSetCloexec(inst->pipe[0]);
|
||||
UTI_FdSetCloexec(inst->pipe[1]);
|
||||
@@ -254,13 +254,7 @@ NSR_GetLocalRefid(IPAddr *address)
|
||||
}
|
||||
|
||||
int
|
||||
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
|
||||
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
39
sys.c
39
sys.c
@@ -30,23 +30,36 @@
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "sys.h"
|
||||
#include "sys_null.h"
|
||||
#include "logging.h"
|
||||
|
||||
#if defined(LINUX)
|
||||
#include "sys_linux.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(SOLARIS)
|
||||
#include "sys_solaris.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(NETBSD) || defined(FREEBSD)
|
||||
#include "sys_netbsd.h"
|
||||
#include "sys_posix.h"
|
||||
#elif defined(MACOSX)
|
||||
#include "sys_macosx.h"
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int null_driver;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Initialise(void)
|
||||
SYS_Initialise(int clock_control)
|
||||
{
|
||||
null_driver = !clock_control;
|
||||
if (null_driver) {
|
||||
SYS_Null_Initialise();
|
||||
return;
|
||||
}
|
||||
#if defined(LINUX)
|
||||
SYS_Linux_Initialise();
|
||||
#elif defined(SOLARIS)
|
||||
@@ -65,6 +78,10 @@ SYS_Initialise(void)
|
||||
void
|
||||
SYS_Finalise(void)
|
||||
{
|
||||
if (null_driver) {
|
||||
SYS_Null_Finalise();
|
||||
return;
|
||||
}
|
||||
#if defined(LINUX)
|
||||
SYS_Linux_Finalise();
|
||||
#elif defined(SOLARIS)
|
||||
@@ -83,7 +100,7 @@ SYS_Finalise(void)
|
||||
void SYS_DropRoot(uid_t uid, gid_t gid)
|
||||
{
|
||||
#if defined(LINUX) && defined (FEAT_PRIVDROP)
|
||||
SYS_Linux_DropRoot(uid, gid);
|
||||
SYS_Linux_DropRoot(uid, gid, !null_driver);
|
||||
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
|
||||
SYS_Solaris_DropRoot(uid, gid);
|
||||
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
|
||||
@@ -91,7 +108,7 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
|
||||
#elif defined(MACOSX) && defined(FEAT_PRIVDROP)
|
||||
SYS_MacOSX_DropRoot(uid, gid);
|
||||
#else
|
||||
LOG_FATAL(LOGF_Sys, "dropping root privileges not supported");
|
||||
LOG_FATAL("dropping root privileges not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -102,7 +119,7 @@ void SYS_EnableSystemCallFilter(int level)
|
||||
#if defined(LINUX) && defined(FEAT_SCFILTER)
|
||||
SYS_Linux_EnableSystemCallFilter(level);
|
||||
#else
|
||||
LOG_FATAL(LOGF_Sys, "system call filter not supported");
|
||||
LOG_FATAL("system call filter not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -110,12 +127,12 @@ void SYS_EnableSystemCallFilter(int level)
|
||||
|
||||
void SYS_SetScheduler(int SchedPriority)
|
||||
{
|
||||
#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
|
||||
SYS_Linux_SetScheduler(SchedPriority);
|
||||
#elif defined(MACOSX)
|
||||
#if defined(MACOSX)
|
||||
SYS_MacOSX_SetScheduler(SchedPriority);
|
||||
#elif defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
SYS_Posix_SetScheduler(SchedPriority);
|
||||
#else
|
||||
LOG_FATAL(LOGF_Sys, "scheduler priority setting not supported");
|
||||
LOG_FATAL("scheduler priority setting not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -123,10 +140,10 @@ void SYS_SetScheduler(int SchedPriority)
|
||||
|
||||
void SYS_LockMemory(void)
|
||||
{
|
||||
#if defined(LINUX) && defined(HAVE_MLOCKALL)
|
||||
SYS_Linux_MemLockAll(1);
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
SYS_Posix_MemLockAll();
|
||||
#else
|
||||
LOG_FATAL(LOGF_Sys, "memory locking not supported");
|
||||
LOG_FATAL("memory locking not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
2
sys.h
2
sys.h
@@ -30,7 +30,7 @@
|
||||
#define GOT_SYS_H
|
||||
|
||||
/* Called at the start of the run to do initialisation */
|
||||
extern void SYS_Initialise(void);
|
||||
extern void SYS_Initialise(int clock_control);
|
||||
|
||||
/* Called at the end of the run to do final clean-up */
|
||||
extern void SYS_Finalise(void);
|
||||
|
||||
@@ -129,7 +129,7 @@ start_fastslew(void)
|
||||
|
||||
drv_accrue_offset(offset_register, 0.0);
|
||||
|
||||
DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register);
|
||||
DEBUG_LOG("fastslew offset=%e", offset_register);
|
||||
|
||||
offset_register = 0.0;
|
||||
fastslew_active = 1;
|
||||
@@ -246,7 +246,7 @@ update_slew(void)
|
||||
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
|
||||
slew_start = now;
|
||||
|
||||
DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
|
||||
DEBUG_LOG("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);
|
||||
}
|
||||
@@ -333,7 +333,7 @@ apply_step_offset(double offset)
|
||||
UTI_TimespecToTimeval(&new_time, &new_time_tv);
|
||||
|
||||
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
|
||||
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
|
||||
DEBUG_LOG("settimeofday() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
459
sys_linux.c
459
sys_linux.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -33,27 +33,14 @@
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#if defined(HAVE_SCHED_SETSCHEDULER)
|
||||
# include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
# include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PRIVDROP
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/capability.h>
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
#include <linux/ptp_clock.h>
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_SCFILTER
|
||||
#include <sys/prctl.h>
|
||||
#include <seccomp.h>
|
||||
#include <termios.h>
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
#include <linux/ptp_clock.h>
|
||||
#endif
|
||||
#ifdef FEAT_PPS
|
||||
#include <linux/pps.h>
|
||||
#endif
|
||||
@@ -65,9 +52,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PRIVDROP
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/capability.h>
|
||||
#endif
|
||||
|
||||
#include "sys_linux.h"
|
||||
#include "sys_timex.h"
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "privops.h"
|
||||
#include "util.h"
|
||||
@@ -238,7 +231,7 @@ guess_hz(void)
|
||||
}
|
||||
|
||||
/* oh dear. doomed. */
|
||||
LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %d", tick);
|
||||
LOG_FATAL("Can't determine hz from tick %d", tick);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -281,11 +274,11 @@ get_kernel_version(int *major, int *minor, int *patch)
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
LOG_FATAL(LOGF_SysLinux, "uname() failed");
|
||||
LOG_FATAL("uname() failed");
|
||||
|
||||
*patch = 0;
|
||||
if (sscanf(uts.release, "%d.%d.%d", major, minor, patch) < 2)
|
||||
LOG_FATAL(LOGF_SysLinux, "Could not parse kernel version");
|
||||
LOG_FATAL("Could not parse kernel version");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -307,22 +300,28 @@ get_version_specific_details(void)
|
||||
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
|
||||
max_tick_bias = nominal_tick / 10;
|
||||
|
||||
/* We can't reliably detect the internal kernel HZ, it may not even be fixed
|
||||
(CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
|
||||
tick_update_hz = 100;
|
||||
/* In modern kernels the frequency of the clock is updated immediately in the
|
||||
adjtimex() system call. Assume a maximum delay of 10 microseconds. */
|
||||
tick_update_hz = 100000;
|
||||
|
||||
get_kernel_version(&major, &minor, &patch);
|
||||
DEBUG_LOG(LOGF_SysLinux, "Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
|
||||
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
|
||||
|
||||
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
|
||||
LOG_FATAL(LOGF_SysLinux, "Kernel version not supported, sorry.");
|
||||
LOG_FATAL("Kernel version not supported, sorry.");
|
||||
}
|
||||
|
||||
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
|
||||
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
|
||||
/* Tickless kernels before 2.6.33 accumulated ticks only in
|
||||
half-second intervals */
|
||||
/* In tickless kernels before 2.6.33 the frequency is updated in
|
||||
a half-second interval */
|
||||
tick_update_hz = 2;
|
||||
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
|
||||
/* In kernels before 4.19 the frequency is updated only on internal ticks
|
||||
(CONFIG_HZ). As their rate cannot be reliably detected from the user
|
||||
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
|
||||
assume the lowest commonly used constant rate */
|
||||
tick_update_hz = 100;
|
||||
}
|
||||
|
||||
/* ADJ_SETOFFSET support */
|
||||
@@ -332,8 +331,8 @@ get_version_specific_details(void)
|
||||
have_setoffset = 1;
|
||||
}
|
||||
|
||||
DEBUG_LOG(LOGF_SysLinux, "hz=%d nominal_tick=%d max_tick_bias=%d",
|
||||
hz, nominal_tick, max_tick_bias);
|
||||
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
|
||||
hz, nominal_tick, max_tick_bias, tick_update_hz);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -378,6 +377,18 @@ test_step_offset(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
report_time_adjust_blockers(void)
|
||||
{
|
||||
#if defined(FEAT_PRIVDROP) && defined(CAP_IS_SUPPORTED)
|
||||
if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
|
||||
return;
|
||||
LOG(LOGS_WARN, "CAP_SYS_TIME not present");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Initialisation code for this module */
|
||||
|
||||
@@ -386,10 +397,12 @@ SYS_Linux_Initialise(void)
|
||||
{
|
||||
get_version_specific_details();
|
||||
|
||||
report_time_adjust_blockers();
|
||||
|
||||
reset_adjtime_offset();
|
||||
|
||||
if (have_setoffset && !test_step_offset()) {
|
||||
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
|
||||
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
|
||||
have_setoffset = 0;
|
||||
}
|
||||
|
||||
@@ -413,27 +426,30 @@ SYS_Linux_Finalise(void)
|
||||
|
||||
#ifdef FEAT_PRIVDROP
|
||||
void
|
||||
SYS_Linux_DropRoot(uid_t uid, gid_t gid)
|
||||
SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
|
||||
{
|
||||
const char *cap_text;
|
||||
char cap_text[256];
|
||||
cap_t cap;
|
||||
|
||||
if (prctl(PR_SET_KEEPCAPS, 1)) {
|
||||
LOG_FATAL(LOGF_SysLinux, "prctl() failed");
|
||||
LOG_FATAL("prctl() failed");
|
||||
}
|
||||
|
||||
UTI_DropRoot(uid, gid);
|
||||
|
||||
/* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */
|
||||
cap_text = CNF_GetNTPPort() ?
|
||||
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
|
||||
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
|
||||
and keep CAP_SYS_TIME only if the clock control is enabled */
|
||||
if (snprintf(cap_text, sizeof (cap_text), "%s %s",
|
||||
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
|
||||
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
|
||||
assert(0);
|
||||
|
||||
if ((cap = cap_from_text(cap_text)) == NULL) {
|
||||
LOG_FATAL(LOGF_SysLinux, "cap_from_text() failed");
|
||||
LOG_FATAL("cap_from_text() failed");
|
||||
}
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
LOG_FATAL(LOGF_SysLinux, "cap_set_proc() failed");
|
||||
LOG_FATAL("cap_set_proc() failed");
|
||||
}
|
||||
|
||||
cap_free(cap);
|
||||
@@ -452,7 +468,7 @@ void check_seccomp_applicability(void)
|
||||
|
||||
CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user);
|
||||
if (mail_enabled)
|
||||
LOG_FATAL(LOGF_SysLinux, "mailonchange directive cannot be used with -F enabled");
|
||||
LOG_FATAL("mailonchange directive cannot be used with -F enabled");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -465,29 +481,32 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
SCMP_SYS(adjtimex), SCMP_SYS(clock_gettime), SCMP_SYS(gettimeofday),
|
||||
SCMP_SYS(settimeofday), SCMP_SYS(time),
|
||||
/* Process */
|
||||
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit),
|
||||
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
|
||||
SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(wait4),
|
||||
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getpid),
|
||||
SCMP_SYS(getrlimit), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn),
|
||||
SCMP_SYS(rt_sigprocmask), SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn),
|
||||
SCMP_SYS(wait4), SCMP_SYS(waitpid),
|
||||
/* Memory */
|
||||
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
|
||||
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
|
||||
/* Filesystem */
|
||||
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
|
||||
SCMP_SYS(_llseek), SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown),
|
||||
SCMP_SYS(chown32), SCMP_SYS(faccessat), SCMP_SYS(fchmodat), SCMP_SYS(fchownat),
|
||||
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(getdents), SCMP_SYS(getdents64),
|
||||
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
|
||||
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
|
||||
SCMP_SYS(lseek), SCMP_SYS(newfstatat), SCMP_SYS(rename), SCMP_SYS(renameat),
|
||||
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
|
||||
SCMP_SYS(unlink), SCMP_SYS(unlinkat),
|
||||
/* Socket */
|
||||
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
|
||||
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
|
||||
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
|
||||
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
|
||||
SCMP_SYS(recv), SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
|
||||
SCMP_SYS(send), SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
|
||||
/* TODO: check socketcall arguments */
|
||||
SCMP_SYS(socketcall),
|
||||
/* General I/O */
|
||||
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(pipe),
|
||||
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
|
||||
SCMP_SYS(set_robust_list), SCMP_SYS(write),
|
||||
SCMP_SYS(pipe2), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(pselect6), SCMP_SYS(read),
|
||||
SCMP_SYS(futex), SCMP_SYS(select), SCMP_SYS(set_robust_list), SCMP_SYS(write),
|
||||
/* Miscellaneous */
|
||||
SCMP_SYS(uname),
|
||||
SCMP_SYS(getrandom), SCMP_SYS(sysinfo), SCMP_SYS(uname),
|
||||
};
|
||||
|
||||
const int socket_domains[] = {
|
||||
@@ -509,12 +528,21 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
#endif
|
||||
};
|
||||
|
||||
const static int fcntls[] = { F_GETFD, F_SETFD };
|
||||
const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL };
|
||||
|
||||
const static unsigned long ioctls[] = {
|
||||
FIONREAD, TCGETS,
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
PTP_SYS_OFFSET,
|
||||
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
|
||||
#ifdef PTP_PIN_SETFUNC
|
||||
PTP_PIN_SETFUNC,
|
||||
#endif
|
||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||
PTP_SYS_OFFSET_EXTENDED,
|
||||
#endif
|
||||
#ifdef PTP_SYS_OFFSET_PRECISE
|
||||
PTP_SYS_OFFSET_PRECISE,
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FEAT_PPS
|
||||
PPS_FETCH,
|
||||
@@ -541,7 +569,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
|
||||
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
|
||||
if (ctx == NULL)
|
||||
LOG_FATAL(LOGF_SysLinux, "Failed to initialize seccomp");
|
||||
LOG_FATAL("Failed to initialize seccomp");
|
||||
|
||||
/* Add system calls that are always allowed */
|
||||
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
|
||||
@@ -582,76 +610,19 @@ SYS_Linux_EnableSystemCallFilter(int level)
|
||||
}
|
||||
|
||||
if (seccomp_load(ctx) < 0)
|
||||
LOG_FATAL(LOGF_SysLinux, "Failed to load seccomp rules");
|
||||
LOG_FATAL("Failed to load seccomp rules");
|
||||
|
||||
LOG(LOGS_INFO, LOGF_SysLinux, "Loaded seccomp filter");
|
||||
LOG(LOGS_INFO, "Loaded seccomp filter");
|
||||
seccomp_release(ctx);
|
||||
return;
|
||||
|
||||
add_failed:
|
||||
LOG_FATAL(LOGF_SysLinux, "Failed to add seccomp rules");
|
||||
LOG_FATAL("Failed to add seccomp rules");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_SCHED_SETSCHEDULER)
|
||||
/* Install SCHED_FIFO real-time scheduler with specified priority */
|
||||
void SYS_Linux_SetScheduler(int SchedPriority)
|
||||
{
|
||||
int pmax, pmin;
|
||||
struct sched_param sched;
|
||||
|
||||
if (SchedPriority < 1 || SchedPriority > 99) {
|
||||
LOG_FATAL(LOGF_SysLinux, "Bad scheduler priority: %d", SchedPriority);
|
||||
} else {
|
||||
sched.sched_priority = SchedPriority;
|
||||
pmax = sched_get_priority_max(SCHED_FIFO);
|
||||
pmin = sched_get_priority_min(SCHED_FIFO);
|
||||
if ( SchedPriority > pmax ) {
|
||||
sched.sched_priority = pmax;
|
||||
}
|
||||
else if ( SchedPriority < pmin ) {
|
||||
sched.sched_priority = pmin;
|
||||
}
|
||||
if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) {
|
||||
LOG(LOGS_ERR, LOGF_SysLinux, "sched_setscheduler() failed");
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG(LOGF_SysLinux, "Enabled SCHED_FIFO with priority %d",
|
||||
sched.sched_priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SCHED_SETSCHEDULER */
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
/* Lock the process into RAM so that it will never be swapped out */
|
||||
void SYS_Linux_MemLockAll(int LockAll)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
if (LockAll == 1 ) {
|
||||
/* Make sure that we will be able to lock all the memory we need */
|
||||
/* even after dropping privileges. This does not actually reaerve any memory */
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_SysLinux, "setrlimit() failed: not locking into RAM");
|
||||
}
|
||||
else {
|
||||
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
|
||||
LOG(LOGS_ERR, LOGF_SysLinux, "mlockall() failed");
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG(LOGF_SysLinux, "Successfully locked into RAM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_MLOCKALL */
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
||||
{
|
||||
@@ -661,3 +632,265 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
||||
|
||||
return kernelvercmp(req_major, req_minor, 0, major, minor, patch) <= 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
|
||||
#define PHC_READINGS 10
|
||||
|
||||
static int
|
||||
process_phc_readings(struct timespec ts[][3], int n, double precision,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
||||
{
|
||||
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
|
||||
int i, combined;
|
||||
|
||||
if (n > PTP_MAX_SAMPLES)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
|
||||
|
||||
if (delays[i] < 0.0) {
|
||||
/* Step in the middle of a PHC reading? */
|
||||
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!i || delays[i] < min_delay)
|
||||
min_delay = delays[i];
|
||||
}
|
||||
|
||||
sys_prec = LCL_GetSysPrecisionAsQuantum();
|
||||
|
||||
/* Combine best readings */
|
||||
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
|
||||
if (delays[i] > min_delay + MAX(sys_prec, precision))
|
||||
continue;
|
||||
|
||||
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
|
||||
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
|
||||
combined++;
|
||||
}
|
||||
|
||||
assert(combined);
|
||||
|
||||
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
|
||||
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
|
||||
*err = MAX(min_delay / 2.0, precision);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset sys_off;
|
||||
int i;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
ts[i][1].tv_nsec = sys_off.ts[i * 2 + 1].nsec;
|
||||
ts[i][2].tv_sec = sys_off.ts[i * 2 + 2].sec;
|
||||
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset_extended sys_off;
|
||||
int i;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
|
||||
ts[i][1].tv_nsec = sys_off.ts[i][1].nsec;
|
||||
ts[i][2].tv_sec = sys_off.ts[i][2].sec;
|
||||
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
#ifdef PTP_SYS_OFFSET_PRECISE
|
||||
struct ptp_sys_offset_precise sys_off;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET_PRECISE, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_PRECISE",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
phc_ts->tv_sec = sys_off.device.sec;
|
||||
phc_ts->tv_nsec = sys_off.device.nsec;
|
||||
sys_ts->tv_sec = sys_off.sys_realtime.sec;
|
||||
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
|
||||
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_OpenPHC(const char *path, int phc_index)
|
||||
{
|
||||
struct ptp_clock_caps caps;
|
||||
char phc_path[64];
|
||||
int phc_fd;
|
||||
|
||||
if (!path) {
|
||||
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
|
||||
return -1;
|
||||
path = phc_path;
|
||||
}
|
||||
|
||||
phc_fd = open(path, O_RDONLY);
|
||||
if (phc_fd < 0) {
|
||||
LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure it is a PHC */
|
||||
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
|
||||
LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno));
|
||||
close(phc_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
|
||||
return phc_fd;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
||||
{
|
||||
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
|
||||
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 2;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 3 || !*reading_mode) &&
|
||||
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 3;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 1 || !*reading_mode) &&
|
||||
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
*reading_mode = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
||||
int rising, int falling, int enable)
|
||||
{
|
||||
struct ptp_extts_request extts_req;
|
||||
#ifdef PTP_PIN_SETFUNC
|
||||
struct ptp_pin_desc pin_desc;
|
||||
|
||||
memset(&pin_desc, 0, sizeof (pin_desc));
|
||||
pin_desc.index = pin;
|
||||
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
|
||||
pin_desc.chan = channel;
|
||||
|
||||
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
DEBUG_LOG("Missing PTP_PIN_SETFUNC");
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
memset(&extts_req, 0, sizeof (extts_req));
|
||||
extts_req.index = channel;
|
||||
extts_req.flags = (enable ? PTP_ENABLE_FEATURE : 0) |
|
||||
(rising ? PTP_RISING_EDGE : 0) |
|
||||
(falling ? PTP_FALLING_EDGE : 0);
|
||||
|
||||
if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_req)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_EXTTS_REQUEST", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
|
||||
{
|
||||
struct ptp_extts_event extts_event;
|
||||
|
||||
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
|
||||
DEBUG_LOG("Could not read PHC extts event");
|
||||
return 0;
|
||||
}
|
||||
|
||||
phc_ts->tv_sec = extts_event.t.sec;
|
||||
phc_ts->tv_nsec = extts_event.t.nsec;
|
||||
*channel = extts_event.index;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
16
sys_linux.h
16
sys_linux.h
@@ -31,14 +31,20 @@ extern void SYS_Linux_Initialise(void);
|
||||
|
||||
extern void SYS_Linux_Finalise(void);
|
||||
|
||||
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid);
|
||||
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
|
||||
|
||||
extern void SYS_Linux_EnableSystemCallFilter(int level);
|
||||
|
||||
extern void SYS_Linux_MemLockAll(int LockAll);
|
||||
|
||||
extern void SYS_Linux_SetScheduler(int SchedPriority);
|
||||
|
||||
extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
|
||||
|
||||
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
|
||||
|
||||
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
|
||||
|
||||
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
||||
int rising, int falling, int enable);
|
||||
|
||||
extern int SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel);
|
||||
|
||||
#endif /* GOT_SYS_LINUX_H */
|
||||
|
||||
65
sys_macosx.c
65
sys_macosx.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2001
|
||||
* Copyright (C) J. Hannken-Illjes 2001
|
||||
* Copyright (C) Bryan Christianson 2015
|
||||
* Copyright (C) Bryan Christianson 2015, 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -46,6 +46,13 @@
|
||||
#include "privops.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
#include <dlfcn.h>
|
||||
#include "sys_netbsd.h"
|
||||
|
||||
static int have_ntp_adjtime = 0;
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* This register contains the number of seconds by which the local
|
||||
@@ -116,7 +123,7 @@ clock_initialise(void)
|
||||
newadj.tv_usec = 0;
|
||||
|
||||
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
|
||||
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,10 +161,9 @@ start_adjust(void)
|
||||
|
||||
predicted_error = (current_drift_removal_interval - drift_removal_elapsed) / 2.0 * current_freq;
|
||||
|
||||
DEBUG_LOG(LOGF_SysMacOSX, "drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
|
||||
1.0e6 * drift_removal_elapsed,
|
||||
1.0e6 * current_drift_removal_interval,
|
||||
1.0e6 * predicted_error);
|
||||
DEBUG_LOG("drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
|
||||
1.0e6 * drift_removal_elapsed, 1.0e6 * current_drift_removal_interval,
|
||||
1.0e6 * predicted_error);
|
||||
|
||||
adjust_required = - (accrued_error + offset_register + predicted_error);
|
||||
|
||||
@@ -166,7 +172,7 @@ start_adjust(void)
|
||||
rounding_error = adjust_required - adjustment_requested;
|
||||
|
||||
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
|
||||
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
}
|
||||
|
||||
old_adjust_remaining = UTI_TimevalToDouble(&oldadj);
|
||||
@@ -190,7 +196,7 @@ stop_adjust(void)
|
||||
zeroadj.tv_usec = 0;
|
||||
|
||||
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
|
||||
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
}
|
||||
|
||||
LCL_ReadRawTime(&T1);
|
||||
@@ -239,7 +245,7 @@ apply_step_offset(double offset)
|
||||
UTI_TimespecToTimeval(&new_time, &new_time_tv);
|
||||
|
||||
if (PRV_SetTime(&new_time_tv, NULL) < 0) {
|
||||
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
|
||||
DEBUG_LOG("settimeofday() failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -338,14 +344,14 @@ set_sync_status(int synchronised, double est_error, double max_error)
|
||||
/* update the RTC by applying a step of 0.0 secs */
|
||||
apply_step_offset(0.0);
|
||||
last_rtc_sync = now;
|
||||
DEBUG_LOG(LOGF_SysMacOSX, "rtc synchronised");
|
||||
DEBUG_LOG("rtc synchronised");
|
||||
}
|
||||
}
|
||||
|
||||
interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES);
|
||||
drift_removal_interval = MAX(interval, DRIFT_REMOVAL_INTERVAL_MIN);
|
||||
|
||||
DEBUG_LOG(LOGF_SysMacOSX, "est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
|
||||
DEBUG_LOG("est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
|
||||
est_error * 1.0e6, current_freq * 1.0e6, interval, drift_removal_interval);
|
||||
}
|
||||
|
||||
@@ -389,7 +395,7 @@ set_realtime(void)
|
||||
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG(LOGS_WARN, LOGF_SysMacOSX, "Cannot set real-time priority: %d", kr);
|
||||
LOG(LOGS_WARN, "Cannot set real-time priority: %d", kr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -418,8 +424,8 @@ void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_MacOSX_Initialise(void)
|
||||
static void
|
||||
legacy_MacOSX_Initialise(void)
|
||||
{
|
||||
clock_initialise();
|
||||
|
||||
@@ -435,8 +441,8 @@ SYS_MacOSX_Initialise(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_MacOSX_Finalise(void)
|
||||
static void
|
||||
legacy_MacOSX_Finalise(void)
|
||||
{
|
||||
SCH_RemoveTimeout(drift_removal_id);
|
||||
|
||||
@@ -445,4 +451,31 @@ SYS_MacOSX_Finalise(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_MacOSX_Initialise(void)
|
||||
{
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
have_ntp_adjtime = (dlsym(RTLD_NEXT, "ntp_adjtime") != NULL);
|
||||
if (have_ntp_adjtime) {
|
||||
SYS_NetBSD_Initialise();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
legacy_MacOSX_Initialise();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_MacOSX_Finalise(void)
|
||||
{
|
||||
#ifdef HAVE_MACOS_SYS_TIMEX
|
||||
if (have_ntp_adjtime) {
|
||||
SYS_NetBSD_Finalise();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
legacy_MacOSX_Finalise();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
17
sys_netbsd.c
17
sys_netbsd.c
@@ -65,14 +65,14 @@ accrue_offset(double offset, double corr_rate)
|
||||
UTI_DoubleToTimeval(-offset, &newadj);
|
||||
|
||||
if (PRV_AdjustTime(&newadj, &oldadj) < 0)
|
||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
|
||||
/* Add the old remaining adjustment if not zero */
|
||||
doldadj = UTI_TimevalToDouble(&oldadj);
|
||||
if (doldadj != 0.0) {
|
||||
UTI_DoubleToTimeval(-offset + doldadj, &newadj);
|
||||
if (PRV_AdjustTime(&newadj, NULL) < 0)
|
||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +84,18 @@ get_offset_correction(struct timespec *raw,
|
||||
{
|
||||
struct timeval remadj;
|
||||
double adjustment_remaining;
|
||||
#ifdef MACOSX
|
||||
struct timeval tv = {0, 0};
|
||||
|
||||
if (PRV_AdjustTime(&tv, &remadj) < 0)
|
||||
LOG_FATAL("adjtime() failed");
|
||||
|
||||
if (PRV_AdjustTime(&remadj, NULL) < 0)
|
||||
LOG_FATAL("adjtime() failed");
|
||||
#else
|
||||
if (PRV_AdjustTime(NULL, &remadj) < 0)
|
||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
||||
LOG_FATAL("adjtime() failed");
|
||||
#endif
|
||||
|
||||
adjustment_remaining = UTI_TimevalToDouble(&remadj);
|
||||
|
||||
@@ -138,7 +147,7 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
|
||||
/* Check if we have write access to /dev/clockctl */
|
||||
fd = open("/dev/clockctl", O_WRONLY);
|
||||
if (fd < 0)
|
||||
LOG_FATAL(LOGF_SysNetBSD, "Can't write to /dev/clockctl");
|
||||
LOG_FATAL("Can't write to /dev/clockctl");
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
140
sys_null.c
Normal file
140
sys_null.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You 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.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Null clock driver for operation with no clock control.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "sys_null.h"
|
||||
|
||||
#include "local.h"
|
||||
#include "localp.h"
|
||||
#include "logging.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Current frequency offset of the system clock (in ppm) */
|
||||
static double freq;
|
||||
|
||||
/* Offset of the system clock at the last update */
|
||||
static double offset_register;
|
||||
|
||||
/* Time of the last update */
|
||||
static struct timespec last_update;
|
||||
|
||||
/* Minimum interval between updates when frequency is constant */
|
||||
#define MIN_UPDATE_INTERVAL 1000.0
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
update_offset(void)
|
||||
{
|
||||
struct timespec now;
|
||||
double duration;
|
||||
|
||||
LCL_ReadRawTime(&now);
|
||||
duration = UTI_DiffTimespecsToDouble(&now, &last_update);
|
||||
offset_register += 1.0e-6 * freq * duration;
|
||||
last_update = now;
|
||||
|
||||
DEBUG_LOG("System clock offset=%e freq=%f", offset_register, freq);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
read_frequency(void)
|
||||
{
|
||||
return freq;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
set_frequency(double freq_ppm)
|
||||
{
|
||||
update_offset();
|
||||
freq = freq_ppm;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
accrue_offset(double offset, double corr_rate)
|
||||
{
|
||||
offset_register += offset;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
apply_step_offset(double offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
offset_convert(struct timespec *raw, double *corr, double *err)
|
||||
{
|
||||
double duration;
|
||||
|
||||
duration = UTI_DiffTimespecsToDouble(raw, &last_update);
|
||||
|
||||
if (duration > MIN_UPDATE_INTERVAL) {
|
||||
update_offset();
|
||||
duration = 0.0;
|
||||
}
|
||||
|
||||
*corr = -1.0e-6 * freq * duration - offset_register;
|
||||
|
||||
if (err)
|
||||
*err = 0.0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Null_Initialise(void)
|
||||
{
|
||||
offset_register = 0.0;
|
||||
LCL_ReadRawTime(&last_update);
|
||||
|
||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset,
|
||||
apply_step_offset, offset_convert, NULL, NULL);
|
||||
|
||||
LOG(LOGS_INFO, "Disabled control of system clock");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Null_Finalise(void)
|
||||
{
|
||||
}
|
||||
34
sys_null.h
Normal file
34
sys_null.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You 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 null clock driver
|
||||
*/
|
||||
|
||||
#ifndef GOT_SYS_NULL_H
|
||||
#define GOT_SYS_NULL_H
|
||||
|
||||
extern void SYS_Null_Initialise(void);
|
||||
|
||||
extern void SYS_Null_Finalise(void);
|
||||
|
||||
#endif
|
||||
109
sys_posix.c
Normal file
109
sys_posix.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
This module is for POSIX compliant operating systems.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#if defined(HAVE_SETRLIMIT_MEMLOCK)
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include "sys_posix.h"
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "util.h"
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_PTHREAD_SETSCHEDPARAM)
|
||||
/* Install SCHED_FIFO real-time scheduler with specified priority */
|
||||
void
|
||||
SYS_Posix_SetScheduler(int priority)
|
||||
{
|
||||
struct sched_param sched;
|
||||
int pmax, pmin;
|
||||
|
||||
if (priority < 1 || priority > 99)
|
||||
LOG_FATAL("Bad scheduler priority: %d", priority);
|
||||
|
||||
sched.sched_priority = priority;
|
||||
pmax = sched_get_priority_max(SCHED_FIFO);
|
||||
pmin = sched_get_priority_min(SCHED_FIFO);
|
||||
if (priority > pmax) {
|
||||
sched.sched_priority = pmax;
|
||||
} else if (priority < pmin) {
|
||||
sched.sched_priority = pmin;
|
||||
}
|
||||
|
||||
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched) < 0) {
|
||||
LOG(LOGS_ERR, "pthread_setschedparam() failed");
|
||||
} else {
|
||||
DEBUG_LOG("Enabled SCHED_FIFO with priority %d", sched.sched_priority);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_PTHREAD_SETSCHEDPARAM */
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(HAVE_MLOCKALL)
|
||||
/* Lock the process into RAM so that it will never be swapped out */
|
||||
void
|
||||
SYS_Posix_MemLockAll(void)
|
||||
{
|
||||
#if defined(HAVE_SETRLIMIT_MEMLOCK)
|
||||
struct rlimit rlim;
|
||||
|
||||
/* Ensure we can reserve as much as we need */
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
LOG(LOGS_ERR, "setrlimit() failed");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
|
||||
LOG(LOGS_ERR, "mlockall() failed");
|
||||
} else {
|
||||
DEBUG_LOG("Successfully locked into RAM");
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_MLOCKALL */
|
||||
36
sys_posix.h
Normal file
36
sys_posix.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
The header file for shared Posix functionality
|
||||
*/
|
||||
|
||||
#ifndef GOT_SYS_POSIX_H
|
||||
#define GOT_SYS_POSIX_H
|
||||
|
||||
extern void SYS_Posix_MemLockAll(void);
|
||||
|
||||
extern void SYS_Posix_SetScheduler(int priority);
|
||||
|
||||
#endif /* GOT_SYS_POSIX_H */
|
||||
73
sys_timex.c
73
sys_timex.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -61,7 +61,10 @@
|
||||
#define MIN_TICK_RATE 100
|
||||
|
||||
/* Saved timex status */
|
||||
static int status;
|
||||
static int sys_status;
|
||||
|
||||
/* Saved TAI-UTC offset */
|
||||
static int sys_tai_offset;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -95,33 +98,47 @@ set_frequency(double freq_ppm)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
set_leap(int leap)
|
||||
set_leap(int leap, int tai_offset)
|
||||
{
|
||||
struct timex txc;
|
||||
int applied;
|
||||
int applied, prev_status;
|
||||
|
||||
applied = 0;
|
||||
if (!leap) {
|
||||
txc.modes = 0;
|
||||
if (SYS_Timex_Adjust(&txc, 1) == TIME_WAIT)
|
||||
applied = 1;
|
||||
}
|
||||
txc.modes = 0;
|
||||
applied = SYS_Timex_Adjust(&txc, 0) == TIME_WAIT;
|
||||
|
||||
status &= ~(STA_INS | STA_DEL);
|
||||
prev_status = sys_status;
|
||||
sys_status &= ~(STA_INS | STA_DEL);
|
||||
|
||||
if (leap > 0)
|
||||
status |= STA_INS;
|
||||
sys_status |= STA_INS;
|
||||
else if (leap < 0)
|
||||
status |= STA_DEL;
|
||||
sys_status |= STA_DEL;
|
||||
|
||||
txc.modes = MOD_STATUS;
|
||||
txc.status = status;
|
||||
txc.status = sys_status;
|
||||
|
||||
#ifdef MOD_TAI
|
||||
if (tai_offset) {
|
||||
txc.modes |= MOD_TAI;
|
||||
txc.constant = tai_offset;
|
||||
|
||||
if (applied && !(sys_status & (STA_INS | STA_DEL)))
|
||||
sys_tai_offset += prev_status & STA_INS ? 1 : -1;
|
||||
|
||||
if (sys_tai_offset != tai_offset) {
|
||||
sys_tai_offset = tai_offset;
|
||||
LOG(LOGS_INFO, "System clock TAI offset set to %d seconds", tai_offset);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SYS_Timex_Adjust(&txc, 0);
|
||||
|
||||
LOG(LOGS_INFO, LOGF_SysTimex, "System clock status %s leap second",
|
||||
leap ? (leap > 0 ? "set to insert" : "set to delete") :
|
||||
(applied ? "reset after" : "set to not insert/delete"));
|
||||
if (prev_status != sys_status) {
|
||||
LOG(LOGS_INFO, "System clock status %s leap second",
|
||||
leap ? (leap > 0 ? "set to insert" : "set to delete") :
|
||||
(applied ? "reset after" : "set to not insert/delete"));
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -149,16 +166,17 @@ set_sync_status(int synchronised, double est_error, double max_error)
|
||||
#endif
|
||||
|
||||
if (synchronised)
|
||||
status &= ~STA_UNSYNC;
|
||||
sys_status &= ~STA_UNSYNC;
|
||||
else
|
||||
status |= STA_UNSYNC;
|
||||
sys_status |= STA_UNSYNC;
|
||||
|
||||
txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR;
|
||||
txc.status = status;
|
||||
txc.status = sys_status;
|
||||
txc.esterror = est_error * 1.0e6;
|
||||
txc.maxerror = max_error * 1.0e6;
|
||||
|
||||
SYS_Timex_Adjust(&txc, 1);
|
||||
if (SYS_Timex_Adjust(&txc, 1) < 0)
|
||||
;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -168,17 +186,18 @@ initialise_timex(void)
|
||||
{
|
||||
struct timex txc;
|
||||
|
||||
status = STA_UNSYNC;
|
||||
sys_status = STA_UNSYNC;
|
||||
sys_tai_offset = 0;
|
||||
|
||||
/* Reset PLL offset */
|
||||
txc.modes = MOD_OFFSET | MOD_STATUS;
|
||||
txc.status = STA_PLL | status;
|
||||
txc.status = STA_PLL | sys_status;
|
||||
txc.offset = 0;
|
||||
SYS_Timex_Adjust(&txc, 0);
|
||||
|
||||
/* Turn PLL off */
|
||||
txc.modes = MOD_STATUS;
|
||||
txc.status = status;
|
||||
txc.status = sys_status;
|
||||
SYS_Timex_Adjust(&txc, 0);
|
||||
}
|
||||
|
||||
@@ -238,11 +257,9 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
|
||||
|
||||
if (state < 0) {
|
||||
if (!ignore_error)
|
||||
LOG_FATAL(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
|
||||
txc->modes, strerror(errno));
|
||||
LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
|
||||
else
|
||||
DEBUG_LOG(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
|
||||
txc->modes, strerror(errno));
|
||||
DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
17
sysincl.h
17
sysincl.h
@@ -36,11 +36,10 @@
|
||||
#include <float.h>
|
||||
#include <glob.h>
|
||||
#include <grp.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <resolv.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
@@ -59,21 +58,17 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS)
|
||||
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS) || defined(HAVE_MACOS_SYS_TIMEX)
|
||||
#include <sys/timex.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#elif HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
/* Tough */
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_IPV6
|
||||
/* For inet_ntop() */
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETRANDOM
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
|
||||
#endif /* GOT_SYSINCL_H */
|
||||
|
||||
14
tempcomp.c
14
tempcomp.c
@@ -92,7 +92,7 @@ read_timeout(void *arg)
|
||||
if (fabs(comp) <= MAX_COMP) {
|
||||
comp = LCL_SetTempComp(comp);
|
||||
|
||||
DEBUG_LOG(LOGF_TempComp, "tempcomp updated to %f for %f", comp, temp);
|
||||
DEBUG_LOG("tempcomp updated to %f for %f", comp, temp);
|
||||
|
||||
if (logfileid != -1) {
|
||||
struct timespec now;
|
||||
@@ -102,13 +102,11 @@ read_timeout(void *arg)
|
||||
UTI_TimeToLogForm(now.tv_sec), temp, comp);
|
||||
}
|
||||
} else {
|
||||
LOG(LOGS_WARN, LOGF_TempComp,
|
||||
"Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
|
||||
LOG(LOGS_WARN, "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);
|
||||
LOG(LOGS_WARN, "Could not read temperature from %s", filename);
|
||||
}
|
||||
|
||||
if (f)
|
||||
@@ -126,7 +124,7 @@ read_points(const char *filename)
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (!f) {
|
||||
LOG_FATAL(LOGF_TempComp, "Could not open tempcomp point file %s", filename);
|
||||
LOG_FATAL("Could not open tempcomp point file %s", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,7 +133,7 @@ read_points(const char *filename)
|
||||
while (fgets(line, sizeof (line), f)) {
|
||||
p = (struct Point *)ARR_GetNewElement(points);
|
||||
if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
|
||||
LOG_FATAL(LOGF_TempComp, "Could not read tempcomp point from %s", filename);
|
||||
LOG_FATAL("Could not read tempcomp point from %s", filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -143,7 +141,7 @@ read_points(const char *filename)
|
||||
fclose(f);
|
||||
|
||||
if (ARR_GetSize(points) < 2)
|
||||
LOG_FATAL(LOGF_TempComp, "Not enough points in %s", filename);
|
||||
LOG_FATAL("Not enough points in %s", filename);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
cd ../..
|
||||
|
||||
export CFLAGS="-O2 -Werror -Wpointer-arith -Wformat-signedness -Wno-unknown-warning-option -D_FORTIFY_SOURCE=2"
|
||||
|
||||
for opts in \
|
||||
"--enable-debug" \
|
||||
"--enable-ntp-signd" \
|
||||
@@ -23,6 +25,6 @@ for opts in \
|
||||
"--disable-cmdmon --disable-refclock" \
|
||||
"--disable-cmdmon --disable-ntp --disable-refclock"
|
||||
do
|
||||
./configure $opts
|
||||
./configure $opts || exit 1
|
||||
make "$@" || exit 1
|
||||
done
|
||||
|
||||
@@ -6,8 +6,9 @@ for opts in \
|
||||
"--host-system=Linux" \
|
||||
"--host-system=NetBSD" \
|
||||
"--host-system=FreeBSD" \
|
||||
"--without-nss" \
|
||||
"--without-tomcrypt --without-nss"
|
||||
"--without-nettle" \
|
||||
"--without-nettle --without-nss" \
|
||||
"--without-nettle --without-nss --without-tomcrypt"
|
||||
do
|
||||
./configure $opts
|
||||
scan-build make "$@" || exit 1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user