1/*
2 * Copyright (C) 2016 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#include "common/libs/fs/shared_fd.h"
17
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <cstddef>
21#include <errno.h>
22#include <fcntl.h>
23#include <netinet/in.h>
24#include <unistd.h>
25
26#include "common/libs/auto_resources/auto_resources.h"
27#include "common/libs/glog/logging.h"
28#include "common/libs/fs/shared_select.h"
29
30// #define ENABLE_GCE_SHARED_FD_LOGGING 1
31
32namespace {
33using cvd::SharedFDSet;
34
35void MarkAll(const SharedFDSet& input, fd_set* dest, int* max_index) {
36  for (SharedFDSet::const_iterator it = input.begin(); it != input.end();
37       ++it) {
38    (*it)->Set(dest, max_index);
39  }
40}
41
42void CheckMarked(fd_set* in_out_mask, SharedFDSet* in_out_set) {
43  if (!in_out_set) {
44    return;
45  }
46  SharedFDSet save;
47  save.swap(in_out_set);
48  for (SharedFDSet::iterator it = save.begin(); it != save.end(); ++it) {
49    if ((*it)->IsSet(in_out_mask)) {
50      in_out_set->Set(*it);
51    }
52  }
53}
54}  // namespace
55
56namespace cvd {
57
58bool FileInstance::CopyFrom(FileInstance& in) {
59  AutoFreeBuffer buffer;
60  buffer.Resize(8192);
61  while (true) {
62    ssize_t num_read = in.Read(buffer.data(), buffer.size());
63    if (!num_read) {
64      return true;
65    }
66    if (num_read == -1) {
67      return false;
68    }
69    if (num_read > 0) {
70      if (Write(buffer.data(), num_read) != num_read) {
71        // The caller will have to log an appropriate message.
72        return false;
73      }
74    }
75  }
76  return true;
77}
78
79void FileInstance::Close() {
80  AutoFreeBuffer message;
81  if (fd_ == -1) {
82    errno_ = EBADF;
83  } else if (close(fd_) == -1) {
84    errno_ = errno;
85    if (identity_.size()) {
86      message.PrintF("%s: %s failed (%s)", __FUNCTION__, identity_.data(),
87                     StrError());
88      Log(message.data());
89    }
90  } else {
91    if (identity_.size()) {
92      message.PrintF("%s: %s succeeded", __FUNCTION__, identity_.data());
93      Log(message.data());
94    }
95  }
96  fd_ = -1;
97}
98
99void FileInstance::Identify(const char* identity) {
100  identity_.PrintF("fd=%d @%p is %s", fd_, this, identity);
101  AutoFreeBuffer message;
102  message.PrintF("%s: %s", __FUNCTION__, identity_.data());
103  Log(message.data());
104}
105
106bool FileInstance::IsSet(fd_set* in) const {
107  if (IsOpen() && FD_ISSET(fd_, in)) {
108    return true;
109  }
110  return false;
111}
112
113#if ENABLE_GCE_SHARED_FD_LOGGING
114void FileInstance::Log(const char* message) {
115  LOG(INFO) << message;
116}
117#else
118void FileInstance::Log(const char*) {}
119#endif
120
121void FileInstance::Set(fd_set* dest, int* max_index) const {
122  if (!IsOpen()) {
123    return;
124  }
125  if (fd_ >= *max_index) {
126    *max_index = fd_ + 1;
127  }
128  FD_SET(fd_, dest);
129}
130
131int Select(SharedFDSet* read_set, SharedFDSet* write_set,
132           SharedFDSet* error_set, struct timeval* timeout) {
133  int max_index = 0;
134  fd_set readfds;
135  FD_ZERO(&readfds);
136  if (read_set) {
137    MarkAll(*read_set, &readfds, &max_index);
138  }
139  fd_set writefds;
140  FD_ZERO(&writefds);
141  if (write_set) {
142    MarkAll(*write_set, &writefds, &max_index);
143  }
144  fd_set errorfds;
145  FD_ZERO(&errorfds);
146  if (error_set) {
147    MarkAll(*error_set, &errorfds, &max_index);
148  }
149
150  int rval = TEMP_FAILURE_RETRY(
151      select(max_index, &readfds, &writefds, &errorfds, timeout));
152  FileInstance::Log("select\n");
153  CheckMarked(&readfds, read_set);
154  CheckMarked(&writefds, write_set);
155  CheckMarked(&errorfds, error_set);
156  return rval;
157}
158
159static void MakeAddress(const char* name, bool abstract,
160                        struct sockaddr_un* dest, socklen_t* len) {
161  memset(dest, 0, sizeof(*dest));
162  dest->sun_family = AF_UNIX;
163  // sun_path is NOT expected to be nul-terminated.
164  // See man 7 unix.
165  size_t namelen;
166  if (abstract) {
167    // ANDROID_SOCKET_NAMESPACE_ABSTRACT
168    namelen = strlen(name);
169    CHECK_LE(namelen, sizeof(dest->sun_path) - 1)
170        << "MakeAddress failed. Name=" << name << " is longer than allowed.";
171    dest->sun_path[0] = 0;
172    memcpy(dest->sun_path + 1, name, namelen);
173  } else {
174    // ANDROID_SOCKET_NAMESPACE_RESERVED
175    // ANDROID_SOCKET_NAMESPACE_FILESYSTEM
176    // TODO(pinghao): Distinguish between them?
177    namelen = strlen(name);
178    CHECK_LE(namelen, sizeof(dest->sun_path))
179        << "MakeAddress failed. Name=" << name << " is longer than allowed.";
180    strncpy(dest->sun_path, name, strlen(name));
181  }
182  *len = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
183}
184
185SharedFD SharedFD::SocketSeqPacketServer(const char* name, mode_t mode) {
186  return SocketLocalServer(name, false, SOCK_SEQPACKET, mode);
187}
188
189SharedFD SharedFD::SocketSeqPacketClient(const char* name) {
190  return SocketLocalClient(name, false, SOCK_SEQPACKET);
191}
192
193SharedFD SharedFD::TimerFD(int clock, int flags) {
194  int fd = timerfd_create(clock, flags);
195  if (fd == -1) {
196    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
197  } else {
198    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
199  }
200}
201
202SharedFD SharedFD::Accept(const FileInstance& listener, struct sockaddr* addr,
203                          socklen_t* addrlen) {
204  return SharedFD(
205      std::shared_ptr<FileInstance>(listener.Accept(addr, addrlen)));
206}
207
208SharedFD SharedFD::Accept(const FileInstance& listener) {
209  return SharedFD::Accept(listener, NULL, NULL);
210}
211
212SharedFD SharedFD::Dup(int unmanaged_fd) {
213  int fd = dup(unmanaged_fd);
214  return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
215}
216
217bool SharedFD::Pipe(SharedFD* fd0, SharedFD* fd1) {
218  int fds[2];
219  int rval = pipe(fds);
220  if (rval != -1) {
221    (*fd0) = std::shared_ptr<FileInstance>(new FileInstance(fds[0], errno));
222    (*fd1) = std::shared_ptr<FileInstance>(new FileInstance(fds[1], errno));
223    return true;
224  }
225  return false;
226}
227
228SharedFD SharedFD::Event(int initval, int flags) {
229  return std::shared_ptr<FileInstance>(
230      new FileInstance(eventfd(initval, flags), errno));
231}
232
233SharedFD SharedFD::Epoll(int flags) {
234  return std::shared_ptr<FileInstance>(
235      new FileInstance(epoll_create1(flags), errno));
236}
237
238inline bool SharedFD::SocketPair(int domain, int type, int protocol,
239                                 SharedFD* fd0, SharedFD* fd1) {
240  int fds[2];
241  int rval = socketpair(domain, type, protocol, fds);
242  if (rval != -1) {
243    (*fd0) = std::shared_ptr<FileInstance>(new FileInstance(fds[0], errno));
244    (*fd1) = std::shared_ptr<FileInstance>(new FileInstance(fds[1], errno));
245    return true;
246  }
247  return false;
248}
249
250SharedFD SharedFD::Open(const char* path, int flags, mode_t mode) {
251  int fd = TEMP_FAILURE_RETRY(open(path, flags, mode));
252  if (fd == -1) {
253    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
254  } else {
255    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
256  }
257}
258
259SharedFD SharedFD::Socket(int domain, int socket_type, int protocol) {
260  int fd = TEMP_FAILURE_RETRY(socket(domain, socket_type, protocol));
261  if (fd == -1) {
262    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
263  } else {
264    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
265  }
266}
267
268SharedFD SharedFD::SocketLocalClient(const char* name, bool abstract,
269                                     int in_type) {
270  struct sockaddr_un addr;
271  socklen_t addrlen;
272  MakeAddress(name, abstract, &addr, &addrlen);
273  SharedFD rval = SharedFD::Socket(PF_UNIX, in_type, 0);
274  if (!rval->IsOpen()) {
275    return rval;
276  }
277  if (rval->Connect(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
278    LOG(ERROR) << "Connect failed; name=" << name << ": " << rval->StrError();
279    return SharedFD(
280        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
281  }
282  return rval;
283}
284
285SharedFD SharedFD::SocketLocalClient(int port, int type) {
286  sockaddr_in addr{};
287  addr.sin_family = AF_INET;
288  addr.sin_port = htons(port);
289  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
290  SharedFD rval = SharedFD::Socket(AF_INET, type, 0);
291  if (!rval->IsOpen()) {
292    return rval;
293  }
294  if (rval->Connect(reinterpret_cast<const sockaddr*>(&addr),
295                    sizeof addr) < 0) {
296    LOG(ERROR) << "Connect() failed" << rval->StrError();
297    return SharedFD(
298        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
299  }
300  return rval;
301}
302
303SharedFD SharedFD::SocketLocalServer(int port, int type) {
304  struct sockaddr_in addr;
305  memset(&addr, 0, sizeof(addr));
306  addr.sin_family = AF_INET;
307  addr.sin_port = htons(port);
308  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
309  SharedFD rval = SharedFD::Socket(AF_INET, type, 0);
310  if(!rval->IsOpen()) {
311    return rval;
312  }
313  int n = 1;
314  if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
315    LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
316    return SharedFD(
317        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
318  }
319  if(rval->Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
320    LOG(ERROR) << "Bind failed " << rval->StrError();
321    return SharedFD(
322        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
323  }
324  if (type == SOCK_STREAM) {
325    if (rval->Listen(4) < 0) {
326      LOG(ERROR) << "Listen failed " << rval->StrError();
327      return SharedFD(std::shared_ptr<FileInstance>(
328          new FileInstance(-1, rval->GetErrno())));
329    }
330  }
331  return rval;
332}
333
334SharedFD SharedFD::SocketLocalServer(const char* name, bool abstract,
335                                     int in_type, mode_t mode) {
336  // DO NOT UNLINK addr.sun_path. It does NOT have to be null-terminated.
337  // See man 7 unix for more details.
338  if (!abstract) (void)unlink(name);
339
340  struct sockaddr_un addr;
341  socklen_t addrlen;
342  MakeAddress(name, abstract, &addr, &addrlen);
343  SharedFD rval = SharedFD::Socket(PF_UNIX, in_type, 0);
344  if (!rval->IsOpen()) {
345    return rval;
346  }
347
348  int n = 1;
349  if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
350    LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
351    return SharedFD(
352        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
353  }
354  if (rval->Bind(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
355    LOG(ERROR) << "Bind failed; name=" << name << ": " << rval->StrError();
356    return SharedFD(
357        std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
358  }
359
360  /* Only the bottom bits are really the socket type; there are flags too. */
361  constexpr int SOCK_TYPE_MASK = 0xf;
362
363  // Connection oriented sockets: start listening.
364  if ((in_type & SOCK_TYPE_MASK) == SOCK_STREAM) {
365    // Follows the default from socket_local_server
366    if (rval->Listen(1) == -1) {
367      LOG(ERROR) << "Listen failed: " << rval->StrError();
368      return SharedFD(std::shared_ptr<FileInstance>(
369          new FileInstance(-1, rval->GetErrno())));
370    }
371  }
372
373  if (!abstract) {
374    if (TEMP_FAILURE_RETRY(chmod(name, mode)) == -1) {
375      LOG(ERROR) << "chmod failed: " << strerror(errno);
376      // However, continue since we do have a listening socket
377    }
378  }
379  return rval;
380}
381
382}  // namespace cvd
383