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