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

Commit 3e1b654d authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Add an option to zip incident report" into rvc-dev am: ec6198e9

Change-Id: Ie4221d5b1f1aeb0b8f0e3307a4263c5b099ddc5a
parents 548a77ae ec6198e9
Loading
Loading
Loading
Loading
+5 −1
Original line number Original line Diff line number Diff line
@@ -231,6 +231,7 @@ usage(FILE* out)
    fprintf(out, "  -l           list available sections\n");
    fprintf(out, "  -l           list available sections\n");
    fprintf(out, "  -p           privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
    fprintf(out, "  -p           privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
    fprintf(out, "  -r REASON    human readable description of why the report is taken.\n");
    fprintf(out, "  -r REASON    human readable description of why the report is taken.\n");
    fprintf(out, "  -z           gzip the incident report, i.e. pipe the output through gzip.\n");
    fprintf(out, "\n");
    fprintf(out, "\n");
    fprintf(out, "and one of these destinations:\n");
    fprintf(out, "and one of these destinations:\n");
    fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
    fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
@@ -255,7 +256,7 @@ main(int argc, char** argv)


    // Parse the args
    // Parse the args
    int opt;
    int opt;
    while ((opt = getopt(argc, argv, "bhdlp:r:s:u")) != -1) {
    while ((opt = getopt(argc, argv, "bhdlp:r:s:uz")) != -1) {
        switch (opt) {
        switch (opt) {
            case 'h':
            case 'h':
                usage(stdout);
                usage(stdout);
@@ -302,6 +303,9 @@ main(int argc, char** argv)
                destination = DEST_BROADCAST;
                destination = DEST_BROADCAST;
                receiverArg = optarg;
                receiverArg = optarg;
                break;
                break;
            case 'z':
                args.setGzip(true);
                break;
            default:
            default:
                usage(stderr);
                usage(stderr);
                return 1;
                return 1;
+57 −3
Original line number Original line Diff line number Diff line
@@ -35,10 +35,12 @@
#include <dirent.h>
#include <dirent.h>
#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#include <string>
#include <string>
#include <time.h>
#include <time.h>
#include <wait.h>


namespace android {
namespace android {
namespace os {
namespace os {
@@ -51,6 +53,8 @@ using namespace android::util;
 *      frameworks/base/core/proto/android/os/incident.proto
 *      frameworks/base/core/proto/android/os/incident.proto
 */
 */
const int FIELD_ID_METADATA = 2;
const int FIELD_ID_METADATA = 2;
// Args for exec gzip
static const char* GZIP[] = {"/system/bin/gzip", NULL};


IncidentMetadata_Destination privacy_policy_to_dest(uint8_t privacyPolicy) {
IncidentMetadata_Destination privacy_policy_to_dest(uint8_t privacyPolicy) {
    switch (privacyPolicy) {
    switch (privacyPolicy) {
@@ -142,7 +146,8 @@ ReportRequest::ReportRequest(const IncidentReportArgs& a,
         mListener(listener),
         mListener(listener),
         mFd(fd),
         mFd(fd),
         mIsStreaming(fd >= 0),
         mIsStreaming(fd >= 0),
         mStatus(NO_ERROR) {
         mStatus(OK),
         mZipPid(-1) {
}
}


ReportRequest::~ReportRequest() {
ReportRequest::~ReportRequest() {
@@ -153,7 +158,14 @@ ReportRequest::~ReportRequest() {
}
}


bool ReportRequest::ok() {
bool ReportRequest::ok() {
    return mFd >= 0 && mStatus == NO_ERROR;
    if (mStatus != OK) {
        return false;
    }
    if (!args.gzip()) {
        return mFd >= 0;
    }
    // Send a blank signal to check if mZipPid is alive
    return mZipPid > 0 && kill(mZipPid, 0) == 0;
}
}


bool ReportRequest::containsSection(int sectionId) const {
bool ReportRequest::containsSection(int sectionId) const {
@@ -161,10 +173,45 @@ bool ReportRequest::containsSection(int sectionId) const {
}
}


void ReportRequest::closeFd() {
void ReportRequest::closeFd() {
    if (mIsStreaming && mFd >= 0) {
    if (!mIsStreaming) {
        return;
    }
    if (mFd >= 0) {
        close(mFd);
        close(mFd);
        mFd = -1;
        mFd = -1;
    }
    }
    if (mZipPid > 0) {
        mZipPipe.close();
        // Gzip may take some time.
        status_t err = wait_child(mZipPid, /* timeout_ms= */ 10 * 1000);
        if (err != 0) {
            ALOGW("[ReportRequest] abnormal child process: %s", strerror(-err));
        }
    }
}

int ReportRequest::getFd() {
    return mZipPid > 0 ? mZipPipe.writeFd().get() : mFd;
}

status_t ReportRequest::initGzipIfNecessary() {
    if (!mIsStreaming || !args.gzip()) {
        return OK;
    }
    if (!mZipPipe.init()) {
        ALOGE("[ReportRequest] Failed to setup pipe for gzip");
        mStatus = -errno;
        return mStatus;
    }
    int status = 0;
    pid_t pid = fork_execute_cmd((char* const*)GZIP, mZipPipe.readFd().release(), mFd, &status);
    if (pid < 0 || status != 0) {
        mStatus = status;
        return mStatus;
    }
    mZipPid = pid;
    mFd = -1;
    return OK;
}
}


// ================================================================================
// ================================================================================
@@ -562,6 +609,13 @@ void Reporter::runReport(size_t* reportByteSize) {
        reportId = (spec.tv_sec) * 1000 + spec.tv_nsec;
        reportId = (spec.tv_sec) * 1000 + spec.tv_nsec;
    }
    }


    mBatch->forEachStreamingRequest([](const sp<ReportRequest>& request) {
        status_t err = request->initGzipIfNecessary();
        if (err != 0) {
            ALOGW("Error forking gzip: %s", strerror(err));
        }
    });

    // Write the incident report headers - each request gets its own headers.  It's different
    // Write the incident report headers - each request gets its own headers.  It's different
    // from the other top-level fields in IncidentReport that are the sections where the rest
    // from the other top-level fields in IncidentReport that are the sections where the rest
    // is all shared data (although with their own individual privacy filtering).
    // is all shared data (although with their own individual privacy filtering).
+6 −1
Original line number Original line Diff line number Diff line
@@ -15,6 +15,7 @@
 */
 */
#pragma once
#pragma once


#include "incidentd_util.h"
#include "FdBuffer.h"
#include "FdBuffer.h"
#include "WorkDirectory.h"
#include "WorkDirectory.h"


@@ -63,10 +64,12 @@ public:


    sp<IIncidentReportStatusListener> getListener() { return mListener; }
    sp<IIncidentReportStatusListener> getListener() { return mListener; }


    int getFd() { return mFd; }
    int getFd();


    int setPersistedFd(int fd);
    int setPersistedFd(int fd);


    status_t initGzipIfNecessary();

    void closeFd();
    void closeFd();


private:
private:
@@ -74,6 +77,8 @@ private:
    int mFd;
    int mFd;
    bool mIsStreaming;
    bool mIsStreaming;
    status_t mStatus;
    status_t mStatus;
    pid_t mZipPid;
    Fpipe mZipPipe;
};
};


// ================================================================================
// ================================================================================
+33 −3
Original line number Original line Diff line number Diff line
@@ -16,10 +16,10 @@


#include "Log.h"
#include "Log.h"


#include "WorkDirectory.h"
#include "incidentd_util.h"

#include "proto_util.h"
#include "proto_util.h"
#include "PrivacyFilter.h"
#include "PrivacyFilter.h"
#include "WorkDirectory.h"


#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <private/android_filesystem_config.h>
#include <private/android_filesystem_config.h>
@@ -68,6 +68,9 @@ const ComponentName DROPBOX_SENTINEL("android", "DROPBOX");
/** metadata field id in IncidentProto */
/** metadata field id in IncidentProto */
const int FIELD_ID_INCIDENT_METADATA = 2;
const int FIELD_ID_INCIDENT_METADATA = 2;


// Args for exec gzip
static const char* GZIP[] = {"/system/bin/gzip", NULL};

/**
/**
 * Read a protobuf from disk into the message.
 * Read a protobuf from disk into the message.
 */
 */
@@ -292,6 +295,7 @@ void ReportFile::addReport(const IncidentReportArgs& args) {
        report->set_cls(args.receiverCls());
        report->set_cls(args.receiverCls());
        report->set_privacy_policy(args.getPrivacyPolicy());
        report->set_privacy_policy(args.getPrivacyPolicy());
        report->set_all_sections(args.all());
        report->set_all_sections(args.all());
        report->set_gzip(args.gzip());
        for (int section: args.sections()) {
        for (int section: args.sections()) {
            report->add_section(section);
            report->add_section(section);
        }
        }
@@ -417,6 +421,24 @@ status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& a
        return BAD_VALUE;
        return BAD_VALUE;
    }
    }


    pid_t zipPid = 0;
    if (args.gzip()) {
        Fpipe zipPipe;
        if (!zipPipe.init()) {
            ALOGE("[ReportFile] Failed to setup pipe for gzip");
            close(writeFd);
            return -errno;
        }
        int status = 0;
        zipPid = fork_execute_cmd((char* const*)GZIP, zipPipe.readFd().release(), writeFd, &status);
        close(writeFd);
        if (zipPid < 0 || status != 0) {
            ALOGE("[ReportFile] Failed to fork and exec gzip");
            return status;
        }
        writeFd = zipPipe.writeFd().release();
    }

    status_t err;
    status_t err;


    for (const auto& report : mEnvelope.report()) {
    for (const auto& report : mEnvelope.report()) {
@@ -437,6 +459,13 @@ status_t ReportFile::startFilteringData(int writeFd, const IncidentReportArgs& a
    }
    }


    close(writeFd);
    close(writeFd);
    if (zipPid > 0) {
        status_t err = wait_child(zipPid, /* timeout_ms= */ 10 * 1000);
        if (err != 0) {
            ALOGE("[ReportFile] abnormal child process: %s", strerror(-err));
        }
        return err;
    }
    return NO_ERROR;
    return NO_ERROR;
}
}


@@ -815,6 +844,7 @@ void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report&
    out->setAll(report.all_sections());
    out->setAll(report.all_sections());
    out->setReceiverPkg(report.pkg());
    out->setReceiverPkg(report.pkg());
    out->setReceiverCls(report.cls());
    out->setReceiverCls(report.cls());
    out->setGzip(report.gzip());


    const int sectionCount = report.section_size();
    const int sectionCount = report.section_size();
    for (int i = 0; i < sectionCount; i++) {
    for (int i = 0; i < sectionCount; i++) {
+84 −23
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@


#include "incidentd_util.h"
#include "incidentd_util.h"


#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/prctl.h>
#include <wait.h>
#include <wait.h>


@@ -64,28 +65,52 @@ unique_fd& Fpipe::readFd() { return mRead; }


unique_fd& Fpipe::writeFd() { return mWrite; }
unique_fd& Fpipe::writeFd() { return mWrite; }


pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) {
pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output, int* status) {
    // fork used in multithreaded environment, avoid adding unnecessary code in child process
    int in = -1;
    if (input != nullptr) {
        in = input->readFd().release();
        // Auto close write end of the input pipe on exec to prevent leaking fd in child process
        fcntl(input->writeFd().get(), F_SETFD, FD_CLOEXEC);
    }
    int out = output->writeFd().release();
    // Auto close read end of the output pipe on exec
    fcntl(output->readFd().get(), F_SETFD, FD_CLOEXEC);
    return fork_execute_cmd(argv, in, out, status);
}

pid_t fork_execute_cmd(char* const argv[], int in, int out, int* status) {
    int dummy_status = 0;
    if (status == nullptr) {
        status = &dummy_status;
    }
    *status = 0;
    pid_t pid = fork();
    pid_t pid = fork();
    if (pid < 0) {
        *status = -errno;
        return -1;
    }
    if (pid == 0) {
    if (pid == 0) {
        if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 ||
        // In child
                              !input->close())) {
        if (in >= 0 && (TEMP_FAILURE_RETRY(dup2(in, STDIN_FILENO)) < 0 || close(in))) {
            ALOGW("Failed to dup2 stdin.");
            ALOGW("Failed to dup2 stdin.");
            _exit(EXIT_FAILURE);
            _exit(EXIT_FAILURE);
        }
        }
        if (TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
        if (TEMP_FAILURE_RETRY(dup2(out, STDOUT_FILENO)) < 0 || close(out)) {
            !output->close()) {
            ALOGW("Failed to dup2 stdout.");
            ALOGW("Failed to dup2 stdout.");
            _exit(EXIT_FAILURE);
            _exit(EXIT_FAILURE);
        }
        }
        /* make sure the child dies when incidentd dies */
        // Make sure the child dies when incidentd dies
        prctl(PR_SET_PDEATHSIG, SIGKILL);
        prctl(PR_SET_PDEATHSIG, SIGKILL);
        execvp(argv[0], argv);
        execvp(argv[0], argv);
        _exit(errno);  // always exits with failure if any
        _exit(errno);  // always exits with failure if any
    }
    }
    // close the fds used in child process.
    // In parent
    if (input != NULL) input->readFd().reset();
    if ((in >= 0 && close(in) < 0) || close(out) < 0) {
    output->writeFd().reset();
        ALOGW("Failed to close pd. Killing child process");
        *status = -errno;
        kill_child(pid);
        return -1;
    }
    return pid;
    return pid;
}
}


@@ -120,9 +145,6 @@ uint64_t Nanotime() {
}
}


// ================================================================================
// ================================================================================
const int WAIT_MAX = 5;
const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};

static status_t statusCode(int status) {
static status_t statusCode(int status) {
    if (WIFSIGNALED(status)) {
    if (WIFSIGNALED(status)) {
        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
@@ -134,26 +156,65 @@ static status_t statusCode(int status) {
    return NO_ERROR;
    return NO_ERROR;
}
}


static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
    sigset_t child_mask, old_mask;
    sigemptyset(&child_mask);
    sigaddset(&child_mask, SIGCHLD);

    if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
        ALOGW("sigprocmask failed: %s", strerror(errno));
        return false;
    }

    timespec ts;
    ts.tv_sec = timeout_ms / 1000;
    ts.tv_nsec = (timeout_ms % 1000) * 1000000;
    int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts));
    int saved_errno = errno;

    // Set the signals back the way they were.
    if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
        ALOGW("sigprocmask failed: %s", strerror(errno));
        if (ret == 0) {
            return false;
        }
    }
    if (ret == -1) {
        errno = saved_errno;
        if (errno == EAGAIN) {
            errno = ETIMEDOUT;
        } else {
            ALOGW("sigtimedwait failed: %s", strerror(errno));
        }
        return false;
    }

    pid_t child_pid = waitpid(pid, status, WNOHANG);
    if (child_pid == pid) {
        return true;
    }
    if (child_pid == -1) {
        ALOGW("waitpid failed: %s", strerror(errno));
    } else {
        ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid);
    }
    return false;
}

status_t kill_child(pid_t pid) {
status_t kill_child(pid_t pid) {
    int status;
    int status;
    VLOG("try to kill child process %d", pid);
    kill(pid, SIGKILL);
    kill(pid, SIGKILL);
    if (waitpid(pid, &status, 0) == -1) return -1;
    if (waitpid(pid, &status, 0) == -1) return -1;
    return statusCode(status);
    return statusCode(status);
}
}


status_t wait_child(pid_t pid) {
status_t wait_child(pid_t pid, int timeout_ms) {
    int status;
    int status;
    bool died = false;
    if (waitpid_with_timeout(pid, timeout_ms, &status)) {
    // wait for child to report status up to 1 seconds
    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
        // sleep for 0.2 second
        nanosleep(&WAIT_INTERVAL_NS, NULL);
    }
    if (!died) return kill_child(pid);
        return statusCode(status);
        return statusCode(status);
    }
    }
    return kill_child(pid);
}


}  // namespace incidentd
}  // namespace incidentd
}  // namespace os
}  // namespace os
Loading