1    	/*
2    	 * Copyright (C) 2010 Red Hat, Inc.
3    	 *
4    	 * Author: Angus Salkeld <asalkeld@redhat.com>
5    	 *
6    	 * This file is part of libqb.
7    	 *
8    	 * libqb is free software: you can redistribute it and/or modify
9    	 * it under the terms of the GNU Lesser General Public License as published by
10   	 * the Free Software Foundation, either version 2.1 of the License, or
11   	 * (at your option) any later version.
12   	 *
13   	 * libqb is distributed in the hope that it will be useful,
14   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   	 * GNU Lesser General Public License for more details.
17   	 *
18   	 * You should have received a copy of the GNU Lesser General Public License
19   	 * along with libqb.  If not, see <http://www.gnu.org/licenses/>.
20   	 */
21   	#include "os_base.h"
22   	#include <pthread.h>
23   	
24   	#include <qb/qbdefs.h>
25   	#include <qb/qblist.h>
26   	#include <qb/qbarray.h>
27   	#include <qb/qbloop.h>
28   	#include "loop_int.h"
29   	#include "util_int.h"
30   	#include "tlist.h"
31   	
32   	struct qb_loop_timer {
33   		struct qb_loop_item item;
34   		qb_loop_timer_dispatch_fn dispatch_fn;
35   		enum qb_loop_priority p;
36   		timer_handle timerlist_handle;
37   		enum qb_poll_entry_state state;
38   		int32_t check;
39   		uint32_t install_pos;
40   	};
41   	
42   	struct qb_timer_source {
43   		struct qb_loop_source s;
44   		struct timerlist timerlist;
45   		qb_array_t *timers;
46   		size_t timer_entry_count;
47   		pthread_mutex_t lock;
48   	};
49   	
50   	static void
51   	timer_dispatch(struct qb_loop_item *item, enum qb_loop_priority p)
52   	{
53   		struct qb_loop_timer *timer = (struct qb_loop_timer *)item;
54   	
55   		assert(timer->state == QB_POLL_ENTRY_JOBLIST);
56   		timer->check = 0;
57   		timer->dispatch_fn(timer->item.user_data);
58   		timer->state = QB_POLL_ENTRY_EMPTY;
59   	}
60   	
61   	static int32_t expired_timers;
62   	static void
63   	make_job_from_tmo(void *data)
64   	{
65   		struct qb_loop_timer *t = (struct qb_loop_timer *)data;
66   		struct qb_loop *l = t->item.source->l;
67   	
68   		assert(t->state == QB_POLL_ENTRY_ACTIVE);
69   		qb_loop_level_item_add(&l->level[t->p], &t->item);
70   		t->state = QB_POLL_ENTRY_JOBLIST;
71   		expired_timers++;
72   	}
73   	
74   	static int32_t
75   	expire_the_timers(struct qb_loop_source *s, int32_t ms_timeout)
76   	{
77   		struct qb_timer_source *ts = (struct qb_timer_source *)s;
78   		expired_timers = 0;
79   		if (timerlist_expire(&ts->timerlist) != 0) {
80   			qb_util_log(LOG_ERR, "timerlist_expire failed");
81   		}
82   		return expired_timers;
83   	}
84   	
85   	int32_t
86   	qb_loop_timer_msec_duration_to_expire(struct qb_loop_source * timer_source)
87   	{
88   		struct qb_timer_source *my_src = (struct qb_timer_source *)timer_source;
89   		uint64_t left = timerlist_msec_duration_to_expire(&my_src->timerlist);
90   		if (left != -1 && left > 0xFFFFFFFF) {
91   			left = 0xFFFFFFFE;
92   		}
93   		return left;
94   	}
95   	
96   	struct qb_loop_source *
97   	qb_loop_timer_create(struct qb_loop *l)
98   	{
99   		struct qb_timer_source *my_src = malloc(sizeof(struct qb_timer_source));
100  		if (my_src == NULL) {
101  			return NULL;
102  		}
103  		my_src->s.l = l;
104  		my_src->s.dispatch_and_take_back = timer_dispatch;
105  		my_src->s.poll = expire_the_timers;
106  	
107  		timerlist_init(&my_src->timerlist);
108  		my_src->timers = qb_array_create_2(16, sizeof(struct qb_loop_timer), 16);
109  		my_src->timer_entry_count = 0;
110  		pthread_mutex_init(&my_src->lock, NULL);
111  	
112  		return (struct qb_loop_source *)my_src;
113  	}
114  	
115  	void
116  	qb_loop_timer_destroy(struct qb_loop *l)
117  	{
118  		struct qb_timer_source *my_src =
119  		    (struct qb_timer_source *)l->timer_source;
120  	
121  		timerlist_destroy(&my_src->timerlist);
122  		qb_array_free(my_src->timers);
123  		free(l->timer_source);
124  	}
125  	
126  	static int32_t
127  	_timer_from_handle_(struct qb_timer_source *s,
128  			    qb_loop_timer_handle handle_in,
129  			    struct qb_loop_timer **timer_pt)
130  	{
131  		int32_t rc;
132  		int32_t check;
133  		uint32_t install_pos;
134  		struct qb_loop_timer *timer;
135  	
136  		if (handle_in == 0) {
137  			return -EINVAL;
138  		}
139  	
140  		check = handle_in >> 32;
141  		install_pos = handle_in & UINT32_MAX;
142  	
143  		rc = qb_array_index(s->timers, install_pos, (void **)&timer);
144  		if (rc != 0) {
145  			return rc;
146  		}
147  		if (timer->check != check) {
148  			return -EINVAL;
149  		}
150  		*timer_pt = timer;
151  		return 0;
152  	}
153  	
154  	static int32_t
155  	_get_empty_array_position_(struct qb_timer_source *s)
156  	{
157  		int32_t install_pos;
158  		int32_t res = 0;
159  		struct qb_loop_timer *timer;
160  	
161  		for (install_pos = 0; install_pos < s->timer_entry_count; install_pos++) {
162  			assert(qb_array_index(s->timers, install_pos, (void **)&timer)
163  			       == 0);
164  			if (timer->state == QB_POLL_ENTRY_EMPTY) {
165  				return install_pos;
166  			}
167  		}
168  	
169  		res = qb_array_grow(s->timers, s->timer_entry_count + 1);
170  		if (res != 0) {
171  			return res;
172  		}
173  	
174  		s->timer_entry_count++;
175  		install_pos = s->timer_entry_count - 1;
176  		return install_pos;
177  	}
178  	
179  	int32_t
180  	qb_loop_timer_add(struct qb_loop * lp,
181  			  enum qb_loop_priority p,
182  			  uint64_t nsec_duration,
183  			  void *data,
184  			  qb_loop_timer_dispatch_fn timer_fn,
185  			  qb_loop_timer_handle * timer_handle_out)
186  	{
187  		struct qb_loop_timer *t;
188  		struct qb_timer_source *my_src;
189  		int32_t i;
190  		int res;
191  		struct qb_loop *l = lp;
192  	
193  		if (l == NULL) {
194  			l = qb_loop_default_get();
195  		}
196  	
197  		if (l == NULL || timer_fn == NULL) {
198  			return -EINVAL;
199  		}
200  		my_src = (struct qb_timer_source *)l->timer_source;
201  	
202  		if ( (res=pthread_mutex_lock(&my_src->lock))) {
203  			return -res;
204  		}
205  		i = _get_empty_array_position_(my_src);
206  		assert(qb_array_index(my_src->timers, i, (void **)&t) >= 0);
207  		t->state = QB_POLL_ENTRY_ACTIVE;
208  		t->install_pos = i;
209  		t->item.user_data = data;
210  		t->item.source = (struct qb_loop_source *)my_src;
211  		t->dispatch_fn = timer_fn;
212  		t->p = p;
213  		qb_list_init(&t->item.list);
214  	
215  		/* Unlock here to stop anyone else changing the state while we're initializing */
216  		pthread_mutex_unlock(&my_src->lock);
217  	
218  		/*
219  		 * Make sure just positive integers are used for the integrity(?)
220  		 * checks within 2^32 address space, if we miss 200 times in a row
221  		 * (just 0 is concerned per specification of random), the PRNG may be
222  		 * broken -> the value is unspecified, subject of previous assignment.
223  		 */
224  		for (i = 0; i < 200; i++) {
(1) Event dont_call: "random" should not be used for security-related applications, because linear congruential algorithms are too easy to break.
(2) Event remediation: Use a compliant random number generator, such as "/dev/random" or "/dev/urandom" on Unix-like systems, and CNG (Cryptography API: Next Generation) on Windows.
225  			t->check = random();
226  	
227  			if (t->check > 0) {
228  				break;  /* covers also t->check == UINT32_MAX */
229  			}
230  		}
231  	
232  		if (timer_handle_out) {
233  			*timer_handle_out = (((uint64_t) (t->check)) << 32) | t->install_pos;
234  		}
235  		return timerlist_add_duration(&my_src->timerlist,
236  					      make_job_from_tmo, t,
237  					      nsec_duration, &t->timerlist_handle);
238  	}
239  	
240  	int32_t
241  	qb_loop_timer_del(struct qb_loop * lp, qb_loop_timer_handle th)
242  	{
243  		struct qb_timer_source *s;
244  		struct qb_loop_timer *t;
245  		int32_t res;
246  		struct qb_loop *l = lp;
247  	
248  		if (l == NULL) {
249  			l = qb_loop_default_get();
250  		}
251  		s = (struct qb_timer_source *)l->timer_source;
252  	
253  		res = _timer_from_handle_(s, th, &t);
254  		if (res != 0) {
255  			return res;
256  		}
257  	
258  		if (t->state == QB_POLL_ENTRY_DELETED) {
259  			qb_util_log(LOG_WARNING, "timer already deleted");
260  			return 0;
261  		}
262  		if (t->state != QB_POLL_ENTRY_ACTIVE &&
263  		    t->state != QB_POLL_ENTRY_JOBLIST) {
264  			return -EINVAL;
265  		}
266  		if (t->state == QB_POLL_ENTRY_JOBLIST) {
267  			qb_loop_level_item_del(&l->level[t->p], &t->item);
268  		}
269  	
270  		if (t->timerlist_handle) {
271  			if (timerlist_del(&s->timerlist, t->timerlist_handle) != 0) {
272  				qb_util_log(LOG_ERR, "Could not delete timer from timerlist");
273  			}
274  		}
275  		t->state = QB_POLL_ENTRY_EMPTY;
276  		return 0;
277  	}
278  	
279  	uint64_t
280  	qb_loop_timer_expire_time_get(struct qb_loop * lp, qb_loop_timer_handle th)
281  	{
282  		struct qb_timer_source *s;
283  		struct qb_loop_timer *t;
284  		int32_t res;
285  		struct qb_loop *l = lp;
286  	
287  		if (l == NULL) {
288  			l = qb_loop_default_get();
289  		}
290  		s = (struct qb_timer_source *)l->timer_source;
291  	
292  		res = _timer_from_handle_(s, th, &t);
293  		if (res != 0) {
294  			return 0;
295  		}
296  	
297  		if (t->state != QB_POLL_ENTRY_ACTIVE) {
298  			return 0;
299  		}
300  	
301  		return timerlist_expire_time(&s->timerlist, t->timerlist_handle);
302  	}
303  	
304  	uint64_t
305  	qb_loop_timer_expire_time_remaining(struct qb_loop * lp, qb_loop_timer_handle th)
306  	{
307  	
308  		uint64_t current_ns;
309  		/* NOTE: while it does not appear that absolute timers are used anywhere,
310  		 * we may as well respect this pattern in case that changes.
311  		 * Unfortunately, that means we do need to repeat timer fetch code from qb_loop_timer_expire_time_get
312  		 * rather than just a simple call to qb_loop_timer_expire_time_get and qb_util_nano_current_get.
313  		 */
314  	
315  		struct qb_timer_source *s;
316  		struct qb_loop_timer *t;
317  		int32_t res;
318  		struct qb_loop *l = lp;
319  	
320  		if (l == NULL) {
321  			l = qb_loop_default_get();
322  		}
323  		s = (struct qb_timer_source *)l->timer_source;
324  	
325  		res = _timer_from_handle_(s, th, &t);
326  		if (res != 0) {
327  			return 0;
328  		}
329  		if (t->state != QB_POLL_ENTRY_ACTIVE) {
330  			return 0;
331  		}
332  	
333  		struct timerlist_timer *timer = (struct timerlist_timer *)t->timerlist_handle;
334  		if (timer->is_absolute_timer) {
335  			current_ns = qb_util_nano_from_epoch_get();
336  		}
337  		else {
338  			current_ns = qb_util_nano_current_get();
339  		}
340  		uint64_t timer_ns = timerlist_expire_time(&s->timerlist, t->timerlist_handle);
341  		if (timer_ns < current_ns) {
342  			return 0; // respect the "expired" contract
343  		}
344  		return timer_ns - current_ns;
345  	
346  	
347  	}
348  	
349  	int32_t
350  	qb_loop_timer_is_running(qb_loop_t *l, qb_loop_timer_handle th)
351  	{
352  		return (qb_loop_timer_expire_time_get(l, th) > 0);
353  	}
354