xref: /dragonfly/usr.sbin/dntpd/system.c (revision 335b9e93)
1 /*
2  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/usr.sbin/dntpd/system.c,v 1.9 2007/07/11 00:18:00 swildner Exp $
35  */
36 
37 #include "defs.h"
38 #include <sys/sysctl.h>
39 #include <sys/timex.h>
40 
41 /*
42  * If a system has multiple independant time-correcting mechanisms, this
43  * function should clear out any corrections on those mechanisms that we
44  * will NOT be using.  We can leave a prior correction intact on the
45  * mechanism that we ARE using.
46  *
47  * However, it is usually a good idea to clean out any offset correction
48  * that is still in progress anyway.  We leave the frequency correction
49  * intact.
50  */
51 void
52 sysntp_clear_alternative_corrections(void)
53 {
54     struct timex ntp;
55     int64_t offset;
56 
57     if (no_update_opt)
58 	return;
59 
60     /*
61      * Clear the ntp interface.  We will use the sysctl interface
62      * (XXX)
63      */
64     bzero(&ntp, sizeof(ntp));
65     ntp.modes = MOD_OFFSET | MOD_FREQUENCY;
66     ntp.offset = 0;
67     ntp.freq = 0;
68     ntp_adjtime(&ntp);
69 
70     /*
71      * Clean out any offset still being applied to real time.  Leave
72      * any prior frequency correction intact.
73      */
74     offset = 0;
75     sysctlbyname("kern.ntp.delta", NULL, 0, &offset, sizeof(offset));
76 }
77 
78 /*
79  * Obtain a timestamp that contains ONLY corrections made by the system
80  * to the base time.  The actual value of the timestamp is not relevant,
81  * only the delta from two queries to this routine.
82  *
83  * This is used by DNTPD to determine what corrections the system has made
84  * to the system's real time so DNTPD can uncorrect them for the purpose
85  * of calculating the linear regression.
86  */
87 void
88 sysntp_getbasetime(struct timeval *tvp)
89 {
90     struct timespec ts;
91     int error;
92     size_t ts_size;
93 
94     ts_size = sizeof(ts);
95     error = sysctlbyname("kern.basetime", &ts, &ts_size, NULL, 0);
96     if (error < 0) {
97 	logerr("sysctlbyname(\"kern.basetime\") failed, cannot continue");
98 	exit(1);
99     }
100     ts_to_tv(&ts, tvp);
101 }
102 
103 /*
104  * Return 1 if an offset correction is still running, 0 if it isn't.
105  */
106 int
107 sysntp_offset_correction_is_running(void)
108 {
109     int64_t delta;
110     size_t delta_len;
111 
112     delta_len = sizeof(delta);
113     if (sysctlbyname("kern.ntp.delta", &delta, &delta_len, NULL, 0) == 0) {
114 	if (delta != 0)
115 	    return(1);
116     }
117     return(0);
118 }
119 
120 /*
121  * The offset error is passed as seconds per second.  Only fairly small
122  * offsets are passed to this function (see sysntp_correct_course_offset()
123  * for large corrections).  This function may request that the offset
124  * be corrected by shifting the frequency by returning the frequency shift
125  * (usually a small number in the 1E-6 range) (NOT YET IMPLEMENTED).
126  *
127  * The 64 bit delta is calculated as nanoseconds per second.  Since we are
128  * passed an offset error we must negate the result to correct the error.
129  *
130  * Because offset corrections skew the accuracy of the clock while the
131  * correction is in progress, we do not want to use them once we are
132  * reasonably well synchronized.  We can make small offset corrections
133  * by shifting the frequency a bit.  XXX
134  */
135 double
136 sysntp_correct_offset(double offset)
137 {
138     int64_t delta;
139 
140     /*
141      * Course correction
142      */
143     if (offset < -0.001 || offset > 0.001) {
144 	logdebug(1, "issuing offset adjustment: %7.6fs", -offset);
145 	if (no_update_opt)
146 	    logdebug(1, " (UPDATES DISABLED)");
147 	logdebug(1, "\n");
148 	delta = -(int64_t)(offset * 1.0E+9);
149 	if (no_update_opt == 0)
150 	    sysctlbyname("kern.ntp.delta", NULL, 0, &delta, sizeof(delta));
151 	return(0.0);
152     }
153 
154     /*
155      * Fine correction - do it by adjusting the frequency.
156      * XXX
157      */
158     return(0.0);
159 }
160 
161 /*
162  * This function is used for what should be a one-time correction on
163  * startup.
164  */
165 double
166 sysntp_correct_course_offset(double offset)
167 {
168     struct timeval tv;
169     struct tm *tp;
170     time_t t;
171     char buf[64];
172 
173     offset = -offset;	/* if we are ahead, correct backwards, and vise versa*/
174     if (gettimeofday(&tv, NULL) == 0) {
175 	tv_add_offset(&tv, offset);
176 	if (no_update_opt == 0 && settimeofday(&tv, NULL) < 0) {
177 	    logerr("settimeofday");
178 	} else {
179 	    logdebug(1, "issuing COARSE offset adjustment: %7.6fs, ",
180 		    offset);
181 	    t = tv.tv_sec;
182 	    tp = localtime(&t);
183 	    strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tp);
184 	    logdebug(1, "%s.%03ld", buf, tv.tv_usec / 1000);
185 	    if (no_update_opt)
186 		logdebug(1, " (UPDATES DISABLED)");
187 	    if (quickset_opt)
188 		logdebug(1, " (ONE-TIME QUICKSET)");
189 	    logdebug(1, "\n");
190 	}
191     } else {
192 	logerr("gettimeofday");
193     }
194     return(0.0);
195 }
196 
197 /*
198  * freq is passed as seconds per second.
199  *
200  * The calculated 64 bit correction is nanoseconds per second shifted
201  * left 32.
202  *
203  * Frequency errors greater then 1 second per second will not be corrected.
204  * It doesn't hurt to continue correcting the frequency.
205  */
206 void
207 sysntp_correct_freq(double freq)
208 {
209     static double last_freq;
210     int64_t delta;
211     int loglevel;
212 
213     if (last_freq == 0.0 || fabs(freq - last_freq) >= 20.0E-6)
214 	loglevel = 1;
215     else if (fabs(freq - last_freq) >= 5.0E-6)
216 	loglevel = 2;
217     else
218 	loglevel = 3;
219     last_freq = freq;
220 
221     if (freq >= -1.0 && freq < 1.0) {
222 	logdebug(loglevel, "issuing frequency adjustment: %6.3fppm",
223 		-freq * 1000000.0);
224 	if (no_update_opt)
225 		logdebug(loglevel, " (UPDATES DISABLED)");
226 	logdebug(loglevel, "\n");
227 	delta = -((int64_t)(freq * 1.0E+9) << 32);
228 	if (no_update_opt == 0)
229 	    sysctlbyname("kern.ntp.permanent", NULL, 0, &delta, sizeof(delta));
230     }
231 }
232 
233