| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "incidentd" |
| |
| #include "FdBuffer.h" |
| |
| #include <cutils/log.h> |
| #include <utils/SystemClock.h> |
| |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <unistd.h> |
| #include <wait.h> |
| |
| const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB |
| const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max |
| |
| FdBuffer::FdBuffer() |
| :mBuffers(), |
| mStartTime(-1), |
| mFinishTime(-1), |
| mCurrentWritten(-1), |
| mTimedOut(false), |
| mTruncated(false) |
| { |
| } |
| |
| FdBuffer::~FdBuffer() |
| { |
| const int N = mBuffers.size(); |
| for (int i=0; i<N; i++) { |
| uint8_t* buf = mBuffers[i]; |
| free(buf); |
| } |
| } |
| |
| status_t |
| FdBuffer::read(int fd, int64_t timeout) |
| { |
| struct pollfd pfds = { |
| .fd = fd, |
| .events = POLLIN |
| }; |
| mStartTime = uptimeMillis(); |
| |
| fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); |
| |
| uint8_t* buf = NULL; |
| while (true) { |
| if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) { |
| if (mBuffers.size() == MAX_BUFFER_COUNT) { |
| mTruncated = true; |
| break; |
| } |
| buf = (uint8_t*)malloc(BUFFER_SIZE); |
| if (buf == NULL) { |
| return NO_MEMORY; |
| } |
| mBuffers.push_back(buf); |
| mCurrentWritten = 0; |
| } |
| |
| int64_t remainingTime = (mStartTime + timeout) - uptimeMillis(); |
| if (remainingTime <= 0) { |
| mTimedOut = true; |
| break; |
| } |
| |
| int count = poll(&pfds, 1, remainingTime); |
| if (count == 0) { |
| mTimedOut = true; |
| break; |
| } else if (count < 0) { |
| return -errno; |
| } else { |
| if ((pfds.revents & POLLERR) != 0) { |
| return errno != 0 ? -errno : UNKNOWN_ERROR; |
| } else { |
| ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten); |
| if (amt < 0) { |
| if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| continue; |
| } else { |
| return -errno; |
| } |
| } else if (amt == 0) { |
| break; |
| } |
| mCurrentWritten += amt; |
| } |
| } |
| } |
| |
| mFinishTime = uptimeMillis(); |
| return NO_ERROR; |
| } |
| |
| status_t |
| FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs) |
| { |
| struct pollfd pfds[] = { |
| { .fd = fd, .events = POLLIN }, |
| { .fd = toFd, .events = POLLOUT }, |
| { .fd = fromFd, .events = POLLIN }, |
| }; |
| |
| mStartTime = uptimeMillis(); |
| |
| // mark all fds non blocking |
| fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); |
| fcntl(toFd, F_SETFL, fcntl(toFd, F_GETFL, 0) | O_NONBLOCK); |
| fcntl(fromFd, F_SETFL, fcntl(fromFd, F_GETFL, 0) | O_NONBLOCK); |
| |
| // A circular buffer holds data read from fd and writes to parsing process |
| uint8_t cirBuf[BUFFER_SIZE]; |
| size_t cirSize = 0; |
| int rpos = 0, wpos = 0; |
| |
| // This is the buffer used to store processed data |
| uint8_t* buf = NULL; |
| while (true) { |
| if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) { |
| if (mBuffers.size() == MAX_BUFFER_COUNT) { |
| mTruncated = true; |
| break; |
| } |
| buf = (uint8_t*)malloc(BUFFER_SIZE); |
| if (buf == NULL) { |
| return NO_MEMORY; |
| } |
| mBuffers.push_back(buf); |
| mCurrentWritten = 0; |
| } |
| |
| int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis(); |
| if (remainingTime <= 0) { |
| mTimedOut = true; |
| break; |
| } |
| |
| // wait for any pfds to be ready to perform IO |
| int count = poll(pfds, 3, remainingTime); |
| if (count == 0) { |
| mTimedOut = true; |
| break; |
| } else if (count < 0) { |
| return -errno; |
| } |
| |
| // make sure no errors occur on any fds |
| for (int i = 0; i < 3; ++i) { |
| if ((pfds[i].revents & POLLERR) != 0) { |
| return errno != 0 ? -errno : UNKNOWN_ERROR; |
| } |
| } |
| |
| // read from fd |
| if (cirSize != BUFFER_SIZE && pfds[0].fd != -1) { |
| ssize_t amt; |
| if (rpos >= wpos) { |
| amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos); |
| } else { |
| amt = :: read(fd, cirBuf + rpos, wpos - rpos); |
| } |
| if (amt < 0) { |
| if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { |
| return -errno; |
| } // otherwise just continue |
| } else if (amt == 0) { // reach EOF so don't have to poll pfds[0]. |
| ::close(pfds[0].fd); |
| pfds[0].fd = -1; |
| } else { |
| rpos += amt; |
| cirSize += amt; |
| } |
| } |
| |
| // write to parsing process |
| if (cirSize > 0 && pfds[1].fd != -1) { |
| ssize_t amt; |
| if (rpos > wpos) { |
| amt = ::write(toFd, cirBuf + wpos, rpos - wpos); |
| } else { |
| amt = ::write(toFd, cirBuf + wpos, BUFFER_SIZE - wpos); |
| } |
| if (amt < 0) { |
| if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { |
| return -errno; |
| } // otherwise just continue |
| } else { |
| wpos += amt; |
| cirSize -= amt; |
| } |
| } |
| |
| // if buffer is empty and fd is closed, close write fd. |
| if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) { |
| ::close(pfds[1].fd); |
| pfds[1].fd = -1; |
| } |
| |
| // circular buffer, reset rpos and wpos |
| if (rpos >= BUFFER_SIZE) { |
| rpos = 0; |
| } |
| if (wpos >= BUFFER_SIZE) { |
| wpos = 0; |
| } |
| |
| // read from parsing process |
| ssize_t amt = ::read(fromFd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten); |
| if (amt < 0) { |
| if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { |
| return -errno; |
| } // otherwise just continue |
| } else if (amt == 0) { |
| break; |
| } else { |
| mCurrentWritten += amt; |
| } |
| } |
| |
| mFinishTime = uptimeMillis(); |
| return NO_ERROR; |
| } |
| |
| size_t |
| FdBuffer::size() |
| { |
| return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten; |
| } |
| |
| status_t |
| FdBuffer::write(ReportRequestSet* reporter) |
| { |
| const int N = mBuffers.size() - 1; |
| for (int i=0; i<N; i++) { |
| reporter->write(mBuffers[i], BUFFER_SIZE); |
| } |
| reporter->write(mBuffers[N], mCurrentWritten); |
| return NO_ERROR; |
| } |
| |
| |