xref: /original-bsd/usr.sbin/timed/timed/correct.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)correct.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 #ifdef sgi
13 #ident "$Revision: 1.16 $"
14 #endif
15 
16 #include "globals.h"
17 #include <math.h>
18 #include <sys/types.h>
19 #include <sys/times.h>
20 #ifdef sgi
21 #include <sys/syssgi.h>
22 #endif /* sgi */
23 
24 static void adjclock __P((struct timeval *));
25 
26 /*
27  * sends to the slaves the corrections for their clocks after fixing our
28  * own
29  */
30 void
31 correct(avdelta)
32 	long avdelta;
33 {
34 	struct hosttbl *htp;
35 	int corr;
36 	struct timeval adjlocal;
37 	struct tsp to;
38 	struct tsp *answer;
39 
40 	mstotvround(&adjlocal, avdelta);
41 
42 	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
43 		if (htp->delta != HOSTDOWN)  {
44 			corr = avdelta - htp->delta;
45 /* If the other machine is off in the weeds, set its time directly.
46  *	If a slave gets the wrong day, the original code would simply
47  *	fix the minutes.  If you fix a network partition, you can get
48  *	into such situations.
49  */
50 			if (htp->need_set
51 			    || corr >= MAXADJ*1000
52 			    || corr <= -MAXADJ*1000) {
53 				htp->need_set = 0;
54 				(void)gettimeofday(&to.tsp_time,0);
55 				timevaladd(&to.tsp_time, &adjlocal);
56 				to.tsp_type = TSP_SETTIME;
57 			} else {
58 				mstotvround(&to.tsp_time, corr);
59 				to.tsp_type = TSP_ADJTIME;
60 			}
61 			(void)strcpy(to.tsp_name, hostname);
62 			answer = acksend(&to, &htp->addr, htp->name,
63 					 TSP_ACK, 0, 0);
64 			if (!answer) {
65 				htp->delta = HOSTDOWN;
66 				syslog(LOG_WARNING,
67 				       "no reply to time correction from %s",
68 				       htp->name);
69 				if (++htp->noanswer >= LOSTHOST) {
70 					if (trace) {
71 						fprintf(fd,
72 					     "purging %s for not answering\n",
73 							htp->name);
74 						(void)fflush(fd);
75 					}
76 					htp = remmach(htp);
77 				}
78 			}
79 		}
80 	}
81 
82 	/*
83 	 * adjust our own clock now that we are not sending it out
84 	 */
85 	adjclock(&adjlocal);
86 }
87 
88 
89 static void
90 adjclock(corr)
91 	struct timeval *corr;
92 {
93 	static int passes = 0;
94 	static int smoother = 0;
95 	long delta;			/* adjustment in usec */
96 	long ndelta;
97 	struct timeval now;
98 	struct timeval adj;
99 
100 	if (!timerisset(corr))
101 		return;
102 
103 	adj = *corr;
104 	if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) {
105 		delta = adj.tv_sec*1000000 + adj.tv_usec;
106 		/* If the correction is less than the minimum round
107 		 *	trip time for an ICMP packet, and thus
108 		 *	less than the likely error in the measurement,
109 		 *	do not do the entire correction.  Do half
110 		 *	or a quarter of it.
111 		 */
112 
113 		if (delta > -MIN_ROUND*1000
114 		    && delta < MIN_ROUND*1000) {
115 			if (smoother <= 4)
116 				smoother++;
117 			ndelta = delta >> smoother;
118 			if (trace)
119 				fprintf(fd,
120 					"trimming delta %ld usec to %ld\n",
121 					delta, ndelta);
122 			adj.tv_usec = ndelta;
123 			adj.tv_sec = 0;
124 		} else if (smoother > 0) {
125 			smoother--;
126 		}
127 		if (0 > adjtime(corr, 0)) {
128 			syslog(LOG_ERR, "adjtime: %m");
129 		}
130 		if (passes > 1
131 		    && (delta < -BIG_ADJ || delta > BIG_ADJ)) {
132 			smoother = 0;
133 			passes = 0;
134 			syslog(LOG_WARNING,
135 			       "large time adjustment of %+.3f sec",
136 			       delta/1000000.0);
137 		}
138 	} else {
139 		syslog(LOG_WARNING,
140 		       "clock correction %d sec too large to adjust",
141 		       adj.tv_sec);
142 		(void) gettimeofday(&now, 0);
143 		timevaladd(&now, corr);
144 		if (settimeofday(&now, 0) < 0)
145 			syslog(LOG_ERR, "settimeofday: %m");
146 	}
147 
148 #ifdef sgi
149 	/* Accumulate the total change, and use it to adjust the basic
150 	 * clock rate.
151 	 */
152 	if (++passes > 2) {
153 #define F_USEC_PER_SEC	(1000000*1.0)	/* reduce typos */
154 #define F_NSEC_PER_SEC	(F_USEC_PER_SEC*1000.0)
155 
156 		extern char *timetrim_fn;
157 		extern char *timetrim_wpat;
158 		extern long timetrim;
159 		extern double tot_adj, hr_adj;	/* totals in nsec */
160 		extern double tot_ticks, hr_ticks;
161 
162 		static double nag_tick;
163 		double cur_ticks, hr_delta_ticks, tot_delta_ticks;
164 		double tru_tot_adj, tru_hr_adj; /* nsecs of adjustment */
165 		double tot_trim, hr_trim;   /* nsec/sec */
166 		struct tms tm;
167 		FILE *timetrim_st;
168 
169 		cur_ticks = times(&tm);
170 		tot_adj += delta*1000.0;
171 		hr_adj += delta*1000.0;
172 
173 		tot_delta_ticks = cur_ticks-tot_ticks;
174 		if (tot_delta_ticks >= 16*SECDAY*CLK_TCK) {
175 			tot_adj -= rint(tot_adj/16);
176 			tot_ticks += rint(tot_delta_ticks/16);
177 			tot_delta_ticks = cur_ticks-tot_ticks;
178 		}
179 		hr_delta_ticks = cur_ticks-hr_ticks;
180 
181 		tru_hr_adj = hr_adj + timetrim*rint(hr_delta_ticks/CLK_TCK);
182 		tru_tot_adj = (tot_adj
183 			       + timetrim*rint(tot_delta_ticks/CLK_TCK));
184 
185 		if (hr_delta_ticks >= SECDAY*CLK_TCK
186 		    || (tot_delta_ticks < 4*SECDAY*CLK_TCK
187 			&& hr_delta_ticks >= SECHR*CLK_TCK)
188 		    || (trace && hr_delta_ticks >= (SECHR/10)*CLK_TCK)) {
189 
190 			tot_trim = rint(tru_tot_adj*CLK_TCK/tot_delta_ticks);
191 			hr_trim = rint(tru_hr_adj*CLK_TCK/hr_delta_ticks);
192 
193 			if (trace
194 			    || (abs(timetrim - hr_trim) > 100000.0
195 				&& 0 == timetrim_fn
196 				&& ((cur_ticks - nag_tick)
197 				    >= 24*SECDAY*CLK_TCK))) {
198 				nag_tick = cur_ticks;
199 				syslog(LOG_NOTICE,
200 		   "%+.3f/%.2f or %+.3f/%.2f sec/hr; timetrim=%+.0f or %+.0f",
201 				       tru_tot_adj/F_NSEC_PER_SEC,
202 				       tot_delta_ticks/(SECHR*CLK_TCK*1.0),
203 				       tru_hr_adj/F_NSEC_PER_SEC,
204 				       hr_delta_ticks/(SECHR*CLK_TCK*1.0),
205 				       tot_trim,
206 				       hr_trim);
207 			}
208 
209 			if (tot_trim < -MAX_TRIM || tot_trim > MAX_TRIM) {
210 				tot_ticks = hr_ticks;
211 				tot_adj = hr_adj;
212 			} else if (0 > syssgi(SGI_SETTIMETRIM,
213 					      (long)tot_trim)) {
214 				syslog(LOG_ERR, "SETTIMETRIM(%d): %m",
215 				       (long)tot_trim);
216 			} else {
217 				if (0 != timetrim_fn) {
218 				    timetrim_st = fopen(timetrim_fn, "w");
219 				    if (0 == timetrim_st) {
220 					syslog(LOG_ERR, "fopen(%s): %m",
221 					       timetrim_fn);
222 				    } else {
223 					if (0 > fprintf(timetrim_st,
224 							timetrim_wpat,
225 							(long)tot_trim,
226 							tru_tot_adj,
227 							tot_delta_ticks)) {
228 						syslog(LOG_ERR,
229 						       "fprintf(%s): %m",
230 						       timetrim_fn);
231 					}
232 					(void)fclose(timetrim_st);
233 				    }
234 				}
235 
236 				tot_adj -= ((tot_trim - timetrim)
237 					    * rint(tot_delta_ticks/CLK_TCK));
238 				timetrim = tot_trim;
239 			}
240 
241 			hr_ticks = cur_ticks;
242 			hr_adj = 0;
243 		}
244 	}
245 #endif /* sgi */
246 }
247 
248 
249 /* adjust the time in a message by the time it
250  *	spent in the queue
251  */
252 void
253 adj_msg_time(msg, now)
254 	struct tsp *msg;
255 	struct timeval *now;
256 {
257 	msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec);
258 	msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec);
259 
260 	while (msg->tsp_time.tv_usec < 0) {
261 		msg->tsp_time.tv_sec--;
262 		msg->tsp_time.tv_usec += 1000000;
263 	}
264 	while (msg->tsp_time.tv_usec >= 1000000) {
265 		msg->tsp_time.tv_sec++;
266 		msg->tsp_time.tv_usec -= 1000000;
267 	}
268 }
269