Commit | Line | Data |
---|---|---|
dc32c900 | 1 | /* |
2de37ed5 | 2 | * Copyright (c) 1985 Regents of the University of California. |
dc32c900 RG |
3 | * All rights reserved. The Berkeley software License Agreement |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
ece8decf | 8 | static char sccsid[] = "@(#)master.c 2.14 (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 | |
77491c98 | 21 | extern u_short sequence; |
e472b464 | 22 | |
dc32c900 RG |
23 | #ifdef MEASURE |
24 | int header; | |
ece8decf | 25 | FILE *fp = NULL; |
dc32c900 RG |
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; | |
dc32c900 RG |
41 | long pollingtime; |
42 | struct timeval wait; | |
43 | struct timeval time; | |
e81c670f | 44 | struct timeval otime; |
dc32c900 | 45 | struct timezone tzone; |
dc32c900 RG |
46 | struct tsp *msg, to; |
47 | struct sockaddr_in saveaddr; | |
dc32c900 RG |
48 | int findhost(); |
49 | char *date(); | |
dc32c900 RG |
50 | struct tsp *readmsg(); |
51 | struct tsp *answer, *acksend(); | |
65215728 | 52 | char olddate[32]; |
75d5ce8a JB |
53 | struct sockaddr_in server; |
54 | register struct netinfo *ntp; | |
dc32c900 RG |
55 | |
56 | #ifdef MEASURE | |
ece8decf JB |
57 | if (fp == NULL) { |
58 | fp = fopen("/usr/adm/timed.masterlog", "w"); | |
59 | setlinebuf(fp); | |
60 | } | |
dc32c900 RG |
61 | #endif |
62 | ||
2de37ed5 | 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; | |
03b1f474 | 81 | to.tsp_seq = sequence++; |
e472b464 JB |
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); | |
e472b464 JB |
89 | } |
90 | } | |
dc32c900 RG |
91 | } |
92 | ||
93 | wait.tv_sec = pollingtime - time.tv_sec; | |
94 | wait.tv_usec = 0; | |
75d5ce8a | 95 | msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL); |
dc32c900 RG |
96 | if (msg != NULL) { |
97 | switch (msg->tsp_type) { | |
98 | ||
99 | case TSP_MASTERREQ: | |
dc32c900 RG |
100 | break; |
101 | case TSP_SLAVEUP: | |
9a957f87 JB |
102 | ind = addmach(msg->tsp_name, &from); |
103 | newslave(ind, msg->tsp_seq); | |
dc32c900 | 104 | break; |
5807ba7f | 105 | case TSP_SETDATE: |
dc32c900 | 106 | saveaddr = from; |
65215728 JB |
107 | /* |
108 | * the following line is necessary due to syslog | |
109 | * calling ctime() which clobbers the static buffer | |
110 | */ | |
111 | (void)strcpy(olddate, date()); | |
dc32c900 | 112 | (void)gettimeofday(&time, &tzone); |
e81c670f | 113 | otime = time; |
5807ba7f JB |
114 | time.tv_sec = msg->tsp_time.tv_sec; |
115 | time.tv_usec = msg->tsp_time.tv_usec; | |
dc32c900 | 116 | (void)settimeofday(&time, &tzone); |
f99c5f73 | 117 | syslog(LOG_NOTICE, "date changed from: %s", olddate); |
e81c670f | 118 | logwtmp(otime, time); |
dc32c900 RG |
119 | msg->tsp_type = TSP_DATEACK; |
120 | msg->tsp_vers = TSPVERSION; | |
121 | (void)strcpy(msg->tsp_name, hostname); | |
122 | bytenetorder(msg); | |
dc32c900 | 123 | if (sendto(sock, (char *)msg, sizeof(struct tsp), 0, |
75d5ce8a | 124 | &saveaddr, sizeof(struct sockaddr_in)) < 0) { |
f99c5f73 | 125 | syslog(LOG_ERR, "sendto: %m"); |
dc32c900 RG |
126 | exit(1); |
127 | } | |
128 | spreadtime(); | |
129 | pollingtime = 0; | |
130 | break; | |
5807ba7f | 131 | case TSP_SETDATEREQ: |
dc32c900 RG |
132 | ind = findhost(msg->tsp_name); |
133 | if (ind < 0) { | |
2de37ed5 | 134 | syslog(LOG_WARNING, |
f99c5f73 JB |
135 | "DATEREQ from uncontrolled machine"); |
136 | break; | |
dc32c900 RG |
137 | } |
138 | if (hp[ind].seq != msg->tsp_seq) { | |
139 | hp[ind].seq = msg->tsp_seq; | |
65215728 JB |
140 | /* |
141 | * the following line is necessary due to syslog | |
142 | * calling ctime() which clobbers the static buffer | |
143 | */ | |
144 | (void)strcpy(olddate, date()); | |
dc32c900 | 145 | (void)gettimeofday(&time, &tzone); |
e81c670f | 146 | otime = time; |
5807ba7f JB |
147 | time.tv_sec = msg->tsp_time.tv_sec; |
148 | time.tv_usec = msg->tsp_time.tv_usec; | |
dc32c900 | 149 | (void)settimeofday(&time, &tzone); |
f99c5f73 JB |
150 | syslog(LOG_NOTICE, |
151 | "date changed by %s from: %s", | |
152 | msg->tsp_name, olddate); | |
e81c670f | 153 | logwtmp(otime, time); |
dc32c900 RG |
154 | spreadtime(); |
155 | pollingtime = 0; | |
156 | } | |
157 | break; | |
158 | case TSP_MSITE: | |
dc32c900 RG |
159 | case TSP_MSITEREQ: |
160 | break; | |
161 | case TSP_TRACEON: | |
162 | if (!(trace)) { | |
75d5ce8a | 163 | fd = fopen(tracefile, "w"); |
9799484a | 164 | setlinebuf(fd); |
dc32c900 RG |
165 | fprintf(fd, "Tracing started on: %s\n\n", |
166 | date()); | |
dc32c900 RG |
167 | } |
168 | trace = ON; | |
169 | break; | |
170 | case TSP_TRACEOFF: | |
171 | if (trace) { | |
172 | fprintf(fd, "Tracing ended on: %s\n", date()); | |
f99c5f73 | 173 | (void)fclose(fd); |
dc32c900 | 174 | } |
75d5ce8a JB |
175 | #ifdef GPROF |
176 | moncontrol(0); | |
177 | _mcleanup(); | |
178 | moncontrol(1); | |
179 | #endif | |
dc32c900 RG |
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; | |
75d5ce8a JB |
186 | answer = acksend(&to, &server, msg->tsp_name, TSP_ACK, |
187 | (struct netinfo *)NULL); | |
dc32c900 | 188 | if (answer == NULL) { |
f99c5f73 | 189 | syslog(LOG_ERR, "election error"); |
dc32c900 | 190 | } else { |
75d5ce8a | 191 | (void) addmach(msg->tsp_name, &from); |
dc32c900 RG |
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 | ||
9a957f87 | 204 | if (fromnet == NULL) |
75d5ce8a | 205 | break; |
dc32c900 RG |
206 | for(;;) { |
207 | to.tsp_type = TSP_RESOLVE; | |
9a957f87 JB |
208 | answer = acksend(&to, &fromnet->dest_addr, |
209 | (char *)ANYADDR, TSP_MASTERACK, fromnet); | |
dc32c900 RG |
210 | if (answer == NULL) |
211 | break; | |
dc32c900 RG |
212 | to.tsp_type = TSP_QUIT; |
213 | server = from; | |
b6cf6f3e JB |
214 | msg = acksend(&to, &server, answer->tsp_name, |
215 | TSP_ACK, (struct netinfo *)NULL); | |
dc32c900 | 216 | if (msg == NULL) { |
f99c5f73 JB |
217 | syslog(LOG_ERR, "error on sending QUIT"); |
218 | } else { | |
75d5ce8a | 219 | (void) addmach(answer->tsp_name, &from); |
dc32c900 RG |
220 | } |
221 | } | |
9a957f87 | 222 | masterup(fromnet); |
dc32c900 RG |
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 | |
ece8decf JB |
236 | if (fp != NULL) { |
237 | (void)fclose(fp); | |
238 | fp = NULL; | |
239 | } | |
dc32c900 RG |
240 | #endif |
241 | longjmp(jmpenv, 2); | |
242 | break; | |
e472b464 JB |
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) { | |
2de37ed5 MK |
255 | syslog(LOG_WARNING, |
256 | "loop breakage: no reply to QUIT"); | |
e472b464 JB |
257 | } else { |
258 | (void)addmach(msg->tsp_name, &from); | |
259 | } | |
dc32c900 RG |
260 | default: |
261 | if (trace) { | |
262 | fprintf(fd, "garbage: "); | |
9a957f87 | 263 | print(msg, &from); |
dc32c900 RG |
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 | ||
75d5ce8a JB |
276 | synch(mydelta) |
277 | long mydelta; | |
dc32c900 RG |
278 | { |
279 | int i; | |
280 | int measure_status; | |
281 | long netdelta; | |
282 | struct timeval tack; | |
283 | #ifdef MEASURE | |
284 | #define MAXLINES 8 | |
f99c5f73 | 285 | static int lines = 1; |
dc32c900 RG |
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++) { | |
dc32c900 | 308 | tack.tv_sec = 0; |
5807ba7f JB |
309 | tack.tv_usec = 500000; |
310 | if ((measure_status = measure(&tack, &hp[i].addr)) <0) { | |
f99c5f73 | 311 | syslog(LOG_ERR, "measure: %m"); |
dc32c900 RG |
312 | exit(1); |
313 | } | |
314 | hp[i].delta = measure_delta; | |
315 | if (measure_status == GOOD) | |
316 | machup++; | |
317 | } | |
75d5ce8a JB |
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 | } | |
dc32c900 RG |
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)); | |
dc32c900 RG |
343 | #endif |
344 | for(i=1; i<slvcount; i++) { | |
345 | if (hp[i].delta == HOSTDOWN) { | |
9a957f87 | 346 | rmmach(i); |
dc32c900 RG |
347 | #ifdef MEASURE |
348 | header = ON; | |
349 | #endif | |
350 | } | |
351 | } | |
75d5ce8a JB |
352 | } else { |
353 | if (status & SLAVE) { | |
354 | correct(mydelta); | |
355 | } | |
dc32c900 RG |
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; | |
dc32c900 RG |
367 | struct tsp to; |
368 | struct tsp *answer, *acksend(); | |
369 | ||
370 | for(i=1; i<slvcount; i++) { | |
dc32c900 | 371 | to.tsp_type = TSP_SETTIME; |
dc32c900 | 372 | (void)strcpy(to.tsp_name, hostname); |
f99c5f73 | 373 | (void)gettimeofday(&to.tsp_time, (struct timezone *)0); |
75d5ce8a JB |
374 | answer = acksend(&to, &hp[i].addr, hp[i].name, TSP_ACK, |
375 | (struct netinfo *)NULL); | |
dc32c900 | 376 | if (answer == NULL) { |
2de37ed5 MK |
377 | syslog(LOG_WARNING, |
378 | "no reply to SETTIME from: %s", hp[i].name); | |
dc32c900 RG |
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 | ||
75d5ce8a | 404 | addmach(name, addr) |
dc32c900 | 405 | char *name; |
75d5ce8a | 406 | struct sockaddr_in *addr; |
dc32c900 RG |
407 | { |
408 | int ret; | |
409 | int findhost(); | |
dc32c900 RG |
410 | |
411 | ret = findhost(name); | |
412 | if (ret < 0) { | |
75d5ce8a | 413 | hp[slvcount].addr = *addr; |
f99c5f73 JB |
414 | hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN); |
415 | (void)strcpy(hp[slvcount].name, name); | |
dc32c900 RG |
416 | hp[slvcount].seq = 0; |
417 | ret = slvcount; | |
418 | if (slvcount < NHOSTS) | |
419 | slvcount++; | |
420 | else { | |
2de37ed5 | 421 | syslog(LOG_ERR, "no more slots in host table"); |
dc32c900 RG |
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 | ||
4b50a4be JB |
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); | |
9a957f87 JB |
461 | free(hp[ind].name); |
462 | hp[ind] = hp[--slvcount]; | |
4b50a4be JB |
463 | } |
464 | ||
dc32c900 RG |
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 | ||
75d5ce8a JB |
475 | masterup(net) |
476 | struct netinfo *net; | |
dc32c900 RG |
477 | { |
478 | struct timeval wait; | |
dc32c900 RG |
479 | struct tsp to, *msg, *readmsg(); |
480 | ||
481 | to.tsp_type = TSP_MASTERUP; | |
75d5ce8a | 482 | to.tsp_vers = TSPVERSION; |
dc32c900 | 483 | (void)strcpy(to.tsp_name, hostname); |
75d5ce8a JB |
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 | } | |
dc32c900 RG |
490 | |
491 | for (;;) { | |
492 | wait.tv_sec = 1; | |
493 | wait.tv_usec = 0; | |
75d5ce8a | 494 | msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait, net); |
dc32c900 | 495 | if (msg != NULL) { |
0e37d038 | 496 | (void) addmach(msg->tsp_name, &from); |
dc32c900 RG |
497 | } else |
498 | break; | |
499 | } | |
500 | } | |
9a957f87 JB |
501 | |
502 | newslave(ind, seq) | |
77491c98 | 503 | u_short seq; |
9a957f87 JB |
504 | { |
505 | struct tsp to; | |
506 | struct tsp *answer, *acksend(); | |
9a957f87 JB |
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); | |
0e37d038 | 520 | (void) gettimeofday(&to.tsp_time, |
9a957f87 | 521 | (struct timezone *)0); |
9a957f87 JB |
522 | answer = acksend(&to, &hp[ind].addr, |
523 | hp[ind].name, TSP_ACK, | |
524 | (struct netinfo *)NULL); | |
525 | if (answer == NULL) { | |
2de37ed5 MK |
526 | syslog(LOG_WARNING, |
527 | "no reply to initial SETTIME from: %s", | |
9a957f87 JB |
528 | hp[ind].name); |
529 | rmmach(ind); | |
530 | } | |
531 | } | |
532 | } | |
e81c670f JL |
533 | |
534 | char *wtmpfile = "/usr/adm/wtmp"; | |
535 | struct utmp wtmp[2] = { | |
536 | { "|", "", "", 0 }, | |
537 | { "{", "", "", 0 } | |
538 | }; | |
539 | ||
e81c670f JL |
540 | logwtmp(otime, ntime) |
541 | struct timeval otime, ntime; | |
542 | { | |
543 | int f; | |
544 | ||
20ee898c JL |
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) | |
e81c670f | 548 | return; |
e81c670f JL |
549 | if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) { |
550 | (void) write(f, (char *)wtmp, sizeof(wtmp)); | |
551 | (void) close(f); | |
552 | } | |
553 | } |