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