diff options
Diffstat (limited to 'cmds/incidentd/src/Reporter.cpp')
-rw-r--r-- | cmds/incidentd/src/Reporter.cpp | 159 |
1 files changed, 67 insertions, 92 deletions
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 1ecb291c84a1..b9f479bd683f 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -17,13 +17,12 @@ #define LOG_TAG "incidentd" #include "Reporter.h" -#include "protobuf.h" #include "report_directory.h" #include "section_list.h" -#include <private/android_filesystem_config.h> #include <android/os/DropBoxManager.h> +#include <private/android_filesystem_config.h> #include <utils/SystemClock.h> #include <sys/types.h> @@ -35,21 +34,7 @@ /** * The directory where the incident reports are stored. */ -static const String8 INCIDENT_DIRECTORY("/data/incidents"); - -static status_t -write_all(int fd, uint8_t const* buf, size_t size) -{ - while (size > 0) { - ssize_t amt = ::write(fd, buf, size); - if (amt < 0) { - return -errno; - } - size -= amt; - buf += amt; - } - return NO_ERROR; -} +static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/"; // ================================================================================ ReportRequest::ReportRequest(const IncidentReportArgs& a, @@ -63,13 +48,24 @@ ReportRequest::ReportRequest(const IncidentReportArgs& a, ReportRequest::~ReportRequest() { + if (fd >= 0) { + // clean up the opened file descriptor + close(fd); + } +} + +bool +ReportRequest::ok() +{ + return fd >= 0 && err == NO_ERROR; } // ================================================================================ ReportRequestSet::ReportRequestSet() :mRequests(), - mWritableCount(0), - mMainFd(-1) + mSections(), + mMainFd(-1), + mMainDest(-1) { } @@ -77,56 +73,36 @@ ReportRequestSet::~ReportRequestSet() { } +// TODO: dedup on exact same args and fd, report the status back to listener! void ReportRequestSet::add(const sp<ReportRequest>& request) { mRequests.push_back(request); - mWritableCount++; + mSections.merge(request->args); } void ReportRequestSet::setMainFd(int fd) { mMainFd = fd; - mWritableCount++; } -status_t -ReportRequestSet::write(uint8_t const* buf, size_t size) +void +ReportRequestSet::setMainDest(int dest) { - status_t err = EBADF; - - // The streaming ones - int const N = mRequests.size(); - for (int i=N-1; i>=0; i--) { - sp<ReportRequest> request = mRequests[i]; - if (request->fd >= 0 && request->err == NO_ERROR) { - err = write_all(request->fd, buf, size); - if (err != NO_ERROR) { - request->err = err; - mWritableCount--; - } - } - } - - // The dropbox file - if (mMainFd >= 0) { - err = write_all(mMainFd, buf, size); - if (err != NO_ERROR) { - mMainFd = -1; - mWritableCount--; - } - } - - // Return an error only when there are no FDs to write. - return mWritableCount > 0 ? NO_ERROR : err; + mMainDest = dest; } +bool +ReportRequestSet::containsSection(int id) { + return mSections.containsSection(id); +} // ================================================================================ -Reporter::Reporter() - :args(), - batch() +Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; }; + +Reporter::Reporter(const char* directory) + :batch() { char buf[100]; @@ -134,10 +110,15 @@ Reporter::Reporter() mMaxSize = 100 * 1024 * 1024; mMaxCount = 100; + // string ends up with '/' is a directory + String8 dir = String8(directory); + if (directory[dir.size() - 1] != '/') dir += "/"; + mIncidentDirectory = dir.string(); + // There can't be two at the same time because it's on one thread. mStartTime = time(NULL); - strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime)); - mFilename = INCIDENT_DIRECTORY + buf; + strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime)); + mFilename = mIncidentDirectory + buf; } Reporter::~Reporter() @@ -151,34 +132,38 @@ Reporter::runReport() status_t err = NO_ERROR; bool needMainFd = false; int mainFd = -1; + int mainDest = -1; + HeaderSection headers; // See if we need the main file for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->fd < 0 && mainFd < 0) { needMainFd = true; + mainDest = (*it)->args.dest(); break; } } if (needMainFd) { // Create the directory - err = create_directory(INCIDENT_DIRECTORY); + if (!isTest) err = create_directory(mIncidentDirectory); if (err != NO_ERROR) { - goto done; + goto DONE; } // If there are too many files in the directory (for whatever reason), // delete the oldest ones until it's under the limit. Doing this first // does mean that we can go over, so the max size is not a hard limit. - clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount); + if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount); // Open the file. err = create_file(&mainFd); if (err != NO_ERROR) { - goto done; + goto DONE; } // Add to the set batch.setMainFd(mainFd); + batch.setMainDest(mainDest); } // Tell everyone that we're starting. @@ -189,32 +174,14 @@ Reporter::runReport() } // Write the incident headers - for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { - const sp<ReportRequest> request = (*it); - const vector<vector<int8_t>>& headers = request->args.headers(); - - for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); - buf++) { - int fd = request->fd >= 0 ? request->fd : mainFd; - - uint8_t buffer[20]; - uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER, - buf->size()); - write_all(fd, buffer, p-buffer); - - write_all(fd, (uint8_t const*)buf->data(), buf->size()); - // If there was an error now, there will be an error later and we will remove - // it from the list then. - } - } + headers.Execute(&batch); // For each of the report fields, see if we need it, and if so, execute the command // and report to those that care that we're doing it. for (const Section** section=SECTION_LIST; *section; section++) { const int id = (*section)->id; - ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string()); - - if (this->args.containsSection(id)) { + if (this->batch.containsSection(id)) { + ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string()); // Notify listener of starting for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { @@ -226,9 +193,9 @@ Reporter::runReport() // Execute - go get the data and write it into the file descriptors. err = (*section)->Execute(&batch); if (err != NO_ERROR) { - ALOGW("Incident section %s (%d) failed. Stopping report.", - (*section)->name.string(), id); - goto done; + ALOGW("Incident section %s (%d) failed: %s. Stopping report.", + (*section)->name.string(), id, strerror(-err)); + goto DONE; } // Notify listener of starting @@ -238,10 +205,11 @@ Reporter::runReport() IIncidentReportStatusListener::STATUS_FINISHED); } } + ALOGD("Finish incident report section %d '%s'", id, (*section)->name.string()); } } -done: +DONE: // Close the file. if (mainFd >= 0) { close(mainFd); @@ -270,7 +238,7 @@ done: // If the status was ok, delete the file. If not, leave it around until the next // boot or the next checkin. If the directory gets too big older files will // be rotated out. - unlink(mFilename.c_str()); + if(!isTest) unlink(mFilename.c_str()); } return REPORT_FINISHED; @@ -284,7 +252,7 @@ Reporter::create_file(int* fd) { const char* filename = mFilename.c_str(); - *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660); + *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); if (*fd < 0) { ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno)); return -errno; @@ -293,7 +261,7 @@ Reporter::create_file(int* fd) // Override umask. Not super critical. If it fails go on with life. chmod(filename, 0660); - if (chown(filename, AID_SYSTEM, AID_SYSTEM)) { + if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) { ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno)); status_t err = -errno; unlink(mFilename.c_str()); @@ -303,28 +271,34 @@ Reporter::create_file(int* fd) return NO_ERROR; } -// ================================================================================ Reporter::run_report_status_t Reporter::upload_backlog() { DIR* dir; struct dirent* entry; struct stat st; + status_t err; + + ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY); + if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) { + ALOGE("directory doesn't exist: %s", strerror(-err)); + return REPORT_FINISHED; + } - if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) { - ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string()); + if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) { + ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY); return REPORT_NEEDS_DROPBOX; } - String8 dirbase(INCIDENT_DIRECTORY + "/"); sp<DropBoxManager> dropbox = new DropBoxManager(); // Enumerate, count and add up size + int count = 0; while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.') { continue; } - String8 filename = dirbase + entry->d_name; + String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name; if (stat(filename.string(), &st) != 0) { ALOGE("Unable to stat file %s", filename.string()); continue; @@ -343,8 +317,9 @@ Reporter::upload_backlog() // boot or the next checkin. If the directory gets too big older files will // be rotated out. unlink(filename.string()); + count++; } - + ALOGD("Successfully uploaded %d files to Dropbox.", count); closedir(dir); return REPORT_FINISHED; |