1 /*
2 * Copyright (C) 2017 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 #include "PipeRelay.h"
18
19 #include <sys/select.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <atomic>
25
26 #include <utils/Thread.h>
27
28 namespace android {
29 namespace lshal {
30
31 static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
32
getThreadName(std::string interfaceName,const std::string & instanceName)33 static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
34 auto dot = interfaceName.rfind(".");
35 if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
36 return "RelayThread_" + interfaceName + "_" + instanceName;
37 }
38
39 struct PipeRelay::RelayThread : public Thread {
40 explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
41 const std::string &fqName);
42
43 bool threadLoop() override;
44 void setFinished();
45
46 private:
47 int mFd;
48 std::ostream &mOutStream;
49 NullableOStream<std::ostream> mErrStream;
50
51 // If we were to use requestExit() and exitPending() instead, threadLoop()
52 // may not run at all by the time ~PipeRelay is called (i.e. debug() has
53 // returned from HAL). By using our own flag, we ensure that select() and
54 // read() are executed until data are drained.
55 std::atomic_bool mFinished;
56
57 std::string mFqName;
58
59 DISALLOW_COPY_AND_ASSIGN(RelayThread);
60 };
61
62 ////////////////////////////////////////////////////////////////////////////////
63
RelayThread(int fd,std::ostream & os,const NullableOStream<std::ostream> & err,const std::string & fqName)64 PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
65 const NullableOStream<std::ostream> &err,
66 const std::string &fqName)
67 : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
68
threadLoop()69 bool PipeRelay::RelayThread::threadLoop() {
70 char buffer[1024];
71
72 fd_set set;
73 FD_ZERO(&set);
74 FD_SET(mFd, &set);
75
76 struct timeval timeout = READ_TIMEOUT;
77
78 int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
79 if (res < 0) {
80 mErrStream << "debug " << mFqName << ": select() failed";
81 return false;
82 }
83
84 if (res == 0 || !FD_ISSET(mFd, &set)) {
85 if (mFinished) {
86 mErrStream << "debug " << mFqName
87 << ": timeout reading from pipe, output may be truncated.";
88 return false;
89 }
90 // timeout, but debug() has not returned, so wait for HAL to finish.
91 return true;
92 }
93
94 // FD_ISSET(mFd, &set) == true. Data available, start reading
95 ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
96
97 if (n < 0) {
98 mErrStream << "debug " << mFqName << ": read() failed";
99 }
100
101 if (n <= 0) {
102 return false;
103 }
104
105 mOutStream.write(buffer, n);
106
107 return true;
108 }
109
setFinished()110 void PipeRelay::RelayThread::setFinished() {
111 mFinished = true;
112 }
113
114 ////////////////////////////////////////////////////////////////////////////////
115
PipeRelay(std::ostream & os,const NullableOStream<std::ostream> & err,const std::string & interfaceName,const std::string & instanceName)116 PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
117 const std::string &interfaceName, const std::string &instanceName)
118 : mInitCheck(NO_INIT) {
119 int res = pipe(mFds);
120
121 if (res < 0) {
122 mInitCheck = -errno;
123 return;
124 }
125
126 mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
127 mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
128 }
129
CloseFd(int * fd)130 void PipeRelay::CloseFd(int *fd) {
131 if (*fd >= 0) {
132 close(*fd);
133 *fd = -1;
134 }
135 }
136
~PipeRelay()137 PipeRelay::~PipeRelay() {
138 CloseFd(&mFds[1]);
139
140 if (mThread != nullptr) {
141 mThread->setFinished();
142 mThread->join();
143 mThread.clear();
144 }
145
146 CloseFd(&mFds[0]);
147 }
148
initCheck() const149 status_t PipeRelay::initCheck() const {
150 return mInitCheck;
151 }
152
fd() const153 int PipeRelay::fd() const {
154 return mFds[1];
155 }
156
157 } // namespace lshal
158 } // namespace android
159