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