// license:BSD-3-Clause // copyright-holders:Olivier Galibert, R. Belmont, Vas Crabb //============================================================ // // sdldomain.c - SDL socket (unix) access functions // // SDLMAME by Olivier Galibert and R. Belmont // //============================================================ #include "posixfile.h" #include #include #include #include #include #include #include #include #include namespace { char const *const posixfile_domain_identifier = "domain."; class posix_osd_domain : public osd_file { public: posix_osd_domain(posix_osd_domain const &) = delete; posix_osd_domain(posix_osd_domain &&) = delete; posix_osd_domain& operator=(posix_osd_domain const &) = delete; posix_osd_domain& operator=(posix_osd_domain &&) = delete; posix_osd_domain(int sock, bool listening) : m_sock(sock) , m_listening(listening) { assert(m_sock >= 0); } virtual ~posix_osd_domain() { ::close(m_sock); } virtual error read(void *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override { fd_set readfds; FD_ZERO(&readfds); FD_SET(m_sock, &readfds); struct timeval timeout; timeout.tv_sec = timeout.tv_usec = 0; if (select(m_sock + 1, &readfds, nullptr, nullptr, &timeout) < 0) { char line[80]; std::sprintf(line, "%s : %s : %d ", __func__, __FILE__, __LINE__); std::perror(line); return errno_to_file_error(errno); } else if (FD_ISSET(m_sock, &readfds)) { if (!m_listening) { // connected socket ssize_t const result = ::read(m_sock, buffer, count); if (result < 0) { return errno_to_file_error(errno); } else { actual = std::uint32_t(size_t(result)); return error::NONE; } } else { // listening socket int const accepted = ::accept(m_sock, nullptr, nullptr); if (accepted < 0) { return errno_to_file_error(errno); } else { ::close(m_sock); m_sock = accepted; m_listening = false; actual = 0; return error::NONE; } } } else { return error::FAILURE; } } virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override { ssize_t const result = ::write(m_sock, buffer, count); if (result < 0) return errno_to_file_error(errno); actual = std::uint32_t(size_t(result)); return error::NONE; } virtual error truncate(std::uint64_t offset) override { // doesn't make sense on socket return error::INVALID_ACCESS; } virtual error flush() override { // there's no simple way to flush buffers on a socket anyway return error::NONE; } private: int m_sock; bool m_listening; }; } // anonymous namespace bool posix_check_domain_path(std::string const &path) { if (strncmp(path.c_str(), posixfile_domain_identifier, strlen(posixfile_domain_identifier)) == 0) return true; return false; } osd_file::error posix_open_domain(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) { struct sockaddr_un sau; memset(&sau, 0, sizeof(sau)); sau.sun_family = AF_UNIX; strncpy(sau.sun_path, &path.c_str()[strlen(posixfile_domain_identifier)], sizeof(sau.sun_path)-1); int const sock = ::socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) return errno_to_file_error(errno); fcntl(sock, F_SETFL, O_NONBLOCK); // listening socket support if (openflags & OPEN_FLAG_CREATE) { if (::bind(sock, reinterpret_cast(&sau), sizeof(struct sockaddr_un)) < 0) { int const err = errno; ::close(sock); return errno_to_file_error(err); } // start to listen... if (::listen(sock, 1) < 0) { int const err = errno; ::close(sock); return errno_to_file_error(err); } // mark socket as "listening" try { file = std::make_unique(sock, true); filesize = 0; return osd_file::error::NONE; } catch (...) { ::close(sock); return osd_file::error::OUT_OF_MEMORY; } } else { if (::connect(sock, reinterpret_cast(&sau), sizeof(struct sockaddr_un)) < 0) { ::close(sock); return osd_file::error::ACCESS_DENIED; // have to return this value or bitb won't try to bind on connect failure } try { file = std::make_unique(sock, false); filesize = 0; return osd_file::error::NONE; } catch (...) { ::close(sock); return osd_file::error::OUT_OF_MEMORY; } } }