| /* |
| * 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 <errno.h> |
| #include <event2/event.h> |
| #include <sys/wait.h> |
| #include <sys/types.h> |
| |
| #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; |
| } |