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