| #include "uds/ipc_helper.h" |
| |
| #include <alloca.h> |
| #include <errno.h> |
| #include <log/log.h> |
| #include <poll.h> |
| #include <string.h> |
| #include <sys/inotify.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| |
| #include <algorithm> |
| |
| #include <pdx/service.h> |
| #include <pdx/utility.h> |
| |
| namespace android { |
| namespace pdx { |
| namespace uds { |
| |
| namespace { |
| |
| constexpr size_t kMaxFdCount = |
| 256; // Total of 1KiB of data to transfer these FDs. |
| |
| // Default implementations of Send/Receive interfaces to use standard socket |
| // send/sendmsg/recv/recvmsg functions. |
| class SocketSender : public SendInterface { |
| public: |
| ssize_t Send(int socket_fd, const void* data, size_t size, |
| int flags) override { |
| return send(socket_fd, data, size, flags); |
| } |
| ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) override { |
| return sendmsg(socket_fd, msg, flags); |
| } |
| } g_socket_sender; |
| |
| class SocketReceiver : public RecvInterface { |
| public: |
| ssize_t Receive(int socket_fd, void* data, size_t size, int flags) override { |
| return recv(socket_fd, data, size, flags); |
| } |
| ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) override { |
| return recvmsg(socket_fd, msg, flags); |
| } |
| } g_socket_receiver; |
| |
| } // anonymous namespace |
| |
| // Helper wrappers around send()/sendmsg() which repeat send() calls on data |
| // that was not sent with the initial call to send/sendmsg. This is important to |
| // handle transmissions interrupted by signals. |
| Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd, |
| const void* data, size_t size) { |
| Status<void> ret; |
| const uint8_t* ptr = static_cast<const uint8_t*>(data); |
| while (size > 0) { |
| ssize_t size_written = |
| RETRY_EINTR(sender->Send(socket_fd.Get(), ptr, size, MSG_NOSIGNAL)); |
| if (size_written < 0) { |
| ret.SetError(errno); |
| ALOGE("SendAll: Failed to send data over socket: %s", |
| ret.GetErrorMessage().c_str()); |
| break; |
| } |
| size -= size_written; |
| ptr += size_written; |
| } |
| return ret; |
| } |
| |
| Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd, |
| const msghdr* msg) { |
| Status<void> ret; |
| ssize_t sent_size = |
| RETRY_EINTR(sender->SendMessage(socket_fd.Get(), msg, MSG_NOSIGNAL)); |
| if (sent_size < 0) { |
| ret.SetError(errno); |
| ALOGE("SendMsgAll: Failed to send data over socket: %s", |
| ret.GetErrorMessage().c_str()); |
| return ret; |
| } |
| |
| ssize_t chunk_start_offset = 0; |
| for (size_t i = 0; i < msg->msg_iovlen; i++) { |
| ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len; |
| if (sent_size < chunk_end_offset) { |
| size_t offset_within_chunk = sent_size - chunk_start_offset; |
| size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk; |
| const uint8_t* chunk_base = |
| static_cast<const uint8_t*>(msg->msg_iov[i].iov_base); |
| ret = SendAll(sender, socket_fd, chunk_base + offset_within_chunk, |
| data_size); |
| if (!ret) |
| break; |
| sent_size += data_size; |
| } |
| chunk_start_offset = chunk_end_offset; |
| } |
| return ret; |
| } |
| |
| // Helper wrappers around recv()/recvmsg() which repeat recv() calls on data |
| // that was not received with the initial call to recvmsg(). This is important |
| // to handle transmissions interrupted by signals as well as the case when |
| // initial data did not arrive in a single chunk over the socket (e.g. socket |
| // buffer was full at the time of transmission, and only portion of initial |
| // message was sent and the rest was blocked until the buffer was cleared by the |
| // receiving side). |
| Status<void> RecvMsgAll(RecvInterface* receiver, |
| const BorrowedHandle& socket_fd, msghdr* msg) { |
| Status<void> ret; |
| ssize_t size_read = RETRY_EINTR(receiver->ReceiveMessage( |
| socket_fd.Get(), msg, MSG_WAITALL | MSG_CMSG_CLOEXEC)); |
| if (size_read < 0) { |
| ret.SetError(errno); |
| ALOGE("RecvMsgAll: Failed to receive data from socket: %s", |
| ret.GetErrorMessage().c_str()); |
| return ret; |
| } else if (size_read == 0) { |
| ret.SetError(ESHUTDOWN); |
| ALOGW("RecvMsgAll: Socket has been shut down"); |
| return ret; |
| } |
| |
| ssize_t chunk_start_offset = 0; |
| for (size_t i = 0; i < msg->msg_iovlen; i++) { |
| ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len; |
| if (size_read < chunk_end_offset) { |
| size_t offset_within_chunk = size_read - chunk_start_offset; |
| size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk; |
| uint8_t* chunk_base = static_cast<uint8_t*>(msg->msg_iov[i].iov_base); |
| ret = RecvAll(receiver, socket_fd, chunk_base + offset_within_chunk, |
| data_size); |
| if (!ret) |
| break; |
| size_read += data_size; |
| } |
| chunk_start_offset = chunk_end_offset; |
| } |
| return ret; |
| } |
| |
| Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd, |
| void* data, size_t size) { |
| Status<void> ret; |
| uint8_t* ptr = static_cast<uint8_t*>(data); |
| while (size > 0) { |
| ssize_t size_read = RETRY_EINTR(receiver->Receive( |
| socket_fd.Get(), ptr, size, MSG_WAITALL | MSG_CMSG_CLOEXEC)); |
| if (size_read < 0) { |
| ret.SetError(errno); |
| ALOGE("RecvAll: Failed to receive data from socket: %s", |
| ret.GetErrorMessage().c_str()); |
| break; |
| } else if (size_read == 0) { |
| ret.SetError(ESHUTDOWN); |
| ALOGW("RecvAll: Socket has been shut down"); |
| break; |
| } |
| size -= size_read; |
| ptr += size_read; |
| } |
| return ret; |
| } |
| |
| uint32_t kMagicPreamble = 0x7564736d; // 'udsm'. |
| |
| struct MessagePreamble { |
| uint32_t magic{0}; |
| uint32_t data_size{0}; |
| uint32_t fd_count{0}; |
| }; |
| |
| Status<void> SendPayload::Send(const BorrowedHandle& socket_fd) { |
| return Send(socket_fd, nullptr); |
| } |
| |
| Status<void> SendPayload::Send(const BorrowedHandle& socket_fd, |
| const ucred* cred, const iovec* data_vec, |
| size_t vec_count) { |
| if (file_handles_.size() > kMaxFdCount) { |
| ALOGE( |
| "SendPayload::Send: Trying to send too many file descriptors (%zu), " |
| "max allowed = %zu", |
| file_handles_.size(), kMaxFdCount); |
| return ErrorStatus{EINVAL}; |
| } |
| |
| SendInterface* sender = sender_ ? sender_ : &g_socket_sender; |
| MessagePreamble preamble; |
| preamble.magic = kMagicPreamble; |
| preamble.data_size = buffer_.size(); |
| preamble.fd_count = file_handles_.size(); |
| |
| msghdr msg = {}; |
| msg.msg_iovlen = 2 + vec_count; |
| msg.msg_iov = static_cast<iovec*>(alloca(sizeof(iovec) * msg.msg_iovlen)); |
| msg.msg_iov[0].iov_base = &preamble; |
| msg.msg_iov[0].iov_len = sizeof(preamble); |
| msg.msg_iov[1].iov_base = buffer_.data(); |
| msg.msg_iov[1].iov_len = buffer_.size(); |
| for (size_t i = 0; i < vec_count; i++) |
| msg.msg_iov[i + 2] = data_vec[i]; |
| |
| if (cred || !file_handles_.empty()) { |
| const size_t fd_bytes = file_handles_.size() * sizeof(int); |
| msg.msg_controllen = (cred ? CMSG_SPACE(sizeof(ucred)) : 0) + |
| (fd_bytes == 0 ? 0 : CMSG_SPACE(fd_bytes)); |
| msg.msg_control = alloca(msg.msg_controllen); |
| |
| cmsghdr* control = CMSG_FIRSTHDR(&msg); |
| if (cred) { |
| control->cmsg_level = SOL_SOCKET; |
| control->cmsg_type = SCM_CREDENTIALS; |
| control->cmsg_len = CMSG_LEN(sizeof(ucred)); |
| memcpy(CMSG_DATA(control), cred, sizeof(ucred)); |
| control = CMSG_NXTHDR(&msg, control); |
| } |
| |
| if (fd_bytes) { |
| control->cmsg_level = SOL_SOCKET; |
| control->cmsg_type = SCM_RIGHTS; |
| control->cmsg_len = CMSG_LEN(fd_bytes); |
| memcpy(CMSG_DATA(control), file_handles_.data(), fd_bytes); |
| } |
| } |
| |
| return SendMsgAll(sender, socket_fd, &msg); |
| } |
| |
| // MessageWriter |
| void* SendPayload::GetNextWriteBufferSection(size_t size) { |
| return buffer_.grow_by(size); |
| } |
| |
| OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; } |
| |
| // OutputResourceMapper |
| Status<FileReference> SendPayload::PushFileHandle(const LocalHandle& handle) { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.Get()); |
| return ref; |
| } else { |
| return handle.Get(); |
| } |
| } |
| |
| Status<FileReference> SendPayload::PushFileHandle( |
| const BorrowedHandle& handle) { |
| if (handle) { |
| const int ref = file_handles_.size(); |
| file_handles_.push_back(handle.Get()); |
| return ref; |
| } else { |
| return handle.Get(); |
| } |
| } |
| |
| Status<FileReference> SendPayload::PushFileHandle(const RemoteHandle& handle) { |
| return handle.Get(); |
| } |
| |
| Status<ChannelReference> SendPayload::PushChannelHandle( |
| const LocalChannelHandle& /*handle*/) { |
| return ErrorStatus{EOPNOTSUPP}; |
| } |
| Status<ChannelReference> SendPayload::PushChannelHandle( |
| const BorrowedChannelHandle& /*handle*/) { |
| return ErrorStatus{EOPNOTSUPP}; |
| } |
| Status<ChannelReference> SendPayload::PushChannelHandle( |
| const RemoteChannelHandle& /*handle*/) { |
| return ErrorStatus{EOPNOTSUPP}; |
| } |
| |
| Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd) { |
| return Receive(socket_fd, nullptr); |
| } |
| |
| Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd, |
| ucred* cred) { |
| RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver; |
| MessagePreamble preamble; |
| msghdr msg = {}; |
| iovec recv_vect = {&preamble, sizeof(preamble)}; |
| msg.msg_iov = &recv_vect; |
| msg.msg_iovlen = 1; |
| const size_t receive_fd_bytes = kMaxFdCount * sizeof(int); |
| msg.msg_controllen = CMSG_SPACE(sizeof(ucred)) + CMSG_SPACE(receive_fd_bytes); |
| msg.msg_control = alloca(msg.msg_controllen); |
| |
| Status<void> ret = RecvMsgAll(receiver, socket_fd, &msg); |
| if (!ret) |
| return ret; |
| |
| if (preamble.magic != kMagicPreamble) { |
| ALOGE("ReceivePayload::Receive: Message header is invalid"); |
| ret.SetError(EIO); |
| return ret; |
| } |
| |
| buffer_.resize(preamble.data_size); |
| file_handles_.clear(); |
| read_pos_ = 0; |
| |
| bool cred_available = false; |
| file_handles_.reserve(preamble.fd_count); |
| cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); |
| while (cmsg) { |
| if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS && |
| cred && cmsg->cmsg_len == CMSG_LEN(sizeof(ucred))) { |
| cred_available = true; |
| memcpy(cred, CMSG_DATA(cmsg), sizeof(ucred)); |
| } else if (cmsg->cmsg_level == SOL_SOCKET && |
| cmsg->cmsg_type == SCM_RIGHTS) { |
| socklen_t payload_len = cmsg->cmsg_len - CMSG_LEN(0); |
| const int* fds = reinterpret_cast<const int*>(CMSG_DATA(cmsg)); |
| size_t fd_count = payload_len / sizeof(int); |
| std::transform(fds, fds + fd_count, std::back_inserter(file_handles_), |
| [](int fd) { return LocalHandle{fd}; }); |
| } |
| cmsg = CMSG_NXTHDR(&msg, cmsg); |
| } |
| |
| ret = RecvAll(receiver, socket_fd, buffer_.data(), buffer_.size()); |
| if (!ret) |
| return ret; |
| |
| if (cred && !cred_available) { |
| ALOGE("ReceivePayload::Receive: Failed to obtain message credentials"); |
| ret.SetError(EIO); |
| } |
| |
| return ret; |
| } |
| |
| // MessageReader |
| MessageReader::BufferSection ReceivePayload::GetNextReadBufferSection() { |
| return {buffer_.data() + read_pos_, &*buffer_.end()}; |
| } |
| |
| void ReceivePayload::ConsumeReadBufferSectionData(const void* new_start) { |
| read_pos_ = PointerDistance(new_start, buffer_.data()); |
| } |
| |
| InputResourceMapper* ReceivePayload::GetInputResourceMapper() { return this; } |
| |
| // InputResourceMapper |
| bool ReceivePayload::GetFileHandle(FileReference ref, LocalHandle* handle) { |
| if (ref < 0) { |
| *handle = LocalHandle{ref}; |
| return true; |
| } |
| if (static_cast<size_t>(ref) > file_handles_.size()) |
| return false; |
| *handle = std::move(file_handles_[ref]); |
| return true; |
| } |
| |
| bool ReceivePayload::GetChannelHandle(ChannelReference /*ref*/, |
| LocalChannelHandle* /*handle*/) { |
| return false; |
| } |
| |
| Status<void> SendData(const BorrowedHandle& socket_fd, const void* data, |
| size_t size) { |
| return SendAll(&g_socket_sender, socket_fd, data, size); |
| } |
| |
| Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data, |
| size_t count) { |
| msghdr msg = {}; |
| msg.msg_iov = const_cast<iovec*>(data); |
| msg.msg_iovlen = count; |
| return SendMsgAll(&g_socket_sender, socket_fd, &msg); |
| } |
| |
| Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data, |
| size_t size) { |
| return RecvAll(&g_socket_receiver, socket_fd, data, size); |
| } |
| |
| Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd, |
| const iovec* data, size_t count) { |
| msghdr msg = {}; |
| msg.msg_iov = const_cast<iovec*>(data); |
| msg.msg_iovlen = count; |
| return RecvMsgAll(&g_socket_receiver, socket_fd, &msg); |
| } |
| |
| size_t CountVectorSize(const iovec* vector, size_t count) { |
| return std::accumulate( |
| vector, vector + count, size_t{0}, |
| [](size_t size, const iovec& vec) { return size + vec.iov_len; }); |
| } |
| |
| void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request, |
| int opcode, uint32_t send_len, uint32_t max_recv_len, |
| bool is_impulse) { |
| request->op = opcode; |
| request->cred.pid = getpid(); |
| request->cred.uid = geteuid(); |
| request->cred.gid = getegid(); |
| request->send_len = send_len; |
| request->max_recv_len = max_recv_len; |
| request->is_impulse = is_impulse; |
| } |
| |
| Status<void> WaitForEndpoint(const std::string& endpoint_path, |
| int64_t timeout_ms) { |
| // Endpoint path must be absolute. |
| if (endpoint_path.empty() || endpoint_path.front() != '/') |
| return ErrorStatus(EINVAL); |
| |
| // Create inotify fd. |
| LocalHandle fd{inotify_init()}; |
| if (!fd) |
| return ErrorStatus(errno); |
| |
| // Set the inotify fd to non-blocking. |
| int ret = fcntl(fd.Get(), F_GETFL); |
| fcntl(fd.Get(), F_SETFL, ret | O_NONBLOCK); |
| |
| // Setup the pollfd. |
| pollfd pfd = {fd.Get(), POLLIN, 0}; |
| |
| // Find locations of each path separator. |
| std::vector<size_t> separators{0}; // The path is absolute, so '/' is at #0. |
| size_t pos = endpoint_path.find('/', 1); |
| while (pos != std::string::npos) { |
| separators.push_back(pos); |
| pos = endpoint_path.find('/', pos + 1); |
| } |
| separators.push_back(endpoint_path.size()); |
| |
| // Walk down the path, checking for existence and waiting if needed. |
| pos = 1; |
| size_t links = 0; |
| std::string current; |
| while (pos < separators.size() && links <= MAXSYMLINKS) { |
| std::string previous = current; |
| current = endpoint_path.substr(0, separators[pos]); |
| |
| // Check for existence; proceed to setup a watch if not. |
| if (access(current.c_str(), F_OK) < 0) { |
| if (errno != ENOENT) |
| return ErrorStatus(errno); |
| |
| // Extract the name of the path component to wait for. |
| std::string next = current.substr( |
| separators[pos - 1] + 1, separators[pos] - separators[pos - 1] - 1); |
| |
| // Add a watch on the last existing directory we reach. |
| int wd = inotify_add_watch( |
| fd.Get(), previous.c_str(), |
| IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO); |
| if (wd < 0) { |
| if (errno != ENOENT) |
| return ErrorStatus(errno); |
| // Restart at the beginning if previous was deleted. |
| links = 0; |
| current.clear(); |
| pos = 1; |
| continue; |
| } |
| |
| // Make sure current didn't get created before the watch was added. |
| ret = access(current.c_str(), F_OK); |
| if (ret < 0) { |
| if (errno != ENOENT) |
| return ErrorStatus(errno); |
| |
| bool exit_poll = false; |
| while (!exit_poll) { |
| // Wait for an event or timeout. |
| ret = poll(&pfd, 1, timeout_ms); |
| if (ret <= 0) |
| return ErrorStatus(ret == 0 ? ETIMEDOUT : errno); |
| |
| // Read events. |
| char buffer[sizeof(inotify_event) + NAME_MAX + 1]; |
| |
| ret = read(fd.Get(), buffer, sizeof(buffer)); |
| if (ret < 0) { |
| if (errno == EAGAIN || errno == EWOULDBLOCK) |
| continue; |
| else |
| return ErrorStatus(errno); |
| } else if (static_cast<size_t>(ret) < sizeof(struct inotify_event)) { |
| return ErrorStatus(EIO); |
| } |
| |
| auto* event = reinterpret_cast<const inotify_event*>(buffer); |
| auto* end = reinterpret_cast<const inotify_event*>(buffer + ret); |
| while (event < end) { |
| std::string event_for; |
| if (event->len > 0) |
| event_for = event->name; |
| |
| if (event->mask & (IN_CREATE | IN_MOVED_TO)) { |
| // See if this is the droid we're looking for. |
| if (next == event_for) { |
| exit_poll = true; |
| break; |
| } |
| } else if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { |
| // Restart at the beginning if our watch dir is deleted. |
| links = 0; |
| current.clear(); |
| pos = 0; |
| exit_poll = true; |
| break; |
| } |
| |
| event = reinterpret_cast<const inotify_event*>(AdvancePointer( |
| event, sizeof(struct inotify_event) + event->len)); |
| } // while (event < end) |
| } // while (!exit_poll) |
| } // Current dir doesn't exist. |
| ret = inotify_rm_watch(fd.Get(), wd); |
| if (ret < 0 && errno != EINVAL) |
| return ErrorStatus(errno); |
| } // if (access(current.c_str(), F_OK) < 0) |
| |
| // Check for symbolic link and update link count. |
| struct stat stat_buf; |
| ret = lstat(current.c_str(), &stat_buf); |
| if (ret < 0 && errno != ENOENT) |
| return ErrorStatus(errno); |
| else if (ret == 0 && S_ISLNK(stat_buf.st_mode)) |
| links++; |
| pos++; |
| } // while (pos < separators.size() && links <= MAXSYMLINKS) |
| |
| return {}; |
| } |
| |
| } // namespace uds |
| } // namespace pdx |
| } // namespace android |