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