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