diff --git a/src/loader.cpp b/src/loader.cpp index 7724682..18c2932 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -1,5 +1,6 @@ #include "loader.h" +#include #include #include @@ -23,6 +24,12 @@ namespace anm2ed constexpr auto SOCKET_ADDRESS = "127.0.0.1"; constexpr auto SOCKET_PORT = 11414; +#ifdef _WIN32 + constexpr int SOCKET_ERROR_ADDRESS_IN_USE = WSAEADDRINUSE; +#else + constexpr int SOCKET_ERROR_ADDRESS_IN_USE = EADDRINUSE; +#endif + namespace { bool socket_paths_send(Socket& socket, const std::vector& paths) @@ -75,8 +82,6 @@ namespace anm2ed if (!testSocket.open(SERVER)) logger.warning(std::format("Failed to open socket; single instancing will not work.")); - bool isPrimaryInstance = false; - if (testSocket.bind({SOCKET_ADDRESS, SOCKET_PORT})) { socket = std::move(testSocket); @@ -85,13 +90,19 @@ namespace anm2ed logger.warning("Could not listen on socket; single instancing disabled."); else { - isPrimaryInstance = true; isSocketThread = true; logger.info(std::format("Opened socket at {}:{}", SOCKET_ADDRESS, SOCKET_PORT)); } } else { + if (testSocket.last_error() != SOCKET_ERROR_ADDRESS_IN_USE) + { + logger.fatal(std::format("Failed to bind single-instance socket (error {}).", testSocket.last_error())); + isError = true; + return; + } + logger.info(std::format("Existing instance of program exists; passing arguments...")); Socket clientSocket; if (!clientSocket.open(CLIENT)) @@ -103,11 +114,8 @@ namespace anm2ed else logger.info("Sent arguments to existing instance. Exiting."); - if (!isPrimaryInstance) - { - isError = true; - return; - } + isError = true; + return; } settings = Settings(settings_path()); diff --git a/src/socket.cpp b/src/socket.cpp index 4b408e7..e4bc500 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -1,10 +1,12 @@ #include "socket.h" +#include + namespace anm2ed { -#ifdef _WIN32 namespace { +#ifdef _WIN32 struct WSAInitializer { WSAInitializer() @@ -16,12 +18,25 @@ namespace anm2ed }; WSAInitializer initializer{}; - } #endif - Socket::Socket() : handle(SOCKET_INVALID), role(CLIENT) {} + int socket_last_error() + { +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif + } + } - Socket::Socket(Socket&& other) noexcept : handle(other.handle), role(other.role) { other.handle = SOCKET_INVALID; } + Socket::Socket() : handle(SOCKET_INVALID), role(CLIENT), lastError(0) {} + + Socket::Socket(Socket&& other) noexcept : handle(other.handle), role(other.role), lastError(other.lastError) + { + other.handle = SOCKET_INVALID; + other.lastError = 0; + } Socket& Socket::operator=(Socket&& other) noexcept { @@ -31,6 +46,8 @@ namespace anm2ed handle = other.handle; role = other.role; other.handle = SOCKET_INVALID; + lastError = other.lastError; + other.lastError = 0; } return *this; @@ -45,24 +62,44 @@ namespace anm2ed handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (!is_valid()) return false; + if (!is_valid()) + { + lastError = socket_last_error(); + return false; + } if (role == SERVER) { #ifdef _WIN32 BOOL opt = TRUE; + if (::setsockopt(handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast(&opt), sizeof(opt)) != 0) + { + lastError = socket_last_error(); + close(); + return false; + } #else int opt = 1; + if (::setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&opt), sizeof(opt)) != 0) + { + lastError = socket_last_error(); + close(); + return false; + } #endif - ::setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&opt), sizeof(opt)); } + lastError = 0; return true; } bool Socket::bind(const SocketAddress& address) { - if (!is_valid()) return false; + if (!is_valid()) + { + lastError = EINVAL; + return false; + } sockaddr_in addr{}; addr.sin_family = AF_INET; @@ -72,16 +109,38 @@ namespace anm2ed addr.sin_addr.s_addr = htonl(INADDR_ANY); else { - if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) return false; + if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) + { + lastError = socket_last_error(); + return false; + } } - return ::bind(handle, reinterpret_cast(&addr), sizeof(addr)) == 0; + if (::bind(handle, reinterpret_cast(&addr), sizeof(addr)) == 0) + { + lastError = 0; + return true; + } + + lastError = socket_last_error(); + return false; } bool Socket::listen() { - if (!is_valid()) return false; - return ::listen(handle, SOMAXCONN) == 0; + if (!is_valid()) + { + lastError = EINVAL; + return false; + } + if (::listen(handle, SOMAXCONN) == 0) + { + lastError = 0; + return true; + } + + lastError = socket_last_error(); + return false; } Socket Socket::accept() @@ -90,30 +149,54 @@ namespace anm2ed if (!is_valid()) return client; auto accepted = ::accept(handle, nullptr, nullptr); - if (accepted == SOCKET_INVALID) return client; + if (accepted == SOCKET_INVALID) + { + lastError = socket_last_error(); + return client; + } client.close(); client.handle = accepted; client.role = CLIENT; + lastError = 0; return client; } bool Socket::connect(const SocketAddress& address) { - if (!is_valid()) return false; + if (!is_valid()) + { + lastError = EINVAL; + return false; + } sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(address.port); - if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) return false; + if (::inet_pton(AF_INET, address.host.c_str(), &addr.sin_addr) <= 0) + { + lastError = socket_last_error(); + return false; + } - return ::connect(handle, reinterpret_cast(&addr), sizeof(addr)) == 0; + if (::connect(handle, reinterpret_cast(&addr), sizeof(addr)) == 0) + { + lastError = 0; + return true; + } + + lastError = socket_last_error(); + return false; } bool Socket::send(const void* data, size_t size) { - if (!is_valid() || !data || size == 0) return false; + if (!is_valid() || !data || size == 0) + { + lastError = EINVAL; + return false; + } auto bytes = reinterpret_cast(data); size_t totalSent = 0; @@ -121,16 +204,25 @@ namespace anm2ed while (totalSent < size) { auto sent = ::send(handle, bytes + totalSent, static_cast(size - totalSent), 0); - if (sent <= 0) return false; + if (sent <= 0) + { + lastError = socket_last_error(); + return false; + } totalSent += static_cast(sent); } + lastError = 0; return true; } bool Socket::receive(void* buffer, size_t size) { - if (!is_valid() || !buffer || size == 0) return false; + if (!is_valid() || !buffer || size == 0) + { + lastError = EINVAL; + return false; + } auto* bytes = reinterpret_cast(buffer); size_t totalReceived = 0; @@ -138,10 +230,15 @@ namespace anm2ed while (totalReceived < size) { auto received = ::recv(handle, bytes + totalReceived, static_cast(size - totalReceived), 0); - if (received <= 0) return false; + if (received <= 0) + { + lastError = socket_last_error(); + return false; + } totalReceived += static_cast(received); } + lastError = 0; return true; } @@ -155,7 +252,10 @@ namespace anm2ed ::close(handle); #endif handle = SOCKET_INVALID; + lastError = 0; } bool Socket::is_valid() const { return handle != SOCKET_INVALID; } + + int Socket::last_error() const { return lastError; } } diff --git a/src/socket.h b/src/socket.h index ee8e4ea..ee03180 100644 --- a/src/socket.h +++ b/src/socket.h @@ -36,6 +36,7 @@ namespace anm2ed private: socket_handle handle; SocketRole role{}; + int lastError{}; public: Socket(); @@ -57,5 +58,6 @@ namespace anm2ed void close(); bool is_valid() const; + int last_error() const; }; }