1 /*
2  * Copyright 2022, 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 #include <sys/epoll.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 #include <array>
22 #include <set>
23 #include <string>
24 
25 #include <android/os/DropBoxManager.h>
26 
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/properties.h>
30 #include <android-base/strings.h>
31 
32 #include "dmesg_parser.h"
33 
34 // If there are too many reports, the device is horribly broken.
35 const unsigned int kMaxReports = 10;
36 
37 const char kSentPath[] = "/data/misc/dmesgd/sent_reports.txt";
38 
ReadSentReports()39 static std::set<std::string> ReadSentReports() {
40     std::set<std::string> ret;
41     std::string content;
42     if (!android::base::ReadFileToString(kSentPath, &content)) {
43         PLOG(ERROR) << kSentPath << " is empty";
44         return ret;
45     }
46     auto lines = android::base::Split(content, "\n");
47     for (auto line : lines) {
48         ret.insert(line);
49     }
50     LOG(ERROR) << "Read " << ret.size() << " records from " << kSentPath;
51     return ret;
52 }
53 
WriteSentReports(std::set<std::string> reports)54 static void WriteSentReports(std::set<std::string> reports) {
55     if (!android::base::WriteStringToFile(android::base::Join(reports, ""), kSentPath)) {
56         PLOG(ERROR) << "Failed to write to " << kSentPath;
57     }
58 }
59 
60 const char kUnknown[] = "UNKNOWN";
61 
GetOneBootHeader(const std::string & pretty,const std::string & pname)62 static std::string GetOneBootHeader(const std::string& pretty, const std::string& pname) {
63     return pretty + ": " + android::base::GetProperty(pname, kUnknown) + "\n";
64 };
65 
GetBootHeaders()66 static std::string GetBootHeaders() {
67     std::string ret = GetOneBootHeader("Build", "ro.build.fingerprint");
68     ret += GetOneBootHeader("Hardware", "ro.product.board");
69     ret += GetOneBootHeader("Revision", "ro.revision");
70     ret += GetOneBootHeader("Bootloader", "ro.bootloader");
71     ret += GetOneBootHeader("Radio", "gsm.version.baseband");
72 
73     std::string version;
74     if (!android::base::ReadFileToString("/proc/version", &version)) version = kUnknown;
75     ret += "Kernel: " + version + "\n\n";
76     return ret;
77 }
78 
StoreReport(const std::string & tag,const std::string & report)79 static bool StoreReport(const std::string& tag, const std::string& report) {
80     std::string boot_headers = GetBootHeaders();
81     android::sp<android::os::DropBoxManager> dropbox(new android::os::DropBoxManager());
82     auto status = dropbox->addText(android::String16(tag.c_str()), boot_headers + report);
83     if (!status.isOk()) {
84         LOG(ERROR) << "Dropbox failed";
85         return false;
86     }
87     return true;
88 }
89 
ProcessDmesg(std::set<std::string> & sent_reports)90 static int ProcessDmesg(std::set<std::string> &sent_reports) {
91     std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dmesg", "r"), pclose);
92     if (!pipe) {
93         PLOG(ERROR) << "popen() failed!";
94         return 1;
95     }
96     dmesg_parser::DmesgParser dmesg_parser;
97 
98     char* buffer = NULL;
99     size_t buffer_size = 0;
100     while (getline(&buffer, &buffer_size, pipe.get()) != -1) {
101         std::string line(buffer);
102         if (line.back() != '\n') line += "\n";
103         dmesg_parser.ProcessLine(line);
104         if (dmesg_parser.ReportReady()) {
105             std::string tag = "SYSTEM_" + dmesg_parser.ReportType() + "_ERROR_REPORT";
106             std::string title = dmesg_parser.ReportTitle();
107             if ((sent_reports.find(title) == sent_reports.end()) &&
108                 (sent_reports.size() < kMaxReports)) {
109                 if (StoreReport(tag, dmesg_parser.FlushReport())) sent_reports.insert(title);
110             }
111         }
112     }
113     free(buffer);
114     return 0;
115 }
116 
main(int,char * [])117 int main(int, char*[]) {
118     auto sent_reports = ReadSentReports();
119     int result = ProcessDmesg(sent_reports);
120     WriteSentReports(sent_reports);
121     return result;
122 }
123