diff --git a/zyklonb.c b/zyklonb.c index 01c7089..6b06caa 100644 --- a/zyklonb.c +++ b/zyklonb.c @@ -1744,6 +1744,49 @@ parse_config (struct bot_context *ctx, struct error **e) return false; } +static void +on_plugin_death (struct plugin *plugin, int status) +{ + struct bot_context *ctx = plugin->ctx; + + // TODO: callbacks on children death, so that we may tell the user + // "plugin `name' died like a dirty jewish pig"; use `status' + if (!plugin->is_zombie && WIFSIGNALED (status)) + { + const char *notes = ""; +#ifdef WCOREDUMP + if (WCOREDUMP (status)) + notes = " (core dumped)"; +#endif + print_warning ("Plugin `%s' died from signal %d%s", + plugin->name, WTERMSIG (status), notes); + } + + // Let's go through the zombie state to simplify things a bit + // TODO: might not be a completely bad idea to restart the plugin + plugin_zombify (plugin); + + plugin->pid = -1; + + // In theory we could close `read_fd', set `read_event->closed' to true + // and expect epoll to no longer return events for the descriptor, as + // all the pipe ends should be closed by then (the child is dead, so its + // pipe FDs have been closed [assuming it hasn't forked without closing + // the descriptors, which would be evil], and we would have closed all + // of our FDs for this pipe as well). In practice that doesn't work. + poller_fd_reset (&plugin->read_event); + + xclose (plugin->read_fd); + plugin->read_fd = -1; + + LIST_UNLINK (ctx->plugins, plugin); + plugin_free (plugin); + free (plugin); + + // Living child processes block us from quitting + try_finish_quit (ctx); +} + static void on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx) { @@ -1781,46 +1824,9 @@ on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx) break; struct plugin *plugin = plugin_find_by_pid (ctx, zombie); - // Something has died but we don't recognize it (re-exec?) - if (!soft_assert (plugin != NULL)) - continue; - - // TODO: callbacks on children death, so that we may tell the user - // "plugin `name' died like a dirty jewish pig"; use `status' - if (!plugin->is_zombie && WIFSIGNALED (status)) - { - const char *notes = ""; -#ifdef WCOREDUMP - if (WCOREDUMP (status)) - notes = " (core dumped)"; -#endif - print_warning ("Plugin `%s' died from signal %d%s", - plugin->name, WTERMSIG (status), notes); - } - - // Let's go through the zombie state to simplify things a bit - // TODO: might not be a completely bad idea to restart the plugin - plugin_zombify (plugin); - - plugin->pid = -1; - - // In theory we could close `read_fd', set `read_event->closed' to true - // and expect epoll to no longer return events for the descriptor, as - // all the pipe ends should be closed by then (the child is dead, so its - // pipe FDs have been closed [assuming it hasn't forked without closing - // the descriptors, which would be evil], and we would have closed all - // of our FDs for this pipe as well). In practice that doesn't work. - poller_fd_reset (&plugin->read_event); - - xclose (plugin->read_fd); - plugin->read_fd = -1; - - LIST_UNLINK (ctx->plugins, plugin); - plugin_free (plugin); - free (plugin); - - // Living child processes block us from quitting - try_finish_quit (ctx); + // XXX: re-exec if something has died that we don't recognize? + if (soft_assert (plugin != NULL)) + on_plugin_death (plugin, status); } }