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
23 /* due to MinGW/splint emitting "< Location unknown >: Previous use of" */
24 #if defined(HAVE_SYS_RESOURCE_H) && !defined(S_SPLINT_S)
25 #include <sys/resource.h>
26 #endif
27
28 #include <signal.h>
29
30 #if defined(__DARWIN_NSIG)
31 #define QB_MAX_NUM_SIGNALS __DARWIN_NSIG
32 #else
33 #if defined(NSIG)
34 #define QB_MAX_NUM_SIGNALS NSIG - 1
35 #else
36 #define QB_MAX_NUM_SIGNALS 31
37 #endif
38 #endif
39
40 #include "loop_poll_int.h"
41
42 /*
43 * Define this to log slow (>10ms) jobs.
44 */
45 #undef DEBUG_DISPATCH_TIME
46
47 /* logs, std(in|out|err), pipe */
48 #define POLL_FDS_USED_MISC 50
49
50 #ifdef HAVE_EPOLL
51 #define USE_EPOLL 1
52 #else
53 #ifdef HAVE_KQUEUE
54 #define USE_KQUEUE 1
55 #else
56 #define USE_POLL 1
57 #endif /* HAVE_KQUEUE */
58 #endif /* HAVE_EPOLL */
59
60 static int32_t _qb_signal_add_to_jobs_(struct qb_loop *l,
61 struct qb_poll_entry *pe);
62
63 static void
64 _poll_entry_check_generate_(struct qb_poll_entry *pe)
65 {
66 int32_t i;
67
68 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. |
69 pe->check = random();
70
71 if (pe->check != 0 && pe->check != UINT32_MAX) {
72 break;
73 }
74 }
75 }
76
77 static void
78 _poll_entry_mark_deleted_(struct qb_poll_entry *pe)
79 {
80 pe->ufd.fd = -1;
81 pe->state = QB_POLL_ENTRY_DELETED;
82 pe->check = 0;
83 }
84
85 static void
86 _poll_entry_empty_(struct qb_poll_entry *pe)
87 {
88 memset(pe, 0, sizeof(struct qb_poll_entry));
89 pe->ufd.fd = -1;
90 }
91
92 static void
93 _poll_dispatch_and_take_back_(struct qb_loop_item *item,
94 enum qb_loop_priority p)
95 {
96 struct qb_poll_entry *pe = (struct qb_poll_entry *)item;
97 int32_t res;
98 #ifdef DEBUG_DISPATCH_TIME
99 uint64_t start;
100 uint64_t stop;
101 int32_t log_warn = QB_FALSE;
102
103 start = qb_util_nano_current_get();
104 #endif /* DEBUG_DISPATCH_TIME */
105
106 assert(pe->state == QB_POLL_ENTRY_JOBLIST);
107 assert(pe->item.type == QB_LOOP_FD);
108
109 res = pe->poll_dispatch_fn(pe->ufd.fd,
110 pe->ufd.revents,
111 pe->item.user_data);
112 if (res < 0) {
113 _poll_entry_mark_deleted_(pe);
114 } else if (pe->state != QB_POLL_ENTRY_DELETED) {
115 pe->state = QB_POLL_ENTRY_ACTIVE;
116 pe->ufd.revents = 0;
117 }
118 #ifdef DEBUG_DISPATCH_TIME
119 if (pe->state == QB_POLL_ENTRY_ACTIVE) {
120 pe->runs++;
121 if ((pe->runs % 50) == 0) {
122 log_warn = QB_TRUE;
123 }
124 stop = qb_util_nano_current_get();
125 if ((stop - start) > (10 * QB_TIME_NS_IN_MSEC)) {
126 log_warn = QB_TRUE;
127 }
128
129 if (log_warn && pe->item.type == QB_LOOP_FD) {
130 qb_util_log(LOG_INFO,
131 "[fd:%d] dispatch:%p runs:%d duration:%d ms",
132 pe->ufd.fd, pe->poll_dispatch_fn,
133 pe->runs,
134 (int32_t) ((stop -
135 start) / QB_TIME_NS_IN_MSEC));
136 }
137 }
138 #endif /* DEBUG_DISPATCH_TIME */
139 }
140
141 void
142 qb_poll_fds_usage_check_(struct qb_poll_source *s)
143 {
144 struct rlimit lim;
145 static int32_t socks_limit = 0;
146 int32_t send_event = QB_FALSE;
147 int32_t socks_used = 0;
148 int32_t socks_avail = 0;
149 struct qb_poll_entry *pe;
150 int32_t i;
151
152 if (socks_limit == 0) {
153 if (getrlimit(RLIMIT_NOFILE, &lim) == -1) {
154 qb_util_perror(LOG_WARNING, "getrlimit");
155 return;
156 }
157 socks_limit = lim.rlim_cur;
158 socks_limit -= POLL_FDS_USED_MISC;
159 if (socks_limit < 0) {
160 socks_limit = 0;
161 }
162 }
163
164 for (i = 0; i < s->poll_entry_count; i++) {
165 assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
166 if ((pe->state == QB_POLL_ENTRY_ACTIVE ||
167 pe->state == QB_POLL_ENTRY_JOBLIST) && pe->ufd.fd != -1) {
168 socks_used++;
169 }
170 if (pe->state == QB_POLL_ENTRY_DELETED) {
171 _poll_entry_empty_(pe);
172 }
173 }
174
175 socks_avail = socks_limit - socks_used;
176 if (socks_avail < 0) {
177 socks_avail = 0;
178 }
179 send_event = QB_FALSE;
180 if (s->not_enough_fds) {
181 if (socks_avail > 2) {
182 s->not_enough_fds = QB_FALSE;
183 send_event = QB_TRUE;
184 }
185 } else {
186 if (socks_avail <= 1) {
187 s->not_enough_fds = QB_TRUE;
188 send_event = QB_TRUE;
189 }
190 }
191 if (send_event && s->low_fds_event_fn) {
192 s->low_fds_event_fn(s->not_enough_fds, socks_avail);
193 }
194 }
195
196
197 struct qb_loop_source *
198 qb_loop_poll_create(struct qb_loop *l)
199 {
200 struct qb_poll_source *s = malloc(sizeof(struct qb_poll_source));
201 if (s == NULL) {
202 return NULL;
203 }
204 s->s.l = l;
205 s->s.dispatch_and_take_back = _poll_dispatch_and_take_back_;
206
207 s->poll_entries = qb_array_create_2(16, sizeof(struct qb_poll_entry), 16);
208 s->poll_entry_count = 0;
209 s->low_fds_event_fn = NULL;
210 s->not_enough_fds = QB_FALSE;
211
212 #ifdef USE_EPOLL
213 (void)qb_epoll_init(s);
214 #endif
215 #ifdef USE_KQUEUE
216 (void)qb_kqueue_init(s);
217 #endif
218 #ifdef USE_POLL
219 (void)qb_poll_init(s);
220 #endif /* USE_POLL */
221
222 return (struct qb_loop_source *)s;
223 }
224
225 void
226 qb_loop_poll_destroy(struct qb_loop *l)
227 {
228 struct qb_poll_source *s = (struct qb_poll_source *)l->fd_source;
229 qb_array_free(s->poll_entries);
230
231 s->driver.fini(s);
232
233 free(s);
234 }
235
236 int32_t
237 qb_loop_poll_low_fds_event_set(struct qb_loop *l,
238 qb_loop_poll_low_fds_event_fn fn)
239 {
240 struct qb_poll_source *s = (struct qb_poll_source *)l->fd_source;
241 s->low_fds_event_fn = fn;
242
243 return 0;
244 }
245
246 static int32_t
247 _get_empty_array_position_(struct qb_poll_source *s)
248 {
249 int32_t found = QB_FALSE;
250 uint32_t install_pos;
251 int32_t res = 0;
252 struct qb_poll_entry *pe;
253
254 for (install_pos = 0;
255 install_pos < s->poll_entry_count; install_pos++) {
256 assert(qb_array_index
257 (s->poll_entries, install_pos, (void **)&pe) == 0);
258 if (pe->state == QB_POLL_ENTRY_EMPTY) {
259 found = QB_TRUE;
260 break;
261 }
262 }
263
264 if (found == QB_FALSE) {
265 #ifdef USE_POLL
266 struct pollfd *ufds;
267 int32_t new_size = (s->poll_entry_count + 1) * sizeof(struct pollfd);
268 ufds = realloc(s->ufds, new_size);
269 if (ufds == NULL) {
270 return -ENOMEM;
271 }
272 s->ufds = ufds;
273 #endif /* USE_POLL */
274 /*
275 * Grow pollfd list
276 */
277 res = qb_array_grow(s->poll_entries, s->poll_entry_count + 1);
278 if (res != 0) {
279 return res;
280 }
281
282 s->poll_entry_count += 1;
283 install_pos = s->poll_entry_count - 1;
284 }
285 return install_pos;
286 }
287
288 static int32_t
289 _poll_add_(struct qb_loop *l,
290 enum qb_loop_priority p,
291 int32_t fd, int32_t events, void *data, struct qb_poll_entry **pe_pt)
292 {
293 struct qb_poll_entry *pe;
294 uint32_t install_pos;
295 int32_t res = 0;
296 struct qb_poll_source *s;
297
298 if (l == NULL) {
299 return -EINVAL;
300 }
301
302 s = (struct qb_poll_source *)l->fd_source;
303
304 install_pos = _get_empty_array_position_(s);
305
306 assert(qb_array_index(s->poll_entries, install_pos, (void **)&pe) == 0);
307 pe->state = QB_POLL_ENTRY_ACTIVE;
308 pe->install_pos = install_pos;
309 _poll_entry_check_generate_(pe);
310 pe->ufd.fd = fd;
311 pe->ufd.events = events;
312 pe->ufd.revents = 0;
313 pe->item.user_data = data;
314 pe->item.source = (struct qb_loop_source *)l->fd_source;
315 pe->p = p;
316 pe->runs = 0;
317 res = s->driver.add(s, pe, fd, events);
318 if (res == 0) {
319 *pe_pt = pe;
320 return 0;
321 } else {
322 pe->state = QB_POLL_ENTRY_EMPTY;
323 return res;
324 }
325 }
326
327 static int32_t
328 _qb_poll_add_to_jobs_(struct qb_loop *l, struct qb_poll_entry *pe)
329 {
330 assert(pe->item.type == QB_LOOP_FD);
331 qb_loop_level_item_add(&l->level[pe->p], &pe->item);
332 pe->state = QB_POLL_ENTRY_JOBLIST;
333 return 1;
334 }
335
336 int32_t
337 qb_loop_poll_add(struct qb_loop * lp,
338 enum qb_loop_priority p,
339 int32_t fd,
340 int32_t events,
341 void *data, qb_loop_poll_dispatch_fn dispatch_fn)
342 {
343 struct qb_poll_entry *pe = NULL;
344 int32_t size;
345 int32_t new_size;
346 int32_t res;
347 struct qb_loop *l = lp;
348
349 if (l == NULL) {
350 l = qb_loop_default_get();
351 }
352
353 size = ((struct qb_poll_source *)l->fd_source)->poll_entry_count;
354 res = _poll_add_(l, p, fd, events, data, &pe);
355 if (res != 0) {
356 qb_util_perror(LOG_ERR,
357 "couldn't add poll entryfor FD %d", fd);
358 return res;
359 }
360 new_size = ((struct qb_poll_source *)l->fd_source)->poll_entry_count;
361
362 pe->poll_dispatch_fn = dispatch_fn;
363 pe->item.type = QB_LOOP_FD;
364 pe->add_to_jobs = _qb_poll_add_to_jobs_;
365
366 if (new_size > size) {
367 qb_util_log(LOG_TRACE,
368 "grown poll array to %d for FD %d", new_size, fd);
369 }
370
371 return res;
372 }
373
374 int32_t
375 qb_loop_poll_mod(struct qb_loop * lp,
376 enum qb_loop_priority p,
377 int32_t fd,
378 int32_t events,
379 void *data, qb_loop_poll_dispatch_fn dispatch_fn)
380 {
381 uint32_t i;
382 int32_t res = 0;
383 struct qb_poll_entry *pe;
384 struct qb_poll_source *s;
385 struct qb_loop *l = lp;
386
387 if (l == NULL) {
388 l = qb_loop_default_get();
389 }
390 s = (struct qb_poll_source *)l->fd_source;
391
392 /*
393 * Find file descriptor to modify events and dispatch function
394 */
395 for (i = 0; i < s->poll_entry_count; i++) {
396 assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
397 if (pe->ufd.fd != fd) {
398 continue;
399 }
400 if (pe->state == QB_POLL_ENTRY_DELETED || pe->check == 0) {
401 qb_util_log(LOG_ERR,
402 "poll_mod : can't modify entry already deleted");
403 return -EBADF;
404 }
405 pe->poll_dispatch_fn = dispatch_fn;
406 pe->item.user_data = data;
407 pe->p = p;
408 if (pe->ufd.events != events) {
409 res = s->driver.mod(s, pe, fd, events);
410 pe->ufd.events = events;
411 }
412 return res;
413 }
414
415 return -EBADF;
416 }
417
418 int32_t
419 qb_loop_poll_del(struct qb_loop * lp, int32_t fd)
420 {
421 int32_t i;
422 int32_t res = 0;
423 struct qb_poll_entry *pe;
424 struct qb_poll_source *s;
425 struct qb_loop *l = lp;
426
427 if (l == NULL) {
428 l = qb_loop_default_get();
429 }
430 s = (struct qb_poll_source *)l->fd_source;
431 for (i = 0; i < s->poll_entry_count; i++) {
432 assert(qb_array_index(s->poll_entries, i, (void **)&pe) == 0);
433 if (pe->ufd.fd != fd || pe->item.type != QB_LOOP_FD) {
434 continue;
435 }
436 if (pe->state == QB_POLL_ENTRY_DELETED ||
437 pe->state == QB_POLL_ENTRY_EMPTY) {
438 return 0;
439 }
440 if (pe->state == QB_POLL_ENTRY_JOBLIST) {
441 qb_loop_level_item_del(&l->level[pe->p], &pe->item);
442 }
443 res = s->driver.del(s, pe, fd, i);
444 _poll_entry_mark_deleted_(pe);
445 return res;
446 }
447
448 return -EBADF;
449 }
450
451 static int32_t pipe_fds[2] = { -1, -1 };
452
453 struct qb_signal_source {
454 struct qb_loop_source s;
455 struct qb_list_head sig_head;
456 sigset_t signal_superset;
457 };
458
459 struct qb_loop_sig {
460 struct qb_loop_item item;
461 int32_t signal;
462 enum qb_loop_priority p;
463 qb_loop_signal_dispatch_fn dispatch_fn;
464 struct qb_loop_sig *cloned_from;
465 };
466
467 static void
468 _handle_real_signal_(int signal_num, siginfo_t * si, void *context)
469 {
470 int32_t sig = signal_num;
471 int32_t res = 0;
472
473 if (pipe_fds[1] > 0) {
474 try_again:
475 res = write(pipe_fds[1], &sig, sizeof(int32_t));
476 if (res == -1 && errno == EAGAIN) {
477 goto try_again;
478 }
479 }
480 }
481
482 static void
483 _signal_dispatch_and_take_back_(struct qb_loop_item *item,
484 enum qb_loop_priority p)
485 {
486 struct qb_loop_sig *sig = (struct qb_loop_sig *)item;
487 int32_t res;
488
489 res = sig->dispatch_fn(sig->signal, sig->item.user_data);
490 if (res != 0) {
491 (void)qb_loop_signal_del(sig->cloned_from->item.source->l,
492 sig->cloned_from);
493 }
494 free(sig);
495 }
496
497 struct qb_loop_source *
498 qb_loop_signals_create(struct qb_loop *l)
499 {
500 int32_t res = 0;
501 struct qb_poll_entry *pe;
502 struct qb_signal_source *s = calloc(1, sizeof(struct qb_signal_source));
503
504 if (s == NULL) {
505 return NULL;
506 }
507 s->s.l = l;
508 s->s.dispatch_and_take_back = _signal_dispatch_and_take_back_;
509 s->s.poll = NULL;
510 qb_list_init(&s->sig_head);
511 sigemptyset(&s->signal_superset);
512
513 if (pipe_fds[0] < 0) {
514 res = pipe(pipe_fds);
515 if (res == -1) {
516 res = -errno;
517 qb_util_perror(LOG_ERR, "Can't light pipe");
518 goto error_exit;
519 }
520 (void)qb_sys_fd_nonblock_cloexec_set(pipe_fds[0]);
521 (void)qb_sys_fd_nonblock_cloexec_set(pipe_fds[1]);
522
523 res = _poll_add_(l, QB_LOOP_HIGH,
524 pipe_fds[0], POLLIN, NULL, &pe);
525 if (res == 0) {
526 pe->poll_dispatch_fn = NULL;
527 pe->item.type = QB_LOOP_SIG;
528 pe->add_to_jobs = _qb_signal_add_to_jobs_;
529 } else {
530 qb_util_perror(LOG_ERR, "Can't smoke pipe");
531 goto error_exit;
532 }
533 }
534
535 return (struct qb_loop_source *)s;
536
537 error_exit:
538 errno = -res;
539 free(s);
540 if (pipe_fds[0] >= 0) {
541 close(pipe_fds[0]);
542 }
543 if (pipe_fds[1] >= 0) {
544 close(pipe_fds[1]);
545 }
546 return NULL;
547 }
548
549 void
550 qb_loop_signals_destroy(struct qb_loop *l)
551 {
552 struct qb_signal_source *s =
553 (struct qb_signal_source *)l->signal_source;
554 struct qb_list_head *list;
555 struct qb_list_head *n;
556 struct qb_loop_item *item;
557
558 close(pipe_fds[0]);
559 pipe_fds[0] = -1;
560 close(pipe_fds[1]);
561 pipe_fds[1] = -1;
562
563 qb_list_for_each_safe(list, n, &s->sig_head) {
564 item = qb_list_entry(list, struct qb_loop_item, list);
565 qb_list_del(&item->list);
566 free(item);
567 }
568
569 free(l->signal_source);
570 }
571
572 static int32_t
573 _qb_signal_add_to_jobs_(struct qb_loop *l, struct qb_poll_entry *pe)
574 {
575 struct qb_signal_source *s =
576 (struct qb_signal_source *)l->signal_source;
577 struct qb_list_head *list;
578 struct qb_loop_sig *sig;
579 struct qb_loop_item *item;
580 struct qb_loop_sig *new_sig_job;
581 int32_t the_signal;
582 ssize_t res;
583 int32_t jobs_added = 0;
584
585 res = read(pipe_fds[0], &the_signal, sizeof(int32_t));
586 if (res != sizeof(int32_t)) {
587 qb_util_perror(LOG_WARNING, "failed to read pipe");
588 return 0;
589 }
590 pe->ufd.revents = 0;
591
592 qb_list_for_each(list, &s->sig_head) {
593 item = qb_list_entry(list, struct qb_loop_item, list);
594 sig = (struct qb_loop_sig *)item;
595 if (sig->signal == the_signal) {
596 new_sig_job = calloc(1, sizeof(struct qb_loop_sig));
597 if (new_sig_job == NULL) {
598 return jobs_added;
599 }
600 memcpy(new_sig_job, sig, sizeof(struct qb_loop_sig));
601
602 qb_util_log(LOG_TRACE,
603 "adding signal [%d] to job queue %p",
604 the_signal, sig);
605
606 new_sig_job->cloned_from = sig;
607 qb_loop_level_item_add(&l->level[sig->p],
608 &new_sig_job->item);
609 jobs_added++;
610 }
611 }
612 return jobs_added;
613 }
614
615 static void
616 _adjust_sigactions_(struct qb_signal_source *s)
617 {
618 struct qb_loop_sig *sig;
619 struct qb_loop_item *item;
620 struct sigaction sa;
621 int32_t i;
622 int32_t needed;
623
624 sa.sa_flags = SA_SIGINFO;
625 sa.sa_sigaction = _handle_real_signal_;
626 sigemptyset(&s->signal_superset);
627 sigemptyset(&sa.sa_mask);
628
629 /* re-set to default */
630 for (i = 0; i < QB_MAX_NUM_SIGNALS; i++) {
631 needed = QB_FALSE;
632 qb_list_for_each_entry(item, &s->sig_head, list) {
633 sig = (struct qb_loop_sig *)item;
634 if (i == sig->signal) {
635 needed = QB_TRUE;
636 break;
637 }
638 }
639 if (needed) {
640 sigaddset(&s->signal_superset, i);
641 sigaction(i, &sa, NULL);
642 }
643 }
644 }
645
646 int32_t
647 qb_loop_signal_add(qb_loop_t * lp,
648 enum qb_loop_priority p,
649 int32_t the_sig,
650 void *data,
651 qb_loop_signal_dispatch_fn dispatch_fn,
652 qb_loop_signal_handle * handle)
653 {
654 struct qb_loop_sig *sig;
655 struct qb_signal_source *s;
656 struct qb_loop *l = lp;
657
658 if (l == NULL) {
659 l = qb_loop_default_get();
660 }
661 if (l == NULL || dispatch_fn == NULL) {
662 return -EINVAL;
663 }
664 if (p < QB_LOOP_LOW || p > QB_LOOP_HIGH) {
665 return -EINVAL;
666 }
667 s = (struct qb_signal_source *)l->signal_source;
668 sig = calloc(1, sizeof(struct qb_loop_sig));
669 if (sig == NULL) {
670 return -errno;
671 }
672
673 sig->dispatch_fn = dispatch_fn;
674 sig->p = p;
675 sig->signal = the_sig;
676 sig->item.user_data = data;
677 sig->item.source = l->signal_source;
678 sig->item.type = QB_LOOP_SIG;
679
680 qb_list_init(&sig->item.list);
681 qb_list_add_tail(&sig->item.list, &s->sig_head);
682
683 if (sigismember(&s->signal_superset, the_sig) != 1) {
684 _adjust_sigactions_(s);
685 }
686 if (handle) {
687 *handle = sig;
688 }
689
690 return 0;
691 }
692
693 int32_t
694 qb_loop_signal_mod(qb_loop_t * lp,
695 enum qb_loop_priority p,
696 int32_t the_sig,
697 void *data,
698 qb_loop_signal_dispatch_fn dispatch_fn,
699 qb_loop_signal_handle handle)
700 {
701 struct qb_signal_source *s;
702 struct qb_loop_sig *sig = (struct qb_loop_sig *)handle;
703 struct qb_loop *l = lp;
704
705 if (l == NULL) {
706 l = qb_loop_default_get();
707 }
708 if (l == NULL || dispatch_fn == NULL || handle == NULL) {
709 return -EINVAL;
710 }
711 if (p < QB_LOOP_LOW || p > QB_LOOP_HIGH) {
712 return -EINVAL;
713 }
714 s = (struct qb_signal_source *)l->signal_source;
715
716 sig->item.user_data = data;
717 sig->item.type = QB_LOOP_SIG;
718 sig->dispatch_fn = dispatch_fn;
719 sig->p = p;
720
721 if (sig->signal != the_sig) {
722 (void)signal(sig->signal, SIG_DFL);
723 sig->signal = the_sig;
724 _adjust_sigactions_(s);
725 }
726
727 return 0;
728 }
729
730 int32_t
731 qb_loop_signal_del(qb_loop_t * lp, qb_loop_signal_handle handle)
732 {
733 struct qb_signal_source *s;
734 struct qb_loop_sig *sig = (struct qb_loop_sig *)handle;
735 struct qb_loop_sig *sig_clone;
736 struct qb_loop *l = lp;
737 struct qb_loop_item *item;
738
739 if (l == NULL) {
740 l = qb_loop_default_get();
741 }
742 if (l == NULL || handle == NULL) {
743 return -EINVAL;
744 }
745 s = (struct qb_signal_source *)l->signal_source;
746
747 qb_list_for_each_entry(item, &l->level[sig->p].wait_head, list) {
748 if (item->type != QB_LOOP_SIG) {
749 continue;
750 }
751 sig_clone = (struct qb_loop_sig *)item;
752 if (sig_clone->cloned_from == sig) {
753 qb_util_log(LOG_TRACE, "deleting sig in WAITLIST");
754 qb_list_del(&sig_clone->item.list);
755 free(sig_clone);
756 break;
757 }
758 }
759
760 qb_list_for_each_entry(item, &l->level[sig->p].job_head, list) {
761 if (item->type != QB_LOOP_SIG) {
762 continue;
763 }
764 sig_clone = (struct qb_loop_sig *)item;
765 if (sig_clone->cloned_from == sig) {
766 qb_loop_level_item_del(&l->level[sig->p], item);
767 qb_util_log(LOG_TRACE, "deleting sig in JOBLIST");
768 break;
769 }
770 }
771
772 qb_list_del(&sig->item.list);
773 (void)signal(sig->signal, SIG_DFL);
774 free(sig);
775 _adjust_sigactions_(s);
776 return 0;
777 }
778