From 1185050767eb3e92d28e298e3f55f6bb29282ac7 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Sun, 5 Mar 2017 14:28:27 -0800 Subject: [PATCH] Log stderr of secilc This makes init log stderr of secilc invoked to compile SELinux policy. Having an explanation for why secilc failed is very useful for debugging boot issues. Test: Device with PRODUCT_FULL_TREBLE boots up just fine Test: Modified init.cpp to reference non-existent .cil file on a device with PRODUCT_FULL_TREBLE and confirmed that dmesg now contains the error message from secilc saying that the file was not found. Bug: 31363362 Change-Id: I6a3b3576daf0d6fd09e2c79bc43ae63850f44a00 --- init/init.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/init/init.cpp b/init/init.cpp index 28e600803..81f228c40 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -621,11 +621,21 @@ static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_ /* * Forks, executes the provided program in the child, and waits for the completion in the parent. + * Child's stderr is captured and logged using LOG(ERROR). * * Returns true if the child exited with status code 0, returns false otherwise. */ static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[], char* const envp[]) { + // Create a pipe used for redirecting child process's output. + // * pipe_fds[0] is the FD the parent will use for reading. + // * pipe_fds[1] is the FD the child will use for writing. + int pipe_fds[2]; + if (pipe(pipe_fds) == -1) { + PLOG(ERROR) << "Failed to create pipe"; + return false; + } + pid_t child_pid = fork(); if (child_pid == -1) { PLOG(ERROR) << "Failed to fork for " << filename; @@ -634,6 +644,18 @@ static bool fork_execve_and_wait_for_completion(const char* filename, char* cons if (child_pid == 0) { // fork succeeded -- this is executing in the child process + + // Close the pipe FD not used by this process + TEMP_FAILURE_RETRY(close(pipe_fds[0])); + + // Redirect stderr to the pipe FD provided by the parent + if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) { + PLOG(ERROR) << "Failed to redirect stderr of " << filename; + _exit(127); + return false; + } + TEMP_FAILURE_RETRY(close(pipe_fds[1])); + if (execve(filename, argv, envp) == -1) { PLOG(ERROR) << "Failed to execve " << filename; return false; @@ -644,6 +666,30 @@ static bool fork_execve_and_wait_for_completion(const char* filename, char* cons return false; } else { // fork succeeded -- this is executing in the original/parent process + + // Close the pipe FD not used by this process + TEMP_FAILURE_RETRY(close(pipe_fds[1])); + + // Log the redirected output of the child process. + // It's unfortunate that there's no standard way to obtain an istream for a file descriptor. + // As a result, we're buffering all output and logging it in one go at the end of the + // invocation, instead of logging it as it comes in. + const int child_out_fd = pipe_fds[0]; + std::string child_output; + if (!android::base::ReadFdToString(child_out_fd, &child_output)) { + PLOG(ERROR) << "Failed to capture full output of " << filename; + } + TEMP_FAILURE_RETRY(close(child_out_fd)); + if (!child_output.empty()) { + // Log captured output, line by line, because LOG expects to be invoked for each line + std::istringstream in(child_output); + std::string line; + while (std::getline(in, line)) { + LOG(ERROR) << filename << ": " << line; + } + } + + // Wait for child to terminate int status; if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) { PLOG(ERROR) << "Failed to wait for " << filename;