1 /*****
2 *
3 * Copyright (C) 2001-2015 CS-SI. All Rights Reserved.
4 * Author: Yoann Vandoorselaere <yoann.v@prelude-ids.com>
5 *
6 * This file is part of the Prelude library.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program 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 General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 *****/
23 
24 #include "config.h"
25 #include "libmissing.h"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include <assert.h>
34 
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # if HAVE_SYS_TIME_H
40 #  include <sys/time.h>
41 # else
42 #  include <time.h>
43 # endif
44 #endif
45 
46 #include "glthread/thread.h"
47 #include "glthread/lock.h"
48 #include "glthread/cond.h"
49 
50 #include "prelude-list.h"
51 #include "prelude-inttypes.h"
52 #include "prelude-linked-object.h"
53 #include "prelude-timer.h"
54 #include "prelude-log.h"
55 #include "prelude-io.h"
56 #include "prelude-async.h"
57 
58 
59 static PRELUDE_LIST(joblist);
60 
61 static prelude_async_flags_t async_flags = 0;
62 static prelude_bool_t stop_processing = FALSE;
63 
64 
65 static gl_thread_t thread;
66 static gl_cond_t cond = gl_cond_initializer;
67 static gl_lock_t mutex = gl_lock_initializer;
68 
69 static volatile sig_atomic_t is_initialized = FALSE;
70 
71 
72 
timespec_elapsed(struct timespec * end,struct timespec * start)73 static int timespec_elapsed(struct timespec *end, struct timespec *start)
74 {
75         int diff = end->tv_sec - start->tv_sec;
76 
77         if ( end->tv_nsec < start->tv_nsec )
78                 diff -= 1;
79 
80         return diff;
81 }
82 
83 
timespec_expired(struct timespec * end,struct timespec * start)84 static prelude_bool_t timespec_expired(struct timespec *end, struct timespec *start)
85 {
86         return ( timespec_elapsed(end, start) ) ? TRUE : FALSE;
87 }
88 
89 
get_timespec(struct timespec * ts)90 static inline struct timespec *get_timespec(struct timespec *ts)
91 {
92         struct timeval now;
93 
94         gettimeofday(&now, NULL);
95 
96         ts->tv_sec = now.tv_sec;
97         ts->tv_nsec = now.tv_usec * 1000;
98 
99         return ts;
100 }
101 
102 
103 
wait_timer_and_data(prelude_async_flags_t * flags)104 static int wait_timer_and_data(prelude_async_flags_t *flags)
105 {
106         int ret;
107         struct timespec ts;
108         static struct timespec last_wakeup;
109         prelude_bool_t no_job_available = TRUE;
110 
111         get_timespec(&last_wakeup);
112         last_wakeup.tv_sec--;
113 
114         while ( no_job_available ) {
115                 ret = 0;
116 
117                 gl_lock_lock(mutex);
118 
119                 ts.tv_sec = last_wakeup.tv_sec + 1;
120                 ts.tv_nsec = last_wakeup.tv_nsec;
121 
122                 while ( (no_job_available = prelude_list_is_empty(&joblist)) &&
123                         ! stop_processing && async_flags == *flags && ret != ETIMEDOUT ) {
124                         ret = glthread_cond_timedwait(&cond, &mutex, &ts);
125                 }
126 
127                 if ( no_job_available && stop_processing ) {
128                         gl_lock_unlock(mutex);
129                         return -1;
130                 }
131 
132                 *flags = async_flags;
133                 gl_lock_unlock(mutex);
134 
135                 if ( ret == ETIMEDOUT || timespec_expired(get_timespec(&ts), &last_wakeup) ) {
136                         prelude_timer_wake_up();
137                         last_wakeup.tv_sec = ts.tv_sec;
138                         last_wakeup.tv_nsec = ts.tv_nsec;
139                 }
140         }
141 
142         return 0;
143 }
144 
145 
146 
147 
wait_data(prelude_async_flags_t * flags)148 static int wait_data(prelude_async_flags_t *flags)
149 {
150         gl_lock_lock(mutex);
151 
152         while ( prelude_list_is_empty(&joblist) && ! stop_processing && async_flags == *flags )
153                 gl_cond_wait(cond, mutex);
154 
155         if ( prelude_list_is_empty(&joblist) && stop_processing ) {
156                 gl_lock_unlock(mutex);
157                 return -1;
158         }
159 
160         *flags = async_flags;
161         gl_lock_unlock(mutex);
162 
163         return 0;
164 }
165 
166 
167 
get_next_job(void)168 static prelude_async_object_t *get_next_job(void)
169 {
170         prelude_list_t *tmp;
171         prelude_async_object_t *obj = NULL;
172 
173         gl_lock_lock(mutex);
174 
175         prelude_list_for_each(&joblist, tmp) {
176                 obj = prelude_linked_object_get_object(tmp);
177                 prelude_linked_object_del((prelude_linked_object_t *) obj);
178                 break;
179         }
180 
181         gl_lock_unlock(mutex);
182 
183         return obj;
184 }
185 
186 
187 
async_thread(void * arg)188 static void *async_thread(void *arg)
189 {
190         int ret;
191         sigset_t set;
192         prelude_async_object_t *obj;
193         prelude_async_flags_t nflags = async_flags;
194 
195         ret = sigfillset(&set);
196         if ( ret < 0 ) {
197                 prelude_log(PRELUDE_LOG_ERR, "sigfillset error: %s.\n", strerror(errno));
198                 return NULL;
199         }
200 
201         ret = glthread_sigmask(SIG_BLOCK, &set, NULL);
202         if ( ret < 0 ) {
203                 prelude_log(PRELUDE_LOG_ERR, "pthread_sigmask error: %s.\n", strerror(errno));
204                 return NULL;
205         }
206 
207         while ( 1 ) {
208 
209                 if ( nflags & PRELUDE_ASYNC_FLAGS_TIMER )
210                         ret = wait_timer_and_data(&nflags);
211                 else
212                         ret = wait_data(&nflags);
213 
214                 if ( ret < 0 ) {
215                         /*
216                          * On some implementation (namely, recent Linux + glibc version),
217                          * calling pthread_exit() from a shared library and joining the thread from
218                          * an atexit callback result in a deadlock.
219                          *
220                          * Appear to be related to:
221                          * http://sources.redhat.com/bugzilla/show_bug.cgi?id=654
222                          *
223                          * Simply returning from the thread seems to fix this problem.
224                          */
225                         break;
226                 }
227 
228                 while ( (obj = get_next_job()) )
229                         obj->_async_func(obj, obj->_async_data);
230         }
231 
232         return NULL;
233 }
234 
235 
236 
do_init_async(void)237 static int do_init_async(void)
238 {
239         int ret;
240 
241         ret = glthread_create(&thread, async_thread, NULL);
242         if ( ret != 0 ) {
243                 prelude_log(PRELUDE_LOG_ERR, "error creating asynchronous thread: %s.\n", strerror(ret));
244                 return ret;
245         }
246 
247         /*
248          * There is a problem with OpenBSD, where using atexit() from a multithread
249          * application result in a deadlock. No workaround has been found at the moment.
250          *
251          */
252 #if ! defined(__OpenBSD__)
253         return atexit(prelude_async_exit);
254 #else
255         return 0;
256 #endif
257 }
258 
259 
260 
261 /**
262  * prelude_async_set_flags:
263  * @flags: flags you want to set
264  *
265  * Sets flags to the asynchronous subsystem.
266  *
267  */
prelude_async_set_flags(prelude_async_flags_t flags)268 void prelude_async_set_flags(prelude_async_flags_t flags)
269 {
270         gl_lock_lock(mutex);
271 
272         async_flags = flags;
273         gl_cond_signal(cond);
274 
275         gl_lock_unlock(mutex);
276 }
277 
278 
279 
280 /**
281  * prelude_async_get_flags:
282  *
283  * Retrieves flags from the asynchronous subsystem
284  *
285  * Returns: asynchronous flags
286  */
prelude_async_get_flags(void)287 prelude_async_flags_t prelude_async_get_flags(void)
288 {
289         return async_flags;
290 }
291 
292 
293 
294 /**
295  * prelude_async_init:
296  *
297  * Initialize the asynchronous subsystem.
298  *
299  * Returns: 0 on success, -1 if an error occured.
300  */
prelude_async_init(void)301 int prelude_async_init(void)
302 {
303         if ( ! is_initialized ) {
304                 is_initialized = TRUE;
305                 stop_processing = FALSE;
306 
307                 return do_init_async();
308         }
309 
310         return 0;
311 }
312 
313 
314 
315 /**
316  * prelude_async_add:
317  * @obj: Pointer to a #prelude_async_t object.
318  *
319  * Adds @obj to the asynchronous processing list.
320  */
prelude_async_add(prelude_async_object_t * obj)321 void prelude_async_add(prelude_async_object_t *obj)
322 {
323         gl_lock_lock(mutex);
324 
325         prelude_linked_object_add_tail(&joblist, (prelude_linked_object_t *) obj);
326         gl_cond_signal(cond);
327 
328         gl_lock_unlock(mutex);
329 }
330 
331 
332 
333 /**
334  * prelude_async_del:
335  * @obj: Pointer to a #prelude_async_t object.
336  *
337  * Deletes @obj from the asynchronous processing list.
338  */
prelude_async_del(prelude_async_object_t * obj)339 void prelude_async_del(prelude_async_object_t *obj)
340 {
341         gl_lock_lock(mutex);
342         prelude_linked_object_del((prelude_linked_object_t *) obj);
343         gl_lock_unlock(mutex);
344 }
345 
346 
347 
prelude_async_exit(void)348 void prelude_async_exit(void)
349 {
350         prelude_bool_t has_job;
351 
352         if ( ! is_initialized )
353                 return;
354 
355         gl_lock_lock(mutex);
356 
357         stop_processing = TRUE;
358         gl_cond_signal(cond);
359         has_job = ! prelude_list_is_empty(&joblist);
360 
361         gl_lock_unlock(mutex);
362 
363         if ( has_job )
364                 prelude_log(PRELUDE_LOG_INFO, "Waiting for asynchronous operation to complete.\n");
365 
366         gl_thread_join(thread, NULL);
367         gl_cond_destroy(cond);
368         gl_lock_destroy(mutex);
369 
370         is_initialized = FALSE;
371 }
372 
373 
374 
_prelude_async_fork_prepare(void)375 void _prelude_async_fork_prepare(void)
376 {
377         gl_lock_lock(mutex);
378 }
379 
380 
381 
_prelude_async_fork_parent(void)382 void _prelude_async_fork_parent(void)
383 {
384         gl_lock_unlock(mutex);
385 }
386 
387 
388 
_prelude_async_fork_child(void)389 void _prelude_async_fork_child(void)
390 {
391         is_initialized = FALSE;
392         prelude_list_init(&joblist);
393         gl_lock_init(mutex);
394         gl_cond_init(cond);
395 }
396