diff options
| author | 2016-11-16 17:48:25 -0800 | |
|---|---|---|
| committer | 2016-11-22 01:17:23 +0000 | |
| commit | bda15a00929b836a53bf03473b1ec36285e5944b (patch) | |
| tree | 98551fc277fe4d817ad189120521170c62fd9b10 | |
| parent | df02bffc32c2c89ee3d9955bb7beb2c408227fbf (diff) | |
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
| -rw-r--r-- | cmds/dumpstate/Android.mk | 27 | ||||
| -rw-r--r-- | cmds/dumpstate/DumpstateUtil.h | 144 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 5 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.h | 117 | ||||
| -rw-r--r-- | cmds/dumpstate/tests/dumpstate_test.cpp | 2 | ||||
| -rw-r--r-- | cmds/dumpstate/utils.cpp | 98 |
6 files changed, 234 insertions, 159 deletions
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index e11bf30c29..5b0ced9758 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -22,6 +22,27 @@ COMMON_SHARED_LIBRARIES := \ liblog \ libselinux \ libutils +COMMON_STATIC_LIBRARIES := \ + libdumpstateutil \ + $(COMMON_ZIP_LIBRARIES) + +# ====================# +# libdumpstateutil # +# ====================# +include $(CLEAR_VARS) + +LOCAL_MODULE := libdumpstateutil + +LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS) +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_SRC_FILES := \ + utils.cpp # TODO: temporary, until functions are moved to DumpstateUtil.cpp +# TODO: include just what it uses once split from utils.cpp +LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES) +LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) + +include $(BUILD_STATIC_LIBRARY) # ====================# # libdumpstateheaders # @@ -35,7 +56,7 @@ LOCAL_MODULE := libdumpstateheaders LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \ $(COMMON_SHARED_LIBRARIES) LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \ - $(COMMON_ZIP_LIBRARIES) + $(COMMON_STATIC_LIBRARIES) # Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_ LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS) LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS) @@ -81,7 +102,7 @@ LOCAL_MODULE := dumpstate LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES) -LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) +LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) LOCAL_HAL_STATIC_LIBRARIES := libdumpstate @@ -106,7 +127,7 @@ LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \ DumpstateService.cpp \ tests/dumpstate_test.cpp -LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) \ +LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) \ libgmock LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES) diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h new file mode 100644 index 0000000000..9c60f0d75c --- /dev/null +++ b/cmds/dumpstate/DumpstateUtil.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_ +#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_ + +/* + * Defines the Linux user that should be executing a command. + */ +enum RootMode { + /* Explicitly change the `uid` and `gid` to be `shell`.*/ + DROP_ROOT, + /* Don't change the `uid` and `gid`. */ + DONT_DROP_ROOT, + /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */ + SU_ROOT +}; + +/* + * Defines what should happen with the `stdout` stream of a command. + */ +enum StdoutMode { + /* Don't change `stdout`. */ + NORMAL_STDOUT, + /* Redirect `stdout` to `stderr`. */ + REDIRECT_TO_STDERR +}; + +/* + * Value object used to set command options. + * + * Typically constructed using a builder with chained setters. Examples: + * + * CommandOptions::WithTimeout(20).AsRoot().Build(); + * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build(); + * + * Although the builder could be used to dynamically set values. Example: + * + * CommandOptions::CommandOptionsBuilder options = + * CommandOptions::WithTimeout(10); + * if (!is_user_build()) { + * options.AsRoot(); + * } + * RunCommand("command", {"args"}, options.Build()); + */ +class CommandOptions { + private: + class CommandOptionsValues { + private: + CommandOptionsValues(int64_t timeout); + + int64_t timeout_; + bool always_; + RootMode root_mode_; + StdoutMode stdout_mode_; + std::string logging_message_; + + friend class CommandOptions; + friend class CommandOptionsBuilder; + }; + + CommandOptions(const CommandOptionsValues& values); + + const CommandOptionsValues values; + + public: + class CommandOptionsBuilder { + public: + /* Sets the command to always run, even on `dry-run` mode. */ + CommandOptionsBuilder& Always(); + /* Sets the command's RootMode as `SU_ROOT` */ + CommandOptionsBuilder& AsRoot(); + /* Sets the command's RootMode as `DROP_ROOT` */ + CommandOptionsBuilder& DropRoot(); + /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */ + CommandOptionsBuilder& RedirectStderr(); + /* When not empty, logs a message before executing the command. + * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */ + CommandOptionsBuilder& Log(const std::string& message); + /* Builds the command options. */ + CommandOptions Build(); + + private: + CommandOptionsBuilder(int64_t timeout); + CommandOptionsValues values; + friend class CommandOptions; + }; + + /** Gets the command timeout, in seconds. */ + int64_t Timeout() const; + /* Checks whether the command should always be run, even on dry-run mode. */ + bool Always() const; + /** Gets the RootMode of the command. */ + RootMode RootMode() const; + /** Gets the StdoutMode of the command. */ + StdoutMode StdoutMode() const; + /** Gets the logging message header, it any. */ + std::string LoggingMessage() const; + + /** Creates a builder with the requied timeout. */ + static CommandOptionsBuilder WithTimeout(int64_t timeout); + + // Common options. + static CommandOptions DEFAULT; + static CommandOptions AS_ROOT_5; + static CommandOptions AS_ROOT_10; + static CommandOptions AS_ROOT_20; +}; + +/* + * Forks a command, waits for it to finish, and returns its status. + * + * |fd| file descriptor that receives the command's 'stdout'. + * |full_command| array containing the command (first entry) and its arguments. + * Must contain at least one element. + * |options| optional argument defining the command's behavior. + * |description| optional description of the command to be used on log messages. If empty, + * the command path (without arguments) will be used instead. + */ +int RunCommandToFd(int fd, const std::vector<const char*>& full_command, + const CommandOptions& options = CommandOptions::DEFAULT, + const std::string& description = ""); + +/* + * Dumps the contents of a file into a file descriptor. + * + * |fd| file descriptor where the file is dumped into. + * |path| location of the file to be dumped. + */ +int DumpFileToFd(int fd, const std::string& path); + +#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index ea70fe5540..634857944a 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -90,7 +90,7 @@ static int RunCommand(const std::string& title, const std::vector<std::string>& return ds.RunCommand(title, fullCommand, options); } static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, - const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS, + const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, long dumpsysTimeout = 0) { return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout); } @@ -680,7 +680,8 @@ void Dumpstate::PrintHeader() const { printf("Network: %s\n", network.c_str()); printf("Kernel: "); - JustDumpFile("", "/proc/version"); + fflush(stdout); + DumpFileToFd(STDOUT_FILENO, "/proc/version"); printf("Command line: %s\n", strtok(cmdline_buf, "\n")); printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_, diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 3d3d7ed295..9f23a39a6d 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -40,6 +40,7 @@ #include <android-base/macros.h> #include <ziparchive/zip_writer.h> +#include "DumpstateUtil.h" #include "android/os/BnDumpstate.h" // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to @@ -53,28 +54,6 @@ extern "C" { #endif /* - * Defines the Linux user that should be executing a command. - */ -enum RootMode { - /* Explicitly change the `uid` and `gid` to be `shell`.*/ - DROP_ROOT, - /* Don't change the `uid` and `gid`. */ - DONT_DROP_ROOT, - /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */ - SU_ROOT -}; - -/* - * Defines what should happen with the `stdout` stream of a command. - */ -enum StdoutMode { - /* Don't change `stdout`. */ - NORMAL_STDOUT, - /* Redirect `stdout` to `stderr`. */ - REDIRECT_TO_STDERR -}; - -/* * Helper class used to report how long it takes for a section to finish. * * Typical usage: @@ -102,88 +81,6 @@ class DurationReporter { }; /* - * Value object used to set command options. - * - * Typically constructed using a builder with chained setters. Examples: - * - * CommandOptions::WithTimeout(20).AsRoot().Build(); - * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build(); - * - * Although the builder could be used to dynamically set values. Example: - * - * CommandOptions::CommandOptionsBuilder options = - * CommandOptions::WithTimeout(10); - * if (!is_user_build()) { - * options.AsRoot(); - * } - * RunCommand("command", {"args"}, options.Build()); - */ -class CommandOptions { - private: - class CommandOptionsValues { - private: - CommandOptionsValues(long timeout); - - long timeout_; - bool always_; - RootMode root_mode_; - StdoutMode stdout_mode_; - std::string logging_message_; - - friend class CommandOptions; - friend class CommandOptionsBuilder; - }; - - CommandOptions(const CommandOptionsValues& values); - - const CommandOptionsValues values; - - public: - class CommandOptionsBuilder { - public: - /* Sets the command to always run, even on `dry-run` mode. */ - CommandOptionsBuilder& Always(); - /* Sets the command's RootMode as `SU_ROOT` */ - CommandOptionsBuilder& AsRoot(); - /* Sets the command's RootMode as `DROP_ROOT` */ - CommandOptionsBuilder& DropRoot(); - /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */ - CommandOptionsBuilder& RedirectStderr(); - /* When not empty, logs a message before executing the command. - * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */ - CommandOptionsBuilder& Log(const std::string& message); - /* Builds the command options. */ - CommandOptions Build(); - - private: - CommandOptionsBuilder(long timeout); - CommandOptionsValues values; - friend class CommandOptions; - }; - - /** Gets the command timeout, in seconds. */ - long Timeout() const; - /* Checks whether the command should always be run, even on dry-run mode. */ - bool Always() const; - /** Gets the RootMode of the command. */ - RootMode RootMode() const; - /** Gets the StdoutMode of the command. */ - StdoutMode StdoutMode() const; - /** Gets the logging message header, it any. */ - std::string LoggingMessage() const; - - /** Creates a builder with the requied timeout. */ - static CommandOptionsBuilder WithTimeout(long timeout); - - // Common options. - static CommandOptions DEFAULT; - static CommandOptions DEFAULT_DUMPSYS; - static CommandOptions AS_ROOT_5; - static CommandOptions AS_ROOT_10; - static CommandOptions AS_ROOT_20; -}; - -/* * Keeps track of current progress and estimated max, saving stats on file to tune up future runs. * * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall @@ -272,6 +169,8 @@ class Dumpstate { friend class DumpstateTest; public: + static CommandOptions DEFAULT_DUMPSYS; + static Dumpstate& GetInstance(); /* @@ -316,8 +215,7 @@ class Dumpstate { * timeout from `options`) */ void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, - const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS, - long dumpsys_timeout = 0); + const CommandOptions& options = DEFAULT_DUMPSYS, long dumpsys_timeout = 0); /* * Prints the contents of a file. @@ -454,13 +352,6 @@ class Dumpstate { Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false, const std::string& build_type = "user"); - // Internal version of RunCommand that just runs it, without updating progress. - int JustRunCommand(const char* command, const char* path, std::vector<const char*>& args, - const CommandOptions& options) const; - - // Internal version of RunCommand that just dumps it, without updating progress. - int JustDumpFile(const std::string& title, const std::string& path) const; - // Whether this is a dry run. bool dry_run_; diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 0d68901272..9613576a75 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -548,7 +548,7 @@ class ProgressTest : public DumpstateBaseTest { android::base::StringPrintf("%d %d\n", expected_runs, expected_average); std::string actual_content; ASSERT_TRUE(android::base::ReadFileToString(path, &actual_content)) - << "could not read statsfrom" << path; + << "could not read stats from " << path; ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path; } }; diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index b5f328d732..c322d9febc 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -97,12 +97,12 @@ static const int STATS_MAX_N_RUNS = 1000; 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 @@ CommandOptions CommandOptions::CommandOptionsBuilder::Build() { 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::CommandOptionsValues::CommandOptionsValues(long timeout) CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) { } -long CommandOptions::Timeout() const { +int64_t CommandOptions::Timeout() const { return values.timeout_; } @@ -166,7 +166,7 @@ std::string CommandOptions::LoggingMessage() const { 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 @@ void do_showmap(int pid, const char *name) { } // 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 @@ static int _dump_file_from_fd(const std::string& title, const char* path, int fd 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 @@ static int _dump_file_from_fd(const std::string& title, const char* path, int fd 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,25 +714,14 @@ static int _dump_file_from_fd(const std::string& title, const char* path, int fd 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; } -int Dumpstate::DumpFile(const std::string& title, const std::string& path) { - DurationReporter duration_reporter(title); - if (IsDryRun()) { - if (!title.empty()) { - printf("------ %s (%s) ------\n", title.c_str(), path.c_str()); - printf("\t(skipped on dry run)\n"); - } - UpdateProgress(WEIGHT_FILE); - return 0; - } - return JustDumpFile(title, path); -} - -int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const { +// 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; @@ -744,7 +732,24 @@ int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) c } return -1; } - return _dump_file_from_fd(title, path.c_str(), fd); + 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()) { + if (!title.empty()) { + printf("------ %s (%s) ------\n", title.c_str(), path.c_str()); + printf("\t(skipped on dry run)\n"); + } + UpdateProgress(WEIGHT_FILE); + return 0; + } + return DumpFileToFd(title, STDOUT_FILENO, path); } int read_file_as_long(const char *path, long int *output) { @@ -855,7 +860,7 @@ int dump_file_from_fd(const char *title, const char *path, int fd) { 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 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin 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 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin } } 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 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin 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 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin 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 @@ int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector 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 @@ int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector 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 @@ int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector /* 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) { |