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