1 /* 2 * Copyright 2018 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 #ifndef ANDROID_AUDIO_FD_TO_STRING_H 18 #define ANDROID_AUDIO_FD_TO_STRING_H 19 20 #include <android-base/unique_fd.h> 21 #include <fcntl.h> 22 #include <poll.h> 23 #include <unistd.h> 24 #include <utils/Timers.h> 25 #include <chrono> 26 #include <future> 27 #include <iostream> 28 #include <optional> 29 #include <sstream> 30 #include <string> 31 #include <string_view> 32 33 #include "clock.h" 34 35 namespace android { 36 namespace audio_utils { 37 38 /** 39 * FdToStringOldImpl 40 * 41 * Captures string data written to a file descriptor. 42 * The class will furnish a writable file descriptor by fd(). 43 * The string may be read through closeAndGetString(). 44 */ 45 class FdToStringOldImpl { 46 public: 47 /** 48 * \param prefix is the prefix string prepended to each new line. 49 * \param timeoutMs is the total timeout to wait for obtaining data in milliseconds. 50 */ 51 explicit FdToStringOldImpl(const std::string& prefix = "- ", int timeoutMs = 200) mPrefix(prefix)52 : mPrefix(prefix), mTimeoutTimeNs(systemTime() + timeoutMs * NANOS_PER_MILLISECOND) { 53 const int status = pipe2(mPipeFd, O_CLOEXEC); 54 if (status == 0) { 55 mOutput = std::async(std::launch::async, reader, mPipeFd[0], mTimeoutTimeNs, mPrefix); 56 } 57 // on initialization failure fd() returns -1. 58 } 59 ~FdToStringOldImpl()60 ~FdToStringOldImpl() { 61 for (auto& fd : mPipeFd) { 62 if (fd >= 0) { 63 close(fd); 64 fd = -1; 65 } 66 } 67 } 68 69 /** 70 * Returns the write end of the pipe as a file descriptor or -1 if invalid or already closed. 71 * Do not close this fd directly as this class should own the fd. Instead, use 72 * closeAndGetString() to close the fd and return the string. 73 */ borrowFdUnsafe()74 int borrowFdUnsafe() const { return mPipeFd[1]; } 75 76 /** 77 * Returns the string representation of data written to the fd. Awaits reader thread. 78 * 79 * All writers should have returned by this point. 80 * 81 * An empty string is returned on initialization failure or timeout. Closes fd. 82 */ closeAndGetString()83 std::string closeAndGetString() { 84 if (!mOutput.valid()) return ""; 85 if (mPipeFd[1] >= 0) { 86 close(mPipeFd[1]); 87 mPipeFd[1] = -1; 88 } 89 const int waitMs = toMillisecondTimeoutDelay(systemTime(), mTimeoutTimeNs); 90 std::future_status status = mOutput.wait_for(std::chrono::milliseconds(waitMs)); 91 return status == std::future_status::ready ? mOutput.get() : ""; 92 } 93 94 private: reader(int fd,int64_t timeoutTimeNs,std::string prefix)95 static std::string reader(int fd, int64_t timeoutTimeNs, std::string prefix) { 96 char buf[4096]; 97 int red; 98 std::stringstream ss; 99 bool requiresPrefix = true; 100 101 while (true) { 102 struct pollfd pfd = { 103 .fd = fd, 104 .events = POLLIN | POLLRDHUP, 105 }; 106 const int waitMs = toMillisecondTimeoutDelay(systemTime(), timeoutTimeNs); 107 // ALOGD("waitMs: %d", waitMs); 108 if (waitMs <= 0) break; 109 const int retval = poll(&pfd, 1 /* nfds*/, waitMs); 110 // error, timeout, or hangup (without data to read) 111 if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break; 112 // data should be available 113 if ((red = read(fd, buf, sizeof(buf))) <= 0) break; 114 char *delim, *bptr = buf; 115 while (!prefix.empty() && (delim = (char*)memchr(bptr, '\n', red)) != nullptr) { 116 if (requiresPrefix) ss << prefix; 117 const size_t line = delim - bptr + 1; 118 ss.write(bptr, line); 119 bptr += line; 120 red -= line; 121 requiresPrefix = true; 122 } 123 if (red > 0) { 124 ss << prefix; 125 ss.write(bptr, red); 126 requiresPrefix = false; 127 } 128 } 129 return ss.str(); 130 } 131 132 const std::string mPrefix; 133 const int64_t mTimeoutTimeNs; 134 int mPipeFd[2] = {-1, -1}; 135 std::future<std::string> mOutput; 136 }; 137 138 /** 139 * Launch reader task which accumulates data written to the fd that this class exposes. 140 * Usage as follows: 141 * { 142 * writer = FdToString::createWriter(); // fork point, reader launched 143 * sendFdToWriters(writer.borrowFdUnsafe()); // fd is safe while writer is valid 144 * st = FdToString::closeWriterAndGetString(std::move(writer)); 145 * // join point (writers must be done) 146 * } // writer dtor closes fd, joins reader if close not called 147 * 148 * This class expects that the write fd is unduped when close is called, otherwise the reader will 149 * always hit the timeout. We implicitly trust that the borrowed fd won't be duped (or that its 150 * dupes will be closed by closeWriterAndGetString()). 151 * Note, the reader closes the fd to signal which closes the read end of the pipe. If the writer is 152 * living in a process without signal(SIGPIPE, SIGIGN), they will crash. 153 */ 154 class FdToString { 155 public: 156 class Writer { 157 public: 158 /** 159 * Returns the write end of the pipe as a file descriptor. 160 * Non-Owning reference! This object must be valid to keep accessing the fd. 161 * Do not close this fd directly as this class should own the fd. 162 * Leaking dupes of this fd will keep the reader alive. 163 * Use closeWriterAndGetString(Writer&& w) to consume this object and return the string. 164 * The fd returned by this method is invalid after this point. 165 */ borrowFdUnsafe()166 int borrowFdUnsafe() const { return mWriteFd.get(); } 167 getFd()168 const android::base::unique_fd& getFd() const { return mWriteFd; } 169 170 private: 171 friend FdToString; 172 // Pre-condition: fd and future both valid. Should only be called from create. Writer(android::base::unique_fd writeFd,std::future<std::string> output)173 Writer(android::base::unique_fd writeFd, std::future<std::string> output) 174 : mOutput(std::move(output)), mWriteFd(std::move(writeFd)) {} 175 176 std::future<std::string> mOutput; 177 android::base::unique_fd mWriteFd; // order important! must be destroyed first to join 178 }; 179 180 public: 181 /** 182 * Factory method for Writer object. Launches the async reader. 183 * \param prefix is the prefix string prepended to each new line. 184 * \param timeoutMs is the total timeout to wait for obtaining data in milliseconds. 185 * \returns nullopt on init error. 186 */ 187 static std::optional<Writer> createWriter( 188 std::string_view prefix_ = "- ", 189 std::chrono::milliseconds timeout = std::chrono::milliseconds{200}) { 190 android::base::unique_fd readFd, writeFd; 191 if (!android::base::Pipe(&readFd, &writeFd)) return {}; 192 const auto flags = fcntl(readFd.get(), F_GETFL); 193 if (flags < 0) return {}; 194 // Set (only) the reader as non-blocking. We want to only read until the deadline. 195 if (fcntl(readFd, F_SETFL, flags | O_NONBLOCK) < 0) return {}; 196 const auto deadline = systemTime() + std::chrono::nanoseconds{timeout}.count(); 197 // Launch async reader task, will return after deadline 198 return Writer{ 199 std::move(writeFd), 200 std::async(std::launch::async, 201 // reader task to follow, logically oneshot 202 [fd = std::move(readFd), deadline, 203 prefix = std::string{prefix_}]() mutable { 204 char buf[4096]; 205 std::string out; 206 bool requiresPrefix = true; 207 208 while (true) { 209 struct pollfd pfd = { 210 .fd = fd.get(), 211 .events = POLLIN | POLLRDHUP, 212 }; 213 const int waitMs = 214 toMillisecondTimeoutDelay(systemTime(), deadline); 215 if (waitMs <= 0) break; 216 const int retval = poll(&pfd, 1 /* nfds*/, waitMs); 217 // break on error or timeout 218 if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break; 219 // data is available 220 int red = read(fd, buf, sizeof(buf)); 221 if (red < 0) { 222 break; 223 } else if (red == 0) { 224 continue; 225 } 226 227 std::string_view sv{buf, static_cast<size_t>(red)}; 228 229 if (!prefix.empty()) { 230 size_t ind; 231 while ((ind = sv.find('\n', 0)) != std::string_view::npos) { 232 if (requiresPrefix) { 233 out.append(prefix); 234 } 235 out.append(sv.data(), ind + 1); 236 sv.remove_prefix(ind + 1); 237 requiresPrefix = true; 238 } 239 if (sv.length() > 0) { 240 out.append(sv); 241 requiresPrefix = false; 242 } 243 } else { 244 out.append(sv); 245 } 246 } 247 // Explicit clear, because state is kept until future consumption 248 fd.reset(); 249 return out; 250 })}; 251 } 252 253 /** 254 * Closes the write side. Returns the string representation of data written to the fd. 255 * Awaits reader thread. 256 * 257 * All writers should have returned by this point. 258 * 259 */ closeWriterAndGetString(Writer && writer)260 static std::string closeWriterAndGetString(Writer&& writer) { 261 // Closes the fd, which finishes the reader 262 writer.mWriteFd.reset(); 263 // moved out of future + NVRO 264 return writer.mOutput.get(); 265 } 266 }; 267 268 } // namespace audio_utils 269 } // namespace android 270 271 #endif // !ANDROID_AUDIO_FD_TO_STRING_H 272