be careful with measure file pointer, multiple quits can cause problems
[unix-history] / usr / src / usr.sbin / timed / timed / master.c
CommitLineData
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 8static 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 17extern int machup;
dc32c900 18extern int measure_delta;
75d5ce8a 19extern jmp_buf jmpenv;
dc32c900 20
77491c98 21extern u_short sequence;
e472b464 22
dc32c900
RG
23#ifdef MEASURE
24int header;
ece8decf 25FILE *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
38master()
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
72loop:
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
276synch(mydelta)
277long 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
364spreadtime()
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
383findhost(name)
384char *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 404addmach(name, addr)
dc32c900 405char *name;
75d5ce8a 406struct 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
439rmnetmachs(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 */
456rmmach(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
465prthp()
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
475masterup(net)
476struct 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
502newslave(ind, seq)
77491c98 503u_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
534char *wtmpfile = "/usr/adm/wtmp";
535struct utmp wtmp[2] = {
536 { "|", "", "", 0 },
537 { "{", "", "", 0 }
538};
539
e81c670f
JL
540logwtmp(otime, ntime)
541struct 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}