blob: 4ffc11984224e88a559b44a7ce5d1669bfb1660f [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"
20#include "protobuf.h"
21
22#include "report_directory.h"
23#include "section_list.h"
24
Joe Onorato1754d742016-11-21 17:51:35 -080025#include <android/os/DropBoxManager.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070026#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080027#include <utils/SystemClock.h>
28
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <dirent.h>
32#include <fcntl.h>
33#include <errno.h>
34
35/**
36 * The directory where the incident reports are stored.
37 */
Yi Jinadd11e92017-07-30 16:10:07 -070038static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
Joe Onorato1754d742016-11-21 17:51:35 -080039
Yi Jin0a3406f2017-06-22 19:23:11 -070040// ================================================================================
41static status_t write_all(int fd, uint8_t const* buf, size_t size)
Joe Onorato1754d742016-11-21 17:51:35 -080042{
43 while (size > 0) {
44 ssize_t amt = ::write(fd, buf, size);
45 if (amt < 0) {
46 return -errno;
47 }
48 size -= amt;
49 buf += amt;
50 }
51 return NO_ERROR;
52}
53
54// ================================================================================
55ReportRequest::ReportRequest(const IncidentReportArgs& a,
56 const sp<IIncidentReportStatusListener> &l, int f)
57 :args(a),
58 listener(l),
59 fd(f),
60 err(NO_ERROR)
61{
62}
63
64ReportRequest::~ReportRequest()
65{
66}
67
68// ================================================================================
69ReportRequestSet::ReportRequestSet()
70 :mRequests(),
Yi Jinadd11e92017-07-30 16:10:07 -070071 mSections(),
Joe Onorato1754d742016-11-21 17:51:35 -080072 mWritableCount(0),
73 mMainFd(-1)
74{
75}
76
77ReportRequestSet::~ReportRequestSet()
78{
79}
80
Yi Jinadd11e92017-07-30 16:10:07 -070081// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080082void
83ReportRequestSet::add(const sp<ReportRequest>& request)
84{
85 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070086 mSections.merge(request->args);
Joe Onorato1754d742016-11-21 17:51:35 -080087 mWritableCount++;
88}
89
90void
91ReportRequestSet::setMainFd(int fd)
92{
93 mMainFd = fd;
94 mWritableCount++;
95}
96
97status_t
98ReportRequestSet::write(uint8_t const* buf, size_t size)
99{
100 status_t err = EBADF;
101
102 // The streaming ones
103 int const N = mRequests.size();
104 for (int i=N-1; i>=0; i--) {
105 sp<ReportRequest> request = mRequests[i];
106 if (request->fd >= 0 && request->err == NO_ERROR) {
107 err = write_all(request->fd, buf, size);
108 if (err != NO_ERROR) {
109 request->err = err;
110 mWritableCount--;
111 }
112 }
113 }
114
115 // The dropbox file
116 if (mMainFd >= 0) {
117 err = write_all(mMainFd, buf, size);
118 if (err != NO_ERROR) {
119 mMainFd = -1;
120 mWritableCount--;
121 }
122 }
123
124 // Return an error only when there are no FDs to write.
125 return mWritableCount > 0 ? NO_ERROR : err;
126}
127
Yi Jinadd11e92017-07-30 16:10:07 -0700128bool
129ReportRequestSet::containsSection(int id) {
130 return mSections.containsSection(id);
131}
Joe Onorato1754d742016-11-21 17:51:35 -0800132
133// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -0700134Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
135
136Reporter::Reporter(const char* directory)
137 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -0800138{
139 char buf[100];
140
141 // TODO: Make the max size smaller for user builds.
142 mMaxSize = 100 * 1024 * 1024;
143 mMaxCount = 100;
144
Yi Jinadd11e92017-07-30 16:10:07 -0700145 // string ends up with '/' is a directory
146 String8 dir = String8(directory);
147 if (directory[dir.size() - 1] != '/') dir += "/";
148 mIncidentDirectory = dir.string();
149
Joe Onorato1754d742016-11-21 17:51:35 -0800150 // There can't be two at the same time because it's on one thread.
151 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700152 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
153 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800154}
155
156Reporter::~Reporter()
157{
158}
159
160Reporter::run_report_status_t
161Reporter::runReport()
162{
163
164 status_t err = NO_ERROR;
165 bool needMainFd = false;
166 int mainFd = -1;
167
168 // See if we need the main file
169 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
170 if ((*it)->fd < 0 && mainFd < 0) {
171 needMainFd = true;
172 break;
173 }
174 }
175 if (needMainFd) {
176 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700177 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800178 if (err != NO_ERROR) {
179 goto done;
180 }
181
182 // If there are too many files in the directory (for whatever reason),
183 // delete the oldest ones until it's under the limit. Doing this first
184 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700185 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800186
187 // Open the file.
188 err = create_file(&mainFd);
189 if (err != NO_ERROR) {
190 goto done;
191 }
192
193 // Add to the set
194 batch.setMainFd(mainFd);
195 }
196
197 // Tell everyone that we're starting.
198 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
199 if ((*it)->listener != NULL) {
200 (*it)->listener->onReportStarted();
201 }
202 }
203
204 // Write the incident headers
205 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
206 const sp<ReportRequest> request = (*it);
207 const vector<vector<int8_t>>& headers = request->args.headers();
208
209 for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
210 buf++) {
211 int fd = request->fd >= 0 ? request->fd : mainFd;
212
213 uint8_t buffer[20];
214 uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
215 buf->size());
216 write_all(fd, buffer, p-buffer);
217
218 write_all(fd, (uint8_t const*)buf->data(), buf->size());
219 // If there was an error now, there will be an error later and we will remove
220 // it from the list then.
221 }
222 }
223
224 // For each of the report fields, see if we need it, and if so, execute the command
225 // and report to those that care that we're doing it.
226 for (const Section** section=SECTION_LIST; *section; section++) {
227 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700228 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700229 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800230 // Notify listener of starting
231 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
232 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
233 (*it)->listener->onReportSectionStatus(id,
234 IIncidentReportStatusListener::STATUS_STARTING);
235 }
236 }
237
238 // Execute - go get the data and write it into the file descriptors.
239 err = (*section)->Execute(&batch);
240 if (err != NO_ERROR) {
241 ALOGW("Incident section %s (%d) failed. Stopping report.",
242 (*section)->name.string(), id);
243 goto done;
244 }
245
246 // Notify listener of starting
247 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
248 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
249 (*it)->listener->onReportSectionStatus(id,
250 IIncidentReportStatusListener::STATUS_FINISHED);
251 }
252 }
Yi Jinf32af482017-08-11 15:00:49 -0700253 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800254 }
255 }
256
257done:
258 // Close the file.
259 if (mainFd >= 0) {
260 close(mainFd);
261 }
262
263 // Tell everyone that we're done.
264 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
265 if ((*it)->listener != NULL) {
266 if (err == NO_ERROR) {
267 (*it)->listener->onReportFinished();
268 } else {
269 (*it)->listener->onReportFailed();
270 }
271 }
272 }
273
274 // Put the report into dropbox.
275 if (needMainFd && err == NO_ERROR) {
276 sp<DropBoxManager> dropbox = new DropBoxManager();
277 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
278 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
279 if (!status.isOk()) {
280 return REPORT_NEEDS_DROPBOX;
281 }
282
283 // If the status was ok, delete the file. If not, leave it around until the next
284 // boot or the next checkin. If the directory gets too big older files will
285 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700286 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800287 }
288
289 return REPORT_FINISHED;
290}
291
292/**
293 * Create our output file and set the access permissions to -rw-rw----
294 */
295status_t
296Reporter::create_file(int* fd)
297{
298 const char* filename = mFilename.c_str();
299
Yi Jinadd11e92017-07-30 16:10:07 -0700300 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800301 if (*fd < 0) {
302 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
303 return -errno;
304 }
305
306 // Override umask. Not super critical. If it fails go on with life.
307 chmod(filename, 0660);
308
309 if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
310 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
311 status_t err = -errno;
312 unlink(mFilename.c_str());
313 return err;
314 }
315
316 return NO_ERROR;
317}
318
Joe Onorato1754d742016-11-21 17:51:35 -0800319Reporter::run_report_status_t
320Reporter::upload_backlog()
321{
322 DIR* dir;
323 struct dirent* entry;
324 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700325 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800326
Yi Jinf32af482017-08-11 15:00:49 -0700327 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700328 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
329 ALOGE("directory doesn't exist: %s", strerror(-err));
330 return REPORT_FINISHED;
331 }
332
333 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
334 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800335 return REPORT_NEEDS_DROPBOX;
336 }
337
Joe Onorato1754d742016-11-21 17:51:35 -0800338 sp<DropBoxManager> dropbox = new DropBoxManager();
339
340 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700341 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800342 while ((entry = readdir(dir)) != NULL) {
343 if (entry->d_name[0] == '.') {
344 continue;
345 }
Yi Jinadd11e92017-07-30 16:10:07 -0700346 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800347 if (stat(filename.string(), &st) != 0) {
348 ALOGE("Unable to stat file %s", filename.string());
349 continue;
350 }
351 if (!S_ISREG(st.st_mode)) {
352 continue;
353 }
354
355 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
356 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
357 if (!status.isOk()) {
358 return REPORT_NEEDS_DROPBOX;
359 }
360
361 // If the status was ok, delete the file. If not, leave it around until the next
362 // boot or the next checkin. If the directory gets too big older files will
363 // be rotated out.
364 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700365 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800366 }
Yi Jinf32af482017-08-11 15:00:49 -0700367 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800368 closedir(dir);
369
370 return REPORT_FINISHED;
371}
372