blob: 917b70d003327489131035d06bce868bab2183f4 [file] [log] [blame]
Joe Onorato1754d742016-11-21 17:51:35 -08001/*
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 "incidentd"
18
19#include "Reporter.h"
Joe Onorato1754d742016-11-21 17:51:35 -080020
21#include "report_directory.h"
22#include "section_list.h"
23
Joe Onorato1754d742016-11-21 17:51:35 -080024#include <android/os/DropBoxManager.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070025#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080026#include <utils/SystemClock.h>
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <dirent.h>
31#include <fcntl.h>
32#include <errno.h>
33
34/**
35 * The directory where the incident reports are stored.
36 */
Yi Jinadd11e92017-07-30 16:10:07 -070037static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
Joe Onorato1754d742016-11-21 17:51:35 -080038
Yi Jin0a3406f2017-06-22 19:23:11 -070039// ================================================================================
Joe Onorato1754d742016-11-21 17:51:35 -080040ReportRequest::ReportRequest(const IncidentReportArgs& a,
41 const sp<IIncidentReportStatusListener> &l, int f)
42 :args(a),
43 listener(l),
44 fd(f),
45 err(NO_ERROR)
46{
47}
48
49ReportRequest::~ReportRequest()
50{
51}
52
Yi Jinedfd5bb2017-09-06 17:09:11 -070053bool
54ReportRequest::ok()
55{
56 return fd >= 0 && err == NO_ERROR;
57}
58
Joe Onorato1754d742016-11-21 17:51:35 -080059// ================================================================================
60ReportRequestSet::ReportRequestSet()
61 :mRequests(),
Yi Jinadd11e92017-07-30 16:10:07 -070062 mSections(),
Joe Onorato1754d742016-11-21 17:51:35 -080063 mMainFd(-1)
64{
65}
66
67ReportRequestSet::~ReportRequestSet()
68{
69}
70
Yi Jinadd11e92017-07-30 16:10:07 -070071// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080072void
73ReportRequestSet::add(const sp<ReportRequest>& request)
74{
75 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070076 mSections.merge(request->args);
Joe Onorato1754d742016-11-21 17:51:35 -080077}
78
79void
80ReportRequestSet::setMainFd(int fd)
81{
82 mMainFd = fd;
Joe Onorato1754d742016-11-21 17:51:35 -080083}
84
Yi Jinadd11e92017-07-30 16:10:07 -070085bool
86ReportRequestSet::containsSection(int id) {
87 return mSections.containsSection(id);
88}
Joe Onorato1754d742016-11-21 17:51:35 -080089
90// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -070091Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
92
93Reporter::Reporter(const char* directory)
94 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -080095{
96 char buf[100];
97
98 // TODO: Make the max size smaller for user builds.
99 mMaxSize = 100 * 1024 * 1024;
100 mMaxCount = 100;
101
Yi Jinadd11e92017-07-30 16:10:07 -0700102 // string ends up with '/' is a directory
103 String8 dir = String8(directory);
104 if (directory[dir.size() - 1] != '/') dir += "/";
105 mIncidentDirectory = dir.string();
106
Joe Onorato1754d742016-11-21 17:51:35 -0800107 // There can't be two at the same time because it's on one thread.
108 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700109 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
110 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800111}
112
113Reporter::~Reporter()
114{
115}
116
117Reporter::run_report_status_t
118Reporter::runReport()
119{
120
121 status_t err = NO_ERROR;
122 bool needMainFd = false;
123 int mainFd = -1;
Yi Jinedfd5bb2017-09-06 17:09:11 -0700124 HeaderSection headers;
Joe Onorato1754d742016-11-21 17:51:35 -0800125
126 // See if we need the main file
127 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
128 if ((*it)->fd < 0 && mainFd < 0) {
129 needMainFd = true;
130 break;
131 }
132 }
133 if (needMainFd) {
134 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700135 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800136 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700137 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800138 }
139
140 // If there are too many files in the directory (for whatever reason),
141 // delete the oldest ones until it's under the limit. Doing this first
142 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700143 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800144
145 // Open the file.
146 err = create_file(&mainFd);
147 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700148 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800149 }
150
151 // Add to the set
152 batch.setMainFd(mainFd);
153 }
154
155 // Tell everyone that we're starting.
156 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
157 if ((*it)->listener != NULL) {
158 (*it)->listener->onReportStarted();
159 }
160 }
161
162 // Write the incident headers
Yi Jinedfd5bb2017-09-06 17:09:11 -0700163 headers.Execute(&batch);
Joe Onorato1754d742016-11-21 17:51:35 -0800164
165 // For each of the report fields, see if we need it, and if so, execute the command
166 // and report to those that care that we're doing it.
167 for (const Section** section=SECTION_LIST; *section; section++) {
168 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700169 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700170 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800171 // Notify listener of starting
172 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
173 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
174 (*it)->listener->onReportSectionStatus(id,
175 IIncidentReportStatusListener::STATUS_STARTING);
176 }
177 }
178
179 // Execute - go get the data and write it into the file descriptors.
180 err = (*section)->Execute(&batch);
181 if (err != NO_ERROR) {
Yi Jina5c5e8a2017-09-27 18:24:58 -0700182 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
183 (*section)->name.string(), id, strerror(-err));
Yi Jinedfd5bb2017-09-06 17:09:11 -0700184 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800185 }
186
187 // Notify listener of starting
188 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
189 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
190 (*it)->listener->onReportSectionStatus(id,
191 IIncidentReportStatusListener::STATUS_FINISHED);
192 }
193 }
Yi Jinf32af482017-08-11 15:00:49 -0700194 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800195 }
196 }
197
Yi Jinedfd5bb2017-09-06 17:09:11 -0700198DONE:
Joe Onorato1754d742016-11-21 17:51:35 -0800199 // Close the file.
200 if (mainFd >= 0) {
201 close(mainFd);
202 }
203
204 // Tell everyone that we're done.
205 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
206 if ((*it)->listener != NULL) {
207 if (err == NO_ERROR) {
208 (*it)->listener->onReportFinished();
209 } else {
210 (*it)->listener->onReportFailed();
211 }
212 }
213 }
214
215 // Put the report into dropbox.
216 if (needMainFd && err == NO_ERROR) {
217 sp<DropBoxManager> dropbox = new DropBoxManager();
218 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
219 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
220 if (!status.isOk()) {
221 return REPORT_NEEDS_DROPBOX;
222 }
223
224 // If the status was ok, delete the file. If not, leave it around until the next
225 // boot or the next checkin. If the directory gets too big older files will
226 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700227 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800228 }
229
230 return REPORT_FINISHED;
231}
232
233/**
234 * Create our output file and set the access permissions to -rw-rw----
235 */
236status_t
237Reporter::create_file(int* fd)
238{
239 const char* filename = mFilename.c_str();
240
Yi Jinadd11e92017-07-30 16:10:07 -0700241 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800242 if (*fd < 0) {
243 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
244 return -errno;
245 }
246
247 // Override umask. Not super critical. If it fails go on with life.
248 chmod(filename, 0660);
249
250 if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
251 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
252 status_t err = -errno;
253 unlink(mFilename.c_str());
254 return err;
255 }
256
257 return NO_ERROR;
258}
259
Joe Onorato1754d742016-11-21 17:51:35 -0800260Reporter::run_report_status_t
261Reporter::upload_backlog()
262{
263 DIR* dir;
264 struct dirent* entry;
265 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700266 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800267
Yi Jinf32af482017-08-11 15:00:49 -0700268 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700269 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
270 ALOGE("directory doesn't exist: %s", strerror(-err));
271 return REPORT_FINISHED;
272 }
273
274 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
275 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800276 return REPORT_NEEDS_DROPBOX;
277 }
278
Joe Onorato1754d742016-11-21 17:51:35 -0800279 sp<DropBoxManager> dropbox = new DropBoxManager();
280
281 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700282 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800283 while ((entry = readdir(dir)) != NULL) {
284 if (entry->d_name[0] == '.') {
285 continue;
286 }
Yi Jinadd11e92017-07-30 16:10:07 -0700287 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800288 if (stat(filename.string(), &st) != 0) {
289 ALOGE("Unable to stat file %s", filename.string());
290 continue;
291 }
292 if (!S_ISREG(st.st_mode)) {
293 continue;
294 }
295
296 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
297 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
298 if (!status.isOk()) {
299 return REPORT_NEEDS_DROPBOX;
300 }
301
302 // If the status was ok, delete the file. If not, leave it around until the next
303 // boot or the next checkin. If the directory gets too big older files will
304 // be rotated out.
305 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700306 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800307 }
Yi Jinf32af482017-08-11 15:00:49 -0700308 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800309 closedir(dir);
310
311 return REPORT_FINISHED;
312}
313