security_events: Fix error caused by DTD validation error
[asterisk/asterisk.git] / res / res_timing_timerfd.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \author Mark Michelson <mmichelson@digium.com>
22  *
23  * \brief timerfd timing interface
24  */
25
26 /*** MODULEINFO
27         <depend>timerfd</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 #include <sys/timerfd.h>
34
35 #include "asterisk/module.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/timing.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/time.h"
41
42 static void *timing_funcs_handle;
43
44 static int timerfd_timer_open(void);
45 static void timerfd_timer_close(int handle);
46 static int timerfd_timer_set_rate(int handle, unsigned int rate);
47 static int timerfd_timer_ack(int handle, unsigned int quantity);
48 static int timerfd_timer_enable_continuous(int handle);
49 static int timerfd_timer_disable_continuous(int handle);
50 static enum ast_timer_event timerfd_timer_get_event(int handle);
51 static unsigned int timerfd_timer_get_max_rate(int handle);
52
53 static struct ast_timing_interface timerfd_timing = {
54         .name = "timerfd",
55         .priority = 200,
56         .timer_open = timerfd_timer_open,
57         .timer_close = timerfd_timer_close,
58         .timer_set_rate = timerfd_timer_set_rate,
59         .timer_ack = timerfd_timer_ack,
60         .timer_enable_continuous = timerfd_timer_enable_continuous,
61         .timer_disable_continuous = timerfd_timer_disable_continuous,
62         .timer_get_event = timerfd_timer_get_event,
63         .timer_get_max_rate = timerfd_timer_get_max_rate,
64 };
65
66 static struct ao2_container *timerfd_timers;
67
68 #define TIMERFD_TIMER_BUCKETS 563
69 #define TIMERFD_MAX_RATE 1000
70
71 struct timerfd_timer {
72         int handle;
73         struct itimerspec saved_timer;
74         unsigned int is_continuous:1;
75 };
76
77 static int timerfd_timer_hash(const void *obj, const int flags)
78 {
79         const struct timerfd_timer *timer = obj;
80
81         return timer->handle;
82 }
83
84 static int timerfd_timer_cmp(void *obj, void *args, int flags)
85 {
86         struct timerfd_timer *timer1 = obj, *timer2 = args;
87         return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
88 }
89
90 static void timer_destroy(void *obj)
91 {
92         struct timerfd_timer *timer = obj;
93         close(timer->handle);
94         timer->handle = -1;
95 }
96
97 static int timerfd_timer_open(void)
98 {
99         struct timerfd_timer *timer;
100         int handle;
101
102         if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
103                 ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
104                 return -1;
105         }
106         if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
107                 ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
108                 ao2_ref(timer, -1);
109                 return -1;
110         }
111
112         timer->handle = handle;
113         ao2_link(timerfd_timers, timer);
114         /* Get rid of the reference from the allocation */
115         ao2_ref(timer, -1);
116         return handle;
117 }
118
119 static void timerfd_timer_close(int handle)
120 {
121         struct timerfd_timer *our_timer, find_helper = {
122                 .handle = handle,
123         };
124
125         if (handle == -1) {
126                 ast_log(LOG_ERROR, "Attempting to close timerfd handle -1");
127                 return;
128         }
129
130         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
131                 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
132                 return;
133         }
134
135         ao2_unlink(timerfd_timers, our_timer);
136         ao2_ref(our_timer, -1);
137 }
138
139 static int timerfd_timer_set_rate(int handle, unsigned int rate)
140 {
141         struct timerfd_timer *our_timer, find_helper = {
142                 .handle = handle,
143         };
144         int res = 0;
145
146         if (handle == -1) {
147                 ast_log(LOG_ERROR, "Attempting to set rate on timerfd handle -1");
148                 return -1;
149         }
150
151         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
152                 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
153                 return -1;
154         }
155         ao2_lock(our_timer);
156
157         our_timer->saved_timer.it_value.tv_sec = 0;
158         our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
159         our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec;
160         our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec;
161
162         if (!our_timer->is_continuous) {
163                 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
164         }
165
166         ao2_unlock(our_timer);
167         ao2_ref(our_timer, -1);
168
169         return res;
170 }
171
172 static int timerfd_timer_ack(int handle, unsigned int quantity)
173 {
174         uint64_t expirations;
175         int read_result = 0;
176         int res = 0;
177         struct timerfd_timer *our_timer, find_helper = {
178                 .handle = handle,
179         };
180
181         if (handle == -1) {
182                 ast_log(LOG_ERROR, "Attempting to ack timerfd handle -1");
183                 return -1;
184         }
185
186         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
187                 ast_log(LOG_ERROR, "Couldn't find a timer with handle %d\n", handle);
188                 return -1;
189         }
190
191         ao2_lock(our_timer);
192
193         do {
194                 struct itimerspec timer_status;
195
196                 if (timerfd_gettime(handle, &timer_status)) {
197                         ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", handle, strerror(errno));
198                         expirations = 0;
199                         res = -1;
200                         break;
201                 }
202
203                 if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) {
204                         ast_debug(1, "Avoiding read on disarmed timerfd %d\n", handle);
205                         expirations = 0;
206                         break;
207                 }
208
209                 read_result = read(handle, &expirations, sizeof(expirations));
210                 if (read_result == -1) {
211                         if (errno == EINTR || errno == EAGAIN) {
212                                 continue;
213                         } else {
214                                 ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
215                                 res = -1;
216                                 break;
217                         }
218                 }
219         } while (read_result != sizeof(expirations));
220
221         ao2_unlock(our_timer);
222         ao2_ref(our_timer, -1);
223
224         if (expirations != quantity) {
225                 ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
226         }
227         return res;
228 }
229
230 static int timerfd_timer_enable_continuous(int handle)
231 {
232         int res;
233         struct itimerspec continuous_timer = {
234                 .it_value.tv_nsec = 1L,
235         };
236         struct timerfd_timer *our_timer, find_helper = {
237                 .handle = handle,
238         };
239
240         if (handle == -1) {
241                 ast_log(LOG_ERROR, "Attempting to enable timerfd handle -1");
242                 return -1;
243         }
244
245         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
246                 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
247                 return -1;
248         }
249         ao2_lock(our_timer);
250
251         if (our_timer->is_continuous) {
252                 /*It's already in continous mode, no need to do
253                  * anything further
254                  */
255                 ao2_unlock(our_timer);
256                 ao2_ref(our_timer, -1);
257                 return 0;
258         }
259
260         res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
261         our_timer->is_continuous = 1;
262         ao2_unlock(our_timer);
263         ao2_ref(our_timer, -1);
264         return res;
265 }
266
267 static int timerfd_timer_disable_continuous(int handle)
268 {
269         int res;
270         struct timerfd_timer *our_timer, find_helper = {
271                 .handle = handle,
272         };
273
274         if (handle == -1) {
275                 ast_log(LOG_ERROR, "Attempting to disable timerfd handle -1");
276                 return -1;
277         }
278
279         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
280                 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
281                 return -1;
282         }
283         ao2_lock(our_timer);
284
285         if (!our_timer->is_continuous) {
286                 /* No reason to do anything if we're not
287                  * in continuous mode
288                  */
289                 ao2_unlock(our_timer);
290                 ao2_ref(our_timer, -1);
291                 return 0;
292         }
293
294         res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
295         our_timer->is_continuous = 0;
296         memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
297         ao2_unlock(our_timer);
298         ao2_ref(our_timer, -1);
299         return res;
300 }
301
302 static enum ast_timer_event timerfd_timer_get_event(int handle)
303 {
304         enum ast_timer_event res;
305         struct timerfd_timer *our_timer, find_helper = {
306                 .handle = handle,
307         };
308
309         if (handle == -1) {
310                 ast_log(LOG_ERROR, "Attempting to get event from timerfd handle -1");
311                 return -1;
312         }
313
314         if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
315                 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
316                 return -1;
317         }
318         ao2_lock(our_timer);
319
320         if (our_timer->is_continuous) {
321                 res = AST_TIMING_EVENT_CONTINUOUS;
322         } else {
323                 res = AST_TIMING_EVENT_EXPIRED;
324         }
325
326         ao2_unlock(our_timer);
327         ao2_ref(our_timer, -1);
328         return res;
329 }
330
331 static unsigned int timerfd_timer_get_max_rate(int handle)
332 {
333         return TIMERFD_MAX_RATE;
334 }
335
336 static int load_module(void)
337 {
338         int fd;
339
340         /* Make sure we support the necessary clock type */
341         if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
342                 ast_log(LOG_ERROR, "timerfd_create() not supported by the kernel.  Not loading.\n");
343                 return AST_MODULE_LOAD_DECLINE;
344         }
345
346         close(fd);
347
348         if (!(timerfd_timers = ao2_container_alloc(TIMERFD_TIMER_BUCKETS, timerfd_timer_hash, timerfd_timer_cmp))) {
349                 return AST_MODULE_LOAD_DECLINE;
350         }
351
352         if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) {
353                 ao2_ref(timerfd_timers, -1);
354                 return AST_MODULE_LOAD_DECLINE;
355         }
356
357         return AST_MODULE_LOAD_SUCCESS;
358 }
359
360 static int unload_module(void)
361 {
362         int res;
363
364         if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
365                 ao2_ref(timerfd_timers, -1);
366                 timerfd_timers = NULL;
367         }
368
369         return res;
370 }
371
372 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
373                 .load = load_module,
374                 .unload = unload_module,
375                 .load_pri = AST_MODPRI_TIMING,
376                 );