blob: b9f479bd683f2010e444ad17942aca2afef1d61c [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{
Yi Jin22769e02017-10-16 14:42:50 -070051 if (fd >= 0) {
52 // clean up the opened file descriptor
53 close(fd);
54 }
Joe Onorato1754d742016-11-21 17:51:35 -080055}
56
Yi Jinedfd5bb2017-09-06 17:09:11 -070057bool
58ReportRequest::ok()
59{
60 return fd >= 0 && err == NO_ERROR;
61}
62
Joe Onorato1754d742016-11-21 17:51:35 -080063// ================================================================================
64ReportRequestSet::ReportRequestSet()
65 :mRequests(),
Yi Jinadd11e92017-07-30 16:10:07 -070066 mSections(),
Yi Jin3ec5cc72018-01-26 13:42:43 -080067 mMainFd(-1),
68 mMainDest(-1)
Joe Onorato1754d742016-11-21 17:51:35 -080069{
70}
71
72ReportRequestSet::~ReportRequestSet()
73{
74}
75
Yi Jinadd11e92017-07-30 16:10:07 -070076// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080077void
78ReportRequestSet::add(const sp<ReportRequest>& request)
79{
80 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070081 mSections.merge(request->args);
Joe Onorato1754d742016-11-21 17:51:35 -080082}
83
84void
85ReportRequestSet::setMainFd(int fd)
86{
87 mMainFd = fd;
Joe Onorato1754d742016-11-21 17:51:35 -080088}
89
Yi Jin3ec5cc72018-01-26 13:42:43 -080090void
91ReportRequestSet::setMainDest(int dest)
92{
93 mMainDest = dest;
94}
95
Yi Jinadd11e92017-07-30 16:10:07 -070096bool
97ReportRequestSet::containsSection(int id) {
98 return mSections.containsSection(id);
99}
Joe Onorato1754d742016-11-21 17:51:35 -0800100
101// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -0700102Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
103
104Reporter::Reporter(const char* directory)
105 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -0800106{
107 char buf[100];
108
109 // TODO: Make the max size smaller for user builds.
110 mMaxSize = 100 * 1024 * 1024;
111 mMaxCount = 100;
112
Yi Jinadd11e92017-07-30 16:10:07 -0700113 // string ends up with '/' is a directory
114 String8 dir = String8(directory);
115 if (directory[dir.size() - 1] != '/') dir += "/";
116 mIncidentDirectory = dir.string();
117
Joe Onorato1754d742016-11-21 17:51:35 -0800118 // There can't be two at the same time because it's on one thread.
119 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700120 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
121 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800122}
123
124Reporter::~Reporter()
125{
126}
127
128Reporter::run_report_status_t
129Reporter::runReport()
130{
131
132 status_t err = NO_ERROR;
133 bool needMainFd = false;
134 int mainFd = -1;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800135 int mainDest = -1;
Yi Jinedfd5bb2017-09-06 17:09:11 -0700136 HeaderSection headers;
Joe Onorato1754d742016-11-21 17:51:35 -0800137
138 // See if we need the main file
139 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
140 if ((*it)->fd < 0 && mainFd < 0) {
141 needMainFd = true;
Yi Jin3ec5cc72018-01-26 13:42:43 -0800142 mainDest = (*it)->args.dest();
Joe Onorato1754d742016-11-21 17:51:35 -0800143 break;
144 }
145 }
146 if (needMainFd) {
147 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700148 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800149 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700150 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800151 }
152
153 // If there are too many files in the directory (for whatever reason),
154 // delete the oldest ones until it's under the limit. Doing this first
155 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700156 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800157
158 // Open the file.
159 err = create_file(&mainFd);
160 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700161 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800162 }
163
164 // Add to the set
165 batch.setMainFd(mainFd);
Yi Jin3ec5cc72018-01-26 13:42:43 -0800166 batch.setMainDest(mainDest);
Joe Onorato1754d742016-11-21 17:51:35 -0800167 }
168
169 // Tell everyone that we're starting.
170 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
171 if ((*it)->listener != NULL) {
172 (*it)->listener->onReportStarted();
173 }
174 }
175
176 // Write the incident headers
Yi Jinedfd5bb2017-09-06 17:09:11 -0700177 headers.Execute(&batch);
Joe Onorato1754d742016-11-21 17:51:35 -0800178
179 // For each of the report fields, see if we need it, and if so, execute the command
180 // and report to those that care that we're doing it.
181 for (const Section** section=SECTION_LIST; *section; section++) {
182 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700183 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700184 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800185 // Notify listener of starting
186 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
187 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
188 (*it)->listener->onReportSectionStatus(id,
189 IIncidentReportStatusListener::STATUS_STARTING);
190 }
191 }
192
193 // Execute - go get the data and write it into the file descriptors.
194 err = (*section)->Execute(&batch);
195 if (err != NO_ERROR) {
Yi Jina5c5e8a2017-09-27 18:24:58 -0700196 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
197 (*section)->name.string(), id, strerror(-err));
Yi Jinedfd5bb2017-09-06 17:09:11 -0700198 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800199 }
200
201 // Notify listener of starting
202 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
203 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
204 (*it)->listener->onReportSectionStatus(id,
205 IIncidentReportStatusListener::STATUS_FINISHED);
206 }
207 }
Yi Jinf32af482017-08-11 15:00:49 -0700208 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800209 }
210 }
211
Yi Jinedfd5bb2017-09-06 17:09:11 -0700212DONE:
Joe Onorato1754d742016-11-21 17:51:35 -0800213 // Close the file.
214 if (mainFd >= 0) {
215 close(mainFd);
216 }
217
218 // Tell everyone that we're done.
219 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
220 if ((*it)->listener != NULL) {
221 if (err == NO_ERROR) {
222 (*it)->listener->onReportFinished();
223 } else {
224 (*it)->listener->onReportFailed();
225 }
226 }
227 }
228
229 // Put the report into dropbox.
230 if (needMainFd && err == NO_ERROR) {
231 sp<DropBoxManager> dropbox = new DropBoxManager();
232 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
233 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
234 if (!status.isOk()) {
235 return REPORT_NEEDS_DROPBOX;
236 }
237
238 // If the status was ok, delete the file. If not, leave it around until the next
239 // boot or the next checkin. If the directory gets too big older files will
240 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700241 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800242 }
243
244 return REPORT_FINISHED;
245}
246
247/**
248 * Create our output file and set the access permissions to -rw-rw----
249 */
250status_t
251Reporter::create_file(int* fd)
252{
253 const char* filename = mFilename.c_str();
254
Yi Jinadd11e92017-07-30 16:10:07 -0700255 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800256 if (*fd < 0) {
257 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
258 return -errno;
259 }
260
261 // Override umask. Not super critical. If it fails go on with life.
262 chmod(filename, 0660);
263
Yi Jin4bab3a12018-01-10 16:50:59 -0800264 if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
Joe Onorato1754d742016-11-21 17:51:35 -0800265 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
266 status_t err = -errno;
267 unlink(mFilename.c_str());
268 return err;
269 }
270
271 return NO_ERROR;
272}
273
Joe Onorato1754d742016-11-21 17:51:35 -0800274Reporter::run_report_status_t
275Reporter::upload_backlog()
276{
277 DIR* dir;
278 struct dirent* entry;
279 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700280 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800281
Yi Jinf32af482017-08-11 15:00:49 -0700282 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700283 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
284 ALOGE("directory doesn't exist: %s", strerror(-err));
285 return REPORT_FINISHED;
286 }
287
288 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
289 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800290 return REPORT_NEEDS_DROPBOX;
291 }
292
Joe Onorato1754d742016-11-21 17:51:35 -0800293 sp<DropBoxManager> dropbox = new DropBoxManager();
294
295 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700296 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800297 while ((entry = readdir(dir)) != NULL) {
298 if (entry->d_name[0] == '.') {
299 continue;
300 }
Yi Jinadd11e92017-07-30 16:10:07 -0700301 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800302 if (stat(filename.string(), &st) != 0) {
303 ALOGE("Unable to stat file %s", filename.string());
304 continue;
305 }
306 if (!S_ISREG(st.st_mode)) {
307 continue;
308 }
309
310 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
311 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
312 if (!status.isOk()) {
313 return REPORT_NEEDS_DROPBOX;
314 }
315
316 // If the status was ok, delete the file. If not, leave it around until the next
317 // boot or the next checkin. If the directory gets too big older files will
318 // be rotated out.
319 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700320 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800321 }
Yi Jinf32af482017-08-11 15:00:49 -0700322 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800323 closedir(dir);
324
325 return REPORT_FINISHED;
326}
327