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