]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/timed/timed/master.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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)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, NULL);
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, NULL);
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, NULL);
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(struct tsp *msg)
343 {
344         char tname[MAXHOSTNAMELEN];
345         char olddate[32];
346         struct timeval otime, ntime, tmptv;
347         struct utmpx utx;
348
349         (void)strcpy(tname, msg->tsp_name);
350
351         xmit(TSP_DATEACK, msg->tsp_seq, &from);
352
353         (void)strcpy(olddate, date());
354
355         /* adjust time for residence on the queue */
356         (void)gettimeofday(&otime, NULL);
357         adj_msg_time(msg,&otime);
358
359         tmptv.tv_sec = msg->tsp_time.tv_sec;
360         tmptv.tv_usec = msg->tsp_time.tv_usec;
361         timevalsub(&ntime, &tmptv, &otime);
362         if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
363                 /*
364                  * do not change the clock if we can adjust it
365                  */
366                 dictate = 3;
367                 synch(tvtomsround(ntime));
368         } else {
369                 utx.ut_type = OLD_TIME;
370                 (void)gettimeofday(&utx.ut_tv, NULL);
371                 pututxline(&utx);
372                 (void)settimeofday(&tmptv, 0);
373                 utx.ut_type = NEW_TIME;
374                 (void)gettimeofday(&utx.ut_tv, NULL);
375                 pututxline(&utx);
376                 spreadtime();
377         }
378
379         syslog(LOG_NOTICE, "date changed by %s from %s",
380                tname, olddate);
381 }
382
383
384 /*
385  * synchronize all of the slaves
386  */
387 void
388 synch(long mydelta)
389 {
390         struct hosttbl *htp;
391         int measure_status;
392         struct timeval check, stop, wait;
393
394         if (slvcount > 0) {
395                 if (trace)
396                         fprintf(fd, "measurements starting at %s\n", date());
397                 (void)gettimeofday(&check, NULL);
398                 for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
399                         if (htp->noanswer != 0) {
400                                 measure_status = measure(500, 100,
401                                                          htp->name,
402                                                          &htp->addr,0);
403                         } else {
404                                 measure_status = measure(3000, 100,
405                                                          htp->name,
406                                                          &htp->addr,0);
407                         }
408                         if (measure_status != GOOD) {
409                                 /* The slave did not respond.  We have
410                                  * just wasted lots of time on it.
411                                  */
412                                 htp->delta = HOSTDOWN;
413                                 if (++htp->noanswer >= LOSTHOST) {
414                                         if (trace) {
415                                                 fprintf(fd,
416                                         "purging %s for not answering ICMP\n",
417                                                         htp->name);
418                                                 (void)fflush(fd);
419                                         }
420                                         htp = remmach(htp);
421                                 }
422                         } else {
423                                 htp->delta = measure_delta;
424                         }
425                         (void)gettimeofday(&stop, NULL);
426                         timevalsub(&stop, &stop, &check);
427                         if (stop.tv_sec >= 1) {
428                                 if (trace)
429                                         (void)fflush(fd);
430                                 /*
431                                  * ack messages periodically
432                                  */
433                                 wait.tv_sec = 0;
434                                 wait.tv_usec = 0;
435                                 if (0 != readmsg(TSP_TRACEON,ANYADDR,
436                                                  &wait,0))
437                                         traceon();
438                                 (void)gettimeofday(&check, NULL);
439                         }
440                 }
441                 if (trace)
442                         fprintf(fd, "measurements finished at %s\n", date());
443         }
444         if (!(status & SLAVE)) {
445                 if (!dictate) {
446                         mydelta = networkdelta();
447                 } else {
448                         dictate--;
449                 }
450         }
451         if (trace && (mydelta != 0 || (status & SLAVE)))
452                 fprintf(fd,"local correction of %ld ms.\n", mydelta);
453         correct(mydelta);
454 }
455
456 /*
457  * sends the time to each slave after the master
458  * has received the command to set the network time
459  */
460 void
461 spreadtime(void)
462 {
463         struct hosttbl *htp;
464         struct tsp to;
465         struct tsp *answer;
466         struct timeval tmptv;
467
468 /* Do not listen to the consensus after forcing the time.  This is because
469  *      the consensus takes a while to reach the time we are dictating.
470  */
471         dictate = 2;
472         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
473                 to.tsp_type = TSP_SETTIME;
474                 (void)strcpy(to.tsp_name, hostname);
475                 (void)gettimeofday(&tmptv, NULL);
476                 to.tsp_time.tv_sec = tmptv.tv_sec;
477                 to.tsp_time.tv_usec = tmptv.tv_usec;
478                 answer = acksend(&to, &htp->addr, htp->name,
479                                  TSP_ACK, 0, htp->noanswer);
480                 if (answer == 0) {
481                         /* We client does not respond, then we have
482                          * just wasted lots of time on it.
483                          */
484                         syslog(LOG_WARNING,
485                                "no reply to SETTIME from %s", htp->name);
486                         if (++htp->noanswer >= LOSTHOST) {
487                                 if (trace) {
488                                         fprintf(fd,
489                                              "purging %s for not answering",
490                                                 htp->name);
491                                         (void)fflush(fd);
492                                 }
493                                 htp = remmach(htp);
494                         }
495                 }
496         }
497 }
498
499 void
500 prthp(clock_t delta)
501 {
502         static time_t next_time;
503         time_t this_time;
504         struct tms tm;
505         struct hosttbl *htp;
506         int length, l;
507         int i;
508
509         if (!fd)                        /* quit if tracing already off */
510                 return;
511
512         this_time = times(&tm);
513         if ((time_t)(this_time + delta) < next_time)
514                 return;
515         next_time = this_time + CLK_TCK;
516
517         fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
518         htp = self.l_fwd;
519         length = 1;
520         for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
521                 l = strlen(htp->name) + 1;
522                 if (length+l >= 80) {
523                         fprintf(fd, "\n");
524                         length = 0;
525                 }
526                 length += l;
527                 fprintf(fd, " %s", htp->name);
528         }
529         fprintf(fd, "\n");
530 }
531
532
533 static struct hosttbl *newhost_hash;
534 static struct hosttbl *lasthfree = &hosttbl[0];
535
536
537 struct hosttbl *                        /* answer or 0 */
538 findhost(char *name)
539 {
540         int i, j;
541         struct hosttbl *htp;
542         char *p;
543
544         j= 0;
545         for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
546                 j = (j << 2) ^ *p;
547         newhost_hash = &hosttbl[j % NHOSTS];
548
549         htp = newhost_hash;
550         if (htp->name[0] == '\0')
551                 return(0);
552         do {
553                 if (!strcmp(name, htp->name))
554                         return(htp);
555                 htp = htp->h_fwd;
556         } while (htp != newhost_hash);
557         return(0);
558 }
559
560 /*
561  * add a host to the list of controlled machines if not already there
562  */
563 struct hosttbl *
564 addmach(char *name, struct sockaddr_in *addr, struct netinfo *ntp)
565 {
566         struct hosttbl *ret, *p, *b, *f;
567
568         ret = findhost(name);
569         if (ret == 0) {
570                 if (slvcount >= NHOSTS) {
571                         if (trace) {
572                                 fprintf(fd, "no more slots in host table\n");
573                                 prthp(CLK_TCK);
574                         }
575                         syslog(LOG_ERR, "no more slots in host table");
576                         Mflag = 0;
577                         longjmp(jmpenv, 2); /* give up and be a slave */
578                 }
579
580                 /* if our home hash slot is occupied, find a free entry
581                  * in the hash table
582                  */
583                 if (newhost_hash->name[0] != '\0') {
584                         do {
585                                 ret = lasthfree;
586                                 if (++lasthfree > &hosttbl[NHOSTS])
587                                         lasthfree = &hosttbl[1];
588                         } while (ret->name[0] != '\0');
589
590                         if (!newhost_hash->head) {
591                                 /* Move an interloper using our home.  Use
592                                  * scratch pointers in case the new head is
593                                  * pointing to itself.
594                                  */
595                                 f = newhost_hash->h_fwd;
596                                 b = newhost_hash->h_bak;
597                                 f->h_bak = ret;
598                                 b->h_fwd = ret;
599                                 f = newhost_hash->l_fwd;
600                                 b = newhost_hash->l_bak;
601                                 f->l_bak = ret;
602                                 b->l_fwd = ret;
603                                 bcopy(newhost_hash,ret,sizeof(*ret));
604                                 ret = newhost_hash;
605                                 ret->head = 1;
606                                 ret->h_fwd = ret;
607                                 ret->h_bak = ret;
608                         } else {
609                                 /* link to an existing chain in our home
610                                  */
611                                 ret->head = 0;
612                                 p = newhost_hash->h_bak;
613                                 ret->h_fwd = newhost_hash;
614                                 ret->h_bak = p;
615                                 p->h_fwd = ret;
616                                 newhost_hash->h_bak = ret;
617                         }
618                 } else {
619                         ret = newhost_hash;
620                         ret->head = 1;
621                         ret->h_fwd = ret;
622                         ret->h_bak = ret;
623                 }
624                 ret->addr = *addr;
625                 ret->ntp = ntp;
626                 (void)strncpy(ret->name, name, sizeof(ret->name));
627                 ret->good = good_host_name(name);
628                 ret->l_fwd = &self;
629                 ret->l_bak = self.l_bak;
630                 self.l_bak->l_fwd = ret;
631                 self.l_bak = ret;
632                 slvcount++;
633
634                 ret->noanswer = 0;
635                 ret->need_set = 1;
636
637         } else {
638                 ret->noanswer = (ret->noanswer != 0);
639         }
640
641         /* need to clear sequence number anyhow */
642         ret->seq = 0;
643         return(ret);
644 }
645
646 /*
647  * remove the machine with the given index in the host table.
648  */
649 struct hosttbl *
650 remmach(struct hosttbl *htp)
651 {
652         struct hosttbl *lprv, *hnxt, *f, *b;
653
654         if (trace)
655                 fprintf(fd, "remove %s\n", htp->name);
656
657         /* get out of the lists */
658         htp->l_fwd->l_bak = lprv = htp->l_bak;
659         htp->l_bak->l_fwd = htp->l_fwd;
660         htp->h_fwd->h_bak = htp->h_bak;
661         htp->h_bak->h_fwd = hnxt = htp->h_fwd;
662
663         /* If we are in the home slot, pull up the chain */
664         if (htp->head && hnxt != htp) {
665                 if (lprv == hnxt)
666                         lprv = htp;
667
668                 /* Use scratch pointers in case the new head is pointing to
669                  * itself.
670                  */
671                 f = hnxt->h_fwd;
672                 b = hnxt->h_bak;
673                 f->h_bak = htp;
674                 b->h_fwd = htp;
675                 f = hnxt->l_fwd;
676                 b = hnxt->l_bak;
677                 f->l_bak = htp;
678                 b->l_fwd = htp;
679                 hnxt->head = 1;
680                 bcopy(hnxt, htp, sizeof(*htp));
681                 lasthfree = hnxt;
682         } else {
683                 lasthfree = htp;
684         }
685
686         lasthfree->name[0] = '\0';
687         lasthfree->h_fwd = 0;
688         lasthfree->l_fwd = 0;
689         slvcount--;
690
691         return lprv;
692 }
693
694
695 /*
696  * Remove all the machines from the host table that exist on the given
697  * network.  This is called when a master transitions to a slave on a
698  * given network.
699  */
700 void
701 rmnetmachs(struct netinfo *ntp)
702 {
703         struct hosttbl *htp;
704
705         if (trace)
706                 prthp(CLK_TCK);
707         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
708                 if (ntp == htp->ntp)
709                         htp = remmach(htp);
710         }
711         if (trace)
712                 prthp(CLK_TCK);
713 }
714
715 void
716 masterup(struct netinfo *net)
717 {
718         xmit(TSP_MASTERUP, 0, &net->dest_addr);
719
720         /*
721          * Do not tell new slaves our time for a while.  This ensures
722          * we do not tell them to start using our time, before we have
723          * found a good master.
724          */
725         (void)gettimeofday(&net->slvwait, NULL);
726 }
727
728 void
729 newslave(struct tsp *msg)
730 {
731         struct hosttbl *htp;
732         struct tsp *answer, to;
733         struct timeval now, tmptv;
734
735         if (!fromnet || fromnet->status != MASTER)
736                 return;
737
738         htp = addmach(msg->tsp_name, &from,fromnet);
739         htp->seq = msg->tsp_seq;
740         if (trace)
741                 prthp(0);
742
743         /*
744          * If we are stable, send our time to the slave.
745          * Do not go crazy if the date has been changed.
746          */
747         (void)gettimeofday(&now, NULL);
748         if (now.tv_sec >= fromnet->slvwait.tv_sec+3
749             || now.tv_sec < fromnet->slvwait.tv_sec) {
750                 to.tsp_type = TSP_SETTIME;
751                 (void)strcpy(to.tsp_name, hostname);
752                 (void)gettimeofday(&tmptv, NULL);
753                 to.tsp_time.tv_sec = tmptv.tv_sec;
754                 to.tsp_time.tv_usec = tmptv.tv_usec;
755                 answer = acksend(&to, &htp->addr,
756                                  htp->name, TSP_ACK,
757                                  0, htp->noanswer);
758                 if (answer) {
759                         htp->need_set = 0;
760                 } else {
761                         syslog(LOG_WARNING,
762                                "no reply to initial SETTIME from %s",
763                                htp->name);
764                         htp->noanswer = LOSTHOST;
765                 }
766         }
767 }
768
769
770 /*
771  * react to a TSP_QUIT:
772  */
773 void
774 doquit(struct tsp *msg)
775 {
776         if (fromnet->status == MASTER) {
777                 if (!good_host_name(msg->tsp_name)) {
778                         if (fromnet->quit_count <= 0) {
779                                 syslog(LOG_NOTICE,"untrusted %s told us QUIT",
780                                        msg->tsp_name);
781                                 suppress(&from, msg->tsp_name, fromnet);
782                                 fromnet->quit_count = 1;
783                                 return;
784                         }
785                         syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
786                                msg->tsp_name);
787                         fromnet->quit_count = 2;
788                         fromnet->status = NOMASTER;
789                 } else {
790                         fromnet->status = SLAVE;
791                 }
792                 rmnetmachs(fromnet);
793                 longjmp(jmpenv, 2);             /* give up and be a slave */
794
795         } else {
796                 if (!good_host_name(msg->tsp_name)) {
797                         syslog(LOG_NOTICE, "untrusted %s told us QUIT",
798                                msg->tsp_name);
799                         fromnet->quit_count = 2;
800                 }
801         }
802 }
803
804 void
805 traceon(void)
806 {
807         if (!fd) {
808                 fd = fopen(_PATH_TIMEDLOG, "w");
809                 if (!fd) {
810                         trace = 0;
811                         return;
812                 }
813                 fprintf(fd,"Tracing started at %s\n", date());
814         }
815         trace = 1;
816         get_goodgroup(1);
817         setstatus();
818         prthp(CLK_TCK);
819 }
820
821
822 void
823 traceoff(char *msg)
824 {
825         get_goodgroup(1);
826         setstatus();
827         prthp(CLK_TCK);
828         if (trace) {
829                 fprintf(fd, msg, date());
830                 (void)fclose(fd);
831                 fd = 0;
832         }
833 #ifdef GPROF
834         moncontrol(0);
835         _mcleanup();
836         moncontrol(1);
837 #endif
838         trace = OFF;
839 }