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