xref: /original-bsd/usr.sbin/timed/timed/master.c (revision 6761bafc)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)master.c	2.14 (Berkeley) 06/05/86";
9 #endif not lint
10 
11 #include "globals.h"
12 #include <protocols/timed.h>
13 #include <sys/file.h>
14 #include <setjmp.h>
15 #include <utmp.h>
16 
17 extern int machup;
18 extern int measure_delta;
19 extern jmp_buf jmpenv;
20 
21 extern u_short sequence;
22 
23 #ifdef MEASURE
24 int header;
25 FILE *fp = NULL;
26 #endif
27 
28 /*
29  * The main function of `master' is to periodically compute the differences
30  * (deltas) between its clock and the clocks of the slaves, to compute the
31  * network average delta, and to send to the slaves the differences between
32  * their individual deltas and the network delta.
33  * While waiting, it receives messages from the slaves (i.e. requests for
34  * master's name, remote requests to set the network time, ...), and
35  * takes the appropriate action.
36  */
37 
38 master()
39 {
40 	int ind;
41 	long pollingtime;
42 	struct timeval wait;
43 	struct timeval time;
44 	struct timeval otime;
45 	struct timezone tzone;
46 	struct tsp *msg, to;
47 	struct sockaddr_in saveaddr;
48 	int findhost();
49 	char *date();
50 	struct tsp *readmsg();
51 	struct tsp *answer, *acksend();
52 	char olddate[32];
53 	struct sockaddr_in server;
54 	register struct netinfo *ntp;
55 
56 #ifdef MEASURE
57 	if (fp == NULL) {
58 		fp = fopen("/usr/adm/timed.masterlog", "w");
59 		setlinebuf(fp);
60 	}
61 #endif
62 
63 	syslog(LOG_INFO, "This machine is master");
64 	if (trace)
65 		fprintf(fd, "THIS MACHINE IS MASTER\n");
66 
67 	for (ntp = nettab; ntp != NULL; ntp = ntp->next)
68 		if (ntp->status == MASTER)
69 			masterup(ntp);
70 	pollingtime = 0;
71 
72 loop:
73 	(void)gettimeofday(&time, (struct timezone *)0);
74 	if (time.tv_sec >= pollingtime) {
75 		pollingtime = time.tv_sec + SAMPLEINTVL;
76 		synch(0L);
77 
78 		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
79 			to.tsp_type = TSP_LOOP;
80 			to.tsp_vers = TSPVERSION;
81 			to.tsp_seq = sequence++;
82 			to.tsp_hopcnt = 10;
83 			(void)strcpy(to.tsp_name, hostname);
84 			bytenetorder(&to);
85 			if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
86 			    &ntp->dest_addr, sizeof(struct sockaddr_in)) < 0) {
87 				syslog(LOG_ERR, "sendto: %m");
88 				exit(1);
89 			}
90 		}
91 	}
92 
93 	wait.tv_sec = pollingtime - time.tv_sec;
94 	wait.tv_usec = 0;
95 	msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
96 	if (msg != NULL) {
97 		switch (msg->tsp_type) {
98 
99 		case TSP_MASTERREQ:
100 			break;
101 		case TSP_SLAVEUP:
102 			ind = addmach(msg->tsp_name, &from);
103 			newslave(ind, msg->tsp_seq);
104 			break;
105 		case TSP_SETDATE:
106 			saveaddr = from;
107 			/*
108 			 * the following line is necessary due to syslog
109 			 * calling ctime() which clobbers the static buffer
110 			 */
111 			(void)strcpy(olddate, date());
112 			(void)gettimeofday(&time, &tzone);
113 			otime = time;
114 			time.tv_sec = msg->tsp_time.tv_sec;
115 			time.tv_usec = msg->tsp_time.tv_usec;
116 			(void)settimeofday(&time, &tzone);
117 			syslog(LOG_NOTICE, "date changed from: %s", olddate);
118 			logwtmp(otime, time);
119 			msg->tsp_type = TSP_DATEACK;
120 			msg->tsp_vers = TSPVERSION;
121 			(void)strcpy(msg->tsp_name, hostname);
122 			bytenetorder(msg);
123 			if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
124 			    &saveaddr, sizeof(struct sockaddr_in)) < 0) {
125 				syslog(LOG_ERR, "sendto: %m");
126 				exit(1);
127 			}
128 			spreadtime();
129 			pollingtime = 0;
130 			break;
131 		case TSP_SETDATEREQ:
132 			ind = findhost(msg->tsp_name);
133 			if (ind < 0) {
134 			    syslog(LOG_WARNING,
135 				"DATEREQ from uncontrolled machine");
136 			    break;
137 			}
138 			if (hp[ind].seq !=  msg->tsp_seq) {
139 				hp[ind].seq = msg->tsp_seq;
140 				/*
141 				 * the following line is necessary due to syslog
142 				 * calling ctime() which clobbers the static buffer
143 				 */
144 				(void)strcpy(olddate, date());
145 				(void)gettimeofday(&time, &tzone);
146 				otime = time;
147 				time.tv_sec = msg->tsp_time.tv_sec;
148 				time.tv_usec = msg->tsp_time.tv_usec;
149 				(void)settimeofday(&time, &tzone);
150 				syslog(LOG_NOTICE,
151 				    "date changed by %s from: %s",
152 				    msg->tsp_name, olddate);
153 				logwtmp(otime, time);
154 				spreadtime();
155 				pollingtime = 0;
156 			}
157 			break;
158 		case TSP_MSITE:
159 		case TSP_MSITEREQ:
160 			break;
161 		case TSP_TRACEON:
162 			if (!(trace)) {
163 				fd = fopen(tracefile, "w");
164 				setlinebuf(fd);
165 				fprintf(fd, "Tracing started on: %s\n\n",
166 							date());
167 			}
168 			trace = ON;
169 			break;
170 		case TSP_TRACEOFF:
171 			if (trace) {
172 				fprintf(fd, "Tracing ended on: %s\n", date());
173 				(void)fclose(fd);
174 			}
175 #ifdef GPROF
176 			moncontrol(0);
177 			_mcleanup();
178 			moncontrol(1);
179 #endif
180 			trace = OFF;
181 			break;
182 		case TSP_ELECTION:
183 			to.tsp_type = TSP_QUIT;
184 			(void)strcpy(to.tsp_name, hostname);
185 			server = from;
186 			answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
187 			    (struct netinfo *)NULL);
188 			if (answer == NULL) {
189 				syslog(LOG_ERR, "election error");
190 			} else {
191 				(void) addmach(msg->tsp_name, &from);
192 			}
193 			pollingtime = 0;
194 			break;
195 		case TSP_CONFLICT:
196 			/*
197 			 * After a network partition, there can be
198 			 * more than one master: the first slave to
199 			 * come up will notify here the situation.
200 			 */
201 
202 			(void)strcpy(to.tsp_name, hostname);
203 
204 			if (fromnet == NULL)
205 				break;
206 			for(;;) {
207 				to.tsp_type = TSP_RESOLVE;
208 				answer = acksend(&to, &fromnet->dest_addr,
209 				    (char *)ANYADDR, TSP_MASTERACK, fromnet);
210 				if (answer == NULL)
211 					break;
212 				to.tsp_type = TSP_QUIT;
213 				server = from;
214 				msg = acksend(&to, &server, answer->tsp_name,
215 				    TSP_ACK, (struct netinfo *)NULL);
216 				if (msg == NULL) {
217 					syslog(LOG_ERR, "error on sending QUIT");
218 				} else {
219 					(void) addmach(answer->tsp_name, &from);
220 				}
221 			}
222 			masterup(fromnet);
223 			pollingtime = 0;
224 			break;
225 		case TSP_RESOLVE:
226 			/*
227 			 * do not want to call synch() while waiting
228 			 * to be killed!
229 			 */
230 			(void)gettimeofday(&time, (struct timezone *)0);
231 			pollingtime = time.tv_sec + SAMPLEINTVL;
232 			break;
233 		case TSP_QUIT:
234 			/* become slave */
235 #ifdef MEASURE
236 			if (fp != NULL) {
237 				(void)fclose(fp);
238 				fp = NULL;
239 			}
240 #endif
241 			longjmp(jmpenv, 2);
242 			break;
243 		case TSP_LOOP:
244 			/*
245 			 * We should not have received this from a net
246 			 * we are master on.  There must be two masters
247 			 * in this case.
248 			 */
249 			to.tsp_type = TSP_QUIT;
250 			(void)strcpy(to.tsp_name, hostname);
251 			server = from;
252 			answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
253 				(struct netinfo *)NULL);
254 			if (answer == NULL) {
255 				syslog(LOG_WARNING,
256 				    "loop breakage: no reply to QUIT");
257 			} else {
258 				(void)addmach(msg->tsp_name, &from);
259 			}
260 		default:
261 			if (trace) {
262 				fprintf(fd, "garbage: ");
263 				print(msg, &from);
264 			}
265 			break;
266 		}
267 	}
268 	goto loop;
269 }
270 
271 /*
272  * `synch' synchronizes all the slaves by calling measure,
273  * networkdelta and correct
274  */
275 
276 synch(mydelta)
277 long mydelta;
278 {
279 	int i;
280 	int measure_status;
281 	long netdelta;
282 	struct timeval tack;
283 #ifdef MEASURE
284 #define MAXLINES	8
285 	static int lines = 1;
286 	struct timeval start, end;
287 #endif
288 	int measure();
289 	int correct();
290 	long networkdelta();
291 	char *date();
292 
293 	if (slvcount > 1) {
294 #ifdef MEASURE
295 		(void)gettimeofday(&start, (struct timezone *)0);
296 		if (header == ON || --lines == 0) {
297 			fprintf(fp, "%s\n", date());
298 			for (i=0; i<slvcount; i++)
299 				fprintf(fp, "%.7s\t", hp[i].name);
300 			fprintf(fp, "\n");
301 			lines = MAXLINES;
302 			header = OFF;
303 		}
304 #endif
305 		machup = 1;
306 		hp[0].delta = 0;
307 		for(i=1; i<slvcount; i++) {
308 			tack.tv_sec = 0;
309 			tack.tv_usec = 500000;
310 			if ((measure_status = measure(&tack, &hp[i].addr)) <0) {
311 				syslog(LOG_ERR, "measure: %m");
312 				exit(1);
313 			}
314 			hp[i].delta = measure_delta;
315 			if (measure_status == GOOD)
316 				machup++;
317 		}
318 		if (status & SLAVE) {
319 			/* called by a submaster */
320 			if (trace)
321 				fprintf(fd, "submaster correct: %d ms.\n",
322 				    mydelta);
323 			correct(mydelta);
324 		} else {
325 			if (machup > 1) {
326 				netdelta = networkdelta();
327 				if (trace)
328 					fprintf(fd,
329 					    "master correct: %d ms.\n",
330 					    mydelta);
331 				correct(netdelta);
332 			}
333 		}
334 #ifdef MEASURE
335 		gettimeofday(&end, 0);
336 		end.tv_sec -= start.tv_sec;
337 		end.tv_usec -= start.tv_usec;
338 		if (end.tv_usec < 0) {
339 			end.tv_sec -= 1;
340 			end.tv_usec += 1000000;
341 		}
342 		fprintf(fp, "%d ms.\n", (end.tv_sec*1000+end.tv_usec/1000));
343 #endif
344 		for(i=1; i<slvcount; i++) {
345 			if (hp[i].delta == HOSTDOWN) {
346 				rmmach(i);
347 #ifdef MEASURE
348 				header = ON;
349 #endif
350 			}
351 		}
352 	} else {
353 		if (status & SLAVE) {
354 			correct(mydelta);
355 		}
356 	}
357 }
358 
359 /*
360  * 'spreadtime' sends the time to each slave after the master
361  * has received the command to set the network time
362  */
363 
364 spreadtime()
365 {
366 	int i;
367 	struct tsp to;
368 	struct tsp *answer, *acksend();
369 
370 	for(i=1; i<slvcount; i++) {
371 		to.tsp_type = TSP_SETTIME;
372 		(void)strcpy(to.tsp_name, hostname);
373 		(void)gettimeofday(&to.tsp_time, (struct timezone *)0);
374 		answer = acksend(&to, &hp[i].addr, hp[i].name, TSP_ACK,
375 		    (struct netinfo *)NULL);
376 		if (answer == NULL) {
377 			syslog(LOG_WARNING,
378 			    "no reply to SETTIME from: %s", hp[i].name);
379 		}
380 	}
381 }
382 
383 findhost(name)
384 char *name;
385 {
386 	int i;
387 	int ind;
388 
389 	ind = -1;
390 	for (i=1; i<slvcount; i++) {
391 		if (strcmp(name, hp[i].name) == 0) {
392 			ind = i;
393 			break;
394 		}
395 	}
396 	return(ind);
397 }
398 
399 /*
400  * 'addmach' adds a host to the list of controlled machines
401  * if not already there
402  */
403 
404 addmach(name, addr)
405 char *name;
406 struct sockaddr_in *addr;
407 {
408 	int ret;
409 	int findhost();
410 
411 	ret = findhost(name);
412 	if (ret < 0) {
413 		hp[slvcount].addr = *addr;
414 		hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN);
415 		(void)strcpy(hp[slvcount].name, name);
416 		hp[slvcount].seq = 0;
417 		ret = slvcount;
418 		if (slvcount < NHOSTS)
419 			slvcount++;
420 		else {
421 			syslog(LOG_ERR, "no more slots in host table");
422 		}
423 	} else {
424 		/* need to clear sequence number anyhow */
425 		hp[ret].seq = 0;
426 	}
427 #ifdef MEASURE
428 	header = ON;
429 #endif
430 	return(ret);
431 }
432 
433 /*
434  * Remove all the machines from the host table that exist on the given
435  * network.  This is called when a master transitions to a slave on a
436  * given network.
437  */
438 
439 rmnetmachs(ntp)
440 	register struct netinfo *ntp;
441 {
442 	int i;
443 
444 	if (trace)
445 		prthp();
446 	for (i = 1; i < slvcount; i++)
447 		if ((hp[i].addr.sin_addr.s_addr & ntp->mask) == ntp->net)
448 			rmmach(i--);
449 	if (trace)
450 		prthp();
451 }
452 
453 /*
454  * remove the machine with the given index in the host table.
455  */
456 rmmach(ind)
457 	int ind;
458 {
459 	if (trace)
460 		fprintf(fd, "rmmach: %s\n", hp[ind].name);
461 	free(hp[ind].name);
462 	hp[ind] = hp[--slvcount];
463 }
464 
465 prthp()
466 {
467 	int i;
468 
469 	fprintf(fd, "host table:");
470 	for (i=1; i<slvcount; i++)
471 		fprintf(fd, " %s", hp[i].name);
472 	fprintf(fd, "\n");
473 }
474 
475 masterup(net)
476 struct netinfo *net;
477 {
478 	struct timeval wait;
479 	struct tsp to, *msg, *readmsg();
480 
481 	to.tsp_type = TSP_MASTERUP;
482 	to.tsp_vers = TSPVERSION;
483 	(void)strcpy(to.tsp_name, hostname);
484 	bytenetorder(&to);
485 	if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, &net->dest_addr,
486 	    sizeof(struct sockaddr_in)) < 0) {
487 		syslog(LOG_ERR, "sendto: %m");
488 		exit(1);
489 	}
490 
491 	for (;;) {
492 		wait.tv_sec = 1;
493 		wait.tv_usec = 0;
494 		msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait, net);
495 		if (msg != NULL) {
496 			(void) addmach(msg->tsp_name, &from);
497 		} else
498 			break;
499 	}
500 }
501 
502 newslave(ind, seq)
503 u_short seq;
504 {
505 	struct tsp to;
506 	struct tsp *answer, *acksend();
507 
508 	if (trace)
509 		prthp();
510 	if (seq == 0 || hp[ind].seq !=  seq) {
511 		hp[ind].seq = seq;
512 		to.tsp_type = TSP_SETTIME;
513 		(void)strcpy(to.tsp_name, hostname);
514 		/*
515 		 * give the upcoming slave the time
516 		 * to check its input queue before
517 		 * setting the time
518 		 */
519 		sleep(1);
520 		(void) gettimeofday(&to.tsp_time,
521 		    (struct timezone *)0);
522 		answer = acksend(&to, &hp[ind].addr,
523 		    hp[ind].name, TSP_ACK,
524 		    (struct netinfo *)NULL);
525 		if (answer == NULL) {
526 			syslog(LOG_WARNING,
527 			    "no reply to initial SETTIME from: %s",
528 			    hp[ind].name);
529 			rmmach(ind);
530 		}
531 	}
532 }
533 
534 char *wtmpfile = "/usr/adm/wtmp";
535 struct utmp wtmp[2] = {
536 	{ "|", "", "", 0 },
537 	{ "{", "", "", 0 }
538 };
539 
540 logwtmp(otime, ntime)
541 struct timeval otime, ntime;
542 {
543 	int f;
544 
545 	wtmp[0].ut_time = otime.tv_sec + (otime.tv_usec + 500000) / 1000000;
546 	wtmp[1].ut_time = ntime.tv_sec + (ntime.tv_usec + 500000) / 1000000;
547 	if (wtmp[0].ut_time == wtmp[1].ut_time)
548 		return;
549 	if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
550 		(void) write(f, (char *)wtmp, sizeof(wtmp));
551 		(void) close(f);
552 	}
553 }
554