Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bda15a00 authored by Felipe Leme's avatar Felipe Leme Committed by Steven Moreland
Browse files

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
parent df02bffc
Loading
Loading
Loading
Loading
+24 −3
Original line number Diff line number Diff line
@@ -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)
+144 −0
Original line number Diff line number Diff line
/*
 * 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_
+3 −2
Original line number Diff line number Diff line
@@ -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_,
+4 −113
Original line number Diff line number Diff line
@@ -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
@@ -52,28 +53,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.
 *
@@ -101,88 +80,6 @@ class DurationReporter {
    DISALLOW_COPY_AND_ASSIGN(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.
 *
@@ -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_;

+58 −40
Original line number Diff line number Diff line
@@ -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) {
+1 −1

File changed.

Contains only whitespace changes.

Loading