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