Use android::base::Pipe.
Also clean up the comments regarding updater/recovery protocol.
Test: Sideload a package on taimen. Check that /tmp/recovery.log
contains the log from update_engine_sideload.
Test: Factory reset on taimen. Check that log file has the info from
mke2fs.
Change-Id: If560269ce7007769fc85a63bd228813bdd75bebd
diff --git a/install.cpp b/install.cpp
index 9d8943f..a7868fa 100644
--- a/install.cpp
+++ b/install.cpp
@@ -292,6 +292,11 @@
return INSTALL_ERROR;
}
+ // When executing the update binary contained in the package, the arguments passed are:
+ // - the version number for this interface
+ // - an FD to which the program can write in order to update the progress bar.
+ // - the name of the package zip file.
+ // - an optional argument "retry" if this update is a retry of a failed update attempt.
*cmd = {
binary_path,
std::to_string(kRecoveryApiVersion),
@@ -334,75 +339,56 @@
ReadSourceTargetBuild(metadata, log_buffer);
- int pipefd[2];
- pipe(pipefd);
+ // The updater in child process writes to the pipe to communicate with recovery.
+ android::base::unique_fd pipe_read, pipe_write;
+ if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
+ return INSTALL_CORRUPT;
+ }
+
+ // The updater-recovery communication protocol.
+ //
+ // progress <frac> <secs>
+ // fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
+ // zero, use `set_progress` commands to manually control the progress of this segment of the
+ // bar.
+ //
+ // set_progress <frac>
+ // <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
+ // the most recent progress command.
+ //
+ // ui_print <string>
+ // display <string> on the screen.
+ //
+ // wipe_cache
+ // a wipe of cache will be performed following a successful installation.
+ //
+ // clear_display
+ // turn off the text display.
+ //
+ // enable_reboot
+ // packages can explicitly request that they want the user to be able to reboot during
+ // installation (useful for debugging packages that don't exit).
+ //
+ // retry_update
+ // updater encounters some issue during the update. It requests a reboot to retry the same
+ // package automatically.
+ //
+ // log <string>
+ // updater requests logging the string (e.g. cause of the failure).
+ //
+
std::vector<std::string> args;
if (int update_status =
- is_ab ? SetUpAbUpdateCommands(package, zip, pipefd[1], &args)
- : SetUpNonAbUpdateCommands(package, zip, retry_count, pipefd[1], &args);
+ is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
+ : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
update_status != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return update_status;
}
- // When executing the update binary contained in the package, the
- // arguments passed are:
- //
- // - the version number for this interface
- //
- // - an FD to which the program can write in order to update the
- // progress bar. The program can write single-line commands:
- //
- // progress <frac> <secs>
- // fill up the next <frac> part of of the progress bar
- // over <secs> seconds. If <secs> is zero, use
- // set_progress commands to manually control the
- // progress of this segment of the bar.
- //
- // set_progress <frac>
- // <frac> should be between 0.0 and 1.0; sets the
- // progress bar within the segment defined by the most
- // recent progress command.
- //
- // ui_print <string>
- // display <string> on the screen.
- //
- // wipe_cache
- // a wipe of cache will be performed following a successful
- // installation.
- //
- // clear_display
- // turn off the text display.
- //
- // enable_reboot
- // packages can explicitly request that they want the user
- // to be able to reboot during installation (useful for
- // debugging packages that don't exit).
- //
- // retry_update
- // updater encounters some issue during the update. It requests
- // a reboot to retry the same package automatically.
- //
- // log <string>
- // updater requests logging the string (e.g. cause of the
- // failure).
- //
- // - the name of the package zip file.
- //
- // - an optional argument "retry" if this update is a retry of a failed
- // update attempt.
- //
-
- // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
- auto chr_args = StringVectorToNullTerminatedArray(args);
-
pid_t pid = fork();
-
if (pid == -1) {
- close(pipefd[0]);
- close(pipefd[1]);
PLOG(ERROR) << "Failed to fork update binary";
log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
return INSTALL_ERROR;
@@ -410,16 +396,18 @@
if (pid == 0) {
umask(022);
- close(pipefd[0]);
+ pipe_read.reset();
+
+ // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
+ auto chr_args = StringVectorToNullTerminatedArray(args);
execv(chr_args[0], chr_args.data());
- // Bug: 34769056
- // We shouldn't use LOG/PLOG in the forked process, since they may cause
- // the child process to hang. This deadlock results from an improperly
- // copied mutex in the ui functions.
+ // We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
+ // hang. This deadlock results from an improperly copied mutex in the ui functions.
+ // (Bug: 34769056)
fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
_exit(EXIT_FAILURE);
}
- close(pipefd[1]);
+ pipe_write.reset();
std::atomic<bool> logger_finished(false);
std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
@@ -428,7 +416,7 @@
bool retry_update = false;
char buffer[1024];
- FILE* from_child = fdopen(pipefd[0], "r");
+ FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
std::string line(buffer);
size_t space = line.find_first_of(" \n");
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 7fb4616..935d698 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -217,9 +217,10 @@
}
static void redirect_stdio(const char* filename) {
- int pipefd[2];
- if (pipe(pipefd) == -1) {
- PLOG(ERROR) << "pipe failed";
+ android::base::unique_fd pipe_read, pipe_write;
+ // Create a pipe that allows parent process sending logs over.
+ if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "Failed to create pipe for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really
// anywhere to complain...
@@ -233,7 +234,7 @@
pid_t pid = fork();
if (pid == -1) {
- PLOG(ERROR) << "fork failed";
+ PLOG(ERROR) << "Failed to fork for redirecting stdio";
// Fall back to traditional logging mode without timestamps. If these fail, there's not really
// anywhere to complain...
@@ -246,8 +247,8 @@
}
if (pid == 0) {
- /// Close the unused write end.
- close(pipefd[1]);
+ // Child process reads the incoming logs and doesn't write to the pipe.
+ pipe_write.reset();
auto start = std::chrono::steady_clock::now();
@@ -255,15 +256,13 @@
FILE* log_fp = fopen(filename, "ae");
if (log_fp == nullptr) {
PLOG(ERROR) << "fopen \"" << filename << "\" failed";
- close(pipefd[0]);
_exit(EXIT_FAILURE);
}
- FILE* pipe_fp = fdopen(pipefd[0], "r");
+ FILE* pipe_fp = android::base::Fdopen(std::move(pipe_read), "r");
if (pipe_fp == nullptr) {
PLOG(ERROR) << "fdopen failed";
check_and_fclose(log_fp, filename);
- close(pipefd[0]);
_exit(EXIT_FAILURE);
}
@@ -283,25 +282,23 @@
PLOG(ERROR) << "getline failed";
+ fclose(pipe_fp);
free(line);
check_and_fclose(log_fp, filename);
- close(pipefd[0]);
_exit(EXIT_FAILURE);
} else {
// Redirect stdout/stderr to the logger process. Close the unused read end.
- close(pipefd[0]);
+ pipe_read.reset();
setbuf(stdout, nullptr);
setbuf(stderr, nullptr);
- if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
+ if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) {
PLOG(ERROR) << "dup2 stdout failed";
}
- if (dup2(pipefd[1], STDERR_FILENO) == -1) {
+ if (dup2(pipe_write.get(), STDERR_FILENO) == -1) {
PLOG(ERROR) << "dup2 stderr failed";
}
-
- close(pipefd[1]);
}
}