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