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 "DumpstateInternal.h"
     20 
     21 #include <stdint.h>
     22 #include <stdio.h>
     23 #include <string.h>
     24 #include <sys/capability.h>
     25 #include <sys/prctl.h>
     26 #include <sys/stat.h>
     27 #include <sys/types.h>
     28 
     29 #include <cstdint>
     30 #include <string>
     31 #include <vector>
     32 
     33 #include <android-base/file.h>
     34 #include <cutils/log.h>
     35 #include <private/android_filesystem_config.h>
     36 
     37 uint64_t Nanotime() {
     38     timespec ts;
     39     clock_gettime(CLOCK_MONOTONIC, &ts);
     40     return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
     41 }
     42 
     43 // Switches to non-root user and group.
     44 bool DropRootUser() {
     45     if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
     46         MYLOGD("drop_root_user(): already running as Shell\n");
     47         return true;
     48     }
     49     /* ensure we will keep capabilities when we drop root */
     50     if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
     51         MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
     52         return false;
     53     }
     54 
     55     gid_t groups[] = {AID_LOG,  AID_SDCARD_R,     AID_SDCARD_RW, AID_MOUNT,
     56                       AID_INET, AID_NET_BW_STATS, AID_READPROC,  AID_BLUETOOTH};
     57     if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
     58         MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
     59         return false;
     60     }
     61     if (setgid(AID_SHELL) != 0) {
     62         MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
     63         return false;
     64     }
     65     if (setuid(AID_SHELL) != 0) {
     66         MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
     67         return false;
     68     }
     69 
     70     struct __user_cap_header_struct capheader;
     71     struct __user_cap_data_struct capdata[2];
     72     memset(&capheader, 0, sizeof(capheader));
     73     memset(&capdata, 0, sizeof(capdata));
     74     capheader.version = _LINUX_CAPABILITY_VERSION_3;
     75     capheader.pid = 0;
     76 
     77     capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
     78     capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
     79     capdata[0].inheritable = 0;
     80     capdata[1].inheritable = 0;
     81 
     82     if (capset(&capheader, &capdata[0]) < 0) {
     83         MYLOGE("capset failed: %s\n", strerror(errno));
     84         return false;
     85     }
     86 
     87     return true;
     88 }
     89 
     90 int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
     91                        bool dry_run) {
     92     const char* path = path_string.c_str();
     93     if (!title.empty()) {
     94         dprintf(out_fd, "------ %s (%s", title.c_str(), path);
     95 
     96         struct stat st;
     97         // Only show the modification time of non-device files.
     98         size_t path_len = strlen(path);
     99         if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
    100             (path_len < 5 || memcmp(path, "/sys/", 5)) &&
    101             (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
    102             char stamp[80];
    103             time_t mtime = st.st_mtime;
    104             strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
    105             dprintf(out_fd, ": %s", stamp);
    106         }
    107         dprintf(out_fd, ") ------\n");
    108         fsync(out_fd);
    109     }
    110     if (dry_run) {
    111         if (out_fd != STDOUT_FILENO) {
    112             // There is no title, but we should still print a dry-run message
    113             dprintf(out_fd, "%s: skipped on dry run\n", path);
    114         } else if (!title.empty()) {
    115             dprintf(out_fd, "\t(skipped on dry run)\n");
    116         }
    117         fsync(out_fd);
    118         return 0;
    119     }
    120     bool newline = false;
    121     fd_set read_set;
    122     timeval tm;
    123     while (true) {
    124         FD_ZERO(&read_set);
    125         FD_SET(fd, &read_set);
    126         /* Timeout if no data is read for 30 seconds. */
    127         tm.tv_sec = 30;
    128         tm.tv_usec = 0;
    129         uint64_t elapsed = Nanotime();
    130         int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
    131         if (ret == -1) {
    132             dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
    133             newline = true;
    134             break;
    135         } else if (ret == 0) {
    136             elapsed = Nanotime() - elapsed;
    137             dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
    138             newline = true;
    139             break;
    140         } else {
    141             char buffer[65536];
    142             ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
    143             if (bytes_read > 0) {
    144                 android::base::WriteFully(out_fd, buffer, bytes_read);
    145                 newline = (buffer[bytes_read - 1] == '\n');
    146             } else {
    147                 if (bytes_read == -1) {
    148                     dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
    149                     newline = true;
    150                 }
    151                 break;
    152             }
    153         }
    154     }
    155     close(fd);
    156 
    157     if (!newline) dprintf(out_fd, "\n");
    158     if (!title.empty()) dprintf(out_fd, "\n");
    159     return 0;
    160 }
    161