should ack QUIT's as well
[unix-history] / usr / src / usr.sbin / timed / timed / master.c
CommitLineData
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
65215728 8static char sccsid[] = "@(#)master.c 1.3 (Berkeley) %G%";
dc32c900
RG
9#endif not lint
10
11#include "globals.h"
12#include <protocols/timed.h>
13#include <setjmp.h>
14
dc32c900
RG
15extern struct sockaddr_in from;
16extern struct sockaddr_in server;
17
18extern int trace;
19extern int machup;
20extern int slvcount;
21extern int measure_delta;
22extern int sock;
23extern char hostname[];
24extern struct host hp[];
25extern char *fj;
26extern FILE *fd;
27
28#ifdef MEASURE
29int header;
30char *fi;
31FILE *fp;
32#endif
33
34/*
35 * The main function of `master' is to periodically compute the differences
36 * (deltas) between its clock and the clocks of the slaves, to compute the
37 * network average delta, and to send to the slaves the differences between
38 * their individual deltas and the network delta.
39 * While waiting, it receives messages from the slaves (i.e. requests for
40 * master's name, remote requests to set the network time, ...), and
41 * takes the appropriate action.
42 */
43
44master()
45{
46 int ind;
47 int length;
48 long pollingtime;
49 struct timeval wait;
50 struct timeval time;
51 struct timezone tzone;
52 struct timeval mytime;
53 struct tsp *msg, to;
54 struct sockaddr_in saveaddr;
55 extern jmp_buf jmpenv;
56 int findhost();
57 char *date();
58 char *strcpy();
59 struct tsp *readmsg();
60 struct tsp *answer, *acksend();
65215728 61 char olddate[32];
dc32c900
RG
62
63#ifdef MEASURE
64 fi = "/usr/adm/timed.masterlog";
65 fp = fopen(fi, "w");
66#endif
67
f99c5f73 68 syslog(LOG_INFO, "THIS MACHINE IS MASTER");
dc32c900
RG
69 if (trace)
70 fprintf(fd, "THIS MACHINE IS MASTER\n");
71
72 masterup();
73 pollingtime = 0;
74
75loop:
76 (void)gettimeofday(&time, (struct timezone *)0);
77 if (time.tv_sec >= pollingtime) {
78 pollingtime = time.tv_sec + SAMPLEINTVL;
79 synch();
80 }
81
82 wait.tv_sec = pollingtime - time.tv_sec;
83 wait.tv_usec = 0;
84 msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait);
85 if (msg != NULL) {
86 switch (msg->tsp_type) {
87
88 case TSP_MASTERREQ:
89 ind = addmach(msg->tsp_name);
90 if (trace)
91 prthp();
92 if (hp[ind].seq != msg->tsp_seq) {
93 hp[ind].seq = msg->tsp_seq;
94 bcopy((char *)&hp[ind].addr,
95 (char *)&(server.sin_addr.s_addr),
96 hp[ind].length);
97 to.tsp_type = TSP_SETTIME;
98 (void)strcpy(to.tsp_name, hostname);
99 /*
100 * give the upcoming slave the time
101 * to check its input queue before
102 * setting the time
103 */
104 sleep(1);
105 to.tsp_time.tv_usec = 0;
106 (void)gettimeofday(&mytime, (struct timezone *)0);
107 to.tsp_time.tv_sec = mytime.tv_sec;
108 answer = acksend(&to, hp[ind].name, TSP_ACK);
109 if (answer == NULL) {
f99c5f73 110 syslog(LOG_ERR, "ERROR ON SETTIME machine: %s", hp[ind].name);
dc32c900
RG
111 slvcount--;
112 }
dc32c900
RG
113 }
114 break;
115 case TSP_SLAVEUP:
116 (void) addmach(msg->tsp_name);
dc32c900
RG
117 break;
118 case TSP_DATE:
119 saveaddr = from;
120 msg->tsp_time.tv_usec = 0;
65215728
JB
121 /*
122 * the following line is necessary due to syslog
123 * calling ctime() which clobbers the static buffer
124 */
125 (void)strcpy(olddate, date());
dc32c900
RG
126 (void)gettimeofday(&time, &tzone);
127 time.tv_sec += msg->tsp_time.tv_sec;
128 time.tv_sec++;
129 (void)settimeofday(&time, &tzone);
f99c5f73 130 syslog(LOG_NOTICE, "date changed from: %s", olddate);
dc32c900
RG
131 msg->tsp_type = TSP_DATEACK;
132 msg->tsp_vers = TSPVERSION;
133 (void)strcpy(msg->tsp_name, hostname);
134 bytenetorder(msg);
135 length = sizeof(struct sockaddr_in);
136 if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
137 &saveaddr, length) < 0) {
f99c5f73 138 syslog(LOG_ERR, "sendto: %m");
dc32c900
RG
139 exit(1);
140 }
141 spreadtime();
142 pollingtime = 0;
143 break;
144 case TSP_DATEREQ:
145 ind = findhost(msg->tsp_name);
146 if (ind < 0) {
f99c5f73
JB
147 syslog(LOG_ERR,
148 "DATEREQ from uncontrolled machine");
149 break;
dc32c900
RG
150 }
151 if (hp[ind].seq != msg->tsp_seq) {
152 hp[ind].seq = msg->tsp_seq;
153 msg->tsp_time.tv_usec = 0;
65215728
JB
154 /*
155 * the following line is necessary due to syslog
156 * calling ctime() which clobbers the static buffer
157 */
158 (void)strcpy(olddate, date());
dc32c900
RG
159 (void)gettimeofday(&time, &tzone);
160 time.tv_sec += msg->tsp_time.tv_sec;
161 time.tv_sec++;
162 (void)settimeofday(&time, &tzone);
f99c5f73
JB
163 syslog(LOG_NOTICE,
164 "date changed by %s from: %s",
165 msg->tsp_name, olddate);
dc32c900
RG
166 spreadtime();
167 pollingtime = 0;
168 }
169 break;
170 case TSP_MSITE:
171 msg->tsp_type = TSP_ACK;
172 msg->tsp_vers = TSPVERSION;
173 (void)strcpy(msg->tsp_name, hostname);
174 bytenetorder(msg);
175 length = sizeof(struct sockaddr_in);
176 if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
177 &from, length) < 0) {
f99c5f73 178 syslog(LOG_ERR, "sendto: %m");
dc32c900
RG
179 exit(1);
180 }
181 break;
182 case TSP_MSITEREQ:
183 break;
184 case TSP_TRACEON:
185 if (!(trace)) {
186 fd = fopen(fj, "w");
187 fprintf(fd, "Tracing started on: %s\n\n",
188 date());
189 (void)fflush(fd);
190 }
191 trace = ON;
192 break;
193 case TSP_TRACEOFF:
194 if (trace) {
195 fprintf(fd, "Tracing ended on: %s\n", date());
196 (void)fflush(fd);
f99c5f73 197 (void)fclose(fd);
dc32c900
RG
198 }
199 trace = OFF;
200 break;
201 case TSP_ELECTION:
202 to.tsp_type = TSP_QUIT;
203 (void)strcpy(to.tsp_name, hostname);
204 server = from;
205 answer = acksend(&to, msg->tsp_name, TSP_ACK);
206 if (answer == NULL) {
f99c5f73 207 syslog(LOG_ERR, "election error");
dc32c900
RG
208 } else {
209 (void) addmach(msg->tsp_name);
210 }
211 pollingtime = 0;
212 break;
213 case TSP_CONFLICT:
214 /*
215 * After a network partition, there can be
216 * more than one master: the first slave to
217 * come up will notify here the situation.
218 */
219
220 (void)strcpy(to.tsp_name, hostname);
221
222 for(;;) {
223 to.tsp_type = TSP_RESOLVE;
224 answer = acksend(&to, (char *)ANYADDR,
225 TSP_MASTERACK);
226 if (answer == NULL)
227 break;
dc32c900
RG
228 to.tsp_type = TSP_QUIT;
229 server = from;
230 msg = acksend(&to, answer->tsp_name,
231 TSP_MASTERACK);
232 if (msg == NULL) {
f99c5f73
JB
233 syslog(LOG_ERR, "error on sending QUIT");
234 } else {
235 (void) addmach(answer->tsp_name);
dc32c900
RG
236 }
237 }
238 masterup();
239 pollingtime = 0;
240 break;
241 case TSP_RESOLVE:
242 /*
243 * do not want to call synch() while waiting
244 * to be killed!
245 */
246 (void)gettimeofday(&time, (struct timezone *)0);
247 pollingtime = time.tv_sec + SAMPLEINTVL;
248 break;
249 case TSP_QUIT:
250 /* become slave */
251#ifdef MEASURE
f99c5f73 252 (void)fclose(fp);
dc32c900
RG
253#endif
254 longjmp(jmpenv, 2);
255 break;
256 default:
257 if (trace) {
258 fprintf(fd, "garbage: ");
259 print(msg);
260 }
261 break;
262 }
263 }
264 goto loop;
265}
266
267/*
268 * `synch' synchronizes all the slaves by calling measure,
269 * networkdelta and correct
270 */
271
272synch()
273{
274 int i;
275 int measure_status;
276 long netdelta;
277 struct timeval tack;
278#ifdef MEASURE
279#define MAXLINES 8
f99c5f73 280 static int lines = 1;
dc32c900
RG
281 struct timeval start, end;
282#endif
283 int measure();
284 int correct();
285 long networkdelta();
286 char *date();
287
288 if (slvcount > 1) {
289#ifdef MEASURE
290 (void)gettimeofday(&start, (struct timezone *)0);
291 if (header == ON || --lines == 0) {
292 fprintf(fp, "%s\n", date());
293 for (i=0; i<slvcount; i++)
294 fprintf(fp, "%.7s\t", hp[i].name);
295 fprintf(fp, "\n");
296 lines = MAXLINES;
297 header = OFF;
298 }
299#endif
300 machup = 1;
301 hp[0].delta = 0;
302 for(i=1; i<slvcount; i++) {
303 bcopy((char *)&hp[i].addr,
304 (char *)&(server.sin_addr.s_addr),
305 hp[i].length);
306 tack.tv_sec = 0;
307 tack.tv_usec = 100000;
308 if ((measure_status = measure(&tack, ON)) < 0) {
f99c5f73 309 syslog(LOG_ERR, "measure: %m");
dc32c900
RG
310 exit(1);
311 }
312 hp[i].delta = measure_delta;
313 if (measure_status == GOOD)
314 machup++;
315 }
316 if (machup > 1) {
317 netdelta = networkdelta();
318 correct(netdelta);
319 }
320#ifdef MEASURE
321 gettimeofday(&end, 0);
322 end.tv_sec -= start.tv_sec;
323 end.tv_usec -= start.tv_usec;
324 if (end.tv_usec < 0) {
325 end.tv_sec -= 1;
326 end.tv_usec += 1000000;
327 }
328 fprintf(fp, "%d ms.\n", (end.tv_sec*1000+end.tv_usec/1000));
329 (void)fflush(fp);
330#endif
331 for(i=1; i<slvcount; i++) {
332 if (hp[i].delta == HOSTDOWN) {
333 free((char *)hp[i].name);
334 hp[i] = hp[--slvcount];
335#ifdef MEASURE
336 header = ON;
337#endif
338 }
339 }
340 }
341}
342
343/*
344 * 'spreadtime' sends the time to each slave after the master
345 * has received the command to set the network time
346 */
347
348spreadtime()
349{
350 int i;
dc32c900
RG
351 struct tsp to;
352 struct tsp *answer, *acksend();
353
354 for(i=1; i<slvcount; i++) {
355 bcopy((char *)&hp[i].addr, (char *)&(server.sin_addr.s_addr),
356 hp[i].length);
357 to.tsp_type = TSP_SETTIME;
dc32c900 358 (void)strcpy(to.tsp_name, hostname);
f99c5f73 359 (void)gettimeofday(&to.tsp_time, (struct timezone *)0);
dc32c900
RG
360 answer = acksend(&to, hp[i].name, TSP_ACK);
361 if (answer == NULL) {
f99c5f73 362 syslog(LOG_ERR, "ERROR ON SETTIME machine: %s", hp[i].name);
dc32c900
RG
363 }
364 }
365}
366
367findhost(name)
368char *name;
369{
370 int i;
371 int ind;
372
373 ind = -1;
374 for (i=1; i<slvcount; i++) {
375 if (strcmp(name, hp[i].name) == 0) {
376 ind = i;
377 break;
378 }
379 }
380 return(ind);
381}
382
383/*
384 * 'addmach' adds a host to the list of controlled machines
385 * if not already there
386 */
387
388addmach(name)
389char *name;
390{
391 int ret;
392 int findhost();
393 char *malloc();
394 struct hostent *hptmp, *gethostbyname();
395
396 ret = findhost(name);
397 if (ret < 0) {
398 hptmp = gethostbyname(name);
399 if (hptmp == NULL) {
f99c5f73 400 syslog(LOG_ERR, "gethostbyname: %m");
dc32c900
RG
401 exit(1);
402 }
403 hp[slvcount].length = hptmp->h_length;
404 bcopy((char *)hptmp->h_addr, (char *)&hp[slvcount].addr,
405 hptmp->h_length);
f99c5f73
JB
406 hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN);
407 (void)strcpy(hp[slvcount].name, name);
dc32c900
RG
408 hp[slvcount].seq = 0;
409 ret = slvcount;
410 if (slvcount < NHOSTS)
411 slvcount++;
412 else {
f99c5f73 413 syslog(LOG_ALERT, "no more slots in host table");
dc32c900
RG
414 }
415 } else {
416 /* need to clear sequence number anyhow */
417 hp[ret].seq = 0;
418 }
419#ifdef MEASURE
420 header = ON;
421#endif
422 return(ret);
423}
424
425prthp()
426{
427 int i;
428
429 fprintf(fd, "host table:");
430 for (i=1; i<slvcount; i++)
431 fprintf(fd, " %s", hp[i].name);
432 fprintf(fd, "\n");
433}
434
435masterup()
436{
437 struct timeval wait;
438 char *strcpy();
439 struct tsp to, *msg, *readmsg();
440
441 to.tsp_type = TSP_MASTERUP;
442 (void)strcpy(to.tsp_name, hostname);
443 broadcast(&to);
444
445 for (;;) {
446 wait.tv_sec = 1;
447 wait.tv_usec = 0;
448 msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait);
449 if (msg != NULL) {
450 (void) addmach(msg->tsp_name);
451 } else
452 break;
453 }
454}