1 /* 2 * Copyright (c) 1985 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)master.c 2.15 (Berkeley) 12/23/87"; 15 #endif /* not lint */ 16 17 #include "globals.h" 18 #include <protocols/timed.h> 19 #include <sys/file.h> 20 #include <setjmp.h> 21 #include <utmp.h> 22 23 extern int machup; 24 extern int measure_delta; 25 extern jmp_buf jmpenv; 26 27 extern u_short sequence; 28 29 #ifdef MEASURE 30 int header; 31 FILE *fp = NULL; 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 44 master() 45 { 46 int ind; 47 long pollingtime; 48 struct timeval wait; 49 struct timeval time; 50 struct timeval otime; 51 struct timezone tzone; 52 struct tsp *msg, to; 53 struct sockaddr_in saveaddr; 54 int findhost(); 55 char *date(); 56 struct tsp *readmsg(); 57 struct tsp *answer, *acksend(); 58 char olddate[32]; 59 struct sockaddr_in server; 60 register struct netinfo *ntp; 61 62 #ifdef MEASURE 63 if (fp == NULL) { 64 fp = fopen("/usr/adm/timed.masterlog", "w"); 65 setlinebuf(fp); 66 } 67 #endif 68 69 syslog(LOG_INFO, "This machine is master"); 70 if (trace) 71 fprintf(fd, "THIS MACHINE IS MASTER\n"); 72 73 for (ntp = nettab; ntp != NULL; ntp = ntp->next) 74 if (ntp->status == MASTER) 75 masterup(ntp); 76 pollingtime = 0; 77 78 loop: 79 (void)gettimeofday(&time, (struct timezone *)0); 80 if (time.tv_sec >= pollingtime) { 81 pollingtime = time.tv_sec + SAMPLEINTVL; 82 synch(0L); 83 84 for (ntp = nettab; ntp != NULL; ntp = ntp->next) { 85 to.tsp_type = TSP_LOOP; 86 to.tsp_vers = TSPVERSION; 87 to.tsp_seq = sequence++; 88 to.tsp_hopcnt = 10; 89 (void)strcpy(to.tsp_name, hostname); 90 bytenetorder(&to); 91 if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, 92 &ntp->dest_addr, sizeof(struct sockaddr_in)) < 0) { 93 syslog(LOG_ERR, "sendto: %m"); 94 exit(1); 95 } 96 } 97 } 98 99 wait.tv_sec = pollingtime - time.tv_sec; 100 wait.tv_usec = 0; 101 msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL); 102 if (msg != NULL) { 103 switch (msg->tsp_type) { 104 105 case TSP_MASTERREQ: 106 break; 107 case TSP_SLAVEUP: 108 ind = addmach(msg->tsp_name, &from); 109 newslave(ind, msg->tsp_seq); 110 break; 111 case TSP_SETDATE: 112 saveaddr = from; 113 /* 114 * the following line is necessary due to syslog 115 * calling ctime() which clobbers the static buffer 116 */ 117 (void)strcpy(olddate, date()); 118 (void)gettimeofday(&time, &tzone); 119 otime = time; 120 time.tv_sec = msg->tsp_time.tv_sec; 121 time.tv_usec = msg->tsp_time.tv_usec; 122 (void)settimeofday(&time, &tzone); 123 syslog(LOG_NOTICE, "date changed from: %s", olddate); 124 logwtmp(otime, time); 125 msg->tsp_type = TSP_DATEACK; 126 msg->tsp_vers = TSPVERSION; 127 (void)strcpy(msg->tsp_name, hostname); 128 bytenetorder(msg); 129 if (sendto(sock, (char *)msg, sizeof(struct tsp), 0, 130 &saveaddr, sizeof(struct sockaddr_in)) < 0) { 131 syslog(LOG_ERR, "sendto: %m"); 132 exit(1); 133 } 134 spreadtime(); 135 pollingtime = 0; 136 break; 137 case TSP_SETDATEREQ: 138 ind = findhost(msg->tsp_name); 139 if (ind < 0) { 140 syslog(LOG_WARNING, 141 "DATEREQ from uncontrolled machine"); 142 break; 143 } 144 if (hp[ind].seq != msg->tsp_seq) { 145 hp[ind].seq = msg->tsp_seq; 146 /* 147 * the following line is necessary due to syslog 148 * calling ctime() which clobbers the static buffer 149 */ 150 (void)strcpy(olddate, date()); 151 (void)gettimeofday(&time, &tzone); 152 otime = time; 153 time.tv_sec = msg->tsp_time.tv_sec; 154 time.tv_usec = msg->tsp_time.tv_usec; 155 (void)settimeofday(&time, &tzone); 156 syslog(LOG_NOTICE, 157 "date changed by %s from: %s", 158 msg->tsp_name, olddate); 159 logwtmp(otime, time); 160 spreadtime(); 161 pollingtime = 0; 162 } 163 break; 164 case TSP_MSITE: 165 case TSP_MSITEREQ: 166 break; 167 case TSP_TRACEON: 168 if (!(trace)) { 169 fd = fopen(tracefile, "w"); 170 setlinebuf(fd); 171 fprintf(fd, "Tracing started on: %s\n\n", 172 date()); 173 } 174 trace = ON; 175 break; 176 case TSP_TRACEOFF: 177 if (trace) { 178 fprintf(fd, "Tracing ended on: %s\n", date()); 179 (void)fclose(fd); 180 } 181 #ifdef GPROF 182 moncontrol(0); 183 _mcleanup(); 184 moncontrol(1); 185 #endif 186 trace = OFF; 187 break; 188 case TSP_ELECTION: 189 to.tsp_type = TSP_QUIT; 190 (void)strcpy(to.tsp_name, hostname); 191 server = from; 192 answer = acksend(&to, &server, msg->tsp_name, TSP_ACK, 193 (struct netinfo *)NULL); 194 if (answer == NULL) { 195 syslog(LOG_ERR, "election error"); 196 } else { 197 (void) addmach(msg->tsp_name, &from); 198 } 199 pollingtime = 0; 200 break; 201 case TSP_CONFLICT: 202 /* 203 * After a network partition, there can be 204 * more than one master: the first slave to 205 * come up will notify here the situation. 206 */ 207 208 (void)strcpy(to.tsp_name, hostname); 209 210 if (fromnet == NULL) 211 break; 212 for(;;) { 213 to.tsp_type = TSP_RESOLVE; 214 answer = acksend(&to, &fromnet->dest_addr, 215 (char *)ANYADDR, TSP_MASTERACK, fromnet); 216 if (answer == NULL) 217 break; 218 to.tsp_type = TSP_QUIT; 219 server = from; 220 msg = acksend(&to, &server, answer->tsp_name, 221 TSP_ACK, (struct netinfo *)NULL); 222 if (msg == NULL) { 223 syslog(LOG_ERR, "error on sending QUIT"); 224 } else { 225 (void) addmach(answer->tsp_name, &from); 226 } 227 } 228 masterup(fromnet); 229 pollingtime = 0; 230 break; 231 case TSP_RESOLVE: 232 /* 233 * do not want to call synch() while waiting 234 * to be killed! 235 */ 236 (void)gettimeofday(&time, (struct timezone *)0); 237 pollingtime = time.tv_sec + SAMPLEINTVL; 238 break; 239 case TSP_QUIT: 240 /* become slave */ 241 #ifdef MEASURE 242 if (fp != NULL) { 243 (void)fclose(fp); 244 fp = NULL; 245 } 246 #endif 247 longjmp(jmpenv, 2); 248 break; 249 case TSP_LOOP: 250 /* 251 * We should not have received this from a net 252 * we are master on. There must be two masters 253 * in this case. 254 */ 255 to.tsp_type = TSP_QUIT; 256 (void)strcpy(to.tsp_name, hostname); 257 server = from; 258 answer = acksend(&to, &server, msg->tsp_name, TSP_ACK, 259 (struct netinfo *)NULL); 260 if (answer == NULL) { 261 syslog(LOG_WARNING, 262 "loop breakage: no reply to QUIT"); 263 } else { 264 (void)addmach(msg->tsp_name, &from); 265 } 266 default: 267 if (trace) { 268 fprintf(fd, "garbage: "); 269 print(msg, &from); 270 } 271 break; 272 } 273 } 274 goto loop; 275 } 276 277 /* 278 * `synch' synchronizes all the slaves by calling measure, 279 * networkdelta and correct 280 */ 281 282 synch(mydelta) 283 long mydelta; 284 { 285 int i; 286 int measure_status; 287 long netdelta; 288 struct timeval tack; 289 #ifdef MEASURE 290 #define MAXLINES 8 291 static int lines = 1; 292 struct timeval start, end; 293 #endif 294 int measure(); 295 int correct(); 296 long networkdelta(); 297 char *date(); 298 299 if (slvcount > 1) { 300 #ifdef MEASURE 301 (void)gettimeofday(&start, (struct timezone *)0); 302 if (header == ON || --lines == 0) { 303 fprintf(fp, "%s\n", date()); 304 for (i=0; i<slvcount; i++) 305 fprintf(fp, "%.7s\t", hp[i].name); 306 fprintf(fp, "\n"); 307 lines = MAXLINES; 308 header = OFF; 309 } 310 #endif 311 machup = 1; 312 hp[0].delta = 0; 313 for(i=1; i<slvcount; i++) { 314 tack.tv_sec = 0; 315 tack.tv_usec = 500000; 316 if ((measure_status = measure(&tack, &hp[i].addr)) <0) { 317 syslog(LOG_ERR, "measure: %m"); 318 exit(1); 319 } 320 hp[i].delta = measure_delta; 321 if (measure_status == GOOD) 322 machup++; 323 } 324 if (status & SLAVE) { 325 /* called by a submaster */ 326 if (trace) 327 fprintf(fd, "submaster correct: %d ms.\n", 328 mydelta); 329 correct(mydelta); 330 } else { 331 if (machup > 1) { 332 netdelta = networkdelta(); 333 if (trace) 334 fprintf(fd, 335 "master correct: %d ms.\n", 336 mydelta); 337 correct(netdelta); 338 } 339 } 340 #ifdef MEASURE 341 gettimeofday(&end, 0); 342 end.tv_sec -= start.tv_sec; 343 end.tv_usec -= start.tv_usec; 344 if (end.tv_usec < 0) { 345 end.tv_sec -= 1; 346 end.tv_usec += 1000000; 347 } 348 fprintf(fp, "%d ms.\n", (end.tv_sec*1000+end.tv_usec/1000)); 349 #endif 350 for(i=1; i<slvcount; i++) { 351 if (hp[i].delta == HOSTDOWN) { 352 rmmach(i); 353 #ifdef MEASURE 354 header = ON; 355 #endif 356 } 357 } 358 } else { 359 if (status & SLAVE) { 360 correct(mydelta); 361 } 362 } 363 } 364 365 /* 366 * 'spreadtime' sends the time to each slave after the master 367 * has received the command to set the network time 368 */ 369 370 spreadtime() 371 { 372 int i; 373 struct tsp to; 374 struct tsp *answer, *acksend(); 375 376 for(i=1; i<slvcount; i++) { 377 to.tsp_type = TSP_SETTIME; 378 (void)strcpy(to.tsp_name, hostname); 379 (void)gettimeofday(&to.tsp_time, (struct timezone *)0); 380 answer = acksend(&to, &hp[i].addr, hp[i].name, TSP_ACK, 381 (struct netinfo *)NULL); 382 if (answer == NULL) { 383 syslog(LOG_WARNING, 384 "no reply to SETTIME from: %s", hp[i].name); 385 } 386 } 387 } 388 389 findhost(name) 390 char *name; 391 { 392 int i; 393 int ind; 394 395 ind = -1; 396 for (i=1; i<slvcount; i++) { 397 if (strcmp(name, hp[i].name) == 0) { 398 ind = i; 399 break; 400 } 401 } 402 return(ind); 403 } 404 405 /* 406 * 'addmach' adds a host to the list of controlled machines 407 * if not already there 408 */ 409 410 addmach(name, addr) 411 char *name; 412 struct sockaddr_in *addr; 413 { 414 int ret; 415 int findhost(); 416 417 ret = findhost(name); 418 if (ret < 0) { 419 hp[slvcount].addr = *addr; 420 hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN); 421 (void)strcpy(hp[slvcount].name, name); 422 hp[slvcount].seq = 0; 423 ret = slvcount; 424 if (slvcount < NHOSTS) 425 slvcount++; 426 else { 427 syslog(LOG_ERR, "no more slots in host table"); 428 } 429 } else { 430 /* need to clear sequence number anyhow */ 431 hp[ret].seq = 0; 432 } 433 #ifdef MEASURE 434 header = ON; 435 #endif 436 return(ret); 437 } 438 439 /* 440 * Remove all the machines from the host table that exist on the given 441 * network. This is called when a master transitions to a slave on a 442 * given network. 443 */ 444 445 rmnetmachs(ntp) 446 register struct netinfo *ntp; 447 { 448 int i; 449 450 if (trace) 451 prthp(); 452 for (i = 1; i < slvcount; i++) 453 if ((hp[i].addr.sin_addr.s_addr & ntp->mask) == ntp->net) 454 rmmach(i--); 455 if (trace) 456 prthp(); 457 } 458 459 /* 460 * remove the machine with the given index in the host table. 461 */ 462 rmmach(ind) 463 int ind; 464 { 465 if (trace) 466 fprintf(fd, "rmmach: %s\n", hp[ind].name); 467 free(hp[ind].name); 468 hp[ind] = hp[--slvcount]; 469 } 470 471 prthp() 472 { 473 int i; 474 475 fprintf(fd, "host table:"); 476 for (i=1; i<slvcount; i++) 477 fprintf(fd, " %s", hp[i].name); 478 fprintf(fd, "\n"); 479 } 480 481 masterup(net) 482 struct netinfo *net; 483 { 484 struct timeval wait; 485 struct tsp to, *msg, *readmsg(); 486 487 to.tsp_type = TSP_MASTERUP; 488 to.tsp_vers = TSPVERSION; 489 (void)strcpy(to.tsp_name, hostname); 490 bytenetorder(&to); 491 if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, &net->dest_addr, 492 sizeof(struct sockaddr_in)) < 0) { 493 syslog(LOG_ERR, "sendto: %m"); 494 exit(1); 495 } 496 497 for (;;) { 498 wait.tv_sec = 1; 499 wait.tv_usec = 0; 500 msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait, net); 501 if (msg != NULL) { 502 (void) addmach(msg->tsp_name, &from); 503 } else 504 break; 505 } 506 } 507 508 newslave(ind, seq) 509 u_short seq; 510 { 511 struct tsp to; 512 struct tsp *answer, *acksend(); 513 514 if (trace) 515 prthp(); 516 if (seq == 0 || hp[ind].seq != seq) { 517 hp[ind].seq = seq; 518 to.tsp_type = TSP_SETTIME; 519 (void)strcpy(to.tsp_name, hostname); 520 /* 521 * give the upcoming slave the time 522 * to check its input queue before 523 * setting the time 524 */ 525 sleep(1); 526 (void) gettimeofday(&to.tsp_time, 527 (struct timezone *)0); 528 answer = acksend(&to, &hp[ind].addr, 529 hp[ind].name, TSP_ACK, 530 (struct netinfo *)NULL); 531 if (answer == NULL) { 532 syslog(LOG_WARNING, 533 "no reply to initial SETTIME from: %s", 534 hp[ind].name); 535 rmmach(ind); 536 } 537 } 538 } 539 540 char *wtmpfile = "/usr/adm/wtmp"; 541 struct utmp wtmp[2] = { 542 { "|", "", "", 0 }, 543 { "{", "", "", 0 } 544 }; 545 546 logwtmp(otime, ntime) 547 struct timeval otime, ntime; 548 { 549 int f; 550 551 wtmp[0].ut_time = otime.tv_sec + (otime.tv_usec + 500000) / 1000000; 552 wtmp[1].ut_time = ntime.tv_sec + (ntime.tv_usec + 500000) / 1000000; 553 if (wtmp[0].ut_time == wtmp[1].ut_time) 554 return; 555 if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) { 556 (void) write(f, (char *)wtmp, sizeof(wtmp)); 557 (void) close(f); 558 } 559 } 560