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