]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/timed/timed/master.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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()
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, 0);
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, 0);
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, 0);
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)strcpy(newdate, ctime(&tsp_time_sec));
169                         if (!good_host_name(msg->tsp_name)) {
170                                 syslog(LOG_NOTICE,
171                                        "attempted date change by %s to %s",
172                                        msg->tsp_name, newdate);
173                                 spreadtime();
174                                 break;
175                         }
176
177                         mchgdate(msg);
178                         (void)gettimeofday(&ntime, 0);
179                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
180                         break;
181
182                 case TSP_SETDATEREQ:
183                         if (!fromnet || fromnet->status != MASTER)
184                                 break;
185                         tsp_time_sec = msg->tsp_time.tv_sec;
186                         (void)strcpy(newdate, ctime(&tsp_time_sec));
187                         htp = findhost(msg->tsp_name);
188                         if (htp == 0) {
189                                 syslog(LOG_ERR,
190                                        "attempted SET DATEREQ by uncontrolled %s to %s",
191                                        msg->tsp_name, newdate);
192                                 break;
193                         }
194                         if (htp->seq == msg->tsp_seq)
195                                 break;
196                         htp->seq = msg->tsp_seq;
197                         if (!htp->good) {
198                                 syslog(LOG_NOTICE,
199                                 "attempted SET DATEREQ by untrusted %s to %s",
200                                        msg->tsp_name, newdate);
201                                 spreadtime();
202                                 break;
203                         }
204
205                         mchgdate(msg);
206                         (void)gettimeofday(&ntime, 0);
207                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
208                         break;
209
210                 case TSP_MSITE:
211                         xmit(TSP_ACK, msg->tsp_seq, &from);
212                         break;
213
214                 case TSP_MSITEREQ:
215                         break;
216
217                 case TSP_TRACEON:
218                         traceon();
219                         break;
220
221                 case TSP_TRACEOFF:
222                         traceoff("Tracing ended at %s\n");
223                         break;
224
225                 case TSP_ELECTION:
226                         if (!fromnet)
227                                 break;
228                         if (fromnet->status == MASTER) {
229                                 pollingtime = 0;
230                                 (void)addmach(msg->tsp_name, &from,fromnet);
231                         }
232                         taddr = from;
233                         (void)strcpy(tname, msg->tsp_name);
234                         to.tsp_type = TSP_QUIT;
235                         (void)strcpy(to.tsp_name, hostname);
236                         answer = acksend(&to, &taddr, tname,
237                                          TSP_ACK, 0, 1);
238                         if (answer == NULL) {
239                                 syslog(LOG_ERR, "election error by %s",
240                                        tname);
241                         }
242                         break;
243
244                 case TSP_CONFLICT:
245                         /*
246                          * After a network partition, there can be
247                          * more than one master: the first slave to
248                          * come up will notify here the situation.
249                          */
250                         if (!fromnet || fromnet->status != MASTER)
251                                 break;
252                         (void)strcpy(to.tsp_name, hostname);
253
254                         /* The other master often gets into the same state,
255                          * with boring results if we stay at it forever.
256                          */
257                         ntp = fromnet;  /* (acksend() can leave fromnet=0 */
258                         for (i = 0; i < 3; i++) {
259                                 to.tsp_type = TSP_RESOLVE;
260                                 (void)strcpy(to.tsp_name, hostname);
261                                 answer = acksend(&to, &ntp->dest_addr,
262                                                  ANYADDR, TSP_MASTERACK,
263                                                  ntp, 0);
264                                 if (!answer)
265                                         break;
266                                 htp = addmach(answer->tsp_name,&from,ntp);
267                                 to.tsp_type = TSP_QUIT;
268                                 msg = acksend(&to, &htp->addr, htp->name,
269                                               TSP_ACK, 0, htp->noanswer);
270                                 if (msg == NULL) {
271                                         syslog(LOG_ERR,
272                                     "no response from %s to CONFLICT-QUIT",
273                                                htp->name);
274                                 }
275                         }
276                         masterup(ntp);
277                         pollingtime = 0;
278                         break;
279
280                 case TSP_RESOLVE:
281                         if (!fromnet || fromnet->status != MASTER)
282                                 break;
283                         /*
284                          * do not want to call synch() while waiting
285                          * to be killed!
286                          */
287                         (void)gettimeofday(&ntime, (struct timezone *)0);
288                         pollingtime = ntime.tv_sec + SAMPLEINTVL;
289                         break;
290
291                 case TSP_QUIT:
292                         doquit(msg);            /* become a slave */
293                         break;
294
295                 case TSP_LOOP:
296                         if (!fromnet || fromnet->status != MASTER
297                             || !strcmp(msg->tsp_name, hostname))
298                                 break;
299                         /*
300                          * We should not have received this from a net
301                          * we are master on.  There must be two masters.
302                          */
303                         htp = addmach(msg->tsp_name, &from,fromnet);
304                         to.tsp_type = TSP_QUIT;
305                         (void)strcpy(to.tsp_name, hostname);
306                         answer = acksend(&to, &htp->addr, htp->name,
307                                          TSP_ACK, 0, 1);
308                         if (!answer) {
309                                 syslog(LOG_WARNING,
310                                 "loop breakage: no reply from %s=%s to QUIT",
311                                     htp->name, inet_ntoa(htp->addr.sin_addr));
312                                 (void)remmach(htp);
313                         }
314
315                 case TSP_TEST:
316                         if (trace) {
317                                 fprintf(fd,
318                 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
319                 nnets, nmasternets, nslavenets, nignorednets);
320                                 setstatus();
321                         }
322                         pollingtime = 0;
323                         polls = POLLRATE-1;
324                         break;
325
326                 default:
327                         if (trace) {
328                                 fprintf(fd, "garbage message: ");
329                                 print(msg, &from);
330                         }
331                         break;
332                 }
333         }
334         goto loop;
335 }
336
337
338 /*
339  * change the system date on the master
340  */
341 static void
342 mchgdate(msg)
343         struct tsp *msg;
344 {
345         char tname[MAXHOSTNAMELEN];
346         char olddate[32];
347         struct timeval otime, ntime, tmptv;
348         struct utmpx utx;
349
350         (void)strcpy(tname, msg->tsp_name);
351
352         xmit(TSP_DATEACK, msg->tsp_seq, &from);
353
354         (void)strcpy(olddate, date());
355
356         /* adjust time for residence on the queue */
357         (void)gettimeofday(&otime, 0);
358         adj_msg_time(msg,&otime);
359
360         tmptv.tv_sec = msg->tsp_time.tv_sec;
361         tmptv.tv_usec = msg->tsp_time.tv_usec;
362         timevalsub(&ntime, &tmptv, &otime);
363         if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
364                 /*
365                  * do not change the clock if we can adjust it
366                  */
367                 dictate = 3;
368                 synch(tvtomsround(ntime));
369         } else {
370                 utx.ut_type = OLD_TIME;
371                 gettimeofday(&utx.ut_tv, NULL);
372                 pututxline(&utx);
373                 (void)settimeofday(&tmptv, 0);
374                 utx.ut_type = NEW_TIME;
375                 gettimeofday(&utx.ut_tv, NULL);
376                 pututxline(&utx);
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 }