/* * Copyright (c) 2013-2016, Wind River Systems, Inc. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * 3) Neither the name of Wind River Systems nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "guest_child_death.h" #include #include #include #include #include #include #include #include "guest_limits.h" #include "guest_types.h" #include "guest_debug.h" #include "guest_selobj.h" #define GUEST_CHILD_DEATH_MAX_DISPATCH 32 typedef struct { bool valid; pid_t pid; int exit_code; } GuestChildDeathInfoT; typedef struct { bool valid; pid_t pid; GuestChildDeathCallbackT death_callback; } GuestChildDeathCallbackInfoT; static int _child_death_fd = -1; static GuestChildDeathCallbackInfoT _callbacks[GUEST_CHILD_PROCESS_MAX]; static GuestChildDeathInfoT _child_deaths[GUEST_CHILD_PROCESS_MAX]; static uint64_t _child_death_count = 0; // **************************************************************************** // Guest Child Death - Register // ============================ GuestErrorT guest_child_death_register( pid_t pid, GuestChildDeathCallbackT callback ) { GuestChildDeathCallbackInfoT* callback_info = NULL; unsigned int callbacks_i; for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) { callback_info = &(_callbacks[callbacks_i]); if (callback_info->valid) { if (pid == callback_info->pid) { callback_info->death_callback = callback; break; } } else { callback_info->valid = true; callback_info->pid = pid; callback_info->death_callback = callback; break; } } if (GUEST_CHILD_PROCESS_MAX <= callbacks_i) { DPRINTFE("Failed to register child death callback for pid (%i).", (int) pid); return GUEST_FAILED; } return GUEST_OKAY; } // **************************************************************************** // **************************************************************************** // Guest Child Death - Deregister // ============================== GuestErrorT guest_child_death_deregister( pid_t pid ) { GuestChildDeathCallbackInfoT* callback_info = NULL; unsigned int callbacks_i; for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) { callback_info = &(_callbacks[callbacks_i]); if (!callback_info->valid) continue; if (pid != callback_info->pid) continue; callback_info->valid = 0; callback_info->pid = 0; callback_info->death_callback = NULL; break; } return GUEST_OKAY; } // **************************************************************************** // **************************************************************************** // Guest Child Death - Save // ======================== GuestErrorT guest_child_death_save( pid_t pid, int exit_code ) { uint64_t child_death_count = ++_child_death_count; GuestChildDeathInfoT* info = NULL; int result; result = write(_child_death_fd, &child_death_count, sizeof(child_death_count)); if (0 > result) DPRINTFE("Failed to signal child death, error=%s", strerror(errno)); DPRINTFD("Child process (%i) died.", (int) pid); unsigned int death_i; for (death_i=0; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i) { info = &(_child_deaths[death_i]); if (info->valid) { if (pid == info->pid) { info->exit_code = exit_code; break; } } else { info->valid = true; info->pid = pid; info->exit_code = exit_code; break; } } return GUEST_OKAY; } // **************************************************************************** // **************************************************************************** // Guest Child Death - Dispatch // ============================ static void guest_child_death_dispatch( int selobj ) { static unsigned int _last_entry = 0; uint64_t child_death_count; GuestChildDeathInfoT* info = NULL; GuestChildDeathCallbackInfoT* callback_info = NULL; unsigned int num_child_death_dispatched = 0; int result; result = read(_child_death_fd, &child_death_count, sizeof(child_death_count)); if (0 > result) { if (EINTR == errno) { DPRINTFD("Interrupted on read, error=%s.", strerror(errno)); } else { DPRINTFE("Failed to dispatch, error=%s.", strerror(errno)); } } unsigned int death_i; for( death_i=_last_entry; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i ) { info = &(_child_deaths[death_i]); if (!info->valid) continue; if (0 == info->pid) continue; DPRINTFD("Child process (%i) exited with %i.", (int) info->pid, info->exit_code); unsigned int callbacks_i; for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) { callback_info = &(_callbacks[callbacks_i]); if (callback_info->valid) { if (info->pid == callback_info->pid) { if (NULL != callback_info->death_callback) { callback_info->death_callback(info->pid, info->exit_code); callback_info->valid = false; } } } } info->valid = false; if (GUEST_CHILD_DEATH_MAX_DISPATCH <= ++num_child_death_dispatched) DPRINTFD("Maximum child process death dispatches (%i) reached.", GUEST_CHILD_DEATH_MAX_DISPATCH); } if (GUEST_CHILD_PROCESS_MAX <= death_i) _last_entry = 0; else _last_entry = death_i; // Check for outstanding child process deaths to handle. for (death_i=0; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i) { info = &(_child_deaths[death_i]); if (!info->valid) continue; if (0 == info->pid) continue; result = write(_child_death_fd, &child_death_count, sizeof(child_death_count)); if (0 > result) DPRINTFE("Failed to signal child process death, error=%s", strerror(errno)); break; } } // **************************************************************************** // **************************************************************************** // Guest Child Death - Initialize // ============================== GuestErrorT guest_child_death_initialize( void ) { GuestSelObjCallbacksT callbacks; GuestErrorT error; _child_death_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (0 > _child_death_fd) { DPRINTFE("Failed to open child death file descriptor,error=%s.", strerror(errno)); return GUEST_FAILED; } memset(&callbacks, 0, sizeof(callbacks)); callbacks.read_callback = guest_child_death_dispatch; error = guest_selobj_register(_child_death_fd, &callbacks); if (GUEST_OKAY != error) { DPRINTFE("Failed to register selection object, error=%s.", guest_error_str(error)); close(_child_death_fd); _child_death_fd = -1; return error; } return GUEST_OKAY; } // **************************************************************************** // **************************************************************************** // Guest Child Death - Finalize // ============================ GuestErrorT guest_child_death_finalize( void ) { GuestErrorT error; if (0 <= _child_death_fd) { error = guest_selobj_deregister(_child_death_fd); if (GUEST_OKAY != error) DPRINTFE("Failed to deregister selection object, error=%s.", guest_error_str(error)); close(_child_death_fd); _child_death_fd = -1; } return GUEST_OKAY; } // ****************************************************************************