| /* libnih |
| * |
| * child.c - child process termination handling |
| * |
| * Copyright © 2006 Scott James Remnant <scott@netsplit.com>. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #include <nih/macros.h> |
| #include <nih/alloc.h> |
| #include <nih/list.h> |
| #include <nih/logging.h> |
| |
| #include "child.h" |
| |
| |
| /** |
| * child_watches: |
| * |
| * This is the list of current child watches, not sorted into any |
| * particular order. |
| **/ |
| static NihList *child_watches = NULL; |
| |
| |
| /** |
| * nih_child_init: |
| * |
| * Initialise the list of child watches. |
| **/ |
| static inline void |
| nih_child_init (void) |
| { |
| if (! child_watches) |
| NIH_MUST (child_watches = nih_list_new ()); |
| } |
| |
| |
| /** |
| * nih_child_add_watch: |
| * @parent: parent of watch, |
| * @pid: process id to watch or -1, |
| * @reaper: function to call on termination, |
| * @data: pointer to pass to @reaper. |
| * |
| * Adds @reaper to the list of functions that should be called by |
| * #nih_child_poll if the process with id @pid terminates. If @pid is -1 |
| * then @reaper is called for all children. |
| * |
| * The watch structure is allocated using #nih_alloc and stored in a linked |
| * list, a default destructor is set that removes the watch from the list. |
| * Removal of the watch can be performed by freeing it. |
| * |
| * Returns: the watch information, or %NULL if insufficient memory. |
| **/ |
| NihChildWatch * |
| nih_child_add_watch (void *parent, |
| pid_t pid, |
| NihReaper reaper, |
| void *data) |
| { |
| NihChildWatch *watch; |
| |
| nih_assert (pid != 0); |
| nih_assert (reaper != NULL); |
| |
| nih_child_init (); |
| |
| watch = nih_new (parent, NihChildWatch); |
| if (! watch) |
| return NULL; |
| |
| nih_list_init (&watch->entry); |
| nih_alloc_set_destructor (watch, (NihDestructor)nih_list_destructor); |
| |
| watch->pid = pid; |
| |
| watch->reaper = reaper; |
| watch->data = data; |
| |
| nih_list_add (child_watches, &watch->entry); |
| |
| return watch; |
| } |
| |
| |
| /** |
| * nih_child_poll: |
| * |
| * Repeatedly call #waitid until there are no children waiting to be |
| * reaped. For each child that has terminated, the list of child watches |
| * is iterated and the reaper function for appropriate entries called. |
| * |
| * It is safe for the reaper to remove itself. |
| **/ |
| void |
| nih_child_poll (void) |
| { |
| siginfo_t info; |
| |
| nih_child_init (); |
| |
| info.si_pid = 0; |
| while (waitid (P_ALL, 0, &info, WEXITED | WNOHANG | WNOWAIT) == 0) { |
| pid_t pid; |
| int killed, status; |
| |
| pid = info.si_pid; |
| if (! pid) |
| break; |
| |
| killed = info.si_code == CLD_KILLED ? TRUE : FALSE; |
| status = info.si_status; |
| |
| NIH_LIST_FOREACH_SAFE (child_watches, iter) { |
| NihChildWatch *watch = (NihChildWatch *)iter; |
| |
| if ((watch->pid != pid) && (watch->pid != -1)) |
| continue; |
| |
| watch->reaper (watch->data, pid, killed, status); |
| |
| if (watch->pid != -1) |
| nih_list_free (&watch->entry); |
| } |
| |
| /* Reap the child */ |
| waitid (P_PID, pid, &info, WEXITED); |
| } |
| } |