1 /*
2 * Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifndef _INLINE_FN_H
20 #define _INLINE_FN_H
21
22 #include <stdbool.h>
23 #include <time.h>
24 #include <sys/time.h>
25 #include <assert.h>
26 #include <string.h>
27 #include "timer.h"
28 #include "config.h"
29 #include "transport.h"
30
31 static inline bool
32 is_auth_req(const struct booth_config *conf)
33 {
34 return (conf != NULL) && (conf->authkey[0] != '\0');
35 }
36
37 static inline int
38 get_local_id(void)
39 {
40 return local ? local->site_id : -1;
41 }
42
43 static inline uint32_t
44 get_node_id(struct booth_site *node)
45 {
46 return node ? node->site_id : 0;
47 }
48
49 /** Returns number of seconds left, if any. */
50 static inline int
51 term_time_left(struct ticket_config *tk)
52 {
53 int left = 0;
54
55 if (is_time_set(&tk->term_expires)) {
56 left = time_left(&tk->term_expires);
57 }
58 return (left < 0) ? 0 : left;
59 }
60
61 static inline int
62 leader_and_valid(struct ticket_config *tk)
63 {
64 if (tk->leader != local)
65 return 0;
66
67 return term_time_left(tk);
68 }
69
70 /** Is this some leader? */
71 static inline int
72 is_owned(const struct ticket_config *tk)
73 {
74 return (tk->leader && tk->leader != no_leader);
75 }
76
77 static inline int
78 is_resend(struct ticket_config *tk)
79 {
80 timetype now;
81
82 get_time(&now);
83 return time_sub_int(&now, &tk->req_sent_at) >= tk->timeout;
84 }
85
86 static inline void
87 init_header_bare(const struct booth_config *conf, struct boothc_header *h)
88 {
89 timetype now;
90
91 assert(local && local->site_id);
92 h->magic = htonl(BOOTHC_MAGIC);
93 h->version = htonl(BOOTHC_VERSION);
94 h->from = htonl(local->site_id);
95 if (is_auth_req(conf)) {
96 get_time(&now);
97 h->opts = htonl(BOOTH_OPT_AUTH);
|
(1) Event store_truncates_time_t: |
A "time_t" value is stored in an integer with too few bits to accommodate it. The expression "secs_since_epoch(&now)" is cast to "__uint32_t". |
98 h->secs = htonl(secs_since_epoch(&now));
99 h->usecs = htonl(get_usecs(&now));
100 } else {
101 h->opts = htonl(0);
102 h->secs = htonl(0);
103 h->usecs = htonl(0);
104 }
105 }
106
107 /* get the _real_ message length out of the header
108 */
109 #define sendmsglen(msg) ntohl((msg)->header.length)
110
111 static inline void
112 init_header(const struct booth_config *conf, struct boothc_header *h, int cmd,
113 int request, int options, int result, int reason, int data_len)
114 {
115 init_header_bare(conf, h);
116 h->length = htonl(data_len -
117 (is_auth_req(conf) ? 0 : sizeof(struct hmac)));
118 h->cmd = htonl(cmd);
119 h->request = htonl(request);
120 h->options = htonl(options);
121 h->result = htonl(result);
122 h->reason = htonl(reason);
123 }
124
125 #define my_last_term(tk) \
126 (((tk)->state == ST_CANDIDATE && (tk)->last_valid_tk) ? \
127 (tk)->last_valid_tk->current_term : (tk)->current_term)
128
129 extern int TIME_RES, TIME_MULT;
130
131 #define msg_term_time(msg) \
132 ntohl((msg)->ticket.term_valid_for)*TIME_RES/TIME_MULT
133 #define set_msg_term_time(msg, tk) \
134 (msg)->ticket.term_valid_for = htonl(term_time_left(tk)*TIME_MULT/TIME_RES)
135
136 static inline void
137 init_ticket_msg(const struct booth_config *conf, struct boothc_ticket_msg *msg,
138 int cmd, int request, int rv, int reason,
139 struct ticket_config *tk)
140 {
141 assert(sizeof(msg->ticket.id) == sizeof(tk->name));
142
143 init_header(conf, &msg->header, cmd, request, 0, rv, reason, sizeof(*msg));
144
145 if (!tk) {
146 memset(&msg->ticket, 0, sizeof(msg->ticket));
147 } else {
148 memcpy(msg->ticket.id, tk->name, sizeof(msg->ticket.id));
149
150 msg->ticket.leader = htonl(get_node_id(
151 (tk->leader && tk->leader != no_leader) ? tk->leader :
152 (tk->voted_for ? tk->voted_for : no_leader)));
153 msg->ticket.term = htonl(tk->current_term);
154 set_msg_term_time(msg, tk);
155 }
156 }
157
158 static inline struct booth_transport const *
159 transport(const struct booth_config *conf)
160 {
161 return booth_transport + conf->proto;
162 }
163
164 static inline const char *
165 site_string(const struct booth_site *site)
166 {
167 return site ? site->addr_string : "NONE";
168 }
169
170 static inline uint16_t
171 site_port(const struct booth_site *site)
172 {
173 assert(site != NULL);
174
175 if (site->family == AF_INET) {
176 return ntohs(site->sa4.sin_port);
177 } else if (site->family == AF_INET6) {
178 return ntohs(site->sa6.sin6_port);
179 } else {
180 return 0;
181 }
182 }
183
184 static inline const char *
185 ticket_leader_string(const struct ticket_config *tk)
186 {
187 return site_string(tk->leader);
188 }
189
190 /* We allow half of the uint32_t to be used;
191 * half of that below, half of that above the current known "good" value.
192 * 0 UINT32_MAX
193 * |--------------------------+----------------+------------|
194 * | | |
195 * |--------+-------| allowed range
196 * |
197 * current commit index
198 *
199 * So, on overflow it looks like that:
200 * UINT32_MAX 0
201 * |--------------------------+-----------||---+------------|
202 * | | |
203 * |--------+-------| allowed range
204 * |
205 * current commit index
206 *
207 * This should be possible by using the same datatype and relying
208 * on the under/overflow semantics.
209 *
210 *
211 * Having 30 bits available, and assuming an expire time of
212 * one minute and a (high) commit index step of 64 == 2^6 (because
213 * of weights), we get 2^24 minutes of range - which is ~750
214 * years. "Should be enough for everybody."
215 */
216 static inline int
217 index_is_higher_than(uint32_t c_high, uint32_t c_low)
218 {
219 uint32_t diff;
220
221 if (c_high == c_low)
222 return 0;
223
224 diff = c_high - c_low;
225 if (diff < UINT32_MAX/4)
226 return 1;
227
228 diff = c_low - c_high;
229 if (diff < UINT32_MAX/4)
230 return 0;
231
232 assert(!"commit index out of range - invalid");
233 }
234
235 static inline uint32_t
236 index_max2(uint32_t a, uint32_t b)
237 {
238 return index_is_higher_than(a, b) ? a : b;
239 }
240
241 static inline uint32_t
242 index_max3(uint32_t a, uint32_t b, uint32_t c)
243 {
244 return index_max2( index_max2(a, b), c);
245 }
246
247 /* only invoked when ticket leader */
248 static inline void
249 get_next_election_time(struct ticket_config *tk, timetype *next)
250 {
251 assert(tk->leader == local);
252
253 /* if last_renewal is not set, which is unusual, it may mean
254 * that the ticket never got updated, i.e. nobody acked
255 * ticket updates (say, due to a temporary connection
256 * problem)
257 * we may try a bit later again */
258 if (!is_time_set(&tk->last_renewal)) {
259 time_reset(next);
260 } else {
261 interval_add(&tk->last_renewal, tk->renewal_freq, next);
262 }
263
264 /* if delay_commit is earlier than next, then set next to
265 * delay_commit */
266 if (is_time_set(&tk->delay_commit) &&
267 time_cmp(next, &tk->delay_commit, >)) {
268 copy_time(&tk->delay_commit, next);
269 }
270 }
271
272 static inline void
273 expect_replies(struct ticket_config *tk, int reply_type)
274 {
275 tk->retry_number = 0;
276 tk->acks_expected = reply_type;
277 tk->acks_received = local->bitmask;
278 get_time(&tk->req_sent_at);
279 }
280
281 static inline void
282 no_resends(struct ticket_config *tk)
283 {
284 tk->retry_number = 0;
285 tk->acks_expected = 0;
286 }
287
288 static inline struct booth_site *
289 my_vote(struct ticket_config *tk)
290 {
291 return tk->votes_for[ local->index ];
292 }
293
294 static inline int
295 count_bits(uint64_t val)
296 {
297 return __builtin_popcount(val);
298 }
299
300 static inline int
301 majority_of_bits(const struct booth_config *conf, struct ticket_config *tk,
302 uint64_t val)
303 {
304 /* Use ">" to get majority decision, even for an even number
305 * of participants. */
306 return (count_bits(val) * 2) > conf->site_count;
307 }
308
309 static inline int
310 all_replied(const struct booth_config *conf, struct ticket_config *tk)
311 {
312 return (tk->acks_received ^ conf->all_bits) == 0;
313 }
314
315 static inline int
316 all_sites_replied(const struct booth_config *conf, struct ticket_config *tk)
317 {
318 return ((tk->acks_received & conf->sites_bits) ^ conf->sites_bits) == 0;
319 }
320
321 #endif // _INLINE_FN_H
322