1 /*
2  * Copyright 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_device"
18 #define ATRACE_TAG ATRACE_TAG_ALWAYS
19 
20 #include <inttypes.h>
21 
22 #include <android-base/file.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/properties.h>
25 #include <android-base/unique_fd.h>
26 #include <cutils/trace.h>
27 #include <log/log.h>
28 #include <sys/stat.h>
29 #include <dump/pixel_dump.h>
30 #include "Dumpstate.h"
31 
32 #include "DumpstateUtil.h"
33 
34 #define HW_REVISION "ro.boot.hardware.revision"
35 
36 using android::os::dumpstate::CommandOptions;
37 using android::os::dumpstate::DumpFileToFd;
38 using android::os::dumpstate::PropertiesHelper;
39 using android::os::dumpstate::RunCommandToFd;
40 
41 namespace aidl {
42 namespace android {
43 namespace hardware {
44 namespace dumpstate {
45 
46 typedef std::chrono::time_point<std::chrono::steady_clock> timepoint_t;
47 
48 const char kVerboseLoggingProperty[] = "persist.vendor.verbose_logging_enabled";
49 
startSection(int fd,const std::string & sectionName)50 timepoint_t startSection(int fd, const std::string &sectionName) {
51     ATRACE_BEGIN(sectionName.c_str());
52     ::android::base::WriteStringToFd(
53             "\n"
54             "------ Section start: " + sectionName + " ------\n"
55             "\n", fd);
56     return std::chrono::steady_clock::now();
57 }
58 
endSection(int fd,const std::string & sectionName,timepoint_t startTime)59 void endSection(int fd, const std::string &sectionName, timepoint_t startTime) {
60     ATRACE_END();
61     auto endTime = std::chrono::steady_clock::now();
62     auto elapsedMsec = std::chrono::duration_cast<std::chrono::milliseconds>
63             (endTime - startTime).count();
64 
65     ::android::base::WriteStringToFd(
66             "\n"
67             "------ Section end: " + sectionName + " ------\n"
68             "Elapsed msec: " + std::to_string(elapsedMsec) + "\n"
69             "\n", fd);
70 }
71 
72 // Dump data requested by an argument to the "dump" interface, or help info
73 // if the specified section is not supported.
dumpTextSection(int fd,const std::string & sectionName)74 void Dumpstate::dumpTextSection(int fd, const std::string &sectionName) {
75     bool dumpAll = (sectionName == kAllSections);
76     std::string dumpFiles;
77     struct dirent **dirent_list = NULL;
78     int num_entries = scandir("/vendor/bin/dump", &dirent_list, 0, (int (*)(const struct dirent **, const struct dirent **)) alphasort);
79     if (!dirent_list) {
80         ALOGE("Unable to scan dir: /vendor/bin/dump\n");
81         return;
82     } else if (num_entries <= 0) {
83         ALOGE("No file is found.\n");
84         return;
85     }
86     // Execute all or designated programs under vendor/bin/dump/
87     for (int i = 0; i <  num_entries; i++) {
88         if (dirent_list[i]->d_name[0] == '.') {
89             continue;
90         }
91         std::string bin(dirent_list[i]->d_name);
92         dumpFiles = dumpFiles + " " + bin;
93         if (dumpAll || sectionName == bin) {
94             auto startTime = startSection(fd, bin);
95             RunCommandToFd(fd, "/vendor/bin/dump/"+bin, {"/vendor/bin/dump/"+bin}, CommandOptions::WithTimeout(15).Build());
96             endSection(fd, bin, startTime);
97             if (!dumpAll) {
98                 return;
99             }
100         }
101     }
102 
103     if (dumpAll) {
104         RunCommandToFd(fd, "VENDOR PROPERTIES", {"/vendor/bin/getprop"});
105         return;
106     }
107 
108     // An unsupported section was requested on the command line
109     ::android::base::WriteStringToFd("Unrecognized text section: " + sectionName + "\n", fd);
110     ::android::base::WriteStringToFd("Try \"" + kAllSections + "\" or one of the following:", fd);
111     ::android::base::WriteStringToFd(dumpFiles, fd);
112     ::android::base::WriteStringToFd("\nNote: sections with attachments (e.g. dump_soc) are"
113                                    "not available from the command line.\n", fd);
114     while (num_entries--) {
115         free(dirent_list[num_entries]);
116     }
117     free(dirent_list);
118 }
119 
dumpLogSection(int fd,int fd_bin)120 void Dumpstate::dumpLogSection(int fd, int fd_bin)
121 {
122     std::string logDir = MODEM_LOG_DIRECTORY;
123     const std::string logCombined = logDir + "/combined_logs.tar";
124     const std::string logAllDir = logDir + "/all_logs";
125 
126     RunCommandToFd(fd, "MKDIR LOG", {"/vendor/bin/mkdir", "-p", logAllDir.c_str()}, CommandOptions::WithTimeout(2).Build());
127 
128     dumpTextSection(fd, kAllSections);
129 
130     RunCommandToFd(fd, "TAR LOG", {"/vendor/bin/tar", "cvf", logCombined.c_str(), "-C", logAllDir.c_str(), "."}, CommandOptions::WithTimeout(20).Build());
131     RunCommandToFd(fd, "CHG PERM", {"/vendor/bin/chmod", "a+w", logCombined.c_str()}, CommandOptions::WithTimeout(2).Build());
132 
133     std::vector<uint8_t> buffer(65536);
134     ::android::base::unique_fd fdLog(TEMP_FAILURE_RETRY(open(logCombined.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK)));
135 
136     if (fdLog >= 0) {
137         while (1) {
138             ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fdLog, buffer.data(), buffer.size()));
139 
140             if (bytes_read == 0) {
141                 break;
142             } else if (bytes_read < 0) {
143                 ALOGD("read(%s): %s\n", logCombined.c_str(), strerror(errno));
144                 break;
145             }
146 
147             ssize_t result = TEMP_FAILURE_RETRY(write(fd_bin, buffer.data(), bytes_read));
148 
149             if (result != bytes_read) {
150                 ALOGD("Failed to write %zd bytes, actually written: %zd", bytes_read, result);
151                 break;
152             }
153         }
154     }
155 
156     RunCommandToFd(fd, "RM LOG DIR", { "/vendor/bin/rm", "-r", logAllDir.c_str()}, CommandOptions::WithTimeout(2).Build());
157     RunCommandToFd(fd, "RM LOG", { "/vendor/bin/rm", logCombined.c_str()}, CommandOptions::WithTimeout(2).Build());
158 }
159 
dumpstateBoard(const std::vector<::ndk::ScopedFileDescriptor> & in_fds,IDumpstateDevice::DumpstateMode in_mode,int64_t in_timeoutMillis)160 ndk::ScopedAStatus Dumpstate::dumpstateBoard(const std::vector<::ndk::ScopedFileDescriptor>& in_fds,
161                                              IDumpstateDevice::DumpstateMode in_mode,
162                                              int64_t in_timeoutMillis) {
163     ATRACE_BEGIN("dumpstateBoard");
164     // Unused arguments.
165     (void) in_timeoutMillis;
166 
167     if (in_mode < IDumpstateDevice::DumpstateMode::FULL || in_mode > IDumpstateDevice::DumpstateMode::PROTO) {
168         ALOGE("Invalid mode: %d\n", in_mode);
169         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, "Invalid mode");
170     }
171 
172     if (in_fds.size() < 1) {
173         ALOGE("no FDs\n");
174         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
175                                                                 "No file descriptor");
176     }
177 
178     int fd = in_fds[0].get();
179     if (fd < 0) {
180         ALOGE("invalid FD: %d\n", fd);
181         return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
182                                                                 "Invalid file descriptor");
183     }
184 
185     if (in_fds.size() < 2) {
186           ALOGE("no FD for dumpstate_board binary\n");
187           dumpTextSection(fd, "");
188     } else {
189           int fd_bin = in_fds[1].get();
190           dumpLogSection(fd, fd_bin);
191     }
192 
193     ATRACE_END();
194     return ndk::ScopedAStatus::ok();
195 }
196 
setVerboseLoggingEnabled(bool in_enable)197 ndk::ScopedAStatus Dumpstate::setVerboseLoggingEnabled(bool in_enable) {
198     ::android::base::SetProperty(kVerboseLoggingProperty, in_enable ? "true" : "false");
199     return ndk::ScopedAStatus::ok();
200 }
201 
getVerboseLoggingEnabled(bool * _aidl_return)202 ndk::ScopedAStatus Dumpstate::getVerboseLoggingEnabled(bool* _aidl_return) {
203     *_aidl_return = ::android::base::GetBoolProperty(kVerboseLoggingProperty, false);
204     return ndk::ScopedAStatus::ok();
205 }
206 
207 // Since AIDLs that support the dump() interface are automatically invoked during
208 // bugreport generation and we don't want to generate a second copy of the same
209 // data that will go into dumpstate_board.txt, this function will only do
210 // something if it is called with an option, e.g.
211 //   dumpsys android.hardware.dumpstate.IDumpstateDevice/default all
212 //
213 // Also, note that sections which generate attachments and/or binary data when
214 // included in a bugreport are not available through the dump() interface.
dump(int fd,const char ** args,uint32_t numArgs)215 binder_status_t Dumpstate::dump(int fd, const char** args, uint32_t numArgs) {
216 
217     if (numArgs != 1) {
218         return STATUS_OK;
219     }
220 
221     dumpTextSection(fd, static_cast<std::string>(args[0]));
222 
223     fsync(fd);
224     return STATUS_OK;
225 }
226 
227 }  // namespace dumpstate
228 }  // namespace hardware
229 }  // namespace android
230 }  // namespace aidl
231