Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // This program is free software; you can redistribute it and/or modify
4 : // it under the terms of the GNU General Public License as published by
5 : // the Free Software Foundation; either version 2 of the License, or
6 : // (at your option) any later version.
7 : //
8 : // This program is distributed in the hope that it will be useful,
9 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : // GNU General Public License for more details.
12 : //
13 : // You should have received a copy of the GNU General Public License
14 : // along with this program; if not, write to the Free Software
15 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 :
17 : /** \file
18 : * \brief Implementation of the Signal class.
19 : *
20 : * The Signal class listens for Unix signals to happen. This wakes us
21 : * up when the signal happens.
22 : */
23 :
24 : // self
25 : //
26 : #include "eventdispatcher/signal.h"
27 :
28 : #include "eventdispatcher/exception.h"
29 :
30 :
31 : // snaplogger lib
32 : //
33 : #include "snaplogger/message.h"
34 :
35 :
36 : // C++ lib
37 : //
38 : #include <iostream>
39 :
40 :
41 : // C lib
42 : //
43 : #include <string.h>
44 : #include <signal.h>
45 :
46 :
47 : // last include
48 : //
49 : #include <snapdev/poison.h>
50 :
51 :
52 :
53 :
54 : namespace ed
55 : {
56 :
57 :
58 : namespace
59 : {
60 :
61 :
62 :
63 : /** \brief The array of signals handled by snap_signal objects.
64 : *
65 : * This map holds a list of signal handlers. You cannot register
66 : * the same signal more than once so this map is used to make
67 : * sure that each signal is unique.
68 : *
69 : * \todo
70 : * We may actually want to use a sigset_t object and just set
71 : * bits and remove
72 : *
73 : * \note
74 : * The pointer to the signal object is a bare pointer
75 : * for in part because we cannot use a smart pointer in
76 : * a constructor where we add the signal to this map. Also
77 : * at this time that pointer does not get used so it could
78 : * as well have been a boolean.
79 : *
80 : * \bug
81 : * Having a global list this means signal objects can't safely
82 : * be created before main() gets called.
83 : */
84 : sigset_t g_signal_handlers = sigset_t();
85 :
86 :
87 :
88 : }
89 : // no name namespace
90 :
91 :
92 :
93 :
94 : /** \brief Initializes the signal object.
95 : *
96 : * This function initializes the signal object with the specified
97 : * \p posix_signal which represents a POSIX signal such as SIGHUP,
98 : * SIGTERM, SIGUSR1, SIGUSR2, etc.
99 : *
100 : * The signal automatically gets masked out. This allows us to
101 : * unmask the signal only when we are ready to call ppoll() and
102 : * thus not have the signal break any of our normal user code.
103 : *
104 : * The ppoll() function unblocks all the signals that you listen
105 : * to (i.e. for each snap_signal object you created.) The run()
106 : * loop ends up calling your process_signal() callback function.
107 : *
108 : * Note that the snap_signal callback is called from the normal user
109 : * environment and not directly from the POSIX signal handler.
110 : * This means you can call any function from your callback.
111 : *
112 : * \note
113 : * IMPORTANT: Remember that POSIX signals stop your code at a 'breakable'
114 : * point which in many circumstances can create many problems unless
115 : * you make sure to mask signals while doing work. For example, you
116 : * could end up with a read() returning an error when the file you
117 : * are reading has absolutely no error but a dude decided to signal
118 : * you with a 'kill -HUP 123'...
119 : *
120 : * \code
121 : * {
122 : * // use an RAII masking mechanism
123 : * mask_posix_signal mask();
124 : *
125 : * // do your work (i.e. read/write/etc.)
126 : * ...
127 : * }
128 : * \endcode
129 : *
130 : * \par
131 : * The best way in our processes will be to block all signals except
132 : * while poll() is called (using ppoll() for the feat.)
133 : *
134 : * \note
135 : * By default the constructor masks the specified \p posix_signal and
136 : * it does not restore the signal on destruction. If you want the
137 : * signal to be unmasked on destruction (say to restore the default
138 : * functioning of the SIGINT signal,) then make sure to call the
139 : * unblock_signal() function right after you create your connection.
140 : *
141 : * \warning
142 : * The the signal gets masked by this constructor. If you want to make
143 : * sure that most of your code does not get affected by said signal,
144 : * make sure to create your snap_signal object early on or mask those
145 : * signals beforehand. Otherwise the signal could happen before it
146 : * gets masked. Initialization of your process may not require
147 : * protection anyway.
148 : *
149 : * \bug
150 : * You should not use signal() and setup a handler for the same signal.
151 : * It will not play nice to have both types of signal handlers. That
152 : * being said, we my current testing (as of Ubuntu 16.04), it seems
153 : * to work just fine..
154 : *
155 : * \exception snap_communicator_initialization_error
156 : * Create multiple snap_signal() with the same posix_signal parameter
157 : * is not supported and this exception is raised whenever you attempt
158 : * to do that. Remember that you can have at most one snap_communicator
159 : * object (hence the singleton.)
160 : *
161 : * \exception snap_communicator_runtime_error
162 : * The signalfd() function is expected to create a "socket" (file
163 : * descriptor) listening for incoming signals. If it fails, this
164 : * exception is raised (which is very similar to other socket
165 : * based connections which throw whenever a connection cannot
166 : * be achieved.)
167 : *
168 : * \param[in] posix_signal The signal to be managed by this snap_signal.
169 : */
170 0 : signal::signal(int posix_signal)
171 0 : : f_signal(posix_signal)
172 : {
173 0 : int const r(sigismember(&g_signal_handlers, f_signal));
174 0 : if(r != 0)
175 : {
176 0 : if(r == 1)
177 : {
178 : // this could be fixed, but probably not worth the trouble...
179 : //
180 0 : throw event_dispatcher_initialization_error("the same signal cannot be created more than once in your entire process.");
181 : }
182 :
183 : // f_signal is not considered valid by this OS
184 : //
185 0 : throw event_dispatcher_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
186 : }
187 :
188 : // create a mask for that signal
189 : //
190 : sigset_t set;
191 0 : sigemptyset(&set);
192 0 : sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
193 :
194 : // first we block the signal
195 : //
196 0 : if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
197 : {
198 0 : throw event_dispatcher_runtime_error("sigprocmask() failed to block signal.");
199 : }
200 :
201 : // second we create a "socket" for the signal (really it is a file
202 : // descriptor manager by the kernel)
203 : //
204 0 : f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
205 0 : if(f_socket == -1)
206 : {
207 0 : int const e(errno);
208 0 : std::string err("signalfd() failed to create a signal listener for signal ");
209 0 : err += std::to_string(f_signal);
210 0 : err += " (errno: ";
211 0 : err += std::to_string(e);
212 0 : err += " -- ";
213 0 : err += strerror(e);
214 0 : err += ").";
215 0 : SNAP_LOG_ERROR << err;
216 0 : throw event_dispatcher_runtime_error(err);
217 : }
218 :
219 : // mark this signal as in use
220 : //
221 0 : sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
222 0 : }
223 :
224 :
225 : /** \brief Restore the signal as it was before you created a snap_signal.
226 : *
227 : * The destructor is expected to restore the signal to what it was
228 : * before you create this snap_signal. Of course, if you created
229 : * other signal handlers in between, it will not work right since
230 : * this function will destroy your handler pointer.
231 : *
232 : * To do it right, it has to be done in order (i.e. set handler 1, set
233 : * handler 2, set handler 3, remove handler 3, remove handler 2, remove
234 : * handler 1.) We do not guarantee anything at this level!
235 : */
236 0 : signal::~signal()
237 : {
238 0 : close();
239 0 : }
240 :
241 :
242 : /** \brief Tell that this connection is listening on a Unix signal.
243 : *
244 : * The snap_signal implements the signal listening feature. We use
245 : * a simple flag in the virtual table to avoid a more expansive
246 : * dynamic_cast<>() is a loop that goes over all the connections
247 : * you have defined.
248 : *
249 : * \return The base implementation returns false.
250 : */
251 0 : bool signal::is_signal() const
252 : {
253 0 : return true;
254 : }
255 :
256 :
257 : /** \brief Retrieve the "socket" of the signal object.
258 : *
259 : * Signal objects have a socket (file descriptor) assigned to them
260 : * using the signalfd() function.
261 : *
262 : * \note
263 : * You should not override this function since there is no other
264 : * value it can return.
265 : *
266 : * \return The signal socket to listen on with poll().
267 : */
268 0 : int signal::get_socket() const
269 : {
270 0 : return f_socket;
271 : }
272 :
273 :
274 : /** \brief Retrieve the PID of the child process that just emitted SIGCHLD.
275 : *
276 : * This function returns the process identifier (pid_t) of the child that
277 : * just sent us a SIGCHLD Unix signal.
278 : *
279 : * \exception snap_communicator_runtime_error
280 : * This exception is raised if the function gets called before any signal
281 : * ever occurred.
282 : *
283 : * \return The process identifier (pid_t) of the child that died.
284 : */
285 0 : pid_t signal::get_child_pid() const
286 : {
287 0 : if(f_signal_info.ssi_signo == 0)
288 : {
289 0 : throw event_dispatcher_runtime_error("snap_signal::get_child_pid() called before any signal ever occurred.");
290 : }
291 :
292 0 : return f_signal_info.ssi_pid;
293 : }
294 :
295 :
296 : /** \brief Processes this signal.
297 : *
298 : * This function reads the signal "socket" for all the signal received
299 : * so far.
300 : *
301 : * For each instance found in the signal queue, the process_signal() gets
302 : * called.
303 : */
304 0 : void signal::process()
305 : {
306 : // loop any number of times as required
307 : // (or can we receive a maximum of 1 such signal at a time?)
308 : //
309 0 : while(f_socket != -1)
310 : {
311 0 : int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));
312 0 : if(r == sizeof(f_signal_info))
313 : {
314 0 : process_signal();
315 : }
316 : else
317 : {
318 0 : if(r == -1)
319 : {
320 : // if EAGAIN then we are done as expected, any other error
321 : // is logged
322 : //
323 0 : if(errno != EAGAIN)
324 : {
325 0 : int const e(errno);
326 : SNAP_LOG_ERROR
327 0 : << "an error occurred while reading from the signalfd() file descriptor. (errno: "
328 0 : << e
329 0 : << " -- "
330 0 : << strerror(e)
331 0 : << ").";
332 : }
333 : }
334 : else
335 : {
336 : // what to do? what to do?
337 : SNAP_LOG_ERROR
338 0 : << "reading from the signalfd() file descriptor did not return the expected size. (got "
339 0 : << r
340 0 : << ", expected "
341 0 : << sizeof(f_signal_info)
342 0 : << ")";
343 : }
344 0 : break;
345 : }
346 : }
347 0 : }
348 :
349 :
350 : /** \brief Close the signal file descriptor.
351 : *
352 : * This function closes the file descriptor and, if you called the
353 : * unblock_signal_on_destruction() function, it also restores the
354 : * signal (unblocks it.)
355 : *
356 : * After this call, the connection is pretty much useless (although
357 : * you could still use it as a timer.) You cannot reopen the signal
358 : * file descriptor once closed. Instead, you have to create a new
359 : * connection.
360 : */
361 0 : void signal::close()
362 : {
363 0 : if(f_socket != -1)
364 : {
365 0 : ::close(f_socket);
366 0 : f_socket = -1;
367 :
368 0 : sigdelset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
369 :
370 0 : if(f_unblock)
371 : {
372 : // also unblock the signal
373 : //
374 : sigset_t set;
375 0 : sigemptyset(&set);
376 0 : sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
377 0 : if(sigprocmask(SIG_UNBLOCK, &set, nullptr) != 0)
378 : {
379 : // we cannot throw in a destructor and in most cases this
380 : // happens in the destructor...
381 : //throw snap_communicator_runtime_error("sigprocmask() failed to block signal.");
382 :
383 0 : int const e(errno);
384 : SNAP_LOG_FATAL
385 0 : << "an error occurred while unblocking signal "
386 0 : << f_signal
387 0 : << " with sigprocmask(). (errno: "
388 0 : << e
389 0 : << " -- "
390 0 : << strerror(e);
391 0 : std::cerr << "sigprocmask() failed to unblock signal." << std::endl;
392 :
393 0 : std::terminate();
394 : }
395 : }
396 : }
397 0 : }
398 :
399 :
400 : /** \brief Unmask a signal that was part of a connection.
401 : *
402 : * If you remove a snap_signal connection, you may want to restore
403 : * the mask functionality. By default the signal gets masked but
404 : * it does not get unmasked.
405 : *
406 : * By calling this function just after creation, the signal gets restored
407 : * (unblocked) whenever the snap_signal object gets destroyed.
408 : */
409 0 : void signal::unblock_signal_on_destruction()
410 : {
411 0 : f_unblock = true;
412 0 : }
413 :
414 :
415 :
416 6 : } // namespace ed
417 : // vim: ts=4 sw=4 et
|