1#include <uds/client_channel_factory.h>
2
3#include <errno.h>
4#include <log/log.h>
5#include <sys/socket.h>
6#include <sys/un.h>
7#include <unistd.h>
8
9#include <chrono>
10#include <thread>
11
12#include <uds/channel_manager.h>
13#include <uds/client_channel.h>
14#include <uds/ipc_helper.h>
15
16using std::chrono::duration_cast;
17using std::chrono::steady_clock;
18
19namespace android {
20namespace pdx {
21namespace uds {
22
23std::string ClientChannelFactory::GetRootEndpointPath() {
24  return "/dev/socket/pdx";
25}
26
27std::string ClientChannelFactory::GetEndpointPath(
28    const std::string& endpoint_path) {
29  std::string path;
30  if (!endpoint_path.empty()) {
31    if (endpoint_path.front() == '/')
32      path = endpoint_path;
33    else
34      path = GetRootEndpointPath() + '/' + endpoint_path;
35  }
36  return path;
37}
38
39ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path)
40    : endpoint_path_{GetEndpointPath(endpoint_path)} {}
41
42ClientChannelFactory::ClientChannelFactory(LocalHandle socket)
43    : socket_{std::move(socket)} {}
44
45std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
46    const std::string& endpoint_path) {
47  return std::unique_ptr<pdx::ClientChannelFactory>{
48      new ClientChannelFactory{endpoint_path}};
49}
50
51std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
52    LocalHandle socket) {
53  return std::unique_ptr<pdx::ClientChannelFactory>{
54      new ClientChannelFactory{std::move(socket)}};
55}
56
57Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
58    int64_t timeout_ms) const {
59  Status<void> status;
60
61  bool connected = socket_.IsValid();
62  if (!connected) {
63    socket_.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
64    LOG_ALWAYS_FATAL_IF(
65        endpoint_path_.empty(),
66        "ClientChannelFactory::Connect: unspecified socket path");
67  }
68
69  if (!socket_) {
70    ALOGE("ClientChannelFactory::Connect: socket error: %s", strerror(errno));
71    return ErrorStatus(errno);
72  }
73
74  bool use_timeout = (timeout_ms >= 0);
75  auto now = steady_clock::now();
76  auto time_end = now + std::chrono::milliseconds{timeout_ms};
77
78  int max_eaccess = 5;  // Max number of times to retry when EACCES returned.
79  while (!connected) {
80    int64_t timeout = -1;
81    if (use_timeout) {
82      auto remaining = time_end - now;
83      timeout = duration_cast<std::chrono::milliseconds>(remaining).count();
84      if (timeout < 0)
85        return ErrorStatus(ETIMEDOUT);
86    }
87    sockaddr_un remote;
88    remote.sun_family = AF_UNIX;
89    strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path));
90    remote.sun_path[sizeof(remote.sun_path) - 1] = '\0';
91    ALOGD("ClientChannelFactory: Waiting for endpoint at %s", remote.sun_path);
92    status = WaitForEndpoint(endpoint_path_, timeout);
93    if (!status)
94      return ErrorStatus(status.error());
95
96    ALOGD("ClientChannelFactory: Connecting to %s", remote.sun_path);
97    int ret = RETRY_EINTR(connect(
98        socket_.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
99    if (ret == -1) {
100      ALOGD("ClientChannelFactory: Connect error %d: %s", errno,
101            strerror(errno));
102      // if |max_eaccess| below reaches zero when errno is EACCES, the control
103      // flows into the next "else if" statement and a permanent error is
104      // returned from this function.
105      if (errno == ECONNREFUSED || (errno == EACCES && max_eaccess-- > 0)) {
106        // Connection refused/Permission denied can be the result of connecting
107        // too early (the service socket is created but its access rights are
108        // not set or not being listened to yet).
109        ALOGD("ClientChannelFactory: %s, waiting...", strerror(errno));
110        using namespace std::literals::chrono_literals;
111        std::this_thread::sleep_for(100ms);
112      } else if (errno != ENOENT && errno != ENOTDIR) {
113        // ENOENT/ENOTDIR might mean that the socket file/directory containing
114        // it has been just deleted. Try to wait for its creation and do not
115        // return an error immediately.
116        ALOGE(
117            "ClientChannelFactory::Connect: Failed to initialize connection "
118            "when connecting: %s",
119            strerror(errno));
120        return ErrorStatus(errno);
121      }
122    } else {
123      connected = true;
124      ALOGD("ClientChannelFactory: Connected successfully to %s...",
125            remote.sun_path);
126      ChannelConnectionInfo<LocalHandle> connection_info;
127      status = ReceiveData(socket_.Borrow(), &connection_info);
128      if (!status)
129        return status.error_status();
130      socket_ = std::move(connection_info.channel_fd);
131      if (!socket_) {
132        ALOGE("ClientChannelFactory::Connect: Failed to obtain channel socket");
133        return ErrorStatus(EIO);
134      }
135    }
136    if (use_timeout)
137      now = steady_clock::now();
138  }  // while (!connected)
139
140  RequestHeader<BorrowedHandle> request;
141  InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
142  status = SendData(socket_.Borrow(), request);
143  if (!status)
144    return status.error_status();
145  ResponseHeader<LocalHandle> response;
146  status = ReceiveData(socket_.Borrow(), &response);
147  if (!status)
148    return status.error_status();
149  int ref = response.ret_code;
150  if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size())
151    return ErrorStatus(EIO);
152
153  LocalHandle event_fd = std::move(response.file_descriptors[ref]);
154  return ClientChannel::Create(ChannelManager::Get().CreateHandle(
155      std::move(socket_), std::move(event_fd)));
156}
157
158}  // namespace uds
159}  // namespace pdx
160}  // namespace android
161