]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/timed/timed/master.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)master.c    8.1 (Berkeley) 6/6/93";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41
42 #include "globals.h"
43 #include <sys/file.h>
44 #include <sys/types.h>
45 #include <sys/times.h>
46 #include <setjmp.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(struct tsp *);
58
59 extern void logwtmp(char *, char *, char *);
60
61 /*
62  * The main function of `master' is to periodically compute the differences
63  * (deltas) between its clock and the clocks of the slaves, to compute the
64  * network average delta, and to send to the slaves the differences between
65  * their individual deltas and the network delta.
66  * While waiting, it receives messages from the slaves (i.e. requests for
67  * master's name, remote requests to set the network time, ...), and
68  * takes the appropriate action.
69  */
70 int
71 master()
72 {
73         struct hosttbl *htp;
74         long pollingtime;
75 #define POLLRATE 4
76         int polls;
77         struct timeval wait, ntime;
78         time_t tsp_time_sec;
79         struct tsp *msg, *answer, to;
80         char newdate[32];
81         struct sockaddr_in taddr;
82         char tname[MAXHOSTNAMELEN];
83         struct netinfo *ntp;
84         int i;
85
86         syslog(LOG_NOTICE, "This machine is master");
87         if (trace)
88                 fprintf(fd, "This machine is master\n");
89         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
90                 if (ntp->status == MASTER)
91                         masterup(ntp);
92         }
93         (void)gettimeofday(&ntime, 0);
94         pollingtime = ntime.tv_sec+3;
95         if (justquit)
96                 polls = 0;
97         else
98                 polls = POLLRATE-1;
99
100 /* Process all outstanding messages before spending the long time necessary
101  *      to update all timers.
102  */
103 loop:
104         (void)gettimeofday(&ntime, 0);
105         wait.tv_sec = pollingtime - ntime.tv_sec;
106         if (wait.tv_sec < 0)
107                 wait.tv_sec = 0;
108         wait.tv_usec = 0;
109         msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
110         if (!msg) {
111                 (void)gettimeofday(&ntime, 0);
112                 if (ntime.tv_sec >= pollingtime) {
113                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
114                         get_goodgroup(0);
115
116 /* If a bogus master told us to quit, we can have decided to ignore a
117  * network.  Therefore, periodically try to take over everything.
118  */
119                         polls = (polls + 1) % POLLRATE;
120                         if (0 == polls && nignorednets > 0) {
121                                 trace_msg("Looking for nets to re-master\n");
122                                 for (ntp = nettab; ntp; ntp = ntp->next) {
123                                         if (ntp->status == IGNORE
124                                             || ntp->status == NOMASTER) {
125                                                 lookformaster(ntp);
126                                                 if (ntp->status == MASTER) {
127                                                         masterup(ntp);
128                                                         polls = POLLRATE-1;
129                                                 }
130                                         }
131                                         if (ntp->status == MASTER
132                                             && --ntp->quit_count < 0)
133                                                 ntp->quit_count = 0;
134                                 }
135                                 if (polls != 0)
136                                         setstatus();
137                         }
138
139                         synch(0L);
140
141                         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
142                                 to.tsp_type = TSP_LOOP;
143                                 to.tsp_vers = TSPVERSION;
144                                 to.tsp_seq = sequence++;
145                                 to.tsp_hopcnt = MAX_HOPCNT;
146                                 (void)strcpy(to.tsp_name, hostname);
147                                 bytenetorder(&to);
148                                 if (sendto(sock, (char *)&to,
149                                            sizeof(struct tsp), 0,
150                                            (struct sockaddr*)&ntp->dest_addr,
151                                            sizeof(ntp->dest_addr)) < 0) {
152                                    trace_sendto_err(ntp->dest_addr.sin_addr);
153                                 }
154                         }
155                 }
156
157
158         } else {
159                 switch (msg->tsp_type) {
160
161                 case TSP_MASTERREQ:
162                         break;
163
164                 case TSP_SLAVEUP:
165                         newslave(msg);
166                         break;
167
168                 case TSP_SETDATE:
169                         /*
170                          * XXX check to see it is from ourself
171                          */
172                         tsp_time_sec = msg->tsp_time.tv_sec;
173                         (void)strcpy(newdate, ctime(&tsp_time_sec));
174                         if (!good_host_name(msg->tsp_name)) {
175                                 syslog(LOG_NOTICE,
176                                        "attempted date change by %s to %s",
177                                        msg->tsp_name, newdate);
178                                 spreadtime();
179                                 break;
180                         }
181
182                         mchgdate(msg);
183                         (void)gettimeofday(&ntime, 0);
184                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
185                         break;
186
187                 case TSP_SETDATEREQ:
188                         if (!fromnet || fromnet->status != MASTER)
189                                 break;
190                         tsp_time_sec = msg->tsp_time.tv_sec;
191                         (void)strcpy(newdate, ctime(&tsp_time_sec));
192                         htp = findhost(msg->tsp_name);
193                         if (htp == 0) {
194                                 syslog(LOG_ERR,
195                                        "attempted SET DATEREQ by uncontrolled %s to %s",
196                                        msg->tsp_name, newdate);
197                                 break;
198                         }
199                         if (htp->seq == msg->tsp_seq)
200                                 break;
201                         htp->seq = msg->tsp_seq;
202                         if (!htp->good) {
203                                 syslog(LOG_NOTICE,
204                                 "attempted SET DATEREQ by untrusted %s to %s",
205                                        msg->tsp_name, newdate);
206                                 spreadtime();
207                                 break;
208                         }
209
210                         mchgdate(msg);
211                         (void)gettimeofday(&ntime, 0);
212                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
213                         break;
214
215                 case TSP_MSITE:
216                         xmit(TSP_ACK, msg->tsp_seq, &from);
217                         break;
218
219                 case TSP_MSITEREQ:
220                         break;
221
222                 case TSP_TRACEON:
223                         traceon();
224                         break;
225
226                 case TSP_TRACEOFF:
227                         traceoff("Tracing ended at %s\n");
228                         break;
229
230                 case TSP_ELECTION:
231                         if (!fromnet)
232                                 break;
233                         if (fromnet->status == MASTER) {
234                                 pollingtime = 0;
235                                 (void)addmach(msg->tsp_name, &from,fromnet);
236                         }
237                         taddr = from;
238                         (void)strcpy(tname, msg->tsp_name);
239                         to.tsp_type = TSP_QUIT;
240                         (void)strcpy(to.tsp_name, hostname);
241                         answer = acksend(&to, &taddr, tname,
242                                          TSP_ACK, 0, 1);
243                         if (answer == NULL) {
244                                 syslog(LOG_ERR, "election error by %s",
245                                        tname);
246                         }
247                         break;
248
249                 case TSP_CONFLICT:
250                         /*
251                          * After a network partition, there can be
252                          * more than one master: the first slave to
253                          * come up will notify here the situation.
254                          */
255                         if (!fromnet || fromnet->status != MASTER)
256                                 break;
257                         (void)strcpy(to.tsp_name, hostname);
258
259                         /* The other master often gets into the same state,
260                          * with boring results if we stay at it forever.
261                          */
262                         ntp = fromnet;  /* (acksend() can leave fromnet=0 */
263                         for (i = 0; i < 3; i++) {
264                                 to.tsp_type = TSP_RESOLVE;
265                                 (void)strcpy(to.tsp_name, hostname);
266                                 answer = acksend(&to, &ntp->dest_addr,
267                                                  ANYADDR, TSP_MASTERACK,
268                                                  ntp, 0);
269                                 if (!answer)
270                                         break;
271                                 htp = addmach(answer->tsp_name,&from,ntp);
272                                 to.tsp_type = TSP_QUIT;
273                                 msg = acksend(&to, &htp->addr, htp->name,
274                                               TSP_ACK, 0, htp->noanswer);
275                                 if (msg == NULL) {
276                                         syslog(LOG_ERR,
277                                     "no response from %s to CONFLICT-QUIT",
278                                                htp->name);
279                                 }
280                         }
281                         masterup(ntp);
282                         pollingtime = 0;
283                         break;
284
285                 case TSP_RESOLVE:
286                         if (!fromnet || fromnet->status != MASTER)
287                                 break;
288                         /*
289                          * do not want to call synch() while waiting
290                          * to be killed!
291                          */
292                         (void)gettimeofday(&ntime, (struct timezone *)0);
293                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
294                         break;
295
296                 case TSP_QUIT:
297                         doquit(msg);            /* become a slave */
298                         break;
299
300                 case TSP_LOOP:
301                         if (!fromnet || fromnet->status != MASTER
302                             || !strcmp(msg->tsp_name, hostname))
303                                 break;
304                         /*
305                          * We should not have received this from a net
306                          * we are master on.  There must be two masters.
307                          */
308                         htp = addmach(msg->tsp_name, &from,fromnet);
309                         to.tsp_type = TSP_QUIT;
310                         (void)strcpy(to.tsp_name, hostname);
311                         answer = acksend(&to, &htp->addr, htp->name,
312                                          TSP_ACK, 0, 1);
313                         if (!answer) {
314                                 syslog(LOG_WARNING,
315                                 "loop breakage: no reply from %s=%s to QUIT",
316                                     htp->name, inet_ntoa(htp->addr.sin_addr));
317                                 (void)remmach(htp);
318                         }
319
320                 case TSP_TEST:
321                         if (trace) {
322                                 fprintf(fd,
323                 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
324                 nnets, nmasternets, nslavenets, nignorednets);
325                                 setstatus();
326                         }
327                         pollingtime = 0;
328                         polls = POLLRATE-1;
329                         break;
330
331                 default:
332                         if (trace) {
333                                 fprintf(fd, "garbage message: ");
334                                 print(msg, &from);
335                         }
336                         break;
337                 }
338         }
339         goto loop;
340 }
341
342
343 /*
344  * change the system date on the master
345  */
346 static void
347 mchgdate(msg)
348         struct tsp *msg;
349 {
350         char tname[MAXHOSTNAMELEN];
351         char olddate[32];
352         struct timeval otime, ntime, tmptv;
353
354         (void)strcpy(tname, msg->tsp_name);
355
356         xmit(TSP_DATEACK, msg->tsp_seq, &from);
357
358         (void)strcpy(olddate, date());
359
360         /* adjust time for residence on the queue */
361         (void)gettimeofday(&otime, 0);
362         adj_msg_time(msg,&otime);
363
364         tmptv.tv_sec = msg->tsp_time.tv_sec;
365         tmptv.tv_usec = msg->tsp_time.tv_usec;
366         timevalsub(&ntime, &tmptv, &otime);
367         if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
368                 /*
369                  * do not change the clock if we can adjust it
370                  */
371                 dictate = 3;
372                 synch(tvtomsround(ntime));
373         } else {
374                 logwtmp("|", "date", "");
375                 (void)settimeofday(&tmptv, 0);
376                 logwtmp("{", "date", "");
377                 spreadtime();
378         }
379
380         syslog(LOG_NOTICE, "date changed by %s from %s",
381                tname, olddate);
382 }
383
384
385 /*
386  * synchronize all of the slaves
387  */
388 void
389 synch(mydelta)
390         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, 0);
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, 0);
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, 0);
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()
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, 0);
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 == 0) {
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(delta)
503         clock_t delta;
504 {
505         static time_t next_time;
506         time_t this_time;
507         struct tms tm;
508         struct hosttbl *htp;
509         int length, l;
510         int i;
511
512         if (!fd)                        /* quit if tracing already off */
513                 return;
514
515         this_time = times(&tm);
516         if ((time_t)(this_time + delta) < next_time)
517                 return;
518         next_time = this_time + CLK_TCK;
519
520         fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
521         htp = self.l_fwd;
522         length = 1;
523         for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
524                 l = strlen(htp->name) + 1;
525                 if (length+l >= 80) {
526                         fprintf(fd, "\n");
527                         length = 0;
528                 }
529                 length += l;
530                 fprintf(fd, " %s", htp->name);
531         }
532         fprintf(fd, "\n");
533 }
534
535
536 static struct hosttbl *newhost_hash;
537 static struct hosttbl *lasthfree = &hosttbl[0];
538
539
540 struct hosttbl *                        /* answer or 0 */
541 findhost(name)
542         char *name;
543 {
544         int i, j;
545         struct hosttbl *htp;
546         char *p;
547
548         j= 0;
549         for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
550                 j = (j << 2) ^ *p;
551         newhost_hash = &hosttbl[j % NHOSTS];
552
553         htp = newhost_hash;
554         if (htp->name[0] == '\0')
555                 return(0);
556         do {
557                 if (!strcmp(name, htp->name))
558                         return(htp);
559                 htp = htp->h_fwd;
560         } while (htp != newhost_hash);
561         return(0);
562 }
563
564 /*
565  * add a host to the list of controlled machines if not already there
566  */
567 struct hosttbl *
568 addmach(name, addr, ntp)
569         char *name;
570         struct sockaddr_in *addr;
571         struct netinfo *ntp;
572 {
573         struct hosttbl *ret, *p, *b, *f;
574
575         ret = findhost(name);
576         if (ret == 0) {
577                 if (slvcount >= NHOSTS) {
578                         if (trace) {
579                                 fprintf(fd, "no more slots in host table\n");
580                                 prthp(CLK_TCK);
581                         }
582                         syslog(LOG_ERR, "no more slots in host table");
583                         Mflag = 0;
584                         longjmp(jmpenv, 2); /* give up and be a slave */
585                 }
586
587                 /* if our home hash slot is occupied, find a free entry
588                  * in the hash table
589                  */
590                 if (newhost_hash->name[0] != '\0') {
591                         do {
592                                 ret = lasthfree;
593                                 if (++lasthfree > &hosttbl[NHOSTS])
594                                         lasthfree = &hosttbl[1];
595                         } while (ret->name[0] != '\0');
596
597                         if (!newhost_hash->head) {
598                                 /* Move an interloper using our home.  Use
599                                  * scratch pointers in case the new head is
600                                  * pointing to itself.
601                                  */
602                                 f = newhost_hash->h_fwd;
603                                 b = newhost_hash->h_bak;
604                                 f->h_bak = ret;
605                                 b->h_fwd = ret;
606                                 f = newhost_hash->l_fwd;
607                                 b = newhost_hash->l_bak;
608                                 f->l_bak = ret;
609                                 b->l_fwd = ret;
610                                 bcopy(newhost_hash,ret,sizeof(*ret));
611                                 ret = newhost_hash;
612                                 ret->head = 1;
613                                 ret->h_fwd = ret;
614                                 ret->h_bak = ret;
615                         } else {
616                                 /* link to an existing chain in our home
617                                  */
618                                 ret->head = 0;
619                                 p = newhost_hash->h_bak;
620                                 ret->h_fwd = newhost_hash;
621                                 ret->h_bak = p;
622                                 p->h_fwd = ret;
623                                 newhost_hash->h_bak = ret;
624                         }
625                 } else {
626                         ret = newhost_hash;
627                         ret->head = 1;
628                         ret->h_fwd = ret;
629                         ret->h_bak = ret;
630                 }
631                 ret->addr = *addr;
632                 ret->ntp = ntp;
633                 (void)strncpy(ret->name, name, sizeof(ret->name));
634                 ret->good = good_host_name(name);
635                 ret->l_fwd = &self;
636                 ret->l_bak = self.l_bak;
637                 self.l_bak->l_fwd = ret;
638                 self.l_bak = ret;
639                 slvcount++;
640
641                 ret->noanswer = 0;
642                 ret->need_set = 1;
643
644         } else {
645                 ret->noanswer = (ret->noanswer != 0);
646         }
647
648         /* need to clear sequence number anyhow */
649         ret->seq = 0;
650         return(ret);
651 }
652
653 /*
654  * remove the machine with the given index in the host table.
655  */
656 struct hosttbl *
657 remmach(htp)
658         struct hosttbl *htp;
659 {
660         struct hosttbl *lprv, *hnxt, *f, *b;
661
662         if (trace)
663                 fprintf(fd, "remove %s\n", htp->name);
664
665         /* get out of the lists */
666         htp->l_fwd->l_bak = lprv = htp->l_bak;
667         htp->l_bak->l_fwd = htp->l_fwd;
668         htp->h_fwd->h_bak = htp->h_bak;
669         htp->h_bak->h_fwd = hnxt = htp->h_fwd;
670
671         /* If we are in the home slot, pull up the chain */
672         if (htp->head && hnxt != htp) {
673                 if (lprv == hnxt)
674                         lprv = htp;
675
676                 /* Use scratch pointers in case the new head is pointing to
677                  * itself.
678                  */
679                 f = hnxt->h_fwd;
680                 b = hnxt->h_bak;
681                 f->h_bak = htp;
682                 b->h_fwd = htp;
683                 f = hnxt->l_fwd;
684                 b = hnxt->l_bak;
685                 f->l_bak = htp;
686                 b->l_fwd = htp;
687                 hnxt->head = 1;
688                 bcopy(hnxt, htp, sizeof(*htp));
689                 lasthfree = hnxt;
690         } else {
691                 lasthfree = htp;
692         }
693
694         lasthfree->name[0] = '\0';
695         lasthfree->h_fwd = 0;
696         lasthfree->l_fwd = 0;
697         slvcount--;
698
699         return lprv;
700 }
701
702
703 /*
704  * Remove all the machines from the host table that exist on the given
705  * network.  This is called when a master transitions to a slave on a
706  * given network.
707  */
708 void
709 rmnetmachs(ntp)
710         struct netinfo *ntp;
711 {
712         struct hosttbl *htp;
713
714         if (trace)
715                 prthp(CLK_TCK);
716         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
717                 if (ntp == htp->ntp)
718                         htp = remmach(htp);
719         }
720         if (trace)
721                 prthp(CLK_TCK);
722 }
723
724 void
725 masterup(net)
726         struct netinfo *net;
727 {
728         xmit(TSP_MASTERUP, 0, &net->dest_addr);
729
730         /*
731          * Do not tell new slaves our time for a while.  This ensures
732          * we do not tell them to start using our time, before we have
733          * found a good master.
734          */
735         (void)gettimeofday(&net->slvwait, 0);
736 }
737
738 void
739 newslave(msg)
740         struct tsp *msg;
741 {
742         struct hosttbl *htp;
743         struct tsp *answer, to;
744         struct timeval now, tmptv;
745
746         if (!fromnet || fromnet->status != MASTER)
747                 return;
748
749         htp = addmach(msg->tsp_name, &from,fromnet);
750         htp->seq = msg->tsp_seq;
751         if (trace)
752                 prthp(0);
753
754         /*
755          * If we are stable, send our time to the slave.
756          * Do not go crazy if the date has been changed.
757          */
758         (void)gettimeofday(&now, 0);
759         if (now.tv_sec >= fromnet->slvwait.tv_sec+3
760             || now.tv_sec < fromnet->slvwait.tv_sec) {
761                 to.tsp_type = TSP_SETTIME;
762                 (void)strcpy(to.tsp_name, hostname);
763                 (void)gettimeofday(&tmptv, 0);
764                 to.tsp_time.tv_sec = tmptv.tv_sec;
765                 to.tsp_time.tv_usec = tmptv.tv_usec;
766                 answer = acksend(&to, &htp->addr,
767                                  htp->name, TSP_ACK,
768                                  0, htp->noanswer);
769                 if (answer) {
770                         htp->need_set = 0;
771                 } else {
772                         syslog(LOG_WARNING,
773                                "no reply to initial SETTIME from %s",
774                                htp->name);
775                         htp->noanswer = LOSTHOST;
776                 }
777         }
778 }
779
780
781 /*
782  * react to a TSP_QUIT:
783  */
784 void
785 doquit(msg)
786         struct tsp *msg;
787 {
788         if (fromnet->status == MASTER) {
789                 if (!good_host_name(msg->tsp_name)) {
790                         if (fromnet->quit_count <= 0) {
791                                 syslog(LOG_NOTICE,"untrusted %s told us QUIT",
792                                        msg->tsp_name);
793                                 suppress(&from, msg->tsp_name, fromnet);
794                                 fromnet->quit_count = 1;
795                                 return;
796                         }
797                         syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
798                                msg->tsp_name);
799                         fromnet->quit_count = 2;
800                         fromnet->status = NOMASTER;
801                 } else {
802                         fromnet->status = SLAVE;
803                 }
804                 rmnetmachs(fromnet);
805                 longjmp(jmpenv, 2);             /* give up and be a slave */
806
807         } else {
808                 if (!good_host_name(msg->tsp_name)) {
809                         syslog(LOG_NOTICE, "untrusted %s told us QUIT",
810                                msg->tsp_name);
811                         fromnet->quit_count = 2;
812                 }
813         }
814 }
815
816 void
817 traceon()
818 {
819         if (!fd) {
820                 fd = fopen(_PATH_TIMEDLOG, "w");
821                 if (!fd) {
822                         trace = 0;
823                         return;
824                 }
825                 fprintf(fd,"Tracing started at %s\n", date());
826         }
827         trace = 1;
828         get_goodgroup(1);
829         setstatus();
830         prthp(CLK_TCK);
831 }
832
833
834 void
835 traceoff(msg)
836         char *msg;
837 {
838         get_goodgroup(1);
839         setstatus();
840         prthp(CLK_TCK);
841         if (trace) {
842                 fprintf(fd, msg, date());
843                 (void)fclose(fd);
844                 fd = 0;
845         }
846 #ifdef GPROF
847         moncontrol(0);
848         _mcleanup();
849         moncontrol(1);
850 #endif
851         trace = OFF;
852 }