diff options
-rw-r--r-- | cmds/incidentd/src/Section.cpp | 78 | ||||
-rw-r--r-- | cmds/incidentd/src/incidentd_util.cpp | 60 | ||||
-rw-r--r-- | cmds/incidentd/src/incidentd_util.h | 16 | ||||
-rw-r--r-- | cmds/incidentd/tests/Section_test.cpp | 8 | ||||
-rw-r--r-- | core/proto/android/os/incident.proto | 10 |
5 files changed, 93 insertions, 79 deletions
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 5065a56078d0..b59a32c60632 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -20,7 +20,6 @@ #include <dirent.h> #include <errno.h> -#include <wait.h> #include <mutex> #include <set> @@ -53,49 +52,15 @@ const int FIELD_ID_INCIDENT_HEADER = 1; const int FIELD_ID_INCIDENT_METADATA = 2; // incident section parameters -const int WAIT_MAX = 5; -const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; const char INCIDENT_HELPER[] = "/system/bin/incident_helper"; -const char GZIP[] = "/system/bin/gzip"; +const char* GZIP[] = {"/system/bin/gzip", NULL}; static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) { const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL}; - return fork_execute_cmd(INCIDENT_HELPER, const_cast<char**>(ihArgs), p2cPipe, c2pPipe); + return fork_execute_cmd(const_cast<char**>(ihArgs), p2cPipe, c2pPipe); } // ================================================================================ -static status_t statusCode(int status) { - if (WIFSIGNALED(status)) { - VLOG("return by signal: %s", strerror(WTERMSIG(status))); - return -WTERMSIG(status); - } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { - VLOG("return by exit: %s", strerror(WEXITSTATUS(status))); - return -WEXITSTATUS(status); - } - return NO_ERROR; -} - -static status_t kill_child(pid_t pid) { - int status; - VLOG("try to kill child process %d", pid); - kill(pid, SIGKILL); - if (waitpid(pid, &status, 0) == -1) return -1; - return statusCode(status); -} - -static status_t wait_child(pid_t pid) { - int status; - bool died = false; - // 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); -} -// ================================================================================ static status_t write_section_header(int fd, int sectionId, size_t size) { uint8_t buf[20]; uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size); @@ -328,12 +293,15 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { } // ================================================================================ GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) { - name = "gzip "; - name += filename; va_list args; va_start(args, filename); mFilenames = varargs(filename, args); va_end(args); + name = "gzip"; + for (int i = 0; mFilenames[i] != NULL; i++) { + name += " "; + name += mFilenames[i]; + } } GZipSection::~GZipSection() {} @@ -362,8 +330,7 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { return -errno; } - const char* gzipArgs[]{GZIP, NULL}; - pid_t pid = fork_execute_cmd(GZIP, const_cast<char**>(gzipArgs), &p2cPipe, &c2pPipe); + pid_t pid = fork_execute_cmd((char* const*)GZIP, &p2cPipe, &c2pPipe); if (pid == -1) { ALOGW("GZipSection '%s' failed to fork", this->name.string()); return -errno; @@ -559,19 +526,27 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { // ================================================================================ CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...) : Section(id, timeoutMs) { - name = command; va_list args; va_start(args, command); mCommand = varargs(command, args); va_end(args); + name = "cmd"; + for (int i = 0; mCommand[i] != NULL; i++) { + name += " "; + name += mCommand[i]; + } } CommandSection::CommandSection(int id, const char* command, ...) : Section(id) { - name = command; va_list args; va_start(args, command); mCommand = varargs(command, args); va_end(args); + name = "cmd"; + for (int i = 0; mCommand[i] != NULL; i++) { + name += " "; + name += mCommand[i]; + } } CommandSection::~CommandSection() { free(mCommand); } @@ -586,26 +561,11 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { return -errno; } - pid_t cmdPid = fork(); + pid_t cmdPid = fork_execute_cmd((char* const*)mCommand, NULL, &cmdPipe); if (cmdPid == -1) { ALOGW("CommandSection '%s' failed to fork", this->name.string()); return -errno; } - // child process to execute the command as root - if (cmdPid == 0) { - // replace command's stdout with ihPipe's write Fd - if (dup2(cmdPipe.writeFd().get(), STDOUT_FILENO) != 1 || !ihPipe.close() || - !cmdPipe.close()) { - ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), - strerror(errno)); - _exit(EXIT_FAILURE); - } - execvp(this->mCommand[0], (char* const*)this->mCommand); - int err = errno; // record command error code - ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), - strerror(errno)); - _exit(err); // exit with command error code - } pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe); if (ihPid == -1) { ALOGW("CommandSection '%s' failed to fork", this->name.string()); diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp index d7995133c722..7db1fa7d61df 100644 --- a/cmds/incidentd/src/incidentd_util.cpp +++ b/cmds/incidentd/src/incidentd_util.cpp @@ -19,6 +19,7 @@ #include "incidentd_util.h" #include <sys/prctl.h> +#include <wait.h> #include "section_list.h" @@ -57,27 +58,28 @@ unique_fd& Fpipe::readFd() { return mRead; } unique_fd& Fpipe::writeFd() { return mWrite; } -pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) { +pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) { // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - if (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 || !input->close() || - TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 || + VLOG("[In child]cmd %s", argv[0]); + if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 || + !input->close())) { + ALOGW("Failed to dup2 stdin."); + _exit(EXIT_FAILURE); + } + if (TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 || !output->close()) { - ALOGW("Can't setup stdin and stdout for command %s", cmd); + ALOGW("Failed to dup2 stdout."); _exit(EXIT_FAILURE); } - /* make sure the child dies when incidentd dies */ prctl(PR_SET_PDEATHSIG, SIGKILL); - - execv(cmd, argv); - - ALOGW("%s failed in the child process: %s", cmd, strerror(errno)); - _exit(EXIT_FAILURE); // always exits with failure if any + execvp(argv[0], argv); + _exit(errno); // always exits with failure if any } // close the fds used in child process. - input->readFd().reset(); + if (input != NULL) input->readFd().reset(); output->writeFd().reset(); return pid; } @@ -111,3 +113,39 @@ uint64_t Nanotime() { clock_gettime(CLOCK_MONOTONIC, &ts); return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec); } + +// ================================================================================ +const int WAIT_MAX = 5; +const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; + +static status_t statusCode(int status) { + if (WIFSIGNALED(status)) { + VLOG("return by signal: %s", strerror(WTERMSIG(status))); + return -WTERMSIG(status); + } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { + VLOG("return by exit: %s", strerror(WEXITSTATUS(status))); + return -WEXITSTATUS(status); + } + return NO_ERROR; +} + +status_t kill_child(pid_t pid) { + int status; + VLOG("try to kill child process %d", pid); + kill(pid, SIGKILL); + if (waitpid(pid, &status, 0) == -1) return -1; + return statusCode(status); +} + +status_t wait_child(pid_t pid) { + int status; + bool died = false; + // 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); +} diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h index 228d7762fc81..b5f6e21a9976 100644 --- a/cmds/incidentd/src/incidentd_util.h +++ b/cmds/incidentd/src/incidentd_util.h @@ -18,12 +18,15 @@ #ifndef INCIDENTD_UTIL_H #define INCIDENTD_UTIL_H -#include <android-base/unique_fd.h> - #include <stdarg.h> +#include <unistd.h> + +#include <android-base/unique_fd.h> +#include <utils/Errors.h> #include "Privacy.h" +using namespace android; using namespace android::base; /** @@ -52,8 +55,9 @@ private: /** * Forks and exec a command with two pipes, one connects stdin for input, * one connects stdout for output. It returns the pid of the child. + * Input pipe can be NULL to indicate child process doesn't read stdin. */ -pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output); +pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output); /** * Grabs varargs from stack and stores them in heap with NULL-terminated array. @@ -65,4 +69,10 @@ const char** varargs(const char* first, va_list rest); */ uint64_t Nanotime(); +/** + * Methods to wait or kill child process, return exit status code. + */ +status_t kill_child(pid_t pid); +status_t wait_child(pid_t pid); + #endif // INCIDENTD_UTIL_H diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 55192d02acd3..f93839b62dcf 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -173,12 +173,12 @@ TEST_F(SectionTest, CommandSectionConstructor) { CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL); CommandSection cs4(2, 43214, "single_command", NULL); - EXPECT_THAT(cs1.name.string(), StrEq("echo")); - EXPECT_THAT(cs2.name.string(), StrEq("single_command")); + EXPECT_THAT(cs1.name.string(), StrEq("cmd echo \"this is a test\" ooo")); + EXPECT_THAT(cs2.name.string(), StrEq("cmd single_command")); EXPECT_EQ(3123, cs3.timeoutMs); EXPECT_EQ(43214, cs4.timeoutMs); - EXPECT_THAT(cs3.name.string(), StrEq("echo")); - EXPECT_THAT(cs4.name.string(), StrEq("single_command")); + EXPECT_THAT(cs3.name.string(), StrEq("cmd echo \"this is a test\" ooo")); + EXPECT_THAT(cs4.name.string(), StrEq("cmd single_command")); } TEST_F(SectionTest, CommandSectionEcho) { diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 476d5fe45692..0fea0dc1c428 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -134,8 +134,14 @@ message IncidentProto { // Linux services optional ProcrankProto procrank = 2000 [ - (section).type = SECTION_NONE, // disable procrank until figure out permission - (section).args = "/system/xbin/procrank" + // Disable procrank for reasons below: + // 1. incidentd can't execute `procrank` because it don't have DAC perms + // since it is running as its own uid, no root access. + // 2. the same information is able to be accessed by meminfo dumpsys. + // 3. leave this one here to show case of how to disable a section + // (no removal allowed if you are familiar with PROTOBUF). + (section).type = SECTION_NONE, + (section).args = "procrank" ]; optional PageTypeInfoProto page_type_info = 2001 [ |