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