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