blob: 34930aa57321d079b56b3562eda2355157c64cc4 [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(),
Joe Onorato1754d742016-11-21 17:51:35 -080067 mMainFd(-1)
68{
69}
70
71ReportRequestSet::~ReportRequestSet()
72{
73}
74
Yi Jinadd11e92017-07-30 16:10:07 -070075// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080076void
77ReportRequestSet::add(const sp<ReportRequest>& request)
78{
79 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070080 mSections.merge(request->args);
Joe Onorato1754d742016-11-21 17:51:35 -080081}
82
83void
84ReportRequestSet::setMainFd(int fd)
85{
86 mMainFd = fd;
Joe Onorato1754d742016-11-21 17:51:35 -080087}
88
Yi Jinadd11e92017-07-30 16:10:07 -070089bool
90ReportRequestSet::containsSection(int id) {
91 return mSections.containsSection(id);
92}
Joe Onorato1754d742016-11-21 17:51:35 -080093
94// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -070095Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
96
97Reporter::Reporter(const char* directory)
98 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -080099{
100 char buf[100];
101
102 // TODO: Make the max size smaller for user builds.
103 mMaxSize = 100 * 1024 * 1024;
104 mMaxCount = 100;
105
Yi Jinadd11e92017-07-30 16:10:07 -0700106 // string ends up with '/' is a directory
107 String8 dir = String8(directory);
108 if (directory[dir.size() - 1] != '/') dir += "/";
109 mIncidentDirectory = dir.string();
110
Joe Onorato1754d742016-11-21 17:51:35 -0800111 // There can't be two at the same time because it's on one thread.
112 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700113 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
114 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800115}
116
117Reporter::~Reporter()
118{
119}
120
121Reporter::run_report_status_t
122Reporter::runReport()
123{
124
125 status_t err = NO_ERROR;
126 bool needMainFd = false;
127 int mainFd = -1;
Yi Jinedfd5bb2017-09-06 17:09:11 -0700128 HeaderSection headers;
Joe Onorato1754d742016-11-21 17:51:35 -0800129
130 // See if we need the main file
131 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
132 if ((*it)->fd < 0 && mainFd < 0) {
133 needMainFd = true;
134 break;
135 }
136 }
137 if (needMainFd) {
138 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700139 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800140 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700141 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800142 }
143
144 // If there are too many files in the directory (for whatever reason),
145 // delete the oldest ones until it's under the limit. Doing this first
146 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700147 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800148
149 // Open the file.
150 err = create_file(&mainFd);
151 if (err != NO_ERROR) {
Yi Jinedfd5bb2017-09-06 17:09:11 -0700152 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800153 }
154
155 // Add to the set
156 batch.setMainFd(mainFd);
157 }
158
159 // Tell everyone that we're starting.
160 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
161 if ((*it)->listener != NULL) {
162 (*it)->listener->onReportStarted();
163 }
164 }
165
166 // Write the incident headers
Yi Jinedfd5bb2017-09-06 17:09:11 -0700167 headers.Execute(&batch);
Joe Onorato1754d742016-11-21 17:51:35 -0800168
169 // For each of the report fields, see if we need it, and if so, execute the command
170 // and report to those that care that we're doing it.
171 for (const Section** section=SECTION_LIST; *section; section++) {
172 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700173 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700174 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800175 // Notify listener of starting
176 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
177 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
178 (*it)->listener->onReportSectionStatus(id,
179 IIncidentReportStatusListener::STATUS_STARTING);
180 }
181 }
182
183 // Execute - go get the data and write it into the file descriptors.
184 err = (*section)->Execute(&batch);
185 if (err != NO_ERROR) {
Yi Jina5c5e8a2017-09-27 18:24:58 -0700186 ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
187 (*section)->name.string(), id, strerror(-err));
Yi Jinedfd5bb2017-09-06 17:09:11 -0700188 goto DONE;
Joe Onorato1754d742016-11-21 17:51:35 -0800189 }
190
191 // Notify listener of starting
192 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
193 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
194 (*it)->listener->onReportSectionStatus(id,
195 IIncidentReportStatusListener::STATUS_FINISHED);
196 }
197 }
Yi Jinf32af482017-08-11 15:00:49 -0700198 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800199 }
200 }
201
Yi Jinedfd5bb2017-09-06 17:09:11 -0700202DONE:
Joe Onorato1754d742016-11-21 17:51:35 -0800203 // Close the file.
204 if (mainFd >= 0) {
205 close(mainFd);
206 }
207
208 // Tell everyone that we're done.
209 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
210 if ((*it)->listener != NULL) {
211 if (err == NO_ERROR) {
212 (*it)->listener->onReportFinished();
213 } else {
214 (*it)->listener->onReportFailed();
215 }
216 }
217 }
218
219 // Put the report into dropbox.
220 if (needMainFd && err == NO_ERROR) {
221 sp<DropBoxManager> dropbox = new DropBoxManager();
222 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
223 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
224 if (!status.isOk()) {
225 return REPORT_NEEDS_DROPBOX;
226 }
227
228 // If the status was ok, delete the file. If not, leave it around until the next
229 // boot or the next checkin. If the directory gets too big older files will
230 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700231 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800232 }
233
234 return REPORT_FINISHED;
235}
236
237/**
238 * Create our output file and set the access permissions to -rw-rw----
239 */
240status_t
241Reporter::create_file(int* fd)
242{
243 const char* filename = mFilename.c_str();
244
Yi Jinadd11e92017-07-30 16:10:07 -0700245 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800246 if (*fd < 0) {
247 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
248 return -errno;
249 }
250
251 // Override umask. Not super critical. If it fails go on with life.
252 chmod(filename, 0660);
253
254 if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
255 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
256 status_t err = -errno;
257 unlink(mFilename.c_str());
258 return err;
259 }
260
261 return NO_ERROR;
262}
263
Joe Onorato1754d742016-11-21 17:51:35 -0800264Reporter::run_report_status_t
265Reporter::upload_backlog()
266{
267 DIR* dir;
268 struct dirent* entry;
269 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700270 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800271
Yi Jinf32af482017-08-11 15:00:49 -0700272 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700273 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
274 ALOGE("directory doesn't exist: %s", strerror(-err));
275 return REPORT_FINISHED;
276 }
277
278 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
279 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800280 return REPORT_NEEDS_DROPBOX;
281 }
282
Joe Onorato1754d742016-11-21 17:51:35 -0800283 sp<DropBoxManager> dropbox = new DropBoxManager();
284
285 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700286 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800287 while ((entry = readdir(dir)) != NULL) {
288 if (entry->d_name[0] == '.') {
289 continue;
290 }
Yi Jinadd11e92017-07-30 16:10:07 -0700291 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800292 if (stat(filename.string(), &st) != 0) {
293 ALOGE("Unable to stat file %s", filename.string());
294 continue;
295 }
296 if (!S_ISREG(st.st_mode)) {
297 continue;
298 }
299
300 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
301 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
302 if (!status.isOk()) {
303 return REPORT_NEEDS_DROPBOX;
304 }
305
306 // If the status was ok, delete the file. If not, leave it around until the next
307 // boot or the next checkin. If the directory gets too big older files will
308 // be rotated out.
309 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700310 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800311 }
Yi Jinf32af482017-08-11 15:00:49 -0700312 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800313 closedir(dir);
314
315 return REPORT_FINISHED;
316}
317