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