Home | History | Annotate | Download | only in dumpstate
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "dumpstate"
     18 
     19 #include "DumpstateUtil.h"
     20 
     21 #include <dirent.h>
     22 #include <fcntl.h>
     23 #include <sys/prctl.h>
     24 #include <sys/wait.h>
     25 #include <unistd.h>
     26 
     27 #include <vector>
     28 
     29 #include <android-base/file.h>
     30 #include <android-base/properties.h>
     31 #include <android-base/stringprintf.h>
     32 #include <android-base/strings.h>
     33 #include <cutils/log.h>
     34 
     35 #include "DumpstateInternal.h"
     36 
     37 namespace android {
     38 namespace os {
     39 namespace dumpstate {
     40 
     41 namespace {
     42 
     43 static constexpr const char* kSuPath = "/system/xbin/su";
     44 
     45 static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
     46     sigset_t child_mask, old_mask;
     47     sigemptyset(&child_mask);
     48     sigaddset(&child_mask, SIGCHLD);
     49 
     50     if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
     51         printf("*** sigprocmask failed: %s\n", strerror(errno));
     52         return false;
     53     }
     54 
     55     timespec ts;
     56     ts.tv_sec = timeout_seconds;
     57     ts.tv_nsec = 0;
     58     int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
     59     int saved_errno = errno;
     60     // Set the signals back the way they were.
     61     if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
     62         printf("*** sigprocmask failed: %s\n", strerror(errno));
     63         if (ret == 0) {
     64             return false;
     65         }
     66     }
     67     if (ret == -1) {
     68         errno = saved_errno;
     69         if (errno == EAGAIN) {
     70             errno = ETIMEDOUT;
     71         } else {
     72             printf("*** sigtimedwait failed: %s\n", strerror(errno));
     73         }
     74         return false;
     75     }
     76 
     77     pid_t child_pid = waitpid(pid, status, WNOHANG);
     78     if (child_pid != pid) {
     79         if (child_pid != -1) {
     80             printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
     81         } else {
     82             printf("*** waitpid failed: %s\n", strerror(errno));
     83         }
     84         return false;
     85     }
     86     return true;
     87 }
     88 }  // unnamed namespace
     89 
     90 CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
     91 CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
     92 
     93 CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
     94 }
     95 
     96 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
     97     values.always_ = true;
     98     return *this;
     99 }
    100 
    101 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
    102     values.account_mode_ = SU_ROOT;
    103     return *this;
    104 }
    105 
    106 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
    107     values.account_mode_ = DROP_ROOT;
    108     return *this;
    109 }
    110 
    111 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
    112     values.output_mode_ = REDIRECT_TO_STDERR;
    113     return *this;
    114 }
    115 
    116 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
    117     const std::string& message) {
    118     values.logging_message_ = message;
    119     return *this;
    120 }
    121 
    122 CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
    123     return CommandOptions(values);
    124 }
    125 
    126 CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
    127     : timeout_(timeout),
    128       always_(false),
    129       account_mode_(DONT_DROP_ROOT),
    130       output_mode_(NORMAL_OUTPUT),
    131       logging_message_("") {
    132 }
    133 
    134 CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
    135 }
    136 
    137 int64_t CommandOptions::Timeout() const {
    138     return values.timeout_;
    139 }
    140 
    141 bool CommandOptions::Always() const {
    142     return values.always_;
    143 }
    144 
    145 PrivilegeMode CommandOptions::PrivilegeMode() const {
    146     return values.account_mode_;
    147 }
    148 
    149 OutputMode CommandOptions::OutputMode() const {
    150     return values.output_mode_;
    151 }
    152 
    153 std::string CommandOptions::LoggingMessage() const {
    154     return values.logging_message_;
    155 }
    156 
    157 CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
    158     return CommandOptions::CommandOptionsBuilder(timeout);
    159 }
    160 
    161 std::string PropertiesHelper::build_type_ = "";
    162 int PropertiesHelper::dry_run_ = -1;
    163 
    164 bool PropertiesHelper::IsUserBuild() {
    165     if (build_type_.empty()) {
    166         build_type_ = android::base::GetProperty("ro.build.type", "user");
    167     }
    168     return "user" == build_type_;
    169 }
    170 
    171 bool PropertiesHelper::IsDryRun() {
    172     if (dry_run_ == -1) {
    173         dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0;
    174     }
    175     return dry_run_ == 1;
    176 }
    177 
    178 int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
    179     int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
    180     if (fd < 0) {
    181         int err = errno;
    182         if (title.empty()) {
    183             dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
    184         } else {
    185             dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
    186                     strerror(err));
    187         }
    188         fsync(out_fd);
    189         return -1;
    190     }
    191     return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
    192 }
    193 
    194 int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
    195                    const CommandOptions& options) {
    196     if (full_command.empty()) {
    197         MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
    198         return -1;
    199     }
    200 
    201     int size = full_command.size() + 1;  // null terminated
    202     int starting_index = 0;
    203     if (options.PrivilegeMode() == SU_ROOT) {
    204         starting_index = 2;  // "su" "root"
    205         size += starting_index;
    206     }
    207 
    208     std::vector<const char*> args;
    209     args.resize(size);
    210 
    211     std::string command_string;
    212     if (options.PrivilegeMode() == SU_ROOT) {
    213         args[0] = kSuPath;
    214         command_string += kSuPath;
    215         args[1] = "root";
    216         command_string += " root ";
    217     }
    218     for (size_t i = 0; i < full_command.size(); i++) {
    219         args[i + starting_index] = full_command[i].data();
    220         command_string += args[i + starting_index];
    221         if (i != full_command.size() - 1) {
    222             command_string += " ";
    223         }
    224     }
    225     args[size - 1] = nullptr;
    226 
    227     const char* command = command_string.c_str();
    228 
    229     if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
    230         dprintf(fd, "Skipping '%s' on user build.\n", command);
    231         return 0;
    232     }
    233 
    234     if (!title.empty()) {
    235         dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
    236         fsync(fd);
    237     }
    238 
    239     const std::string& logging_message = options.LoggingMessage();
    240     if (!logging_message.empty()) {
    241         MYLOGI(logging_message.c_str(), command_string.c_str());
    242     }
    243 
    244     bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
    245     bool redirecting_to_fd = STDOUT_FILENO != fd;
    246 
    247     if (PropertiesHelper::IsDryRun() && !options.Always()) {
    248         if (!title.empty()) {
    249             dprintf(fd, "\t(skipped on dry run)\n");
    250         } else if (redirecting_to_fd) {
    251             // There is no title, but we should still print a dry-run message
    252             dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
    253         }
    254         fsync(fd);
    255         return 0;
    256     }
    257 
    258     const char* path = args[0];
    259 
    260     uint64_t start = Nanotime();
    261     pid_t pid = fork();
    262 
    263     /* handle error case */
    264     if (pid < 0) {
    265         if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
    266         MYLOGE("*** fork: %s\n", strerror(errno));
    267         return pid;
    268     }
    269 
    270     /* handle child case */
    271     if (pid == 0) {
    272         if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
    273             if (!silent) {
    274                 dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
    275                         strerror(errno));
    276             }
    277             MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
    278             return -1;
    279         }
    280 
    281         if (silent) {
    282             // Redirects stdout to stderr
    283             TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
    284         } else if (redirecting_to_fd) {
    285             // Redirect stdout to fd
    286             TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
    287             close(fd);
    288         }
    289 
    290         /* make sure the child dies when dumpstate dies */
    291         prctl(PR_SET_PDEATHSIG, SIGKILL);
    292 
    293         /* just ignore SIGPIPE, will go down with parent's */
    294         struct sigaction sigact;
    295         memset(&sigact, 0, sizeof(sigact));
    296         sigact.sa_handler = SIG_IGN;
    297         sigaction(SIGPIPE, &sigact, NULL);
    298 
    299         execvp(path, (char**)args.data());
    300         // execvp's result will be handled after waitpid_with_timeout() below, but
    301         // if it failed, it's safer to exit dumpstate.
    302         MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
    303         // Must call _exit (instead of exit), otherwise it will corrupt the zip
    304         // file.
    305         _exit(EXIT_FAILURE);
    306     }
    307 
    308     /* handle parent case */
    309     int status;
    310     bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
    311     fsync(fd);
    312 
    313     uint64_t elapsed = Nanotime() - start;
    314     if (!ret) {
    315         if (errno == ETIMEDOUT) {
    316             if (!silent)
    317                 dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
    318                         static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
    319             MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
    320                    static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
    321         } else {
    322             if (!silent)
    323                 dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
    324                         static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
    325             MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
    326                    static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
    327         }
    328         kill(pid, SIGTERM);
    329         if (!waitpid_with_timeout(pid, 5, nullptr)) {
    330             kill(pid, SIGKILL);
    331             if (!waitpid_with_timeout(pid, 5, nullptr)) {
    332                 if (!silent)
    333                     dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
    334                             command, pid);
    335                 MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
    336             }
    337         }
    338         return -1;
    339     }
    340 
    341     if (WIFSIGNALED(status)) {
    342         if (!silent)
    343             dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
    344         MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
    345     } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
    346         status = WEXITSTATUS(status);
    347         if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
    348         MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
    349     }
    350 
    351     return status;
    352 }
    353 
    354 int GetPidByName(const std::string& ps_name) {
    355     DIR* proc_dir;
    356     struct dirent* ps;
    357     unsigned int pid;
    358     std::string cmdline;
    359 
    360     if (!(proc_dir = opendir("/proc"))) {
    361         MYLOGE("Can't open /proc\n");
    362         return -1;
    363     }
    364 
    365     while ((ps = readdir(proc_dir))) {
    366         if (!(pid = atoi(ps->d_name))) {
    367             continue;
    368         }
    369         android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline);
    370         if (cmdline.find(ps_name) == std::string::npos) {
    371             continue;
    372         } else {
    373             closedir(proc_dir);
    374             return pid;
    375         }
    376     }
    377     MYLOGE("can't find the pid\n");
    378     closedir(proc_dir);
    379     return -1;
    380 }
    381 
    382 }  // namespace dumpstate
    383 }  // namespace os
    384 }  // namespace android
    385