/* * sigchld.c - event for SIGCHLD * Copyright (c) 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "config.h" #include #include #include #include #include "src/conf.h" #include "src/util.h" #include "src/tlsdate.h" /* Returns 1 if a death was handled, otherwise 0. */ int handle_child_death (struct state *state) { siginfo_t info; int ret; info.si_pid = 0; ret = waitid (P_ALL, -1, &info, WEXITED|WNOHANG); if (ret == -1) { if (errno == ECHILD) return 0; perror ("[event:%s] waitid() failed after SIGCHLD", __func__); return 0; } if (info.si_pid == 0) { return 0; } if (info.si_pid == state->setter_pid) { report_setter_error (&info); event_base_loopbreak (state->base); return 1; } if (info.si_pid != state->tlsdate_pid) { error ("[event:%s] SIGCHLD for an unknown process -- " "pid:%d uid:%d status:%d code:%d", __func__, info.si_pid, info.si_uid, info.si_status, info.si_code); return 1; } verb ("[event:%s] tlsdate reaped => " "pid:%d uid:%d status:%d code:%d", __func__, info.si_pid, info.si_uid, info.si_status, info.si_code); /* If it was still active, remove it. */ event_del (state->events[E_TLSDATE_TIMEOUT]); state->running = 0; state->tlsdate_pid = 0; /* Clean exit - don't rerun! */ if (info.si_status == 0) return 1; verb_debug ("[event:%s] scheduling a retry", __func__); /* Rerun a failed tlsdate */ if (state->backoff < MAX_SANE_BACKOFF) state->backoff *= 2; /* If there is no resolver, call tlsdate directly. */ if (!state->events[E_RESOLVER]) { trigger_event (state, E_TLSDATE, state->backoff); return 1; } /* Run tlsdate even if the resolver doesn't come back. */ trigger_event (state, E_TLSDATE, RESOLVER_TIMEOUT + state->backoff); /* Schedule the resolver. This is always done after tlsdate in case there * is no resolver. */ trigger_event (state, E_RESOLVER, state->backoff); return 1; } /* Returns 1 if a death was handled, otherwise 0. */ int handle_child_stop (struct state *state) { /* Handle unexpected external interactions */ siginfo_t info; int ret; info.si_pid = 0; ret = waitid (P_ALL, -1, &info, WSTOPPED|WCONTINUED|WNOHANG); if (ret == -1) { if (errno == ECHILD) return 0; perror ("[event:%s] waitid() failed after SIGCHLD", __func__); return 0; } if (info.si_pid == 0) return 0; info ("[event:%s] a child has been STOPPED or CONTINUED. Killing it.", __func__); /* Kill it then catch the next SIGCHLD. */ if (kill (info.si_pid, SIGKILL)) { if (errno == EPERM) fatal ("[event:%s] cannot terminate STOPPED privileged child", __func__); if (errno == ESRCH) info ("[event:%s] child gone before we could kill it", __func__); } return 1; } void action_sigchld (evutil_socket_t fd, short what, void *arg) { struct state *state = arg; verb_debug ("[event:%s] a child process has SIGCHLD'd!", __func__); /* Process SIGCHLDs in two steps: death and stopped until all * pending children are sorted. */ if (!handle_child_death (state) && !handle_child_stop (state)) verb ("[event:%s] SIGCHLD fired but no children ready!", __func__); while (handle_child_death (state) || handle_child_stop (state)); } int setup_sigchld_event (struct state *state, int persist) { state->events[E_SIGCHLD] = event_new (state->base, SIGCHLD, EV_SIGNAL| (persist ? EV_PERSIST : 0), action_sigchld, state); if (!state->events[E_SIGCHLD]) return 1; /* Make sure this is lower than SAVE so we get any error * messages back from the time setter. */ event_priority_set (state->events[E_SIGCHLD], PRI_NET); event_add (state->events[E_SIGCHLD], NULL); return 0; }