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