/src/samba/lib/tevent/tevent_wrapper.c
Line | Count | Source |
1 | | /* |
2 | | Infrastructure for event context wrappers |
3 | | |
4 | | Copyright (C) Stefan Metzmacher 2014 |
5 | | |
6 | | ** NOTE! The following LGPL license applies to the tevent |
7 | | ** library. This does NOT imply that all of Samba is released |
8 | | ** under the LGPL |
9 | | |
10 | | This library is free software; you can redistribute it and/or |
11 | | modify it under the terms of the GNU Lesser General Public |
12 | | License as published by the Free Software Foundation; either |
13 | | version 3 of the License, or (at your option) any later version. |
14 | | |
15 | | This library is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | | Lesser General Public License for more details. |
19 | | |
20 | | You should have received a copy of the GNU Lesser General Public |
21 | | License along with this library; if not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "replace.h" |
25 | | #ifdef HAVE_PTHREAD |
26 | | #include "system/threads.h" |
27 | | #endif |
28 | | #define TEVENT_DEPRECATED 1 |
29 | | #include "tevent.h" |
30 | | #include "tevent_internal.h" |
31 | | #include "tevent_util.h" |
32 | | |
33 | | static int tevent_wrapper_glue_context_init(struct tevent_context *ev) |
34 | 0 | { |
35 | 0 | tevent_abort(ev, "tevent_wrapper_glue_context_init() called"); |
36 | 0 | errno = ENOSYS; |
37 | 0 | return -1; |
38 | 0 | } |
39 | | |
40 | | static struct tevent_fd *tevent_wrapper_glue_add_fd(struct tevent_context *ev, |
41 | | TALLOC_CTX *mem_ctx, |
42 | | int fd, uint16_t flags, |
43 | | tevent_fd_handler_t handler, |
44 | | void *private_data, |
45 | | const char *handler_name, |
46 | | const char *location) |
47 | 0 | { |
48 | 0 | struct tevent_wrapper_glue *glue = ev->wrapper.glue; |
49 | 0 | struct tevent_fd *fde = NULL; |
50 | |
|
51 | 0 | if (glue->destroyed) { |
52 | 0 | tevent_abort(ev, "add_fd wrapper use after free"); |
53 | 0 | return NULL; |
54 | 0 | } |
55 | | |
56 | 0 | if (glue->main_ev == NULL) { |
57 | 0 | errno = EINVAL; |
58 | 0 | return NULL; |
59 | 0 | } |
60 | | |
61 | 0 | fde = _tevent_add_fd(glue->main_ev, mem_ctx, fd, flags, |
62 | 0 | handler, private_data, |
63 | 0 | handler_name, location); |
64 | 0 | if (fde == NULL) { |
65 | 0 | return NULL; |
66 | 0 | } |
67 | | |
68 | 0 | fde->wrapper = glue; |
69 | |
|
70 | 0 | return fde; |
71 | 0 | } |
72 | | |
73 | | static struct tevent_timer *tevent_wrapper_glue_add_timer(struct tevent_context *ev, |
74 | | TALLOC_CTX *mem_ctx, |
75 | | struct timeval next_event, |
76 | | tevent_timer_handler_t handler, |
77 | | void *private_data, |
78 | | const char *handler_name, |
79 | | const char *location) |
80 | 0 | { |
81 | 0 | struct tevent_wrapper_glue *glue = ev->wrapper.glue; |
82 | 0 | struct tevent_timer *te = NULL; |
83 | |
|
84 | 0 | if (glue->destroyed) { |
85 | 0 | tevent_abort(ev, "add_timer wrapper use after free"); |
86 | 0 | return NULL; |
87 | 0 | } |
88 | | |
89 | 0 | if (glue->main_ev == NULL) { |
90 | 0 | errno = EINVAL; |
91 | 0 | return NULL; |
92 | 0 | } |
93 | | |
94 | 0 | te = _tevent_add_timer(glue->main_ev, mem_ctx, next_event, |
95 | 0 | handler, private_data, |
96 | 0 | handler_name, location); |
97 | 0 | if (te == NULL) { |
98 | 0 | return NULL; |
99 | 0 | } |
100 | | |
101 | 0 | te->wrapper = glue; |
102 | |
|
103 | 0 | return te; |
104 | 0 | } |
105 | | |
106 | | static void tevent_wrapper_glue_schedule_immediate(struct tevent_immediate *im, |
107 | | struct tevent_context *ev, |
108 | | tevent_immediate_handler_t handler, |
109 | | void *private_data, |
110 | | const char *handler_name, |
111 | | const char *location) |
112 | 0 | { |
113 | 0 | struct tevent_wrapper_glue *glue = ev->wrapper.glue; |
114 | |
|
115 | 0 | if (glue->destroyed) { |
116 | 0 | tevent_abort(ev, "scheduke_immediate wrapper use after free"); |
117 | 0 | return; |
118 | 0 | } |
119 | | |
120 | 0 | if (glue->main_ev == NULL) { |
121 | 0 | tevent_abort(ev, location); |
122 | 0 | errno = EINVAL; |
123 | 0 | return; |
124 | 0 | } |
125 | | |
126 | 0 | _tevent_schedule_immediate(im, glue->main_ev, |
127 | 0 | handler, private_data, |
128 | 0 | handler_name, location); |
129 | |
|
130 | 0 | im->wrapper = glue; |
131 | |
|
132 | 0 | return; |
133 | 0 | } |
134 | | |
135 | | static struct tevent_signal *tevent_wrapper_glue_add_signal(struct tevent_context *ev, |
136 | | TALLOC_CTX *mem_ctx, |
137 | | int signum, int sa_flags, |
138 | | tevent_signal_handler_t handler, |
139 | | void *private_data, |
140 | | const char *handler_name, |
141 | | const char *location) |
142 | 0 | { |
143 | 0 | struct tevent_wrapper_glue *glue = ev->wrapper.glue; |
144 | 0 | struct tevent_signal *se = NULL; |
145 | |
|
146 | 0 | if (glue->destroyed) { |
147 | 0 | tevent_abort(ev, "add_signal wrapper use after free"); |
148 | 0 | return NULL; |
149 | 0 | } |
150 | | |
151 | 0 | if (glue->main_ev == NULL) { |
152 | 0 | errno = EINVAL; |
153 | 0 | return NULL; |
154 | 0 | } |
155 | | |
156 | 0 | se = _tevent_add_signal(glue->main_ev, mem_ctx, |
157 | 0 | signum, sa_flags, |
158 | 0 | handler, private_data, |
159 | 0 | handler_name, location); |
160 | 0 | if (se == NULL) { |
161 | 0 | return NULL; |
162 | 0 | } |
163 | | |
164 | 0 | se->wrapper = glue; |
165 | |
|
166 | 0 | return se; |
167 | 0 | } |
168 | | |
169 | | static int tevent_wrapper_glue_loop_once(struct tevent_context *ev, const char *location) |
170 | 0 | { |
171 | 0 | tevent_abort(ev, "tevent_wrapper_glue_loop_once() called"); |
172 | 0 | errno = ENOSYS; |
173 | 0 | return -1; |
174 | 0 | } |
175 | | |
176 | | static int tevent_wrapper_glue_loop_wait(struct tevent_context *ev, const char *location) |
177 | 0 | { |
178 | 0 | tevent_abort(ev, "tevent_wrapper_glue_loop_wait() called"); |
179 | 0 | errno = ENOSYS; |
180 | 0 | return -1; |
181 | 0 | } |
182 | | |
183 | | static const struct tevent_ops tevent_wrapper_glue_ops = { |
184 | | .context_init = tevent_wrapper_glue_context_init, |
185 | | .add_fd = tevent_wrapper_glue_add_fd, |
186 | | .set_fd_close_fn = tevent_common_fd_set_close_fn, |
187 | | .get_fd_flags = tevent_common_fd_get_flags, |
188 | | .set_fd_flags = tevent_common_fd_set_flags, |
189 | | .add_timer = tevent_wrapper_glue_add_timer, |
190 | | .schedule_immediate = tevent_wrapper_glue_schedule_immediate, |
191 | | .add_signal = tevent_wrapper_glue_add_signal, |
192 | | .loop_once = tevent_wrapper_glue_loop_once, |
193 | | .loop_wait = tevent_wrapper_glue_loop_wait, |
194 | | }; |
195 | | |
196 | | static int tevent_wrapper_context_destructor(struct tevent_context *wrap_ev) |
197 | 0 | { |
198 | 0 | struct tevent_wrapper_glue *glue = wrap_ev->wrapper.glue; |
199 | 0 | struct tevent_context *main_ev = NULL; |
200 | 0 | struct tevent_fd *fd = NULL, *fn = NULL; |
201 | 0 | struct tevent_timer *te = NULL, *tn = NULL; |
202 | 0 | struct tevent_immediate *ie = NULL, *in = NULL; |
203 | 0 | struct tevent_signal *se = NULL, *sn = NULL; |
204 | 0 | #ifdef HAVE_PTHREAD |
205 | 0 | struct tevent_threaded_context *tctx = NULL, *tctxn = NULL; |
206 | 0 | #endif |
207 | |
|
208 | 0 | if (glue == NULL) { |
209 | 0 | tevent_abort(wrap_ev, |
210 | 0 | "tevent_wrapper_context_destructor() active on main"); |
211 | | /* static checker support, return below is never reached */ |
212 | 0 | return -1; |
213 | 0 | } |
214 | | |
215 | 0 | if (glue->destroyed && glue->busy) { |
216 | 0 | tevent_common_check_double_free(wrap_ev, |
217 | 0 | "tevent_context wrapper double free"); |
218 | 0 | } |
219 | 0 | glue->destroyed = true; |
220 | |
|
221 | 0 | if (glue->busy) { |
222 | 0 | return -1; |
223 | 0 | } |
224 | | |
225 | 0 | main_ev = glue->main_ev; |
226 | 0 | if (main_ev == NULL) { |
227 | 0 | return 0; |
228 | 0 | } |
229 | | |
230 | 0 | TEVENT_DEBUG(wrap_ev, TEVENT_DEBUG_TRACE, |
231 | 0 | "Destroying wrapper context %p \"%s\"\n", |
232 | 0 | wrap_ev, talloc_get_name(glue->private_state)); |
233 | |
|
234 | 0 | glue->main_ev = NULL; |
235 | 0 | DLIST_REMOVE(main_ev->wrapper.list, glue); |
236 | |
|
237 | 0 | #ifdef HAVE_PTHREAD |
238 | 0 | for (tctx = main_ev->threaded_contexts; tctx != NULL; tctx = tctxn) { |
239 | 0 | int ret; |
240 | |
|
241 | 0 | tctxn = tctx->next; |
242 | |
|
243 | 0 | if (tctx->event_ctx != glue->wrap_ev) { |
244 | 0 | continue; |
245 | 0 | } |
246 | | |
247 | 0 | ret = pthread_mutex_lock(&tctx->event_ctx_mutex); |
248 | 0 | if (ret != 0) { |
249 | 0 | abort(); |
250 | 0 | } |
251 | | |
252 | | /* |
253 | | * Indicate to the thread that the tevent_context is |
254 | | * gone. The counterpart of this is in |
255 | | * _tevent_threaded_schedule_immediate, there we read |
256 | | * this under the threaded_context's mutex. |
257 | | */ |
258 | | |
259 | 0 | tctx->event_ctx = NULL; |
260 | |
|
261 | 0 | ret = pthread_mutex_unlock(&tctx->event_ctx_mutex); |
262 | 0 | if (ret != 0) { |
263 | 0 | abort(); |
264 | 0 | } |
265 | | |
266 | 0 | DLIST_REMOVE(main_ev->threaded_contexts, tctx); |
267 | 0 | } |
268 | 0 | #endif |
269 | | |
270 | 0 | for (fd = main_ev->fd_events; fd; fd = fn) { |
271 | 0 | fn = fd->next; |
272 | |
|
273 | 0 | if (fd->wrapper != glue) { |
274 | 0 | continue; |
275 | 0 | } |
276 | | |
277 | 0 | tevent_fd_set_flags(fd, 0); |
278 | |
|
279 | 0 | tevent_common_fd_disarm(fd); |
280 | 0 | } |
281 | |
|
282 | 0 | for (te = main_ev->timer_events; te; te = tn) { |
283 | 0 | tn = te->next; |
284 | |
|
285 | 0 | if (te->wrapper != glue) { |
286 | 0 | continue; |
287 | 0 | } |
288 | | |
289 | 0 | te->wrapper = NULL; |
290 | 0 | te->event_ctx = NULL; |
291 | |
|
292 | 0 | if (main_ev->last_zero_timer == te) { |
293 | 0 | main_ev->last_zero_timer = DLIST_PREV(te); |
294 | 0 | } |
295 | 0 | DLIST_REMOVE(main_ev->timer_events, te); |
296 | 0 | } |
297 | |
|
298 | 0 | for (ie = main_ev->immediate_events; ie; ie = in) { |
299 | 0 | in = ie->next; |
300 | |
|
301 | 0 | if (ie->wrapper != glue) { |
302 | 0 | continue; |
303 | 0 | } |
304 | | |
305 | 0 | ie->wrapper = NULL; |
306 | 0 | ie->event_ctx = NULL; |
307 | 0 | ie->cancel_fn = NULL; |
308 | 0 | DLIST_REMOVE(main_ev->immediate_events, ie); |
309 | 0 | } |
310 | |
|
311 | 0 | for (se = main_ev->signal_events; se; se = sn) { |
312 | 0 | sn = se->next; |
313 | |
|
314 | 0 | if (se->wrapper != glue) { |
315 | 0 | continue; |
316 | 0 | } |
317 | | |
318 | 0 | se->wrapper = NULL; |
319 | 0 | tevent_cleanup_pending_signal_handlers(se); |
320 | 0 | } |
321 | |
|
322 | 0 | return 0; |
323 | 0 | } |
324 | | |
325 | | struct tevent_context *_tevent_context_wrapper_create(struct tevent_context *main_ev, |
326 | | TALLOC_CTX *mem_ctx, |
327 | | const struct tevent_wrapper_ops *ops, |
328 | | void *pstate, |
329 | | size_t psize, |
330 | | const char *type, |
331 | | const char *location) |
332 | 0 | { |
333 | 0 | void **ppstate = (void **)pstate; |
334 | 0 | struct tevent_context *ev = NULL; |
335 | |
|
336 | 0 | if (main_ev->wrapper.glue != NULL) { |
337 | | /* |
338 | | * stacking of wrappers is not supported |
339 | | */ |
340 | 0 | tevent_debug(main_ev->wrapper.glue->main_ev, TEVENT_DEBUG_FATAL, |
341 | 0 | "%s: %s() stacking not allowed\n", |
342 | 0 | __func__, location); |
343 | 0 | errno = EINVAL; |
344 | 0 | return NULL; |
345 | 0 | } |
346 | | |
347 | 0 | if (main_ev->nesting.allowed) { |
348 | | /* |
349 | | * wrappers conflict with nesting |
350 | | */ |
351 | 0 | tevent_debug(main_ev, TEVENT_DEBUG_FATAL, |
352 | 0 | "%s: %s() conflicts with nesting\n", |
353 | 0 | __func__, location); |
354 | 0 | errno = EINVAL; |
355 | 0 | return NULL; |
356 | 0 | } |
357 | | |
358 | 0 | ev = talloc_zero(mem_ctx, struct tevent_context); |
359 | 0 | if (ev == NULL) { |
360 | 0 | return NULL; |
361 | 0 | } |
362 | 0 | ev->ops = &tevent_wrapper_glue_ops; |
363 | |
|
364 | 0 | ev->wrapper.glue = talloc_zero(ev, struct tevent_wrapper_glue); |
365 | 0 | if (ev->wrapper.glue == NULL) { |
366 | 0 | talloc_free(ev); |
367 | 0 | return NULL; |
368 | 0 | } |
369 | | |
370 | 0 | talloc_set_destructor(ev, tevent_wrapper_context_destructor); |
371 | |
|
372 | 0 | ev->wrapper.glue->wrap_ev = ev; |
373 | 0 | ev->wrapper.glue->main_ev = main_ev; |
374 | 0 | ev->wrapper.glue->ops = ops; |
375 | 0 | ev->wrapper.glue->private_state = talloc_zero_size(ev->wrapper.glue, psize); |
376 | 0 | if (ev->wrapper.glue->private_state == NULL) { |
377 | 0 | talloc_free(ev); |
378 | 0 | return NULL; |
379 | 0 | } |
380 | 0 | talloc_set_name_const(ev->wrapper.glue->private_state, type); |
381 | |
|
382 | 0 | DLIST_ADD_END(main_ev->wrapper.list, ev->wrapper.glue); |
383 | |
|
384 | 0 | *ppstate = ev->wrapper.glue->private_state; |
385 | 0 | return ev; |
386 | 0 | } |
387 | | |
388 | | bool tevent_context_is_wrapper(struct tevent_context *ev) |
389 | 0 | { |
390 | 0 | if (ev->wrapper.glue != NULL) { |
391 | 0 | return true; |
392 | 0 | } |
393 | | |
394 | 0 | return false; |
395 | 0 | } |
396 | | |
397 | | _PRIVATE_ |
398 | | struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev) |
399 | 0 | { |
400 | 0 | if (ev == NULL) { |
401 | 0 | return NULL; |
402 | 0 | } |
403 | | |
404 | 0 | if (ev->wrapper.glue == NULL) { |
405 | 0 | return ev; |
406 | 0 | } |
407 | | |
408 | 0 | return ev->wrapper.glue->main_ev; |
409 | 0 | } |
410 | | |
411 | | /* |
412 | | * 32 stack elements should be more than enough |
413 | | * |
414 | | * e.g. Samba uses just 8 elements for [un]become_{root,user}() |
415 | | */ |
416 | | #define TEVENT_WRAPPER_STACK_SIZE 32 |
417 | | |
418 | | static struct tevent_wrapper_stack { |
419 | | const void *ev_ptr; |
420 | | const struct tevent_wrapper_glue *wrapper; |
421 | | } wrapper_stack[TEVENT_WRAPPER_STACK_SIZE]; |
422 | | |
423 | | static size_t wrapper_stack_idx; |
424 | | |
425 | | _PRIVATE_ |
426 | | void tevent_wrapper_push_use_internal(struct tevent_context *ev, |
427 | | struct tevent_wrapper_glue *wrapper) |
428 | 0 | { |
429 | | /* |
430 | | * ev and wrapper need to belong together! |
431 | | * It's also fine to only have a raw ev |
432 | | * without a wrapper. |
433 | | */ |
434 | 0 | if (unlikely(ev->wrapper.glue != wrapper)) { |
435 | 0 | tevent_abort(ev, "tevent_wrapper_push_use_internal() invalid arguments"); |
436 | 0 | return; |
437 | 0 | } |
438 | | |
439 | 0 | if (wrapper != NULL) { |
440 | 0 | if (unlikely(wrapper->busy)) { |
441 | 0 | tevent_abort(ev, "wrapper already busy!"); |
442 | 0 | return; |
443 | 0 | } |
444 | 0 | wrapper->busy = true; |
445 | 0 | } |
446 | | |
447 | 0 | if (unlikely(wrapper_stack_idx >= TEVENT_WRAPPER_STACK_SIZE)) { |
448 | 0 | tevent_abort(ev, "TEVENT_WRAPPER_STACK_SIZE overflow"); |
449 | 0 | return; |
450 | 0 | } |
451 | | |
452 | 0 | wrapper_stack[wrapper_stack_idx] = (struct tevent_wrapper_stack) { |
453 | 0 | .ev_ptr = ev, |
454 | 0 | .wrapper = wrapper, |
455 | 0 | }; |
456 | 0 | wrapper_stack_idx++; |
457 | 0 | } |
458 | | |
459 | | _PRIVATE_ |
460 | | void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr, |
461 | | struct tevent_wrapper_glue *wrapper) |
462 | 0 | { |
463 | 0 | struct tevent_context *main_ev = NULL; |
464 | | |
465 | | /* |
466 | | * Note that __ev_ptr might a a stale pointer and should not |
467 | | * be touched, we just compare the pointer value in order |
468 | | * to enforce the stack order. |
469 | | */ |
470 | |
|
471 | 0 | if (wrapper != NULL) { |
472 | 0 | main_ev = wrapper->main_ev; |
473 | 0 | } |
474 | |
|
475 | 0 | if (unlikely(wrapper_stack_idx == 0)) { |
476 | 0 | tevent_abort(main_ev, "tevent_wrapper stack already empty"); |
477 | 0 | return; |
478 | 0 | } |
479 | 0 | wrapper_stack_idx--; |
480 | |
|
481 | 0 | if (wrapper != NULL) { |
482 | 0 | wrapper->busy = false; |
483 | 0 | } |
484 | |
|
485 | 0 | if (wrapper_stack[wrapper_stack_idx].ev_ptr != __ev_ptr) { |
486 | 0 | tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch ev!"); |
487 | 0 | return; |
488 | 0 | } |
489 | 0 | if (wrapper_stack[wrapper_stack_idx].wrapper != wrapper) { |
490 | 0 | tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch wrap!"); |
491 | 0 | return; |
492 | 0 | } |
493 | | |
494 | 0 | if (wrapper == NULL) { |
495 | 0 | return; |
496 | 0 | } |
497 | | |
498 | 0 | if (wrapper->destroyed) { |
499 | | /* |
500 | | * Notice that we can't use TALLOC_FREE() |
501 | | * here because wrapper is a talloc child |
502 | | * of wrapper->wrap_ev. |
503 | | */ |
504 | 0 | talloc_free(wrapper->wrap_ev); |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | | bool _tevent_context_push_use(struct tevent_context *ev, |
509 | | const char *location) |
510 | 0 | { |
511 | 0 | bool ok; |
512 | |
|
513 | 0 | if (ev->wrapper.glue == NULL) { |
514 | 0 | tevent_wrapper_push_use_internal(ev, NULL); |
515 | 0 | return true; |
516 | 0 | } |
517 | | |
518 | 0 | if (ev->wrapper.glue->main_ev == NULL) { |
519 | 0 | return false; |
520 | 0 | } |
521 | | |
522 | 0 | tevent_wrapper_push_use_internal(ev, ev->wrapper.glue); |
523 | 0 | ok = ev->wrapper.glue->ops->before_use(ev->wrapper.glue->wrap_ev, |
524 | 0 | ev->wrapper.glue->private_state, |
525 | 0 | ev->wrapper.glue->main_ev, |
526 | 0 | location); |
527 | 0 | if (!ok) { |
528 | 0 | tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue); |
529 | 0 | return false; |
530 | 0 | } |
531 | | |
532 | 0 | return true; |
533 | 0 | } |
534 | | |
535 | | void _tevent_context_pop_use(struct tevent_context *ev, |
536 | | const char *location) |
537 | 0 | { |
538 | 0 | tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue); |
539 | |
|
540 | 0 | if (ev->wrapper.glue == NULL) { |
541 | 0 | return; |
542 | 0 | } |
543 | | |
544 | 0 | if (ev->wrapper.glue->main_ev == NULL) { |
545 | 0 | return; |
546 | 0 | } |
547 | | |
548 | 0 | ev->wrapper.glue->ops->after_use(ev->wrapper.glue->wrap_ev, |
549 | 0 | ev->wrapper.glue->private_state, |
550 | 0 | ev->wrapper.glue->main_ev, |
551 | 0 | location); |
552 | 0 | } |
553 | | |
554 | | bool tevent_context_same_loop(struct tevent_context *ev1, |
555 | | struct tevent_context *ev2) |
556 | 0 | { |
557 | 0 | struct tevent_context *main_ev1 = tevent_wrapper_main_ev(ev1); |
558 | 0 | struct tevent_context *main_ev2 = tevent_wrapper_main_ev(ev2); |
559 | |
|
560 | 0 | if (main_ev1 == NULL) { |
561 | 0 | return false; |
562 | 0 | } |
563 | | |
564 | 0 | if (main_ev1 == main_ev2) { |
565 | 0 | return true; |
566 | 0 | } |
567 | | |
568 | 0 | return false; |
569 | 0 | } |