date and time created 85/06/22 22:08:00 by gusella
authorRiccardo Gusella <gusella@ucbvax.Berkeley.EDU>
Sun, 23 Jun 1985 13:08:00 +0000 (05:08 -0800)
committerRiccardo Gusella <gusella@ucbvax.Berkeley.EDU>
Sun, 23 Jun 1985 13:08:00 +0000 (05:08 -0800)
SCCS-vsn: usr.sbin/timed/timed/timed.c 1.1

usr/src/usr.sbin/timed/timed/timed.c [new file with mode: 0644]

diff --git a/usr/src/usr.sbin/timed/timed/timed.c b/usr/src/usr.sbin/timed/timed/timed.c
new file mode 100644 (file)
index 0000000..a1776c6
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)timed.c    1.1 (Berkeley) %G%";
+#endif not lint
+
+#include "globals.h"
+#define TSPTYPES
+#include <protocols/timed.h>
+#include <net/if.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <setjmp.h>
+
+#define OFF            0
+#define ON             1
+#define SLAVE          0
+#define MASTER         1
+#define MAXRANDOM      2147483648      /* 2**31, max random number */
+
+int id;
+int trace;
+int sock, sock_raw;
+int status;                            /* either MASTER or SLAVE */
+int backoff;
+int slvcount;                          /* no. of slaves controlled by master */
+int machup;
+u_short sequence;                      /* sequence number */
+long delay1;
+long delay2;
+char hostname[32];
+struct in_addr broadcastaddr;          /* local net broadcast address */
+struct sockaddr_in server;
+struct host hp[NHOSTS];
+char *fj;
+FILE *fd;
+jmp_buf jmpenv;
+
+extern struct sockaddr_in from;
+
+/*
+ * The timedaemons synchronize the clocks of hosts in a local area network.
+ * One daemon runs as master, all the others as slaves. The master
+ * performs the task of computing clock differences and sends correction
+ * values to the slaves. 
+ * Slaves start an election to choose a new master when the latter disappears 
+ * because of a machine crash, network partition, or when killed.
+ * A resolution protocol is used to kill all but one of the masters
+ * that happen to exist in segments of a partitioned network when the 
+ * network partition is fixed.
+ *
+ * Authors: Riccardo Gusella & Stefano Zatti
+ *
+ * For problems and suggestions, please send mail to gusella@BERKELEY
+ */
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+       int on;
+       int ret;
+       long seed;
+       int Mflag;
+       int nflag;
+       char mastername[32];
+       char *netname;
+       struct timeval time;
+       struct servent *srvp;
+       struct netent *getnetent();
+       struct netent *localnet;
+       struct sockaddr_in masteraddr;
+       struct tsp resp, conflict, *answer, *readmsg(), *acksend();
+       long casual();
+       char *malloc(), *strcpy();
+       char *date();
+       struct in_addr inet_makeaddr();
+
+       Mflag = SLAVE;
+       on = 1;
+       backoff = 1;
+       fj = "/usr/adm/timed.log";
+       trace = OFF;
+       nflag = OFF;
+
+       if (getuid() != 0) {
+               printf("Sorry: need to be root\n");
+               exit(1);
+       }
+
+       while (--argc > 0 && **++argv == '-') {
+               (*argv)++;
+               do {
+                       switch (**argv) {
+
+                       case 'M':
+                               Mflag = MASTER; 
+                               break;
+                       case 't':
+                               trace = ON; 
+                               fd = fopen(fj, "w");
+                               fprintf(fd, "Tracing started on: %s\n\n", 
+                                                       date());
+                               (void)fflush(fd);
+                               break;
+                       case 'n':
+                               argc--, argv++;
+                               nflag = ON;
+                               netname = *argv;
+                               while (*(++(*argv)+1)) ;
+                               break;
+                       default:
+                               fprintf(stderr, "timed: -%c: unknown option\n", 
+                                                       **argv);
+                               break;
+                       }
+               } while (*++(*argv));
+       }
+
+       srvp = getservbyname("timed", "udp");
+       if (srvp == 0) {
+               syslog(LOG_ERR, "udp/timed: unknown service\n");
+               exit(1);
+       }
+       server.sin_port = srvp->s_port;
+       server.sin_family = AF_INET;
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0) {
+               syslog(LOG_ERR, "timed: opening socket\n");
+               exit(1);
+       }
+       if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, 
+                                                       sizeof(on)) < 0) {
+               syslog(LOG_ERR, "timed: setsockopt");
+               exit(1);
+       }
+       if (bind(sock, &server, sizeof(server))) {
+               if (errno == EADDRINUSE)
+                       fprintf(stderr, "timed already running\n");
+               else
+                       syslog(LOG_ERR, "timed: bind");
+               exit(1);
+       }
+
+       /* choose a unique seed for random number generation */
+       (void)gettimeofday(&time, (struct timezone *)0);
+       seed = time.tv_sec + time.tv_usec;
+       srandom(seed);
+
+       sequence = casual((long)1, (long)MAXSEQ);     /* initial seq number */
+
+       /* rounds kernel variable time to multiple of 5 ms. */
+       time.tv_sec = 0;
+       time.tv_usec = -((time.tv_usec/1000) % 5) * 1000;
+       (void)adjtime(&time, (struct timeval *)0);
+
+       id = getpid();
+
+       if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
+               syslog(LOG_ERR, "timed: gethostname: %m");
+               exit(1);
+       }
+       hp[0].name = hostname;
+
+       if (nflag) {
+               localnet = getnetbyname(netname);
+               if (localnet == NULL) {
+                       syslog(LOG_ERR, "timed: getnetbyname: %m");
+                       exit(1);
+               }
+               if (localnet == NULL) {
+                       syslog(LOG_ERR, "timed: no network: %m");
+                       exit(1);
+               }
+               broadcastaddr = inet_makeaddr(localnet->n_net,INADDR_BROADCAST);
+       } else {
+               int n;
+               int n_addrlen;
+               char *n_addr;
+               char buf[BUFSIZ];
+               struct ifconf ifc;
+               struct ifreq ifreq, *ifr;
+               struct sockaddr_in *sin;
+       
+               ifc.ifc_len = sizeof(buf);
+               ifc.ifc_buf = buf;
+               if (ioctl(sock, (int)SIOCGIFCONF, (char *)&ifc) < 0) {
+                       syslog(LOG_ERR, "timed: (get interface configuration)");
+                       exit(1);
+               }
+               ifr = ifc.ifc_req;
+               for (n = ifc.ifc_len/sizeof(struct ifreq); n > 0; n--, ifr++) {
+                       ifreq = *ifr;
+                       if (ioctl(sock, (int)SIOCGIFFLAGS, 
+                                               (char *)&ifreq) < 0) {
+                               syslog(LOG_ERR, "timed: (get interface flags)");
+                               continue;
+                       }
+                       if ((ifreq.ifr_flags & IFF_UP) == 0 ||
+                               (ifreq.ifr_flags & IFF_BROADCAST) == 0) {
+                               continue;
+                       }
+                       if (ioctl(sock, (int)SIOCGIFBRDADDR, 
+                                               (char *)&ifreq) < 0) {
+                               syslog(LOG_ERR, "timed: (get broadaddr)");
+                               continue;
+                       }
+                       n_addrlen = sizeof(ifr->ifr_addr);
+                       n_addr = (char *)malloc((unsigned)n_addrlen);
+                       bcopy((char *)&ifreq.ifr_broadaddr, n_addr, n_addrlen);
+                       sin = (struct sockaddr_in *)n_addr;
+                       broadcastaddr = sin->sin_addr;
+                       break;
+               }
+       }
+
+       /* us. delay to be used in response to broadcast */
+       delay1 = casual((long)10000, 200000);   
+
+       /* election timer delay in secs. */
+       delay2 = casual((long)MINTOUT, (long)MAXTOUT);
+
+       /* look for master */
+       resp.tsp_type = TSP_MASTERREQ;
+       (void)strcpy(resp.tsp_name, hostname);
+       answer = acksend(&resp, (char *)ANYADDR, TSP_MASTERACK);
+       if (answer == NULL) {
+               status = MASTER;
+       } else {
+               status = SLAVE;
+               (void)strcpy(mastername, answer->tsp_name);
+               masteraddr = from;
+
+               /*
+                * If network has been partitioned, there might be other
+                * masters; tell the one we have just acknowledged that 
+                * it has to gain control over the others. 
+                */
+               time.tv_sec = 0;
+               time.tv_usec = 300000;
+               answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time);
+               /*
+                * checking also not to send CONFLICT to ack'ed master
+                * due to duplicated MASTERACKs
+                */
+               if (answer != NULL && 
+                               strcmp(answer->tsp_name, mastername) != 0) {
+                       conflict.tsp_type = TSP_CONFLICT;
+                       (void)strcpy(conflict.tsp_name, hostname);
+                       server = masteraddr;
+                       if (acksend(&conflict, (char *)mastername, 
+                                                       TSP_ACK) == NULL) {
+                               syslog(LOG_ERR, "timed: error on sending TSP_CONFLICT\n");
+                               exit(1);
+                       }
+               }
+       }
+       if (Mflag) {
+               /* open raw socket used to measure time differences */
+               sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
+               if (sock_raw < 0)  {
+                       fprintf(stderr, "timed: opening raw socket");
+                       exit (1);
+               }
+
+               /*
+                * number (increased by 1) of slaves controlled by master: 
+                * used in master.c, candidate.c, networkdelta.c, and 
+                * correct.c 
+                */
+               slvcount = 1;
+
+               /*
+                * Various conditions can cause conflict: race between
+                * two just started timedaemons when no master is present,
+                * or timedaemon started during an election.
+                * Conservative approach is taken: give up and became a
+                * slave postponing election of a master until first
+                * timer expires.
+                */
+               if (status == MASTER) {
+                       time.tv_sec = time.tv_usec = 0;
+                       answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR, &time);
+                       if (answer != NULL) {
+                               status = SLAVE;
+                               goto startd;
+                       }
+       
+                       time.tv_sec = time.tv_usec = 0;
+                       answer = readmsg(TSP_MASTERUP, (char *)ANYADDR, &time);
+                       if (answer != NULL) {
+                               status = SLAVE;
+                               goto startd;
+                       }
+       
+                       time.tv_sec = time.tv_usec = 0;
+                       answer = readmsg(TSP_ELECTION, (char *)ANYADDR, &time);
+                       if (answer != NULL) 
+                               status = SLAVE;
+               }
+startd:
+               ret = setjmp(jmpenv);
+               switch (ret) {
+
+               case 0: 
+                       break;
+               case 1: 
+                       /* from slave */
+                       status = election();
+                       break;
+               case 2:
+                       /* from master */
+                       status = SLAVE;
+                       break;
+               default:
+                       /* this should not happen */
+                       syslog(LOG_ERR, 
+                               "timed: error on return from longjmp\n");
+                       break;
+               }
+                       
+               if (status) 
+                       master();
+               else 
+                       slave();
+       } else {
+               /* if Mflag is not set timedaemon is forced to act as a slave */
+               if (setjmp(jmpenv)) {
+                       resp.tsp_type = TSP_SLAVEUP;
+                       (void)strcpy(resp.tsp_name, hostname);
+                       broadcast(&resp);
+               }
+               slave();
+       }
+}
+
+/* 
+ * `casual' returns a random number in the range [inf, sup]
+ */
+
+long casual(inf, sup)
+long inf;
+long sup;
+{
+       float value;
+       long random();
+
+       value = (float)random();
+       value /= MAXRANDOM;
+       if (value < 0) 
+               value = -value;
+       return(inf + (sup - inf) * value);
+}
+
+char *date()
+{
+       char    *ret;
+       char    *asctime();
+       struct  timeval tv;
+       struct  tm *localtime();
+       struct  tm *tp;
+
+       (void)gettimeofday(&tv, (struct timezone *)0);
+       tp = localtime((time_t *)&tv.tv_sec);
+       ret = asctime(tp);
+       ret[19] = '\0';
+       return(ret);
+}