Home | History | Annotate | Download | only in libpdx_uds
      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 
     16 using std::chrono::duration_cast;
     17 using std::chrono::steady_clock;
     18 
     19 namespace android {
     20 namespace pdx {
     21 namespace uds {
     22 
     23 std::string ClientChannelFactory::GetRootEndpointPath() {
     24   return "/dev/socket/pdx";
     25 }
     26 
     27 std::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 
     39 ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path)
     40     : endpoint_path_{GetEndpointPath(endpoint_path)} {}
     41 
     42 ClientChannelFactory::ClientChannelFactory(LocalHandle socket)
     43     : socket_{std::move(socket)} {}
     44 
     45 std::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 
     51 std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
     52     LocalHandle socket) {
     53   return std::unique_ptr<pdx::ClientChannelFactory>{
     54       new ClientChannelFactory{std::move(socket)}};
     55 }
     56 
     57 Status<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