]> git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/master.c
f4dbd7d0fa254ac03fa16b083b8dc43bbdf3bdd0
[apple/network_cmds.git] / timed.tproj / timed.tproj / master.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*-
25 * Copyright (c) 1985, 1993
26 * The Regents of the University of California. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57 #ifndef lint
58 static char sccsid[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
59 #endif /* not lint */
60
61 #ifdef sgi
62 #ident "$Revision: 1.1 $"
63 #endif
64
65 #include "globals.h"
66 #include <sys/file.h>
67 #include <sys/types.h>
68 #include <sys/times.h>
69 #include <setjmp.h>
70 #ifdef sgi
71 #include <sys/schedctl.h>
72 #endif /* sgi */
73 #include <utmp.h>
74 #include "pathnames.h"
75
76 extern int measure_delta;
77 extern jmp_buf jmpenv;
78 extern int Mflag;
79 extern int justquit;
80
81 static int dictate;
82 static int slvcount; /* slaves listening to our clock */
83
84 static void mchgdate __P((struct tsp *));
85
86 #ifdef sgi
87 extern void logwtmp __P((struct timeval *, struct timeval *));
88 #else
89 extern void logwtmp __P((char *, char *, char *));
90 #endif /* sgi */
91
92 /*
93 * The main function of `master' is to periodically compute the differences
94 * (deltas) between its clock and the clocks of the slaves, to compute the
95 * network average delta, and to send to the slaves the differences between
96 * their individual deltas and the network delta.
97 * While waiting, it receives messages from the slaves (i.e. requests for
98 * master's name, remote requests to set the network time, ...), and
99 * takes the appropriate action.
100 */
101 int
102 master()
103 {
104 struct hosttbl *htp;
105 long pollingtime;
106 #define POLLRATE 4
107 int polls;
108 struct timeval wait, ntime;
109 struct tsp *msg, *answer, to;
110 char newdate[32];
111 struct sockaddr_in taddr;
112 char tname[MAXHOSTNAMELEN];
113 struct netinfo *ntp;
114 int i;
115
116 syslog(LOG_NOTICE, "This machine is master");
117 if (trace)
118 fprintf(fd, "This machine is master\n");
119 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
120 if (ntp->status == MASTER)
121 masterup(ntp);
122 }
123 (void)gettimeofday(&ntime, 0);
124 pollingtime = ntime.tv_sec+3;
125 if (justquit)
126 polls = 0;
127 else
128 polls = POLLRATE-1;
129
130 /* Process all outstanding messages before spending the long time necessary
131 * to update all timers.
132 */
133 loop:
134 (void)gettimeofday(&ntime, 0);
135 wait.tv_sec = pollingtime - ntime.tv_sec;
136 if (wait.tv_sec < 0)
137 wait.tv_sec = 0;
138 wait.tv_usec = 0;
139 msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
140 if (!msg) {
141 (void)gettimeofday(&ntime, 0);
142 if (ntime.tv_sec >= pollingtime) {
143 pollingtime = ntime.tv_sec + SAMPLEINTVL;
144 get_goodgroup(0);
145
146 /* If a bogus master told us to quit, we can have decided to ignore a
147 * network. Therefore, periodically try to take over everything.
148 */
149 polls = (polls + 1) % POLLRATE;
150 if (0 == polls && nignorednets > 0) {
151 trace_msg("Looking for nets to re-master\n");
152 for (ntp = nettab; ntp; ntp = ntp->next) {
153 if (ntp->status == IGNORE
154 || ntp->status == NOMASTER) {
155 lookformaster(ntp);
156 if (ntp->status == MASTER) {
157 masterup(ntp);
158 polls = POLLRATE-1;
159 }
160 }
161 if (ntp->status == MASTER
162 && --ntp->quit_count < 0)
163 ntp->quit_count = 0;
164 }
165 if (polls != 0)
166 setstatus();
167 }
168
169 synch(0L);
170
171 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
172 to.tsp_type = TSP_LOOP;
173 to.tsp_vers = TSPVERSION;
174 to.tsp_seq = sequence++;
175 to.tsp_hopcnt = MAX_HOPCNT;
176 (void)strcpy(to.tsp_name, hostname);
177 bytenetorder(&to);
178 if (sendto(sock, (char *)&to,
179 sizeof(struct tsp), 0,
180 (struct sockaddr*)&ntp->dest_addr,
181 sizeof(ntp->dest_addr)) < 0) {
182 trace_sendto_err(ntp->dest_addr.sin_addr);
183 }
184 }
185 }
186
187
188 } else {
189 switch (msg->tsp_type) {
190
191 case TSP_MASTERREQ:
192 break;
193
194 case TSP_SLAVEUP:
195 newslave(msg);
196 break;
197
198 case TSP_SETDATE:
199 /*
200 * XXX check to see it is from ourself
201 */
202 #ifdef sgi
203 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
204 #else
205 (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
206 #endif /* sgi */
207 if (!good_host_name(msg->tsp_name)) {
208 syslog(LOG_NOTICE,
209 "attempted date change by %s to %s",
210 msg->tsp_name, newdate);
211 spreadtime();
212 break;
213 }
214
215 mchgdate(msg);
216 (void)gettimeofday(&ntime, 0);
217 pollingtime = ntime.tv_sec + SAMPLEINTVL;
218 break;
219
220 case TSP_SETDATEREQ:
221 if (!fromnet || fromnet->status != MASTER)
222 break;
223 #ifdef sgi
224 (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
225 #else
226 (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
227 #endif /* sgi */
228 htp = findhost(msg->tsp_name);
229 if (htp == 0) {
230 syslog(LOG_ERR,
231 "attempted SET DATEREQ by uncontrolled %s to %s",
232 msg->tsp_name, newdate);
233 break;
234 }
235 if (htp->seq == msg->tsp_seq)
236 break;
237 htp->seq = msg->tsp_seq;
238 if (!htp->good) {
239 syslog(LOG_NOTICE,
240 "attempted SET DATEREQ by untrusted %s to %s",
241 msg->tsp_name, newdate);
242 spreadtime();
243 break;
244 }
245
246 mchgdate(msg);
247 (void)gettimeofday(&ntime, 0);
248 pollingtime = ntime.tv_sec + SAMPLEINTVL;
249 break;
250
251 case TSP_MSITE:
252 xmit(TSP_ACK, msg->tsp_seq, &from);
253 break;
254
255 case TSP_MSITEREQ:
256 break;
257
258 case TSP_TRACEON:
259 traceon();
260 break;
261
262 case TSP_TRACEOFF:
263 traceoff("Tracing ended at %s\n");
264 break;
265
266 case TSP_ELECTION:
267 if (!fromnet)
268 break;
269 if (fromnet->status == MASTER) {
270 pollingtime = 0;
271 (void)addmach(msg->tsp_name, &from,fromnet);
272 }
273 taddr = from;
274 (void)strcpy(tname, msg->tsp_name);
275 to.tsp_type = TSP_QUIT;
276 (void)strcpy(to.tsp_name, hostname);
277 answer = acksend(&to, &taddr, tname,
278 TSP_ACK, 0, 1);
279 if (answer == NULL) {
280 syslog(LOG_ERR, "election error by %s",
281 tname);
282 }
283 break;
284
285 case TSP_CONFLICT:
286 /*
287 * After a network partition, there can be
288 * more than one master: the first slave to
289 * come up will notify here the situation.
290 */
291 if (!fromnet || fromnet->status != MASTER)
292 break;
293 (void)strcpy(to.tsp_name, hostname);
294
295 /* The other master often gets into the same state,
296 * with boring results if we stay at it forever.
297 */
298 ntp = fromnet; /* (acksend() can leave fromnet=0 */
299 for (i = 0; i < 3; i++) {
300 to.tsp_type = TSP_RESOLVE;
301 (void)strcpy(to.tsp_name, hostname);
302 answer = acksend(&to, &ntp->dest_addr,
303 ANYADDR, TSP_MASTERACK,
304 ntp, 0);
305 if (!answer)
306 break;
307 htp = addmach(answer->tsp_name,&from,ntp);
308 to.tsp_type = TSP_QUIT;
309 msg = acksend(&to, &htp->addr, htp->name,
310 TSP_ACK, 0, htp->noanswer);
311 if (msg == NULL) {
312 syslog(LOG_ERR,
313 "no response from %s to CONFLICT-QUIT",
314 htp->name);
315 }
316 }
317 masterup(ntp);
318 pollingtime = 0;
319 break;
320
321 case TSP_RESOLVE:
322 if (!fromnet || fromnet->status != MASTER)
323 break;
324 /*
325 * do not want to call synch() while waiting
326 * to be killed!
327 */
328 (void)gettimeofday(&ntime, (struct timezone *)0);
329 pollingtime = ntime.tv_sec + SAMPLEINTVL;
330 break;
331
332 case TSP_QUIT:
333 doquit(msg); /* become a slave */
334 break;
335
336 case TSP_LOOP:
337 if (!fromnet || fromnet->status != MASTER
338 || !strcmp(msg->tsp_name, hostname))
339 break;
340 /*
341 * We should not have received this from a net
342 * we are master on. There must be two masters.
343 */
344 htp = addmach(msg->tsp_name, &from,fromnet);
345 to.tsp_type = TSP_QUIT;
346 (void)strcpy(to.tsp_name, hostname);
347 answer = acksend(&to, &htp->addr, htp->name,
348 TSP_ACK, 0, 1);
349 if (!answer) {
350 syslog(LOG_WARNING,
351 "loop breakage: no reply from %s=%s to QUIT",
352 htp->name, inet_ntoa(htp->addr.sin_addr));
353 (void)remmach(htp);
354 }
355
356 case TSP_TEST:
357 if (trace) {
358 fprintf(fd,
359 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
360 nnets, nmasternets, nslavenets, nignorednets);
361 setstatus();
362 }
363 pollingtime = 0;
364 polls = POLLRATE-1;
365 break;
366
367 default:
368 if (trace) {
369 fprintf(fd, "garbage message: ");
370 print(msg, &from);
371 }
372 break;
373 }
374 }
375 goto loop;
376 }
377
378
379 /*
380 * change the system date on the master
381 */
382 static void
383 mchgdate(msg)
384 struct tsp *msg;
385 {
386 char tname[MAXHOSTNAMELEN];
387 char olddate[32];
388 struct timeval otime, ntime;
389
390 (void)strcpy(tname, msg->tsp_name);
391
392 xmit(TSP_DATEACK, msg->tsp_seq, &from);
393
394 (void)strcpy(olddate, date());
395
396 /* adjust time for residence on the queue */
397 (void)gettimeofday(&otime, 0);
398 adj_msg_time(msg,&otime);
399
400 timevalsub(&ntime, &msg->tsp_time, &otime);
401 if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
402 /*
403 * do not change the clock if we can adjust it
404 */
405 dictate = 3;
406 synch(tvtomsround(ntime));
407 } else {
408 #ifdef sgi
409 if (0 > settimeofday(&msg->tsp_time, 0)) {
410 syslog(LOG_ERR, "settimeofday(): %m");
411 }
412 logwtmp(&otime, &msg->tsp_time);
413 #else
414 logwtmp("|", "date", "");
415 (void)settimeofday(&msg->tsp_time, 0);
416 logwtmp("}", "date", "");
417 #endif /* sgi */
418 spreadtime();
419 }
420
421 syslog(LOG_NOTICE, "date changed by %s from %s",
422 tname, olddate);
423 }
424
425
426 /*
427 * synchronize all of the slaves
428 */
429 void
430 synch(mydelta)
431 long mydelta;
432 {
433 struct hosttbl *htp;
434 int measure_status;
435 struct timeval check, stop, wait;
436 #ifdef sgi
437 int pri;
438 #endif /* sgi */
439
440 if (slvcount > 0) {
441 if (trace)
442 fprintf(fd, "measurements starting at %s\n", date());
443 (void)gettimeofday(&check, 0);
444 #ifdef sgi
445 /* run fast to get good time */
446 pri = schedctl(NDPRI,0,NDPHIMIN);
447 if (pri < 0)
448 syslog(LOG_ERR, "schedctl(): %m");
449 #endif /* sgi */
450 for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
451 if (htp->noanswer != 0) {
452 measure_status = measure(500, 100,
453 htp->name,
454 &htp->addr,0);
455 } else {
456 measure_status = measure(3000, 100,
457 htp->name,
458 &htp->addr,0);
459 }
460 if (measure_status != GOOD) {
461 /* The slave did not respond. We have
462 * just wasted lots of time on it.
463 */
464 htp->delta = HOSTDOWN;
465 if (++htp->noanswer >= LOSTHOST) {
466 if (trace) {
467 fprintf(fd,
468 "purging %s for not answering ICMP\n",
469 htp->name);
470 (void)fflush(fd);
471 }
472 htp = remmach(htp);
473 }
474 } else {
475 htp->delta = measure_delta;
476 }
477 (void)gettimeofday(&stop, 0);
478 timevalsub(&stop, &stop, &check);
479 if (stop.tv_sec >= 1) {
480 if (trace)
481 (void)fflush(fd);
482 /*
483 * ack messages periodically
484 */
485 wait.tv_sec = 0;
486 wait.tv_usec = 0;
487 if (0 != readmsg(TSP_TRACEON,ANYADDR,
488 &wait,0))
489 traceon();
490 (void)gettimeofday(&check, 0);
491 }
492 }
493 #ifdef sgi
494 if (pri >= 0)
495 (void)schedctl(NDPRI,0,pri);
496 #endif /* sgi */
497 if (trace)
498 fprintf(fd, "measurements finished at %s\n", date());
499 }
500 if (!(status & SLAVE)) {
501 if (!dictate) {
502 mydelta = networkdelta();
503 } else {
504 dictate--;
505 }
506 }
507 if (trace && (mydelta != 0 || (status & SLAVE)))
508 fprintf(fd,"local correction of %ld ms.\n", mydelta);
509 correct(mydelta);
510 }
511
512 /*
513 * sends the time to each slave after the master
514 * has received the command to set the network time
515 */
516 void
517 spreadtime()
518 {
519 struct hosttbl *htp;
520 struct tsp to;
521 struct tsp *answer;
522
523 /* Do not listen to the consensus after forcing the time. This is because
524 * the consensus takes a while to reach the time we are dictating.
525 */
526 dictate = 2;
527 for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
528 to.tsp_type = TSP_SETTIME;
529 (void)strcpy(to.tsp_name, hostname);
530 (void)gettimeofday(&to.tsp_time, 0);
531 answer = acksend(&to, &htp->addr, htp->name,
532 TSP_ACK, 0, htp->noanswer);
533 if (answer == 0) {
534 /* We client does not respond, then we have
535 * just wasted lots of time on it.
536 */
537 syslog(LOG_WARNING,
538 "no reply to SETTIME from %s", htp->name);
539 if (++htp->noanswer >= LOSTHOST) {
540 if (trace) {
541 fprintf(fd,
542 "purging %s for not answering",
543 htp->name);
544 (void)fflush(fd);
545 }
546 htp = remmach(htp);
547 }
548 }
549 }
550 }
551
552 void
553 prthp(delta)
554 clock_t delta;
555 {
556 static time_t next_time;
557 time_t this_time;
558 struct tms tm;
559 struct hosttbl *htp;
560 int length, l;
561 int i;
562
563 if (!fd) /* quit if tracing already off */
564 return;
565
566 this_time = times(&tm);
567 if (this_time + delta < next_time)
568 return;
569 next_time = this_time + CLK_TCK;
570
571 fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
572 htp = self.l_fwd;
573 length = 1;
574 for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
575 l = strlen(htp->name) + 1;
576 if (length+l >= 80) {
577 fprintf(fd, "\n");
578 length = 0;
579 }
580 length += l;
581 fprintf(fd, " %s", htp->name);
582 }
583 fprintf(fd, "\n");
584 }
585
586
587 static struct hosttbl *newhost_hash;
588 static struct hosttbl *lasthfree = &hosttbl[0];
589
590
591 struct hosttbl * /* answer or 0 */
592 findhost(name)
593 char *name;
594 {
595 int i, j;
596 struct hosttbl *htp;
597 char *p;
598
599 j= 0;
600 for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
601 j = (j << 2) ^ *p;
602 newhost_hash = &hosttbl[j % NHOSTS];
603
604 htp = newhost_hash;
605 if (htp->name[0] == '\0')
606 return(0);
607 do {
608 if (!strcmp(name, htp->name))
609 return(htp);
610 htp = htp->h_fwd;
611 } while (htp != newhost_hash);
612 return(0);
613 }
614
615 /*
616 * add a host to the list of controlled machines if not already there
617 */
618 struct hosttbl *
619 addmach(name, addr, ntp)
620 char *name;
621 struct sockaddr_in *addr;
622 struct netinfo *ntp;
623 {
624 struct hosttbl *ret, *p, *b, *f;
625
626 ret = findhost(name);
627 if (ret == 0) {
628 if (slvcount >= NHOSTS) {
629 if (trace) {
630 fprintf(fd, "no more slots in host table\n");
631 prthp(CLK_TCK);
632 }
633 syslog(LOG_ERR, "no more slots in host table");
634 Mflag = 0;
635 longjmp(jmpenv, 2); /* give up and be a slave */
636 }
637
638 /* if our home hash slot is occupied, find a free entry
639 * in the hash table
640 */
641 if (newhost_hash->name[0] != '\0') {
642 do {
643 ret = lasthfree;
644 if (++lasthfree > &hosttbl[NHOSTS])
645 lasthfree = &hosttbl[1];
646 } while (ret->name[0] != '\0');
647
648 if (!newhost_hash->head) {
649 /* Move an interloper using our home. Use
650 * scratch pointers in case the new head is
651 * pointing to itself.
652 */
653 f = newhost_hash->h_fwd;
654 b = newhost_hash->h_bak;
655 f->h_bak = ret;
656 b->h_fwd = ret;
657 f = newhost_hash->l_fwd;
658 b = newhost_hash->l_bak;
659 f->l_bak = ret;
660 b->l_fwd = ret;
661 bcopy(newhost_hash,ret,sizeof(*ret));
662 ret = newhost_hash;
663 ret->head = 1;
664 ret->h_fwd = ret;
665 ret->h_bak = ret;
666 } else {
667 /* link to an existing chain in our home
668 */
669 ret->head = 0;
670 p = newhost_hash->h_bak;
671 ret->h_fwd = newhost_hash;
672 ret->h_bak = p;
673 p->h_fwd = ret;
674 newhost_hash->h_bak = ret;
675 }
676 } else {
677 ret = newhost_hash;
678 ret->head = 1;
679 ret->h_fwd = ret;
680 ret->h_bak = ret;
681 }
682 ret->addr = *addr;
683 ret->ntp = ntp;
684 (void)strncpy(ret->name, name, sizeof(ret->name));
685 ret->good = good_host_name(name);
686 ret->l_fwd = &self;
687 ret->l_bak = self.l_bak;
688 self.l_bak->l_fwd = ret;
689 self.l_bak = ret;
690 slvcount++;
691
692 ret->noanswer = 0;
693 ret->need_set = 1;
694
695 } else {
696 ret->noanswer = (ret->noanswer != 0);
697 }
698
699 /* need to clear sequence number anyhow */
700 ret->seq = 0;
701 return(ret);
702 }
703
704 /*
705 * remove the machine with the given index in the host table.
706 */
707 struct hosttbl *
708 remmach(htp)
709 struct hosttbl *htp;
710 {
711 struct hosttbl *lprv, *hnxt, *f, *b;
712
713 if (trace)
714 fprintf(fd, "remove %s\n", htp->name);
715
716 /* get out of the lists */
717 htp->l_fwd->l_bak = lprv = htp->l_bak;
718 htp->l_bak->l_fwd = htp->l_fwd;
719 htp->h_fwd->h_bak = htp->h_bak;
720 htp->h_bak->h_fwd = hnxt = htp->h_fwd;
721
722 /* If we are in the home slot, pull up the chain */
723 if (htp->head && hnxt != htp) {
724 if (lprv == hnxt)
725 lprv = htp;
726
727 /* Use scratch pointers in case the new head is pointing to
728 * itself.
729 */
730 f = hnxt->h_fwd;
731 b = hnxt->h_bak;
732 f->h_bak = htp;
733 b->h_fwd = htp;
734 f = hnxt->l_fwd;
735 b = hnxt->l_bak;
736 f->l_bak = htp;
737 b->l_fwd = htp;
738 hnxt->head = 1;
739 bcopy(hnxt, htp, sizeof(*htp));
740 lasthfree = hnxt;
741 } else {
742 lasthfree = htp;
743 }
744
745 lasthfree->name[0] = '\0';
746 lasthfree->h_fwd = 0;
747 lasthfree->l_fwd = 0;
748 slvcount--;
749
750 return lprv;
751 }
752
753
754 /*
755 * Remove all the machines from the host table that exist on the given
756 * network. This is called when a master transitions to a slave on a
757 * given network.
758 */
759 void
760 rmnetmachs(ntp)
761 struct netinfo *ntp;
762 {
763 struct hosttbl *htp;
764
765 if (trace)
766 prthp(CLK_TCK);
767 for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
768 if (ntp == htp->ntp)
769 htp = remmach(htp);
770 }
771 if (trace)
772 prthp(CLK_TCK);
773 }
774
775 void
776 masterup(net)
777 struct netinfo *net;
778 {
779 xmit(TSP_MASTERUP, 0, &net->dest_addr);
780
781 /*
782 * Do not tell new slaves our time for a while. This ensures
783 * we do not tell them to start using our time, before we have
784 * found a good master.
785 */
786 (void)gettimeofday(&net->slvwait, 0);
787 }
788
789 void
790 newslave(msg)
791 struct tsp *msg;
792 {
793 struct hosttbl *htp;
794 struct tsp *answer, to;
795 struct timeval now;
796
797 if (!fromnet || fromnet->status != MASTER)
798 return;
799
800 htp = addmach(msg->tsp_name, &from,fromnet);
801 htp->seq = msg->tsp_seq;
802 if (trace)
803 prthp(0);
804
805 /*
806 * If we are stable, send our time to the slave.
807 * Do not go crazy if the date has been changed.
808 */
809 (void)gettimeofday(&now, 0);
810 if (now.tv_sec >= fromnet->slvwait.tv_sec+3
811 || now.tv_sec < fromnet->slvwait.tv_sec) {
812 to.tsp_type = TSP_SETTIME;
813 (void)strcpy(to.tsp_name, hostname);
814 (void)gettimeofday(&to.tsp_time, 0);
815 answer = acksend(&to, &htp->addr,
816 htp->name, TSP_ACK,
817 0, htp->noanswer);
818 if (answer) {
819 htp->need_set = 0;
820 } else {
821 syslog(LOG_WARNING,
822 "no reply to initial SETTIME from %s",
823 htp->name);
824 htp->noanswer = LOSTHOST;
825 }
826 }
827 }
828
829
830 /*
831 * react to a TSP_QUIT:
832 */
833 void
834 doquit(msg)
835 struct tsp *msg;
836 {
837 if (fromnet->status == MASTER) {
838 if (!good_host_name(msg->tsp_name)) {
839 if (fromnet->quit_count <= 0) {
840 syslog(LOG_NOTICE,"untrusted %s told us QUIT",
841 msg->tsp_name);
842 suppress(&from, msg->tsp_name, fromnet);
843 fromnet->quit_count = 1;
844 return;
845 }
846 syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
847 msg->tsp_name);
848 fromnet->quit_count = 2;
849 fromnet->status = NOMASTER;
850 } else {
851 fromnet->status = SLAVE;
852 }
853 rmnetmachs(fromnet);
854 longjmp(jmpenv, 2); /* give up and be a slave */
855
856 } else {
857 if (!good_host_name(msg->tsp_name)) {
858 syslog(LOG_NOTICE, "untrusted %s told us QUIT",
859 msg->tsp_name);
860 fromnet->quit_count = 2;
861 }
862 }
863 }
864
865 void
866 traceon()
867 {
868 if (!fd) {
869 fd = fopen(_PATH_TIMEDLOG, "w");
870 if (!fd) {
871 trace = 0;
872 return;
873 }
874 fprintf(fd,"Tracing started at %s\n", date());
875 }
876 trace = 1;
877 get_goodgroup(1);
878 setstatus();
879 prthp(CLK_TCK);
880 }
881
882
883 void
884 traceoff(msg)
885 char *msg;
886 {
887 get_goodgroup(1);
888 setstatus();
889 prthp(CLK_TCK);
890 if (trace) {
891 fprintf(fd, msg, date());
892 (void)fclose(fd);
893 fd = 0;
894 }
895 #ifdef GPROF
896 moncontrol(0);
897 _mcleanup();
898 moncontrol(1);
899 #endif
900 trace = OFF;
901 }
902
903
904 #ifdef sgi
905 void
906 logwtmp(otime, ntime)
907 struct timeval *otime, *ntime;
908 {
909 static struct utmp wtmp[2] = {
910 {"","",OTIME_MSG,0,OLD_TIME,0,0,0},
911 {"","",NTIME_MSG,0,NEW_TIME,0,0,0}
912 };
913 static char *wtmpfile = WTMP_FILE;
914 int f;
915
916 wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000;
917 wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000;
918 if (wtmp[0].ut_time == wtmp[1].ut_time)
919 return;
920
921 setutent();
922 (void)pututline(&wtmp[0]);
923 (void)pututline(&wtmp[1]);
924 endutent();
925 if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
926 (void) write(f, (char *)wtmp, sizeof(wtmp));
927 (void) close(f);
928 }
929 }
930 #endif /* sgi */