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