/** * @file IPC.cpp * @author apio (cloudapio.eu) * @brief Inter-process communication primitives. * * @copyright Copyright (c) 2023, the Luna authors. * */ #include #include #include #include #include #include #include namespace os::IPC { Result> ClientConnection::adopt_connection(LocalServer::Client&& connection) { return adopt_owned_if_nonnull(new (std::nothrow) ClientConnection(move(connection))); } ClientConnection::ClientConnection(LocalServer::Client&& connection) : m_connection(move(connection)) { } Result ClientConnection::check_for_messages() { if (m_ipc_in_progress) { m_ipc_in_progress = false; m_message_handler(*this, m_ipc_saved_id, m_arg); return {}; } u8 id; auto rc = m_connection.recv_typed(id); if (rc.has_error()) { if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. if (rc.error() == EINTR) return {}; // Let the caller check for anything having happened because a signal handler ran. return rc.release_error(); } m_message_handler(*this, id, m_arg); return {}; } Result ClientConnection::send_error(int error) { u8 id = 0; TRY(m_connection.send_typed(id)); TRY(m_connection.send_typed(error)); return {}; } Result> Client::connect(StringView path, bool blocking) { auto connection = TRY(LocalClient::connect(path, blocking)); return adopt_owned_if_nonnull(new (std::nothrow) Client(move(connection))); } Result Client::check_for_messages() { if (m_ipc_in_progress) { m_ipc_in_progress = false; m_message_handler(*this, m_ipc_saved_id, m_arg); return {}; } u8 id; auto rc = m_connection->recv_typed(id); if (rc.has_error()) { if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block. if (rc.error() == EINTR) return {}; // Let the caller check for anything having happened because a signal handler ran. return rc.release_error(); } m_message_handler(*this, id, m_arg); return {}; } Client::Client(OwnedPtr&& connection) : m_connection(move(connection)) { } Notifier Notifier::create() { Notifier result; pipe(result.pfds); fcntl(result.pfds[0], F_SETFD, FD_CLOEXEC); return result; } void Notifier::hook() { auto value = String::format("%d"_sv, pfds[1]).release_value(); setenv("LUNA_NOTIFY_FD", value.chars(), 1); } void Notifier::unhook() { unsetenv("LUNA_NOTIFY_FD"); } bool Notifier::wait(int timeout) { close(pfds[1]); struct pollfd fds[] = { { .fd = pfds[0], .events = POLLIN, .revents = 0 } }; poll: int result = poll(fds, 1, timeout); if (result < 0 && errno == EINTR) goto poll; close(pfds[0]); return result > 0; } bool Notifier::run_and_wait(Action&& action, int timeout) { auto notifier = create(); notifier.hook(); action(); notifier.unhook(); return notifier.wait(timeout); } void notify_parent() { char* fd_string = getenv("LUNA_NOTIFY_FD"); if (!fd_string) return; int fd = atoi(fd_string); u8 data = 0; write(fd, &data, 1); close(fd); unsetenv("LUNA_NOTIFY_FD"); } }