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