Line data Source code
1 : // Copyright (c) 2012-2021 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 along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 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_child.h"
30 :
31 : #include "eventdispatcher/communicator.h"
32 : #include "eventdispatcher/exception.h"
33 :
34 :
35 : // snaplogger lib
36 : //
37 : #include <snaplogger/message.h>
38 :
39 :
40 : // cppthread lib
41 : //
42 : #include <cppthread/guard.h>
43 :
44 :
45 : // snapdev lib
46 : //
47 : #include <snapdev/not_used.h>
48 : #include <snapdev/safe_variable.h>
49 :
50 :
51 : // C++ lib
52 : //
53 : #include <iostream>
54 :
55 :
56 : // C lib
57 : //
58 : #include <string.h>
59 : //#include <signal.h>
60 :
61 :
62 : // last include
63 : //
64 : #include <snapdev/poison.h>
65 :
66 :
67 :
68 :
69 : namespace ed
70 : {
71 :
72 :
73 : namespace
74 : {
75 :
76 :
77 :
78 : /** \brief This singleton is saved here once created.
79 : *
80 : * The signal_child object is a singleton. It is created the first time
81 : * you call the get_instance() function. It is used to handle the SIGCHLD
82 : * signal with any number of children from any library or function you
83 : * are running. This allows for one location to manage that specific
84 : * signal, but many location to handle the death of a child process.
85 : */
86 2 : signal_child::pointer_t g_signal_child = signal_child::pointer_t();
87 :
88 :
89 :
90 : }
91 : // no name namespace
92 :
93 :
94 :
95 :
96 : /** \brief Initialize a child_status object.
97 : *
98 : * This constructor saves the wait info of the last waitid() call to this
99 : * object. It represents the current status of the child process.
100 : *
101 : * You can listen to changes to the status of a process. If the process
102 : * is still running, then you get a reply which says the child process
103 : * is not exited, signaled, or stopped. You can decide on which signal
104 : * your callback gets called.
105 : */
106 9 : child_status::child_status(siginfo_t const & info)
107 9 : : f_info(info)
108 : {
109 9 : }
110 :
111 :
112 : /** \brief Return the PID of the concerned child.
113 : *
114 : * This function gives you the PID of the child that died. This is
115 : * particularly useful if you handle multiple children in the
116 : * same callback.
117 : *
118 : * \return The child that just received a status change.
119 : */
120 43 : pid_t child_status::child_pid() const
121 : {
122 43 : return f_info.si_pid;
123 : }
124 :
125 :
126 : /** \brief Return the UID of the user that was running the child.
127 : *
128 : * This function returns the user identifier of the real user who
129 : * was running the child process.
130 : *
131 : * \return The child process user identifier.
132 : */
133 0 : uid_t child_status::child_uid() const
134 : {
135 0 : return f_info.si_uid;
136 : }
137 :
138 :
139 : /** \brief Whether the status means the process is still up and running.
140 : *
141 : * This function returns true if the process did not exit, was not
142 : * signaled, and was not stopped. In all other circumstances, this
143 : * function returns false.
144 : *
145 : * \return true if the process is still running.
146 : */
147 8 : bool child_status::is_running() const
148 : {
149 8 : return !is_exited()
150 0 : && !is_signaled()
151 8 : && !is_stopped();
152 : }
153 :
154 :
155 : /** \brief The process terminated cleanly, with a call to exit().
156 : *
157 : * This function returns true if the child process ended by calling
158 : * the exit() function.
159 : *
160 : * You can further call the exit_code() function to retrieve the
161 : * exit code returned by that process (a number between 0 and 255).
162 : *
163 : * \return true if the process cleanly exited.
164 : */
165 32 : bool child_status::is_exited() const
166 : {
167 32 : return f_info.si_code == CLD_EXITED;
168 : }
169 :
170 :
171 : /** \brief Whether the process terminated because of a signal.
172 : *
173 : * After receiving a signal which kills the process, this function returns
174 : * true. In most cases, we do not expect children to exit with a signal,
175 : * but if that happens, this is how you can detect the matter.
176 : *
177 : * You can further call the is_core_dumped() to detect whether a coredump
178 : * was generated and terminate_signal() to get the signal number that
179 : * terminated this process.
180 : *
181 : * \return true if the process was terminated because of a signal.
182 : */
183 0 : bool child_status::is_signaled() const
184 : {
185 0 : return f_info.si_code == CLD_KILLED || f_info.si_code == CLD_DUMPED;
186 : }
187 :
188 :
189 : /** \brief Whether a core dumped was generated.
190 : *
191 : * When a process receives a signal, it can be asked to generate a core
192 : * dump. In most cases, the core dump size is set to 0 so nothing actually
193 : * gets saved to disk. So this flag may be true, but it doesn't mean you
194 : * will find an actual coredump file in your folder.
195 : *
196 : * \return true if the process was signaled and a core dump generated.
197 : */
198 0 : bool child_status::is_core_dumped() const
199 : {
200 0 : return f_info.si_code == CLD_DUMPED;
201 : }
202 :
203 :
204 : /** \brief The process received a signal to stop.
205 : *
206 : * A SIGSTOP or a trace signal (i.e. as in a debugger). The process is
207 : * still in memory but it is not currently running.
208 : *
209 : * You can further call the stop_signal() to know the signal used to
210 : * stop this process.
211 : *
212 : * \return true if the process stopped.
213 : */
214 0 : bool child_status::is_stopped() const
215 : {
216 : // TODO: have a separate is_trapped()
217 0 : return f_info.si_code == CLD_STOPPED || f_info.si_code == CLD_TRAPPED;
218 : }
219 :
220 :
221 : /** \brief The process was sent the SIGCONT signal.
222 : *
223 : * The process was previously stopped by a SIGSTOP or a trap or some other
224 : * similar signal. It was then continued. This signals the continuation.
225 : *
226 : * \return true if the process was signaled to continue.
227 : */
228 0 : bool child_status::is_continued() const
229 : {
230 0 : return f_info.si_code == CLD_CONTINUED;
231 : }
232 :
233 :
234 : /** \brief Transform the status in a mask.
235 : *
236 : * This function transforms this status in a mask. This is used to know
237 : * which callback to call whenever an event occurs.
238 : *
239 : * The function returns 0 if the current status is not properly understood.
240 : *
241 : * \return The mask representing this child status.
242 : */
243 8 : flag_t child_status::status_mask() const
244 : {
245 8 : if(is_running())
246 : {
247 0 : return SIGNAL_CHILD_FLAG_RUNNING;
248 : }
249 :
250 8 : if(is_exited())
251 : {
252 8 : return SIGNAL_CHILD_FLAG_EXITED;
253 : }
254 :
255 0 : if(is_signaled())
256 : {
257 0 : return SIGNAL_CHILD_FLAG_SIGNALED;
258 : }
259 :
260 0 : if(is_stopped())
261 : {
262 0 : return SIGNAL_CHILD_FLAG_STOPPED;
263 : }
264 :
265 0 : if(is_continued())
266 : {
267 0 : return SIGNAL_CHILD_FLAG_CONTINUED;
268 : }
269 :
270 : // invalid / unknown / not understood status
271 : //
272 0 : return 0;
273 : }
274 :
275 :
276 : /** \brief The exit code of the child process.
277 : *
278 : * This function return the exit code as returned by the child process by
279 : * returning from the main() function or explicitly calling one of the
280 : * exit() functions.
281 : *
282 : * The function returns -1 if the process did not exit normally or is
283 : * still running. Note that the exit() function can only return a number
284 : * between 0 and 255.
285 : *
286 : * \return The exit code.
287 : */
288 8 : int child_status::exit_code() const
289 : {
290 8 : if(is_exited())
291 : {
292 8 : return f_info.si_status;
293 : }
294 0 : return -1;
295 : }
296 :
297 :
298 : /** \brief The signal that terminated the process.
299 : *
300 : * This function returns the signal that terminated this process if
301 : * it was terminated by a signal.
302 : *
303 : * If the process was not terminated by a signal (or is still running)
304 : * then the function returns -1.
305 : *
306 : * \return The signal that terminated the concerned child process.
307 : */
308 0 : int child_status::terminate_signal() const
309 : {
310 0 : if(is_signaled())
311 : {
312 0 : return f_info.si_status;
313 : }
314 0 : return -1;
315 : }
316 :
317 :
318 : /** \brief Return the signal used to stop the process.
319 : *
320 : * This function returns the signal number used to stop the process.
321 : *
322 : * If the process is not stopped, then the function returns -1.
323 : *
324 : * \return The signal used to stop the process.
325 : */
326 0 : int child_status::stop_signal() const
327 : {
328 0 : if(is_stopped())
329 : {
330 0 : return f_info.si_status;
331 : }
332 :
333 0 : return -1;
334 : }
335 :
336 :
337 :
338 :
339 :
340 :
341 :
342 :
343 :
344 : /** \brief Initializes the signal_child object.
345 : *
346 : * This signal_child object is a singleton. It is used to listen on the
347 : * SIGCHLD signal via a ed::signal connection. You can listen for the
348 : * death of your child by listening for its pid_t. It will get called
349 : * on various events (running, exited, signaled, stopped, continued).
350 : */
351 1 : signal_child::signal_child()
352 1 : : signal(SIGCHLD)
353 : {
354 1 : }
355 :
356 :
357 : /** \brief Restore the SIGCHLD signal as it was.
358 : *
359 : * Since the signal_child is a singleton, this function only gets called
360 : * when you exit your process. It is expected that all the children you
361 : * were listening on died before you call this function.
362 : */
363 2 : signal_child::~signal_child()
364 : {
365 2 : }
366 :
367 :
368 : /** \brief Get the pointer to the signal_child singleton.
369 : *
370 : * This function retrieves the pointer to the signal_child singleton.
371 : *
372 : * The first time you call the function, the singleton gets created.
373 : *
374 : * \return The singleton pointer.
375 : */
376 6 : signal_child::pointer_t signal_child::get_instance()
377 : {
378 12 : cppthread::guard lock(*cppthread::g_system_mutex);
379 :
380 6 : if(g_signal_child == nullptr)
381 : {
382 : // WARNING: can't use std::make_shared<> because constructor is
383 : // private (since we have a singleton)
384 : //
385 1 : g_signal_child.reset(new signal_child());
386 : }
387 :
388 12 : return g_signal_child;
389 : }
390 :
391 :
392 : /** \brief Add this connection to the communicator.
393 : *
394 : * \note
395 : * You should not call this function. It automatically gets called when
396 : * you add a listener (see add_listener() in this class). After all, you
397 : * do not need to listen to anything until you ask for it and similarly
398 : * the remove gets called automatically when the listener gets removed
399 : * (which again is automatic once the child dies).
400 : *
401 : * This function adds this connection to the communicator. This function can
402 : * be called any number of times. It will increase a counter which will
403 : * then be decremented by the remove_connection().
404 : *
405 : * This is used because the communicator::add_connection() will not add the
406 : * signal_child connection more than once because many different functions
407 : * and libraries may need to add it and these would not know whether to
408 : * add or remove the connection and in the end we want it to be properly
409 : * accounted for.
410 : *
411 : * You actually will not be able to add it directly using the
412 : * communicator::add_connection(). It will throw if you try to do that.
413 : * Instead, you must call this function.
414 : */
415 8 : void signal_child::add_connection()
416 : {
417 8 : if(f_count == 0)
418 : {
419 : // add the connection to the communicator
420 : //
421 12 : snap::safe_variable safe(f_adding_to_communicator, true, false);
422 6 : ed::communicator::instance()->add_connection(shared_from_this());
423 : }
424 8 : ++f_count;
425 8 : }
426 :
427 :
428 : /** \brief Remove the connection from the communicator.
429 : *
430 : * \note
431 : * You do not need to call this function. The listener callback function
432 : * gets called and assuming the child died (i.e. a child that received a
433 : * signal that killed it or one that called _exit() to terminate) this
434 : * function gets called automatically.
435 : *
436 : * You must call this function to remove the signal child for each time you
437 : * added it with the corresponding add_connection().
438 : *
439 : * This function is used along the add_connection() because the basic
440 : * add & remove functions of the communicator do not allow you to add
441 : * the same connection more than once (which makes sense), yet the signal
442 : * may be added and removed by many different systems. The means it would
443 : * be unlikely that you would know of all the adds and all the removes in
444 : * one place.
445 : *
446 : * \exception event_dispatcher_count_mismatch
447 : * The remove_connection() function must be called exactly once for each
448 : * call to the add_connection() function. If called more than this many
449 : * times, then this exception is raised.
450 : */
451 8 : void signal_child::remove_connection()
452 : {
453 8 : if(f_count == 0)
454 : {
455 : throw event_dispatcher_count_mismatch(
456 : "the signal_child::remove_connection() was called more times"
457 0 : " than the add_connection()");
458 : }
459 :
460 8 : --f_count;
461 8 : if(f_count == 0)
462 : {
463 : // remove the connection to the communicator
464 : //
465 12 : snap::safe_variable safe(f_removing_to_communicator, true, false);
466 6 : ed::communicator::instance()->remove_connection(shared_from_this());
467 : }
468 8 : }
469 :
470 :
471 : /** \brief Process the SIGCHLD signal.
472 : *
473 : * This function process the SIGCHLD signal. Note that the function is
474 : * expected to be called once per SIGCHLD signaled, however, if several
475 : * children die \em simultaneously, then it would not work to process
476 : * only one child at a time. For this reason, we instead process all the
477 : * children that have died in one go and if we get called additional times
478 : * nothing happens.
479 : */
480 15 : void signal_child::process_signal()
481 : {
482 : for(;;)
483 : {
484 : // Note: to retrieve the rusage() of the process, we could use our
485 : // process_info, however, that has to be done while the
486 : // process is still a zombie... if the callback wants to
487 : // do that, then it is possible since the call here uses
488 : // the WNOWAIT (which means the zombie stays until later)
489 : //
490 15 : siginfo_t info = {};
491 : int const r(waitid(
492 : P_ALL
493 : , 0
494 : , &info
495 15 : , WEXITED | WSTOPPED | WCONTINUED | WNOHANG | WNOWAIT));
496 15 : if(r != 0)
497 : {
498 : // if there are no more children, we get an ECHILD error
499 : // and we can ignore those
500 : //
501 6 : if(errno != ECHILD)
502 : {
503 0 : int const e(errno);
504 0 : SNAP_LOG_ERROR
505 0 : << "waitid() failed to wait for a child: "
506 : << e
507 : << ", "
508 0 : << strerror(e)
509 : << SNAP_LOG_SEND;
510 : }
511 6 : return;
512 : }
513 :
514 9 : child_status const status(info);
515 9 : if(status.child_pid() == 0)
516 : {
517 : // no more state changes
518 : //
519 1 : break;
520 : }
521 :
522 16 : callback_t::list_t listeners;
523 : {
524 16 : cppthread::guard lock(f_mutex);
525 8 : listeners = f_listeners;
526 : }
527 :
528 8 : flag_t const mask(status.status_mask());
529 18 : for(auto & listener : listeners)
530 : {
531 20 : if(listener.f_child == status.child_pid()
532 10 : && (listener.f_flags & mask) != 0)
533 : {
534 8 : listener.f_callback(status);
535 : }
536 : }
537 :
538 16 : if(status.is_exited()
539 8 : || status.is_signaled())
540 : {
541 : // release the zombie, we're done
542 : //
543 8 : siginfo_t ignore = {};
544 8 : snap::NOT_USED(waitid(P_PID, status.child_pid(), &ignore, WEXITED));
545 :
546 8 : remove_listener(status.child_pid());
547 : }
548 8 : }
549 : }
550 :
551 :
552 : /** \brief The connection was added to the communicator.
553 : *
554 : * The connection was added, make sure it was by us (through our own
555 : * add_connection() function).
556 : *
557 : * \warning
558 : * The test in this function works only for the very first connection.
559 : * After that, the communicator prevents this callback from happening.
560 : *
561 : * \exception event_dispatcher_runtime_error
562 : * The signal_child connection must be added by the add_connection() function.
563 : * If you directly call the communicator::add_connection(), then this
564 : * exception is raised.
565 : */
566 6 : void signal_child::connection_added()
567 : {
568 6 : if(!f_adding_to_communicator)
569 : {
570 : throw event_dispatcher_runtime_error(
571 : "it looks like you directly called communicator::add_connection()"
572 : " with the signal_child connection. This is not allowed. Make sure"
573 0 : " to call the signal_child::add_connection() instead.");
574 : }
575 6 : }
576 :
577 :
578 : /** \brief The connection was removed from the communicator.
579 : *
580 : * The connection was removed, make sure it was by us (through our own
581 : * remove_connection() function).
582 : *
583 : * \exception event_dispatcher_runtime_error
584 : * The signal_child connection must be added by the add_connection() function.
585 : * If you directly call the communicator::add_connection(), then this
586 : * exception is raised.
587 : */
588 6 : void signal_child::connection_removed()
589 : {
590 6 : if(!f_removing_to_communicator)
591 : {
592 : throw event_dispatcher_runtime_error(
593 : "it looks like you directly called communicator::remove_connection()"
594 : " with the signal_child connection. This is not allowed. Make sure"
595 0 : " to call the signal_child::remove_connection() instead.");
596 : }
597 6 : }
598 :
599 :
600 : /** \brief Add a listener function.
601 : *
602 : * This function adds a listener so when a SIGCHLD occurs with the specified
603 : * \p child, the \p callback gets called.
604 : *
605 : * You can further define which signals you are interested in. In most
606 : * likelihood, only the ed::SIGNAL_CHILD_FLAG_EXITED and the
607 : * ed::SIGNAL_CHILD_FLAG_SIGNALED are going to be useful (i.e. to get
608 : * called when the process dies).
609 : *
610 : * At the time your \p callback function is called, the process is still
611 : * up (as a zombie). This gives you the opportunity to gather information
612 : * about the process. You can do so with the process_info class using
613 : * the callback child_info::child_pid() function to get the necessary
614 : * process identifier. (TODO: test that this is indeed true)
615 : *
616 : * The function can be called multiple times with the same child PID to
617 : * add multiple callbacks (useful if you vary the mask parameter).
618 : *
619 : * This function automatically calls the add_connection() function any
620 : * time it succeeeds in adding a new child/callback listener.
621 : *
622 : * \exception event_dispatcher_invalid_parameter
623 : * The mask cannot be set to zero, the child identifier must be positive,
624 : * the callback pointer cannot be nullptr.
625 : *
626 : * \param[in] child The process identifier (pid_t) of the child to listen on.
627 : * \param[in] callback A function pointer to call when the event occurs.
628 : * \param[in] mask A mask representing the events you are interested in.
629 : */
630 8 : void signal_child::add_listener(
631 : pid_t child
632 : , func_t callback
633 : , flag_t mask)
634 : {
635 8 : if(child <= 0)
636 : {
637 : throw event_dispatcher_invalid_parameter(
638 : "the child parameter must be a valid pid_t (not "
639 0 : + std::to_string(child)
640 0 : + ")");
641 : }
642 8 : if(callback == nullptr)
643 : {
644 0 : throw event_dispatcher_invalid_parameter("callback cannot be nullptr");
645 : }
646 8 : if(mask == 0)
647 : {
648 0 : throw event_dispatcher_invalid_parameter("mask cannot be set to zero");
649 : }
650 :
651 16 : cppthread::guard lock(f_mutex);
652 8 : f_listeners.emplace(f_listeners.end(), callback_t{ child, callback, mask });
653 8 : add_connection();
654 8 : }
655 :
656 :
657 : /** \brief Remove all the listeners for a specific child.
658 : *
659 : * This function is the converse of the add_listener(). It is used to
660 : * remove a listener from the list maintained by the signal_child
661 : * singleton.
662 : *
663 : * This function automatically gets called whenever the signal_child
664 : * detects the death of a child and finds a corresponding listener.
665 : *
666 : * Further, this function automatically calls the remove_connection()
667 : * function when it indeeds removes the specifed \p child. If found
668 : * more than once, then it gets called once for each instance.
669 : *
670 : * \warning
671 : * All the listener that use the specified \p child parameter are
672 : * removed from the list of listeners.
673 : *
674 : * \note
675 : * Whenever you create a child with fork(), make sure to add a listener
676 : * right then before returning to the communicator::run() loop. That
677 : * way everything happens in the right order. Although the functions
678 : * handling the listener are thread safe, a fork() is not. So you
679 : * should not use both together making everything very safe.
680 : *
681 : * \param[in] child The child for which the listener has to be removed.
682 : */
683 8 : void signal_child::remove_listener(pid_t child)
684 : {
685 16 : cppthread::guard lock(f_mutex);
686 :
687 18 : for(auto it(f_listeners.begin()); it != f_listeners.end(); )
688 : {
689 10 : if(it->f_child == child)
690 : {
691 8 : it = f_listeners.erase(it);
692 8 : remove_connection();
693 : }
694 : else
695 : {
696 2 : ++it;
697 : }
698 : }
699 8 : }
700 :
701 :
702 :
703 6 : } // namespace ed
704 : // vim: ts=4 sw=4 et
|