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