init: use signalfd to catch SIGCHLD
Previously, if init received too many SIGCHLD signals, then the write to
signal_write_fd could fail with EAGAIN. The handler tried to log the
EAGAIN error, and init deadlocked if the interrupted init process had
already acquired a logging-related lock.
Bug: b/77867680
Test: manual
Change-Id: Ief0b5e94d8517827a5a7d03773391ba3ba9447c4
diff --git a/init/init.cpp b/init/init.cpp
index efb9c1d..2f3b28a 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -79,7 +79,7 @@
std::string default_console = "/dev/console";
static int epoll_fd = -1;
-static int sigterm_signal_fd = -1;
+static int signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
@@ -492,14 +492,7 @@
sigaction(SIGTRAP, &action, nullptr);
}
-static void HandleSigtermSignal() {
- signalfd_siginfo siginfo;
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
- if (bytes_read != sizeof(siginfo)) {
- PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
- return;
- }
-
+static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
if (siginfo.ssi_pid != 0) {
// Drop any userspace SIGTERM requests.
LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
@@ -509,37 +502,73 @@
HandlePowerctlMessage("shutdown,container");
}
-static void UnblockSigterm() {
- sigset_t mask;
- sigemptyset(&mask);
- sigaddset(&mask, SIGTERM);
+static void HandleSignalFd() {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+ return;
+ }
- if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
- PLOG(FATAL) << "failed to unblock SIGTERM for PID " << getpid();
+ switch (siginfo.ssi_signo) {
+ case SIGCHLD:
+ ReapAnyOutstandingChildren();
+ break;
+ case SIGTERM:
+ HandleSigtermSignal(siginfo);
+ break;
+ default:
+ PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
+ break;
}
}
-static void InstallSigtermHandler() {
+static void UnblockSignals() {
+ const struct sigaction act { .sa_handler = SIG_DFL };
+ sigaction(SIGCHLD, &act, nullptr);
+
sigset_t mask;
sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGTERM);
- if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
- PLOG(FATAL) << "failed to block SIGTERM";
+ if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
+ PLOG(FATAL) << "failed to unblock signals for PID " << getpid();
+ }
+}
+
+static void InstallSignalFdHandler() {
+ // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
+ // SIGCHLD when a child process stops or continues (b/77867680#comment9).
+ const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
+ sigaction(SIGCHLD, &act, nullptr);
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+
+ if (!IsRebootCapable()) {
+ // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+ // In that case, receiving SIGTERM will cause the system to shut down.
+ sigaddset(&mask, SIGTERM);
}
- // Register a handler to unblock SIGTERM in the child processes.
- const int result = pthread_atfork(nullptr, nullptr, &UnblockSigterm);
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+ PLOG(FATAL) << "failed to block signals";
+ }
+
+ // Register a handler to unblock signals in the child processes.
+ const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
if (result != 0) {
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
- if (sigterm_signal_fd == -1) {
- PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+ signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ if (signal_fd == -1) {
+ PLOG(FATAL) << "failed to create signalfd";
}
- register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+ register_epoll_handler(signal_fd, HandleSignalFd);
}
int main(int argc, char** argv) {
@@ -682,13 +711,7 @@
PLOG(FATAL) << "epoll_create1 failed";
}
- sigchld_handler_init();
-
- if (!IsRebootCapable()) {
- // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
- // In that case, receiving SIGTERM will cause the system to shut down.
- InstallSigtermHandler();
- }
+ InstallSignalFdHandler();
property_load_boot_defaults();
export_oem_lock_status();
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 072a0fb..3ec76df 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -39,9 +39,6 @@
namespace android {
namespace init {
-static int signal_write_fd = -1;
-static int signal_read_fd = -1;
-
static bool ReapOneProcess() {
siginfo_t siginfo = {};
// This returns a zombie pid or informs us that there are no zombies left to be reaped.
@@ -102,46 +99,10 @@
return true;
}
-static void handle_signal() {
- // Clear outstanding requests.
- char buf[32];
- read(signal_read_fd, buf, sizeof(buf));
-
- ReapAnyOutstandingChildren();
-}
-
-static void SIGCHLD_handler(int) {
- if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
- PLOG(ERROR) << "write(signal_write_fd) failed";
- }
-}
-
void ReapAnyOutstandingChildren() {
while (ReapOneProcess()) {
}
}
-void sigchld_handler_init() {
- // Create a signalling mechanism for SIGCHLD.
- int s[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
- PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
- }
-
- signal_write_fd = s[0];
- signal_read_fd = s[1];
-
- // Write to signal_write_fd if we catch SIGCHLD.
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIGCHLD_handler;
- act.sa_flags = SA_NOCLDSTOP;
- sigaction(SIGCHLD, &act, 0);
-
- ReapAnyOutstandingChildren();
-
- register_epoll_handler(signal_read_fd, handle_signal);
-}
-
} // namespace init
} // namespace android
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index c86dc8d..30063f2 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -22,8 +22,6 @@
void ReapAnyOutstandingChildren();
-void sigchld_handler_init(void);
-
} // namespace init
} // namespace android