file reorg, pathnames.h, paths.h
[unix-history] / usr / src / usr.sbin / timed / timed / timed.c
CommitLineData
1b69205a 1/*
8b8a47ba 2 * Copyright (c) 1985 Regents of the University of California.
26b5a830
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1b69205a
RG
16 */
17
18#ifndef lint
19char copyright[] =
8b8a47ba 20"@(#) Copyright (c) 1985 Regents of the University of California.\n\
1b69205a 21 All rights reserved.\n";
26b5a830 22#endif /* not lint */
1b69205a
RG
23
24#ifndef lint
7abf8d65 25static char sccsid[] = "@(#)timed.c 2.16 (Berkeley) %G%";
26b5a830 26#endif /* not lint */
1b69205a
RG
27
28#include "globals.h"
29#define TSPTYPES
30#include <protocols/timed.h>
31#include <net/if.h>
32#include <sys/file.h>
33#include <sys/ioctl.h>
34#include <setjmp.h>
383be14d 35#include "pathnames.h"
1b69205a 36
1b69205a
RG
37int id;
38int trace;
19f1ff0f 39int sock, sock_raw = -1;
75d5ce8a 40int status = 0;
1b69205a
RG
41int backoff;
42int slvcount; /* no. of slaves controlled by master */
43int machup;
44u_short sequence; /* sequence number */
45long delay1;
46long delay2;
77491c98 47long random();
964c88f5 48char hostname[MAXHOSTNAMELEN];
1b69205a 49struct host hp[NHOSTS];
383be14d 50char tracefile[] = _PATH_TIMEDLOG;
1b69205a
RG
51FILE *fd;
52jmp_buf jmpenv;
75d5ce8a 53struct netinfo *nettab = NULL;
9a957f87
JB
54int nslavenets; /* Number of networks were I could be a slave */
55int nmasternets; /* Number of networks were I could be a master */
56int nignorednets; /* Number of ignored networks */
57int nnets; /* Number of networks I am connected to */
58struct netinfo *slavenet;
59struct netinfo *firstslavenet();
60int Mflag;
a3f2fd3a
JB
61int justquit = 0;
62
5c0a9ec2
JB
63struct nets {
64 char *name;
65 long net;
66 struct nets *next;
67} *nets = (struct nets *)0;
1b69205a
RG
68
69/*
70 * The timedaemons synchronize the clocks of hosts in a local area network.
71 * One daemon runs as master, all the others as slaves. The master
72 * performs the task of computing clock differences and sends correction
73 * values to the slaves.
74 * Slaves start an election to choose a new master when the latter disappears
75 * because of a machine crash, network partition, or when killed.
76 * A resolution protocol is used to kill all but one of the masters
77 * that happen to exist in segments of a partitioned network when the
78 * network partition is fixed.
79 *
80 * Authors: Riccardo Gusella & Stefano Zatti
1b69205a
RG
81 */
82
83main(argc, argv)
84int argc;
85char **argv;
86{
87 int on;
88 int ret;
89 long seed;
5c0a9ec2 90 int nflag, iflag;
1b69205a
RG
91 struct timeval time;
92 struct servent *srvp;
1b69205a 93 long casual();
1b69205a 94 char *date();
964c88f5 95 int n;
75d5ce8a 96 int flag;
964c88f5
JB
97 char buf[BUFSIZ];
98 struct ifconf ifc;
99 struct ifreq ifreq, *ifr;
75d5ce8a
JB
100 register struct netinfo *ntp;
101 struct netinfo *ntip;
9a957f87 102 struct netinfo *savefromnet;
75d5ce8a
JB
103 struct sockaddr_in server;
104 u_short port;
77491c98 105 uid_t getuid();
9a957f87 106
75d5ce8a
JB
107#ifdef lint
108 ntip = NULL;
109#endif
1b69205a 110
319813b5 111 Mflag = 0;
1b69205a
RG
112 on = 1;
113 backoff = 1;
1b69205a
RG
114 trace = OFF;
115 nflag = OFF;
5c0a9ec2 116 iflag = OFF;
a3f2fd3a 117 openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
1b69205a
RG
118
119 if (getuid() != 0) {
964c88f5 120 fprintf(stderr, "Timed: not superuser\n");
1b69205a
RG
121 exit(1);
122 }
123
124 while (--argc > 0 && **++argv == '-') {
125 (*argv)++;
126 do {
127 switch (**argv) {
128
129 case 'M':
319813b5 130 Mflag = 1;
1b69205a
RG
131 break;
132 case 't':
133 trace = ON;
1b69205a
RG
134 break;
135 case 'n':
136 argc--, argv++;
5c0a9ec2
JB
137 if (iflag) {
138 fprintf(stderr,
139 "timed: -i and -n make no sense together\n");
140 } else {
141 nflag = ON;
77491c98 142 addnetname(*argv);
5c0a9ec2
JB
143 }
144 while (*(++(*argv)+1)) ;
145 break;
146 case 'i':
147 argc--, argv++;
148 if (nflag) {
149 fprintf(stderr,
150 "timed: -i and -n make no sense together\n");
151 } else {
152 iflag = ON;
77491c98 153 addnetname(*argv);
5c0a9ec2 154 }
1b69205a
RG
155 while (*(++(*argv)+1)) ;
156 break;
157 default:
158 fprintf(stderr, "timed: -%c: unknown option\n",
159 **argv);
160 break;
161 }
162 } while (*++(*argv));
163 }
164
964c88f5 165 if (trace == ON) {
75d5ce8a
JB
166 fd = fopen(tracefile, "w");
167 setlinebuf(fd);
964c88f5
JB
168 fprintf(fd, "Tracing started on: %s\n\n",
169 date());
964c88f5 170 }
964c88f5 171
1b69205a
RG
172 srvp = getservbyname("timed", "udp");
173 if (srvp == 0) {
964c88f5 174 syslog(LOG_CRIT, "unknown service 'timed/udp'");
1b69205a
RG
175 exit(1);
176 }
75d5ce8a 177 port = srvp->s_port;
1b69205a
RG
178 server.sin_port = srvp->s_port;
179 server.sin_family = AF_INET;
180 sock = socket(AF_INET, SOCK_DGRAM, 0);
181 if (sock < 0) {
964c88f5 182 syslog(LOG_ERR, "socket: %m");
1b69205a
RG
183 exit(1);
184 }
185 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
186 sizeof(on)) < 0) {
964c88f5 187 syslog(LOG_ERR, "setsockopt: %m");
1b69205a
RG
188 exit(1);
189 }
190 if (bind(sock, &server, sizeof(server))) {
191 if (errno == EADDRINUSE)
964c88f5 192 syslog(LOG_ERR, "server already running");
1b69205a 193 else
964c88f5 194 syslog(LOG_ERR, "bind: %m");
1b69205a
RG
195 exit(1);
196 }
197
198 /* choose a unique seed for random number generation */
199 (void)gettimeofday(&time, (struct timezone *)0);
200 seed = time.tv_sec + time.tv_usec;
201 srandom(seed);
202
03b1f474 203 sequence = random(); /* initial seq number */
1b69205a
RG
204
205 /* rounds kernel variable time to multiple of 5 ms. */
206 time.tv_sec = 0;
207 time.tv_usec = -((time.tv_usec/1000) % 5) * 1000;
208 (void)adjtime(&time, (struct timeval *)0);
209
210 id = getpid();
211
212 if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
964c88f5 213 syslog(LOG_ERR, "gethostname: %m");
1b69205a
RG
214 exit(1);
215 }
216 hp[0].name = hostname;
217
5c0a9ec2
JB
218 if (nflag || iflag) {
219 struct netent *getnetent();
220 struct netent *n;
221 struct nets *np;
222 for ( np = nets ; np ; np = np->next) {
223 n = getnetbyname(np->name);
224 if (n == NULL) {
225 syslog(LOG_ERR, "getnetbyname: unknown net %s",
226 np->name);
227 exit(1);
228 }
229 np->net = n->n_net;
1b69205a 230 }
964c88f5
JB
231 }
232 ifc.ifc_len = sizeof(buf);
233 ifc.ifc_buf = buf;
77491c98 234 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
964c88f5
JB
235 syslog(LOG_ERR, "get interface configuration: %m");
236 exit(1);
237 }
75d5ce8a
JB
238 n = ifc.ifc_len/sizeof(struct ifreq);
239 ntp = NULL;
240 for (ifr = ifc.ifc_req; n > 0; n--, ifr++) {
241 if (ifr->ifr_addr.sa_family != AF_INET)
242 continue;
964c88f5 243 ifreq = *ifr;
75d5ce8a
JB
244 if (ntp == NULL)
245 ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
246 ntp->my_addr =
247 ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
77491c98 248 if (ioctl(sock, SIOCGIFFLAGS,
964c88f5
JB
249 (char *)&ifreq) < 0) {
250 syslog(LOG_ERR, "get interface flags: %m");
251 continue;
1b69205a 252 }
964c88f5 253 if ((ifreq.ifr_flags & IFF_UP) == 0 ||
75d5ce8a
JB
254 ((ifreq.ifr_flags & IFF_BROADCAST) == 0 &&
255 (ifreq.ifr_flags & IFF_POINTOPOINT) == 0)) {
964c88f5 256 continue;
1b69205a 257 }
75d5ce8a
JB
258 if (ifreq.ifr_flags & IFF_BROADCAST)
259 flag = 1;
260 else
261 flag = 0;
77491c98 262 if (ioctl(sock, SIOCGIFNETMASK,
964c88f5 263 (char *)&ifreq) < 0) {
75d5ce8a 264 syslog(LOG_ERR, "get netmask: %m");
964c88f5
JB
265 continue;
266 }
75d5ce8a 267 ntp->mask = ((struct sockaddr_in *)
964c88f5 268 &ifreq.ifr_addr)->sin_addr.s_addr;
75d5ce8a 269 if (flag) {
77491c98 270 if (ioctl(sock, SIOCGIFBRDADDR,
75d5ce8a
JB
271 (char *)&ifreq) < 0) {
272 syslog(LOG_ERR, "get broadaddr: %m");
273 continue;
274 }
275 ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
276 } else {
77491c98 277 if (ioctl(sock, SIOCGIFDSTADDR,
75d5ce8a
JB
278 (char *)&ifreq) < 0) {
279 syslog(LOG_ERR, "get destaddr: %m");
280 continue;
281 }
282 ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
964c88f5 283 }
75d5ce8a 284 ntp->dest_addr.sin_port = port;
5c0a9ec2 285 if (nflag || iflag) {
964c88f5 286 u_long addr, mask;
5c0a9ec2 287 struct nets *n;
964c88f5 288
75d5ce8a
JB
289 addr = ntohl(ntp->dest_addr.sin_addr.s_addr);
290 mask = ntohl(ntp->mask);
964c88f5
JB
291 while ((mask & 1) == 0) {
292 addr >>= 1;
293 mask >>= 1;
1b69205a 294 }
5c0a9ec2
JB
295 for (n = nets ; n ; n = n->next)
296 if (addr == n->net)
297 break;
298 if (nflag && !n || iflag && n)
1b69205a 299 continue;
1b69205a 300 }
75d5ce8a
JB
301 ntp->net = ntp->mask & ntp->dest_addr.sin_addr.s_addr;
302 ntp->next = NULL;
303 if (nettab == NULL) {
304 nettab = ntp;
305 } else {
306 ntip->next = ntp;
307 }
308 ntip = ntp;
309 ntp = NULL;
964c88f5 310 }
75d5ce8a
JB
311 if (ntp)
312 (void) free((char *)ntp);
313 if (nettab == NULL) {
964c88f5
JB
314 syslog(LOG_ERR, "No network usable");
315 exit(1);
1b69205a
RG
316 }
317
9a957f87
JB
318 for (ntp = nettab; ntp != NULL; ntp = ntp->next)
319 lookformaster(ntp);
320 setstatus();
321 /*
322 * Take care of some basic initialization.
323 */
1b69205a
RG
324 /* us. delay to be used in response to broadcast */
325 delay1 = casual((long)10000, 200000);
326
327 /* election timer delay in secs. */
328 delay2 = casual((long)MINTOUT, (long)MAXTOUT);
8b8a47ba
MK
329#ifndef DEBUG
330 if (fork())
331 exit(0);
332 { int s;
333 for (s = getdtablesize(); s >= 0; --s)
334 (void) close(s);
335 (void) open("/dev/null", 0);
336 (void) dup2(0, 1);
337 (void) dup2(0, 2);
338 s = open("/dev/tty", 2);
339 if (s >= 0) {
8b8a47ba
MK
340 (void) close(s);
341 }
342 }
343#endif
344
1b69205a 345
1b69205a 346 if (Mflag) {
1b69205a
RG
347 /*
348 * number (increased by 1) of slaves controlled by master:
349 * used in master.c, candidate.c, networkdelta.c, and
350 * correct.c
351 */
352 slvcount = 1;
1b69205a 353 ret = setjmp(jmpenv);
9a957f87 354
1b69205a
RG
355 switch (ret) {
356
357 case 0:
9a957f87
JB
358 makeslave(firstslavenet());
359 setstatus();
1b69205a
RG
360 break;
361 case 1:
9a957f87
JB
362 /* Just lost our master */
363 setstatus();
364 slavenet->status = election(slavenet);
365 checkignorednets();
366 setstatus();
367 if (slavenet->status == MASTER)
368 makeslave(firstslavenet());
369 else
370 makeslave(slavenet);
371 setstatus();
1b69205a
RG
372 break;
373 case 2:
9a957f87
JB
374 /* Just been told to quit */
375 fromnet->status = SLAVE;
376 setstatus();
377 savefromnet = fromnet;
378 rmnetmachs(fromnet);
379 checkignorednets();
380 if (slavenet)
381 makeslave(slavenet);
382 else
383 makeslave(savefromnet);
384 setstatus();
a3f2fd3a 385 justquit = 1;
1b69205a 386 break;
9a957f87 387
1b69205a
RG
388 default:
389 /* this should not happen */
964c88f5 390 syslog(LOG_ERR, "Attempt to enter invalid state");
1b69205a
RG
391 break;
392 }
393
19f1ff0f
MK
394 if (status & MASTER) {
395 /* open raw socket used to measure time differences */
396 if (sock_raw == -1) {
397 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
398 if (sock_raw < 0) {
399 syslog(LOG_ERR, "opening raw socket: %m");
400 exit (1);
401 }
402 }
403 } else {
404 /* sock_raw is not being used now */
405 if (sock_raw != -1) {
406 (void)close(sock_raw);
407 sock_raw = -1;
408 }
409 }
410
964c88f5 411 if (status == MASTER)
1b69205a
RG
412 master();
413 else
414 slave();
415 } else {
416 /* if Mflag is not set timedaemon is forced to act as a slave */
75d5ce8a 417 status = SLAVE;
1b69205a 418 if (setjmp(jmpenv)) {
9a957f87
JB
419 setstatus();
420 checkignorednets();
9a957f87 421 }
8b8a47ba 422 makeslave(firstslavenet());
cefa40d1 423 makeslave(firstslavenet());
9a957f87
JB
424 for (ntp = nettab; ntp != NULL; ntp = ntp->next)
425 if (ntp->status == MASTER)
426 ntp->status = IGNORE;
427 setstatus();
428 slave();
429 }
430}
431
432/*
433 * Try to become master over ignored nets..
434 */
435checkignorednets()
436{
437 register struct netinfo *ntp;
438 for (ntp = nettab; ntp != NULL; ntp = ntp->next)
439 if (ntp->status == IGNORE)
440 lookformaster(ntp);
441}
442
443lookformaster(ntp)
444 register struct netinfo *ntp;
445{
446 struct tsp resp, conflict, *answer, *readmsg(), *acksend();
447 struct timeval time;
448 char mastername[MAXHOSTNAMELEN];
449 struct sockaddr_in masteraddr;
450
451 ntp->status = SLAVE;
452 /* look for master */
453 resp.tsp_type = TSP_MASTERREQ;
454 (void)strcpy(resp.tsp_name, hostname);
455 answer = acksend(&resp, &ntp->dest_addr, (char *)ANYADDR,
456 TSP_MASTERACK, ntp);
457 if (answer == NULL) {
458 /*
459 * Various conditions can cause conflict: race between
460 * two just started timedaemons when no master is
461 * present, or timedaemon started during an election.
462 * Conservative approach is taken: give up and became a
463 * slave postponing election of a master until first
464 * timer expires.
465 */
466 time.tv_sec = time.tv_usec = 0;
467 answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR,
468 &time, ntp);
469 if (answer != NULL) {
470 ntp->status = SLAVE;
471 return;
472 }
473
474 time.tv_sec = time.tv_usec = 0;
475 answer = readmsg(TSP_MASTERUP, (char *)ANYADDR,
476 &time, ntp);
477 if (answer != NULL) {
478 ntp->status = SLAVE;
479 return;
480 }
481
482 time.tv_sec = time.tv_usec = 0;
483 answer = readmsg(TSP_ELECTION, (char *)ANYADDR,
484 &time, ntp);
485 if (answer != NULL) {
486 ntp->status = SLAVE;
487 return;
488 }
489 ntp->status = MASTER;
490 } else {
491 (void)strcpy(mastername, answer->tsp_name);
492 masteraddr = from;
493
494 /*
495 * If network has been partitioned, there might be other
496 * masters; tell the one we have just acknowledged that
497 * it has to gain control over the others.
498 */
499 time.tv_sec = 0;
500 time.tv_usec = 300000;
501 answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time,
502 ntp);
503 /*
504 * checking also not to send CONFLICT to ack'ed master
505 * due to duplicated MASTERACKs
506 */
507 if (answer != NULL &&
508 strcmp(answer->tsp_name, mastername) != 0) {
509 conflict.tsp_type = TSP_CONFLICT;
510 (void)strcpy(conflict.tsp_name, hostname);
511 if (acksend(&conflict, &masteraddr, mastername,
512 TSP_ACK, (struct netinfo *)NULL) == NULL) {
513 syslog(LOG_ERR,
514 "error on sending TSP_CONFLICT");
75d5ce8a
JB
515 exit(1);
516 }
1b69205a 517 }
1b69205a
RG
518 }
519}
9a957f87
JB
520/*
521 * based on the current network configuration, set the status, and count
522 * networks;
523 */
524setstatus()
525{
526 register struct netinfo *ntp;
527
528 status = 0;
529 nmasternets = nslavenets = nnets = nignorednets = 0;
530 if (trace)
531 fprintf(fd, "Net status:\n");
532 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
77491c98 533 switch ((int)ntp->status) {
9a957f87
JB
534 case MASTER:
535 nmasternets++;
536 break;
537 case SLAVE:
538 nslavenets++;
539 break;
540 case IGNORE:
541 nignorednets++;
542 break;
543 }
544 if (trace) {
545 fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
77491c98 546 switch ((int)ntp->status) {
9a957f87
JB
547 case MASTER:
548 fprintf(fd, "MASTER\n");
549 break;
550 case SLAVE:
551 fprintf(fd, "SLAVE\n");
552 break;
553 case IGNORE:
554 fprintf(fd, "IGNORE\n");
555 break;
556 default:
ff8002b1 557 fprintf(fd, "invalid state %d\n",(int)ntp->status);
9a957f87
JB
558 break;
559 }
560 }
561 nnets++;
562 status |= ntp->status;
563 }
564 status &= ~IGNORE;
565 if (trace)
566 fprintf(fd,
567 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
568 nnets, nmasternets, nslavenets, nignorednets);
569}
570
571makeslave(net)
572 struct netinfo *net;
573{
574 register struct netinfo *ntp;
575
576 for (ntp = nettab; ntp != NULL; ntp = ntp->next)
577 if (ntp->status == SLAVE && ntp != net)
578 ntp->status = IGNORE;
579 slavenet = net;
580}
581
582struct netinfo *
583firstslavenet()
584{
585 register struct netinfo *ntp;
586
587 for (ntp = nettab; ntp != NULL; ntp = ntp->next)
588 if (ntp->status == SLAVE)
589 return (ntp);
590 return ((struct netinfo *)0);
591}
1b69205a 592
8b8a47ba 593/*
1b69205a
RG
594 * `casual' returns a random number in the range [inf, sup]
595 */
596
75d5ce8a
JB
597long
598casual(inf, sup)
1b69205a
RG
599long inf;
600long sup;
601{
602 float value;
1b69205a 603
964c88f5 604 value = (float)(random() & 0x7fffffff) / 0x7fffffff;
1b69205a
RG
605 return(inf + (sup - inf) * value);
606}
607
964c88f5
JB
608char *
609date()
1b69205a 610{
2b79715d 611 char *ctime();
1b69205a 612 struct timeval tv;
1b69205a
RG
613
614 (void)gettimeofday(&tv, (struct timezone *)0);
03b1f474 615 return (ctime(&tv.tv_sec));
1b69205a 616}
5c0a9ec2
JB
617
618addnetname(name)
619 char *name;
620{
03b1f474
MK
621 register struct nets **netlist = &nets;
622
5c0a9ec2
JB
623 while (*netlist)
624 netlist = &((*netlist)->next);
625 *netlist = (struct nets *)malloc(sizeof **netlist);
626 if (*netlist == (struct nets *)0) {
627 syslog(LOG_ERR, "malloc failed");
628 exit(1);
629 }
77491c98 630 bzero((char *)*netlist, sizeof(**netlist));
5c0a9ec2
JB
631 (*netlist)->name = name;
632}