blob: 722bd4fa8b873b9129fc148eb82698283242dce6 [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"
Yi Jin99c248f2017-08-25 18:11:58 -070020#include "io_util.h"
Joe Onorato1754d742016-11-21 17:51:35 -080021#include "protobuf.h"
22
23#include "report_directory.h"
24#include "section_list.h"
25
Joe Onorato1754d742016-11-21 17:51:35 -080026#include <android/os/DropBoxManager.h>
Yi Jin0a3406f2017-06-22 19:23:11 -070027#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080028#include <utils/SystemClock.h>
29
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <dirent.h>
33#include <fcntl.h>
34#include <errno.h>
35
36/**
37 * The directory where the incident reports are stored.
38 */
Yi Jinadd11e92017-07-30 16:10:07 -070039static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
Joe Onorato1754d742016-11-21 17:51:35 -080040
Yi Jin0a3406f2017-06-22 19:23:11 -070041// ================================================================================
Joe Onorato1754d742016-11-21 17:51:35 -080042ReportRequest::ReportRequest(const IncidentReportArgs& a,
43 const sp<IIncidentReportStatusListener> &l, int f)
44 :args(a),
45 listener(l),
46 fd(f),
47 err(NO_ERROR)
48{
49}
50
51ReportRequest::~ReportRequest()
52{
53}
54
55// ================================================================================
56ReportRequestSet::ReportRequestSet()
57 :mRequests(),
Yi Jinadd11e92017-07-30 16:10:07 -070058 mSections(),
Joe Onorato1754d742016-11-21 17:51:35 -080059 mMainFd(-1)
60{
61}
62
63ReportRequestSet::~ReportRequestSet()
64{
65}
66
Yi Jinadd11e92017-07-30 16:10:07 -070067// TODO: dedup on exact same args and fd, report the status back to listener!
Joe Onorato1754d742016-11-21 17:51:35 -080068void
69ReportRequestSet::add(const sp<ReportRequest>& request)
70{
71 mRequests.push_back(request);
Yi Jinadd11e92017-07-30 16:10:07 -070072 mSections.merge(request->args);
Joe Onorato1754d742016-11-21 17:51:35 -080073}
74
75void
76ReportRequestSet::setMainFd(int fd)
77{
78 mMainFd = fd;
Joe Onorato1754d742016-11-21 17:51:35 -080079}
80
Yi Jinadd11e92017-07-30 16:10:07 -070081bool
82ReportRequestSet::containsSection(int id) {
83 return mSections.containsSection(id);
84}
Joe Onorato1754d742016-11-21 17:51:35 -080085
86// ================================================================================
Yi Jinadd11e92017-07-30 16:10:07 -070087Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
88
89Reporter::Reporter(const char* directory)
90 :batch()
Joe Onorato1754d742016-11-21 17:51:35 -080091{
92 char buf[100];
93
94 // TODO: Make the max size smaller for user builds.
95 mMaxSize = 100 * 1024 * 1024;
96 mMaxCount = 100;
97
Yi Jinadd11e92017-07-30 16:10:07 -070098 // string ends up with '/' is a directory
99 String8 dir = String8(directory);
100 if (directory[dir.size() - 1] != '/') dir += "/";
101 mIncidentDirectory = dir.string();
102
Joe Onorato1754d742016-11-21 17:51:35 -0800103 // There can't be two at the same time because it's on one thread.
104 mStartTime = time(NULL);
Yi Jinadd11e92017-07-30 16:10:07 -0700105 strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
106 mFilename = mIncidentDirectory + buf;
Joe Onorato1754d742016-11-21 17:51:35 -0800107}
108
109Reporter::~Reporter()
110{
111}
112
113Reporter::run_report_status_t
114Reporter::runReport()
115{
116
117 status_t err = NO_ERROR;
118 bool needMainFd = false;
119 int mainFd = -1;
120
121 // See if we need the main file
122 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
123 if ((*it)->fd < 0 && mainFd < 0) {
124 needMainFd = true;
125 break;
126 }
127 }
128 if (needMainFd) {
129 // Create the directory
Yi Jinadd11e92017-07-30 16:10:07 -0700130 if (!isTest) err = create_directory(mIncidentDirectory);
Joe Onorato1754d742016-11-21 17:51:35 -0800131 if (err != NO_ERROR) {
132 goto done;
133 }
134
135 // If there are too many files in the directory (for whatever reason),
136 // delete the oldest ones until it's under the limit. Doing this first
137 // does mean that we can go over, so the max size is not a hard limit.
Yi Jinadd11e92017-07-30 16:10:07 -0700138 if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
Joe Onorato1754d742016-11-21 17:51:35 -0800139
140 // Open the file.
141 err = create_file(&mainFd);
142 if (err != NO_ERROR) {
143 goto done;
144 }
145
146 // Add to the set
147 batch.setMainFd(mainFd);
148 }
149
150 // Tell everyone that we're starting.
151 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
152 if ((*it)->listener != NULL) {
153 (*it)->listener->onReportStarted();
154 }
155 }
156
157 // Write the incident headers
158 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
159 const sp<ReportRequest> request = (*it);
160 const vector<vector<int8_t>>& headers = request->args.headers();
161
162 for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
163 buf++) {
164 int fd = request->fd >= 0 ? request->fd : mainFd;
165
166 uint8_t buffer[20];
167 uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
168 buf->size());
169 write_all(fd, buffer, p-buffer);
170
171 write_all(fd, (uint8_t const*)buf->data(), buf->size());
172 // If there was an error now, there will be an error later and we will remove
173 // it from the list then.
174 }
175 }
176
177 // For each of the report fields, see if we need it, and if so, execute the command
178 // and report to those that care that we're doing it.
179 for (const Section** section=SECTION_LIST; *section; section++) {
180 const int id = (*section)->id;
Yi Jinadd11e92017-07-30 16:10:07 -0700181 if (this->batch.containsSection(id)) {
Yi Jinf32af482017-08-11 15:00:49 -0700182 ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800183 // Notify listener of starting
184 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
185 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
186 (*it)->listener->onReportSectionStatus(id,
187 IIncidentReportStatusListener::STATUS_STARTING);
188 }
189 }
190
191 // Execute - go get the data and write it into the file descriptors.
192 err = (*section)->Execute(&batch);
193 if (err != NO_ERROR) {
194 ALOGW("Incident section %s (%d) failed. Stopping report.",
195 (*section)->name.string(), id);
196 goto done;
197 }
198
199 // Notify listener of starting
200 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
201 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
202 (*it)->listener->onReportSectionStatus(id,
203 IIncidentReportStatusListener::STATUS_FINISHED);
204 }
205 }
Yi Jinf32af482017-08-11 15:00:49 -0700206 ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string());
Joe Onorato1754d742016-11-21 17:51:35 -0800207 }
208 }
209
210done:
211 // Close the file.
212 if (mainFd >= 0) {
213 close(mainFd);
214 }
215
216 // Tell everyone that we're done.
217 for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
218 if ((*it)->listener != NULL) {
219 if (err == NO_ERROR) {
220 (*it)->listener->onReportFinished();
221 } else {
222 (*it)->listener->onReportFailed();
223 }
224 }
225 }
226
227 // Put the report into dropbox.
228 if (needMainFd && err == NO_ERROR) {
229 sp<DropBoxManager> dropbox = new DropBoxManager();
230 Status status = dropbox->addFile(String16("incident"), mFilename, 0);
231 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
232 if (!status.isOk()) {
233 return REPORT_NEEDS_DROPBOX;
234 }
235
236 // If the status was ok, delete the file. If not, leave it around until the next
237 // boot or the next checkin. If the directory gets too big older files will
238 // be rotated out.
Yi Jinadd11e92017-07-30 16:10:07 -0700239 if(!isTest) unlink(mFilename.c_str());
Joe Onorato1754d742016-11-21 17:51:35 -0800240 }
241
242 return REPORT_FINISHED;
243}
244
245/**
246 * Create our output file and set the access permissions to -rw-rw----
247 */
248status_t
249Reporter::create_file(int* fd)
250{
251 const char* filename = mFilename.c_str();
252
Yi Jinadd11e92017-07-30 16:10:07 -0700253 *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
Joe Onorato1754d742016-11-21 17:51:35 -0800254 if (*fd < 0) {
255 ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
256 return -errno;
257 }
258
259 // Override umask. Not super critical. If it fails go on with life.
260 chmod(filename, 0660);
261
262 if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
263 ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
264 status_t err = -errno;
265 unlink(mFilename.c_str());
266 return err;
267 }
268
269 return NO_ERROR;
270}
271
Joe Onorato1754d742016-11-21 17:51:35 -0800272Reporter::run_report_status_t
273Reporter::upload_backlog()
274{
275 DIR* dir;
276 struct dirent* entry;
277 struct stat st;
Yi Jinadd11e92017-07-30 16:10:07 -0700278 status_t err;
Joe Onorato1754d742016-11-21 17:51:35 -0800279
Yi Jinf32af482017-08-11 15:00:49 -0700280 ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY);
Yi Jinadd11e92017-07-30 16:10:07 -0700281 if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
282 ALOGE("directory doesn't exist: %s", strerror(-err));
283 return REPORT_FINISHED;
284 }
285
286 if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
287 ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
Joe Onorato1754d742016-11-21 17:51:35 -0800288 return REPORT_NEEDS_DROPBOX;
289 }
290
Joe Onorato1754d742016-11-21 17:51:35 -0800291 sp<DropBoxManager> dropbox = new DropBoxManager();
292
293 // Enumerate, count and add up size
Yi Jinf32af482017-08-11 15:00:49 -0700294 int count = 0;
Joe Onorato1754d742016-11-21 17:51:35 -0800295 while ((entry = readdir(dir)) != NULL) {
296 if (entry->d_name[0] == '.') {
297 continue;
298 }
Yi Jinadd11e92017-07-30 16:10:07 -0700299 String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
Joe Onorato1754d742016-11-21 17:51:35 -0800300 if (stat(filename.string(), &st) != 0) {
301 ALOGE("Unable to stat file %s", filename.string());
302 continue;
303 }
304 if (!S_ISREG(st.st_mode)) {
305 continue;
306 }
307
308 Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
309 ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
310 if (!status.isOk()) {
311 return REPORT_NEEDS_DROPBOX;
312 }
313
314 // If the status was ok, delete the file. If not, leave it around until the next
315 // boot or the next checkin. If the directory gets too big older files will
316 // be rotated out.
317 unlink(filename.string());
Yi Jinf32af482017-08-11 15:00:49 -0700318 count++;
Joe Onorato1754d742016-11-21 17:51:35 -0800319 }
Yi Jinf32af482017-08-11 15:00:49 -0700320 ALOGD("Successfully uploaded %d files to Dropbox.", count);
Joe Onorato1754d742016-11-21 17:51:35 -0800321 closedir(dir);
322
323 return REPORT_FINISHED;
324}
325