1 /*-
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)candidate.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 #ifdef sgi
13 #ident "$Revision: 1.9 $"
14 #endif
15 
16 #include "globals.h"
17 
18 /*
19  * `election' candidates a host as master: it is called by a slave
20  * which runs with the -M option set when its election timeout expires.
21  * Note the conservative approach: if a new timed comes up, or another
22  * candidate sends an election request, the candidature is withdrawn.
23  */
24 int
25 election(net)
26 	struct netinfo *net;
27 {
28 	struct tsp *resp, msg;
29 	struct timeval then, wait;
30 	struct tsp *answer;
31 	struct hosttbl *htp;
32 	char loop_lim = 0;
33 
34 /* This code can get totally confused if it gets slightly behind.  For
35  *	example, if readmsg() has some QUIT messages waiting from the last
36  *	round, we would send an ELECTION message, get the stale QUIT,
37  *	and give up.  This results in network storms when several machines
38  *	do it at once.
39  */
40 	wait.tv_sec = 0;
41 	wait.tv_usec = 0;
42 	while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) {
43 		if (trace)
44 			fprintf(fd, "election: discarded stale REFUSE\n");
45 	}
46 	while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) {
47 		if (trace)
48 			fprintf(fd, "election: discarded stale QUIT\n");
49 	}
50 
51 again:
52 	syslog(LOG_INFO, "This machine is a candidate time master");
53 	if (trace)
54 		fprintf(fd, "This machine is a candidate time master\n");
55 	msg.tsp_type = TSP_ELECTION;
56 	msg.tsp_vers = TSPVERSION;
57 	(void)strcpy(msg.tsp_name, hostname);
58 	bytenetorder(&msg);
59 	if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
60 		   (struct sockaddr*)&net->dest_addr,
61 		   sizeof(struct sockaddr)) < 0) {
62 		trace_sendto_err(net->dest_addr.sin_addr);
63 		return(SLAVE);
64 	}
65 
66 	(void)gettimeofday(&then, 0);
67 	then.tv_sec += 3;
68 	for (;;) {
69 		(void)gettimeofday(&wait, 0);
70 		timevalsub(&wait,&then,&wait);
71 		resp = readmsg(TSP_ANY, ANYADDR, &wait, net);
72 		if (!resp)
73 			return(MASTER);
74 
75 		switch (resp->tsp_type) {
76 
77 		case TSP_ACCEPT:
78 			(void)addmach(resp->tsp_name, &from,fromnet);
79 			break;
80 
81 		case TSP_MASTERUP:
82 		case TSP_MASTERREQ:
83 			/*
84 			 * If another timedaemon is coming up at the same
85 			 * time, give up, and let it be the master.
86 			 */
87 			if (++loop_lim < 5
88 			    && !good_host_name(resp->tsp_name)) {
89 				(void)addmach(resp->tsp_name, &from,fromnet);
90 				suppress(&from, resp->tsp_name, net);
91 				goto again;
92 			}
93 			rmnetmachs(net);
94 			return(SLAVE);
95 
96 		case TSP_QUIT:
97 		case TSP_REFUSE:
98 			/*
99 			 * Collision: change value of election timer
100 			 * using exponential backoff.
101 			 *
102 			 *  Fooey.
103 			 * An exponential backoff on a delay starting at
104 			 * 6 to 15 minutes for a process that takes
105 			 * milliseconds is silly.  It is particularly
106 			 * strange that the original code would increase
107 			 * the backoff without bound.
108 			 */
109 			rmnetmachs(net);
110 			return(SLAVE);
111 
112 		case TSP_ELECTION:
113 			/* no master for another round */
114 			htp = addmach(resp->tsp_name,&from,fromnet);
115 			msg.tsp_type = TSP_REFUSE;
116 			(void)strcpy(msg.tsp_name, hostname);
117 			answer = acksend(&msg, &htp->addr, htp->name,
118 					 TSP_ACK, 0, htp->noanswer);
119 			if (!answer) {
120 				syslog(LOG_ERR, "error in election from %s",
121 				       htp->name);
122 			}
123 			break;
124 
125 		case TSP_SLAVEUP:
126 			(void)addmach(resp->tsp_name, &from,fromnet);
127 			break;
128 
129 		case TSP_SETDATE:
130 		case TSP_SETDATEREQ:
131 			break;
132 
133 		default:
134 			if (trace) {
135 				fprintf(fd, "candidate: ");
136 				print(resp, &from);
137 			}
138 			break;
139 		}
140 	}
141 }
142