Line data Source code
1 : /* npth-sigev.c - signal handling interface
2 : Copyright (C) 2011 g10 Code GmbH
3 :
4 : This file is part of NPTH.
5 :
6 : NPTH is free software; you can redistribute it and/or modify it
7 : under the terms of either
8 :
9 : - the GNU Lesser General Public License as published by the Free
10 : Software Foundation; either version 3 of the License, or (at
11 : your option) any later version.
12 :
13 : or
14 :
15 : - the GNU General Public License as published by the Free
16 : Software Foundation; either version 2 of the License, or (at
17 : your option) any later version.
18 :
19 : or both in parallel, as here.
20 :
21 : NPTH is distributed in the hope that it will be useful, but WITHOUT
22 : ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 : or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
24 : License for more details.
25 :
26 : You should have received a copies of the GNU General Public License
27 : and the GNU Lesser General Public License along with this program;
28 : if not, see <http://www.gnu.org/licenses/>. */
29 :
30 : /* This is a support interface to make it easier to handle signals.
31 :
32 : The interfaces here support one (and only one) thread (here called
33 : "main thread") in the application to monitor several signals while
34 : selecting on filedescriptors.
35 :
36 : First, the main thread should call npth_sigev_init. This
37 : initializes some global data structures used to record interesting
38 : and pending signals.
39 :
40 : Then, the main thread should call npth_sigev_add for every signal
41 : it is interested in observing, and finally npth_sigev_fini. This
42 : will block the signal in the main threads sigmask. Note that these
43 : signals should also be blocked in all other threads. Since they
44 : are blocked in the main thread after calling npth_sigev_add, it is
45 : recommended to call npth_sigev_add in the main thread before
46 : creating any threads.
47 :
48 : The function npth_sigev_sigmask is a convenient function that
49 : returns the sigmask of the thread at time of npth_sigev_init, but
50 : with all registered signals unblocked. It is recommended to do all
51 : other changes to the main thread's sigmask before calling
52 : npth_sigev_init, so that the return value of npth_sigev_sigmask can
53 : be used in the npth_pselect invocation.
54 :
55 : In any case, the main thread should invoke npth_pselect with a
56 : sigmask that has all signals that should be monitored unblocked.
57 :
58 : After npth_pselect returns, npth_sigev_get_pending can be called in
59 : a loop until it returns 0 to iterate over the list of pending
60 : signals. Each time a signal is returned by that function, its
61 : status is reset to non-pending. */
62 :
63 : #ifdef HAVE_CONFIG_H
64 : #include <config.h>
65 : #endif
66 :
67 : #include <signal.h>
68 : #include <assert.h>
69 :
70 : #include "npth.h"
71 :
72 : /* Record events that have been noticed. */
73 : static sigset_t sigev_pending;
74 :
75 : /* The signal mask during normal operation. */
76 : static sigset_t sigev_block;
77 :
78 : /* The signal mask during pselect. */
79 : static sigset_t sigev_unblock;
80 :
81 : /* Registered signal numbers. Needed to iterate over sigset_t.
82 : Bah. */
83 : #define SIGEV_MAX 32
84 : static int sigev_signum[SIGEV_MAX];
85 : static int sigev_signum_cnt;
86 :
87 : /* The internal handler which just sets a global flag. */
88 : static void
89 0 : _sigev_handler (int signum)
90 : {
91 0 : sigaddset (&sigev_pending, signum);
92 0 : }
93 :
94 :
95 : /* Start setting up signal event handling. */
96 : void
97 0 : npth_sigev_init (void)
98 : {
99 0 : sigemptyset (&sigev_pending);
100 0 : pthread_sigmask (SIG_SETMASK, NULL, &sigev_block);
101 0 : pthread_sigmask (SIG_SETMASK, NULL, &sigev_unblock);
102 0 : }
103 :
104 :
105 : /* Add signal SIGNUM to the list of watched signals. */
106 : void
107 0 : npth_sigev_add (int signum)
108 : {
109 : struct sigaction sa;
110 : sigset_t ss;
111 :
112 0 : sigemptyset(&ss);
113 :
114 0 : assert (sigev_signum_cnt < SIGEV_MAX);
115 0 : sigev_signum[sigev_signum_cnt++] = signum;
116 :
117 : /* Make sure we can receive it. */
118 0 : sigdelset (&sigev_unblock, signum);
119 0 : sigaddset (&sigev_block, signum);
120 :
121 0 : sa.sa_handler = _sigev_handler;
122 0 : sa.sa_mask = ss;
123 0 : sa.sa_flags = 0; /* NOT setting SA_RESTART! */
124 :
125 0 : sigaction (signum, &sa, NULL);
126 0 : }
127 :
128 :
129 : #ifdef HAVE_PTHREAD_ATFORK
130 : /* There is non-POSIX operating system where fork is not available to
131 : applications. There, we have no pthread_atfork either. In such a
132 : case, we don't call pthread_atfork. */
133 : static void
134 0 : restore_sigmask_for_child_process (void)
135 : {
136 0 : pthread_sigmask (SIG_SETMASK, &sigev_unblock, NULL);
137 0 : }
138 : #endif
139 :
140 : /* Finish the list of watched signals. This starts to block them,
141 : too. */
142 : void
143 0 : npth_sigev_fini (void)
144 : {
145 : /* Block the interesting signals. */
146 0 : pthread_sigmask (SIG_SETMASK, &sigev_block, NULL);
147 : #ifdef HAVE_PTHREAD_ATFORK
148 0 : pthread_atfork (NULL, NULL, restore_sigmask_for_child_process);
149 : #endif
150 0 : }
151 :
152 :
153 : /* Get the sigmask as needed for pselect. */
154 : sigset_t *
155 0 : npth_sigev_sigmask (void)
156 : {
157 0 : return &sigev_unblock;
158 : }
159 :
160 :
161 : /* Return the next signal event that occured. Returns if none are
162 : left, 1 on success. */
163 : int
164 0 : npth_sigev_get_pending (int *r_signum)
165 : {
166 : int i;
167 0 : for (i = 0; i < sigev_signum_cnt; i++)
168 : {
169 0 : int signum = sigev_signum[i];
170 0 : if (sigismember (&sigev_pending, signum))
171 : {
172 0 : sigdelset (&sigev_pending, signum);
173 0 : *r_signum = signum;
174 0 : return 1;
175 : }
176 : }
177 0 : return 0;
178 : }
179 :
|