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