Moved some functions to DumpstateUtil.h.
dumpstate_board() is been refactored into a HIDL interface, and the HIDL
implementations will need help functions to dump files and run commands into
a file descriptor.
BUG: 31982882
Test: dumpstate_test passes
Test: manual verification
Change-Id: I7a32f0ac236dae34fd85abe47bed0e52a34c5f36
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index b5f328d..c322d9f 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -97,12 +97,12 @@
static const long STATS_MAX_AVERAGE = 100000;
CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
-CommandOptions CommandOptions::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
-CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values(timeout) {
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
@@ -135,7 +135,7 @@
return CommandOptions(values);
}
-CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
+CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
: timeout_(timeout),
always_(false),
root_mode_(DONT_DROP_ROOT),
@@ -146,7 +146,7 @@
CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
}
-long CommandOptions::Timeout() const {
+int64_t CommandOptions::Timeout() const {
return values.timeout_;
}
@@ -166,7 +166,7 @@
return values.logging_message_;
}
-CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
return CommandOptions::CommandOptionsBuilder(timeout);
}
@@ -657,9 +657,9 @@
}
// TODO: when converted to a Dumpstate function, it should be const
-static int _dump_file_from_fd(const std::string& title, const char* path, int fd) {
+static int _dump_file_from_fd_to_fd(const std::string& title, const char* path, int fd, int out_fd) {
if (!title.empty()) {
- printf("------ %s (%s", title.c_str(), path);
+ dprintf(out_fd, "------ %s (%s", title.c_str(), path);
struct stat st;
// Only show the modification time of non-device files.
@@ -671,9 +671,9 @@
char stamp[80];
time_t mtime = st.st_mtime;
strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
- printf(": %s", stamp);
+ dprintf(out_fd, ": %s", stamp);
}
- printf(") ------\n");
+ dprintf(out_fd, ") ------\n");
}
bool newline = false;
@@ -688,24 +688,23 @@
uint64_t elapsed = DurationReporter::Nanotime();
int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
if (ret == -1) {
- printf("*** %s: select failed: %s\n", path, strerror(errno));
+ dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
newline = true;
break;
} else if (ret == 0) {
elapsed = DurationReporter::Nanotime() - elapsed;
- printf("*** %s: Timed out after %.3fs\n", path,
- (float) elapsed / NANOS_PER_SEC);
+ dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
newline = true;
break;
} else {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
if (bytes_read > 0) {
- fwrite(buffer, bytes_read, 1, stdout);
+ android::base::WriteFully(out_fd, buffer, bytes_read);
newline = (buffer[bytes_read-1] == '\n');
} else {
if (bytes_read == -1) {
- printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
+ dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
newline = true;
}
break;
@@ -715,11 +714,31 @@
UpdateProgress(WEIGHT_FILE);
close(fd);
- if (!newline) printf("\n");
- if (!title.empty()) printf("\n");
+ if (!newline) dprintf(out_fd, "\n");
+ if (!title.empty()) dprintf(out_fd, "\n");
return 0;
}
+// Internal function used by both DumpFile and DumpFileToFd - the former wants to print title
+// information, while the later doesn't.
+static int DumpFileToFd(const std::string& title, int out_fd, const std::string& path) {
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd < 0) {
+ int err = errno;
+ if (title.empty()) {
+ printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+ } else {
+ printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
+ }
+ return -1;
+ }
+ return _dump_file_from_fd_to_fd(title, path.c_str(), fd, out_fd);
+}
+
+int DumpFileToFd(int out_fd, const std::string& path) {
+ return DumpFileToFd("", out_fd, path);
+}
+
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
if (IsDryRun()) {
@@ -730,21 +749,7 @@
UpdateProgress(WEIGHT_FILE);
return 0;
}
- return JustDumpFile(title, path);
-}
-
-int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const {
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- int err = errno;
- if (title.empty()) {
- printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
- } else {
- printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
- }
- return -1;
- }
- return _dump_file_from_fd(title, path.c_str(), fd);
+ return DumpFileToFd(title, STDOUT_FILENO, path);
}
int read_file_as_long(const char *path, long int *output) {
@@ -855,7 +860,7 @@
close(fd);
return -1;
}
- return _dump_file_from_fd(title, path, fd);
+ return _dump_file_from_fd_to_fd(title, path, fd, STDOUT_FILENO);
}
bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
@@ -909,6 +914,8 @@
return -1;
}
+ // TODO: SU_ROOT logic must be moved to RunCommandToFd
+
int size = full_command.size() + 1; // null terminated
int starting_index = 0;
if (options.RootMode() == SU_ROOT) {
@@ -935,7 +942,6 @@
}
}
args[i] = nullptr;
- const char* path = args[0];
const char* command = command_string.c_str();
if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
@@ -963,7 +969,7 @@
return 0;
}
- int status = JustRunCommand(command, path, args, options);
+ int status = RunCommandToFd(STDOUT_FILENO, args, options, command);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -978,8 +984,15 @@
return status;
}
-int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
- const CommandOptions& options) const {
+int RunCommandToFd(int fd, const std::vector<const char*>& full_command,
+ const CommandOptions& options, const std::string& description) {
+ if (full_command.empty()) {
+ MYLOGE("No arguments on RunCommandToFd'\n");
+ return -1;
+ }
+ const char* path = full_command[0];
+ const char* command = description.empty() ? path : description.c_str();
+
bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
uint64_t start = DurationReporter::Nanotime();
@@ -1001,9 +1014,13 @@
return -1;
}
+ if (STDOUT_FILENO != fd) {
+ TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
+ close(fd);
+ }
if (silent) {
- // Redirect stderr to stdout
- dup2(STDERR_FILENO, STDOUT_FILENO);
+ // Redirect stderr to fd
+ dup2(STDERR_FILENO, fd);
}
/* make sure the child dies when dumpstate dies */
@@ -1015,11 +1032,10 @@
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
- execvp(path, (char**)args.data());
+ execvp(path, (char**)full_command.data());
// execvp's result will be handled after waitpid_with_timeout() below, but
// if it failed, it's safer to exit dumpstate.
MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
- fflush(stdout);
// Must call _exit (instead of exit), otherwise it will corrupt the zip
// file.
_exit(EXIT_FAILURE);
@@ -1028,6 +1044,8 @@
/* handle parent case */
int status;
bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+ fsync(fd);
+
uint64_t elapsed = DurationReporter::Nanotime() - start;
if (!ret) {
if (errno == ETIMEDOUT) {