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