blob: a04804c8c7702310b9648719853e9f5647d357d4 [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
Yi Jin99c248f2017-08-25 18:11:58 -070019#include "EncodedBuffer.h"
20#include "FdBuffer.h"
21#include "Privacy.h"
Joe Onorato1754d742016-11-21 17:51:35 -080022#include "Section.h"
Yi Jin99c248f2017-08-25 18:11:58 -070023
24#include "io_util.h"
Joe Onorato1754d742016-11-21 17:51:35 -080025#include "protobuf.h"
Yi Jin99c248f2017-08-25 18:11:58 -070026#include "section_list.h"
Joe Onorato1754d742016-11-21 17:51:35 -080027
Yi Jinb44f7d42017-07-21 12:12:59 -070028#include <private/android_filesystem_config.h>
Joe Onorato1754d742016-11-21 17:51:35 -080029#include <binder/IServiceManager.h>
30#include <mutex>
Yi Jin0a3406f2017-06-22 19:23:11 -070031#include <wait.h>
32#include <unistd.h>
Joe Onorato1754d742016-11-21 17:51:35 -080033
34using namespace std;
35
Yi Jinb44f7d42017-07-21 12:12:59 -070036const int WAIT_MAX = 5;
37const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
Yi Jin0a3406f2017-06-22 19:23:11 -070038const char* INCIDENT_HELPER = "/system/bin/incident_helper";
Yi Jinb44f7d42017-07-21 12:12:59 -070039
40static pid_t
41forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
42{
Yi Jin0ed9b682017-08-18 14:51:20 -070043 const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
Yi Jinb44f7d42017-07-21 12:12:59 -070044
45 // fork used in multithreaded environment, avoid adding unnecessary code in child process
46 pid_t pid = fork();
47 if (pid == 0) {
48 // child process executes incident helper as nobody
49 if (setgid(AID_NOBODY) == -1) {
50 ALOGW("%s can't change gid: %s", name, strerror(errno));
51 _exit(EXIT_FAILURE);
52 }
53 if (setuid(AID_NOBODY) == -1) {
54 ALOGW("%s can't change uid: %s", name, strerror(errno));
55 _exit(EXIT_FAILURE);
56 }
57
58 if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close() ||
59 dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
60 ALOGW("%s can't setup stdin and stdout for incident helper", name);
61 _exit(EXIT_FAILURE);
62 }
63
64 execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
65
66 ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
67 _exit(EXIT_FAILURE); // always exits with failure if any
68 }
69 // close the fds used in incident helper
70 close(p2cPipe.readFd());
71 close(c2pPipe.writeFd());
72 return pid;
73}
74
Yi Jin99c248f2017-08-25 18:11:58 -070075// ================================================================================
Yi Jinb44f7d42017-07-21 12:12:59 -070076static status_t killChild(pid_t pid) {
77 int status;
78 kill(pid, SIGKILL);
79 if (waitpid(pid, &status, 0) == -1) return -1;
80 return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
81}
82
83static status_t waitForChild(pid_t pid) {
84 int status;
85 bool died = false;
86 // wait for child to report status up to 1 seconds
87 for(int loop = 0; !died && loop < WAIT_MAX; loop++) {
88 if (waitpid(pid, &status, WNOHANG) == pid) died = true;
89 // sleep for 0.2 second
90 nanosleep(&WAIT_INTERVAL_NS, NULL);
91 }
92 if (!died) return killChild(pid);
93 return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
94}
Joe Onorato1754d742016-11-21 17:51:35 -080095
96// ================================================================================
Yi Jin99c248f2017-08-25 18:11:58 -070097static const Privacy*
98GetPrivacyOfSection(int id)
99{
100 if (id < 0) return NULL;
101 int i=0;
102 while (PRIVACY_POLICY_LIST[i] != NULL) {
103 const Privacy* p = PRIVACY_POLICY_LIST[i];
104 if (p->field_id == (uint32_t)id) return p;
105 if (p->field_id > (uint32_t)id) return NULL;
106 i++;
107 }
108 return NULL;
109}
110
111static status_t
112WriteToRequest(const int id, const int fd, EncodedBuffer& buffer, const PrivacySpec& spec)
113{
114 if (fd < 0) return EBADF;
115 status_t err = NO_ERROR;
116 uint8_t buf[20];
117
118 buffer.clear(); // clear before strip
119 err = buffer.strip(spec); // TODO: don't have to strip again if the spec is the same.
120 if (err != NO_ERROR || buffer.size() == 0) return err;
121 uint8_t *p = write_length_delimited_tag_header(buf, id, buffer.size());
122 err = write_all(fd, buf, p-buf);
123 if (err == NO_ERROR) {
124 err = buffer.flush(fd);
125 ALOGD("Section %d flushed %zu bytes to fd %d with spec %d", id, buffer.size(), fd, spec.dest);
126 }
127 return err;
128}
129
130static status_t
131WriteToReportRequests(const int id, const FdBuffer& buffer, ReportRequestSet* requests)
132{
133 status_t err = EBADF;
134 EncodedBuffer encodedBuffer(buffer, GetPrivacyOfSection(id));
135 int writeable = 0;
136
137 // The streaming ones
138 for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) {
139 sp<ReportRequest> request = *it;
140 PrivacySpec spec; // TODO: this should be derived from each request.
141 err = WriteToRequest(id, request->fd, encodedBuffer, spec);
142 if (err != NO_ERROR) {
143 request->err = err;
144 } else {
145 writeable++;
146 }
147 }
148
149 // The dropbox file
150 if (requests->mainFd() >= 0) {
151 err = WriteToRequest(id, requests->mainFd(), encodedBuffer, get_default_dropbox_spec());
152 if (err != NO_ERROR) {
153 requests->setMainFd(-1);
154 } else {
155 writeable++;
156 }
157 }
158 // only returns error if there is no fd to write to.
159 return writeable > 0 ? NO_ERROR : err;
160}
161
162// ================================================================================
Yi Jinb44f7d42017-07-21 12:12:59 -0700163Section::Section(int i, const int64_t timeoutMs)
164 :id(i), timeoutMs(timeoutMs)
Joe Onorato1754d742016-11-21 17:51:35 -0800165{
166}
167
168Section::~Section()
169{
170}
171
Joe Onorato1754d742016-11-21 17:51:35 -0800172// ================================================================================
Yi Jinb44f7d42017-07-21 12:12:59 -0700173FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
174 : Section(id, timeoutMs), mFilename(filename) {
175 name = filename;
Yi Jin0a3406f2017-06-22 19:23:11 -0700176}
177
178FileSection::~FileSection() {}
179
Yi Jin99c248f2017-08-25 18:11:58 -0700180status_t
181FileSection::Execute(ReportRequestSet* requests) const
182{
Yi Jinb44f7d42017-07-21 12:12:59 -0700183 // read from mFilename first, make sure the file is available
184 // add O_CLOEXEC to make sure it is closed when exec incident helper
George Burgess IV6f9735b2017-08-03 16:08:29 -0700185 int fd = open(mFilename, O_RDONLY | O_CLOEXEC);
Yi Jin0a3406f2017-06-22 19:23:11 -0700186 if (fd == -1) {
187 ALOGW("FileSection '%s' failed to open file", this->name.string());
188 return -errno;
189 }
190
Yi Jinb44f7d42017-07-21 12:12:59 -0700191 FdBuffer buffer;
192 Fpipe p2cPipe;
193 Fpipe c2pPipe;
194 // initiate pipes to pass data to/from incident_helper
195 if (!p2cPipe.init() || !c2pPipe.init()) {
196 ALOGW("FileSection '%s' failed to setup pipes", this->name.string());
Yi Jin0a3406f2017-06-22 19:23:11 -0700197 return -errno;
198 }
199
Yi Jinb44f7d42017-07-21 12:12:59 -0700200 pid_t pid = forkAndExecuteIncidentHelper(this->id, this->name.string(), p2cPipe, c2pPipe);
201 if (pid == -1) {
202 ALOGW("FileSection '%s' failed to fork", this->name.string());
203 return -errno;
204 }
205
206 // parent process
207 status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
208 this->timeoutMs);
209 if (readStatus != NO_ERROR || buffer.timedOut()) {
210 ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
211 this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
212 strerror(-killChild(pid)));
213 return readStatus;
214 }
215
216 status_t ihStatus = waitForChild(pid);
217 if (ihStatus != NO_ERROR) {
218 ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus));
219 return ihStatus;
220 }
221
222 ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
Yi Jin0a3406f2017-06-22 19:23:11 -0700223 (int)buffer.durationMs());
Yi Jin99c248f2017-08-25 18:11:58 -0700224 status_t err = WriteToReportRequests(this->id, buffer, requests);
Yi Jin0a3406f2017-06-22 19:23:11 -0700225 if (err != NO_ERROR) {
226 ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
227 return err;
228 }
229
230 return NO_ERROR;
231}
232
233// ================================================================================
Joe Onorato1754d742016-11-21 17:51:35 -0800234struct WorkerThreadData : public virtual RefBase
235{
236 const WorkerThreadSection* section;
237 int fds[2];
238
239 // Lock protects these fields
240 mutex lock;
241 bool workerDone;
242 status_t workerError;
243
244 WorkerThreadData(const WorkerThreadSection* section);
245 virtual ~WorkerThreadData();
246
247 int readFd() { return fds[0]; }
248 int writeFd() { return fds[1]; }
249};
250
251WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
252 :section(sec),
253 workerDone(false),
254 workerError(NO_ERROR)
255{
256 fds[0] = -1;
257 fds[1] = -1;
258}
259
260WorkerThreadData::~WorkerThreadData()
261{
262}
263
264// ================================================================================
265WorkerThreadSection::WorkerThreadSection(int id)
266 :Section(id)
267{
268}
269
270WorkerThreadSection::~WorkerThreadSection()
271{
272}
273
274static void*
275worker_thread_func(void* cookie)
276{
277 WorkerThreadData* data = (WorkerThreadData*)cookie;
278 status_t err = data->section->BlockingCall(data->writeFd());
279
280 {
281 unique_lock<mutex> lock(data->lock);
282 data->workerDone = true;
283 data->workerError = err;
284 }
285
286 close(data->writeFd());
287 data->decStrong(data->section);
288 // data might be gone now. don't use it after this point in this thread.
289 return NULL;
290}
291
292status_t
293WorkerThreadSection::Execute(ReportRequestSet* requests) const
294{
295 status_t err = NO_ERROR;
296 pthread_t thread;
297 pthread_attr_t attr;
298 bool timedOut = false;
299 FdBuffer buffer;
300
301 // Data shared between this thread and the worker thread.
302 sp<WorkerThreadData> data = new WorkerThreadData(this);
303
304 // Create the pipe
305 err = pipe(data->fds);
306 if (err != 0) {
307 return -errno;
308 }
309
310 // The worker thread needs a reference and we can't let the count go to zero
311 // if that thread is slow to start.
312 data->incStrong(this);
313
314 // Create the thread
315 err = pthread_attr_init(&attr);
316 if (err != 0) {
317 return -err;
318 }
319 // TODO: Do we need to tweak thread priority?
320 err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
321 if (err != 0) {
322 pthread_attr_destroy(&attr);
323 return -err;
324 }
325 err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
326 if (err != 0) {
327 pthread_attr_destroy(&attr);
328 return -err;
329 }
330 pthread_attr_destroy(&attr);
331
332 // Loop reading until either the timeout or the worker side is done (i.e. eof).
Yi Jinb44f7d42017-07-21 12:12:59 -0700333 err = buffer.read(data->readFd(), this->timeoutMs);
Joe Onorato1754d742016-11-21 17:51:35 -0800334 if (err != NO_ERROR) {
335 // TODO: Log this error into the incident report.
336 ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
337 strerror(-err));
338 }
339
340 // Done with the read fd. The worker thread closes the write one so
341 // we never race and get here first.
342 close(data->readFd());
343
344 // If the worker side is finished, then return its error (which may overwrite
345 // our possible error -- but it's more interesting anyway). If not, then we timed out.
346 {
347 unique_lock<mutex> lock(data->lock);
348 if (!data->workerDone) {
349 // We timed out
350 timedOut = true;
351 } else {
352 if (data->workerError != NO_ERROR) {
353 err = data->workerError;
354 // TODO: Log this error into the incident report.
355 ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
356 strerror(-err));
357 }
358 }
359 }
360
361 if (timedOut || buffer.timedOut()) {
362 ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
363 return NO_ERROR;
364 }
365
366 if (buffer.truncated()) {
367 // TODO: Log this into the incident report.
368 }
369
370 // TODO: There was an error with the command or buffering. Report that. For now
371 // just exit with a log messasge.
372 if (err != NO_ERROR) {
373 ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
374 strerror(-err));
375 return NO_ERROR;
376 }
377
378 // Write the data that was collected
Yi Jinb44f7d42017-07-21 12:12:59 -0700379 ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
Joe Onorato1754d742016-11-21 17:51:35 -0800380 (int)buffer.durationMs());
Yi Jin99c248f2017-08-25 18:11:58 -0700381 err = WriteToReportRequests(this->id, buffer, requests);
Joe Onorato1754d742016-11-21 17:51:35 -0800382 if (err != NO_ERROR) {
383 ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
384 return err;
385 }
386
387 return NO_ERROR;
388}
389
390// ================================================================================
Yi Jin99c248f2017-08-25 18:11:58 -0700391void
392CommandSection::init(const char* command, va_list args)
Yi Jinb44f7d42017-07-21 12:12:59 -0700393{
394 va_list copied_args;
Yi Jinb44f7d42017-07-21 12:12:59 -0700395 int numOfArgs = 0;
Yi Jin4ef28b72017-08-14 14:45:28 -0700396
397 va_copy(copied_args, args);
398 while(va_arg(copied_args, const char*) != NULL) {
Yi Jinb44f7d42017-07-21 12:12:59 -0700399 numOfArgs++;
400 }
Yi Jin4ef28b72017-08-14 14:45:28 -0700401 va_end(copied_args);
Yi Jinb44f7d42017-07-21 12:12:59 -0700402
403 // allocate extra 1 for command and 1 for NULL terminator
404 mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
405
406 mCommand[0] = command;
407 name = command;
408 for (int i=0; i<numOfArgs; i++) {
Yi Jin4ef28b72017-08-14 14:45:28 -0700409 const char* arg = va_arg(args, const char*);
Yi Jinb44f7d42017-07-21 12:12:59 -0700410 mCommand[i+1] = arg;
411 name += " ";
412 name += arg;
413 }
414 mCommand[numOfArgs+1] = NULL;
Yi Jinb44f7d42017-07-21 12:12:59 -0700415}
416
417CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
418 : Section(id, timeoutMs)
Joe Onorato1754d742016-11-21 17:51:35 -0800419{
420 va_list args;
Yi Jinb44f7d42017-07-21 12:12:59 -0700421 va_start(args, command);
422 init(command, args);
Joe Onorato1754d742016-11-21 17:51:35 -0800423 va_end(args);
Yi Jinb44f7d42017-07-21 12:12:59 -0700424}
Joe Onorato1754d742016-11-21 17:51:35 -0800425
Yi Jinb44f7d42017-07-21 12:12:59 -0700426CommandSection::CommandSection(int id, const char* command, ...)
427 : Section(id)
428{
429 va_list args;
430 va_start(args, command);
431 init(command, args);
Joe Onorato1754d742016-11-21 17:51:35 -0800432 va_end(args);
433}
434
435CommandSection::~CommandSection()
436{
Yi Jinb44f7d42017-07-21 12:12:59 -0700437 free(mCommand);
Joe Onorato1754d742016-11-21 17:51:35 -0800438}
439
440status_t
Yi Jinb44f7d42017-07-21 12:12:59 -0700441CommandSection::Execute(ReportRequestSet* requests) const
Joe Onorato1754d742016-11-21 17:51:35 -0800442{
Yi Jinb44f7d42017-07-21 12:12:59 -0700443 FdBuffer buffer;
444 Fpipe cmdPipe;
445 Fpipe ihPipe;
446
447 if (!cmdPipe.init() || !ihPipe.init()) {
448 ALOGW("CommandSection '%s' failed to setup pipes", this->name.string());
449 return -errno;
450 }
451
452 pid_t cmdPid = fork();
453 if (cmdPid == -1) {
454 ALOGW("CommandSection '%s' failed to fork", this->name.string());
455 return -errno;
456 }
457 // child process to execute the command as root
458 if (cmdPid == 0) {
459 // replace command's stdout with ihPipe's write Fd
460 if (dup2(cmdPipe.writeFd(), STDOUT_FILENO) != 1 || !ihPipe.close() || !cmdPipe.close()) {
461 ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno));
462 _exit(EXIT_FAILURE);
463 }
464 execv(this->mCommand[0], (char *const *) this->mCommand);
465 int err = errno; // record command error code
466 ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno));
467 _exit(err); // exit with command error code
468 }
469 pid_t ihPid = forkAndExecuteIncidentHelper(this->id, this->name.string(), cmdPipe, ihPipe);
470 if (ihPid == -1) {
471 ALOGW("CommandSection '%s' failed to fork", this->name.string());
472 return -errno;
473 }
474
475 close(cmdPipe.writeFd());
476 status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
477 if (readStatus != NO_ERROR || buffer.timedOut()) {
478 ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
479 "timedout: %s, kill command: %s, kill incident helper: %s",
480 this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
481 strerror(-killChild(cmdPid)), strerror(-killChild(ihPid)));
482 return readStatus;
483 }
484
485 // TODO: wait for command here has one trade-off: the failed status of command won't be detected until
486 // buffer timeout, but it has advatage on starting the data stream earlier.
487 status_t cmdStatus = waitForChild(cmdPid);
488 status_t ihStatus = waitForChild(ihPid);
489 if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
Yi Jinadd11e92017-07-30 16:10:07 -0700490 ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s",
Yi Jinb44f7d42017-07-21 12:12:59 -0700491 this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
492 return cmdStatus != NO_ERROR ? cmdStatus : ihStatus;
493 }
494
495 ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
496 (int)buffer.durationMs());
Yi Jin99c248f2017-08-25 18:11:58 -0700497 status_t err = WriteToReportRequests(this->id, buffer, requests);
Yi Jinb44f7d42017-07-21 12:12:59 -0700498 if (err != NO_ERROR) {
499 ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
500 return err;
501 }
Joe Onorato1754d742016-11-21 17:51:35 -0800502 return NO_ERROR;
503}
504
505// ================================================================================
506DumpsysSection::DumpsysSection(int id, const char* service, ...)
507 :WorkerThreadSection(id),
508 mService(service)
509{
510 name = "dumpsys ";
511 name += service;
512
513 va_list args;
514 va_start(args, service);
515 while (true) {
Yi Jin0a3406f2017-06-22 19:23:11 -0700516 const char* arg = va_arg(args, const char*);
Joe Onorato1754d742016-11-21 17:51:35 -0800517 if (arg == NULL) {
518 break;
519 }
520 mArgs.add(String16(arg));
521 name += " ";
522 name += arg;
523 }
524 va_end(args);
525}
526
527DumpsysSection::~DumpsysSection()
528{
529}
530
531status_t
532DumpsysSection::BlockingCall(int pipeWriteFd) const
533{
534 // checkService won't wait for the service to show up like getService will.
535 sp<IBinder> service = defaultServiceManager()->checkService(mService);
Yi Jin0a3406f2017-06-22 19:23:11 -0700536
Joe Onorato1754d742016-11-21 17:51:35 -0800537 if (service == NULL) {
538 // Returning an error interrupts the entire incident report, so just
539 // log the failure.
540 // TODO: have a meta record inside the report that would log this
541 // failure inside the report, because the fact that we can't find
542 // the service is good data in and of itself. This is running in
543 // another thread so lock that carefully...
544 ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
545 return NO_ERROR;
546 }
547
548 service->dump(pipeWriteFd, mArgs);
549
550 return NO_ERROR;
551}