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.7 2005/09/13 18:13:11 dillon 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() 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 int 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 int 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.6f", -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.6f, ", 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