Fix inconsistencies with handling errors in early network startup

In most cases, when socket creation, bind, listen, or similar fails,
it's best to gracefully shutdown. We do that now.
This commit is contained in:
Lion Kortlepel 2022-03-24 14:05:40 +01:00
parent 4cb299061e
commit dbfe4a4d11
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
3 changed files with 40 additions and 26 deletions

View File

@ -6,10 +6,10 @@
#ifdef BEAMMP_LINUX #ifdef BEAMMP_LINUX
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
using SOCKET = int; using SOCKET = int;
using DWORD = unsigned long; using DWORD = unsigned long;
using PDWORD = unsigned long*; using PDWORD = unsigned long*;
@ -25,10 +25,10 @@ inline void CloseSocketProper(int TheSocket) {
#ifdef BEAMMP_APPLE #ifdef BEAMMP_APPLE
#include <arpa/inet.h> #include <arpa/inet.h>
#include <errno.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
using SOCKET = int; using SOCKET = int;
using DWORD = unsigned long; using DWORD = unsigned long;
using PDWORD = unsigned long*; using PDWORD = unsigned long*;
@ -48,6 +48,11 @@ inline void CloseSocketProper(int TheSocket) {
inline void CloseSocketProper(SOCKET TheSocket) { inline void CloseSocketProper(SOCKET TheSocket) {
shutdown(TheSocket, 2); // 2 == SD_BOTH shutdown(TheSocket, 2); // 2 == SD_BOTH
closesocket(TheSocket); closesocket(TheSocket);
} }
#endif // WIN32 #endif // WIN32
#ifdef INVALID_SOCKET
static inline constexpr int BEAMMP_INVALID_SOCKET = INVALID_SOCKET;
#else
static inline constexpr int BEAMMP_INVALID_SOCKET = -1;
#endif

View File

@ -43,6 +43,7 @@ void Application::GracefullyShutdown() {
beammp_info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down"); beammp_info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down");
mShutdownHandlers[i](); mShutdownHandlers[i]();
} }
// std::exit(-1);
} }
std::string Application::ServerVersionString() { std::string Application::ServerVersionString() {
@ -105,7 +106,7 @@ void Application::CheckForUpdates() {
if (Matches) { if (Matches) {
auto MyVersion = ServerVersion(); auto MyVersion = ServerVersion();
auto RemoteVersion = Version(VersionStrToInts(Response)); auto RemoteVersion = Version(VersionStrToInts(Response));
if (!IsOutdated(MyVersion, RemoteVersion)) { if (IsOutdated(MyVersion, RemoteVersion)) {
std::string RealVersionString = RemoteVersion.AsString(); std::string RealVersionString = RemoteVersion.AsString();
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET)); beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
} else { } else {

View File

@ -108,40 +108,48 @@ void TNetwork::TCPServerMain() {
#if defined(BEAMMP_WINDOWS) #if defined(BEAMMP_WINDOWS)
WSADATA wsaData; WSADATA wsaData;
if (WSAStartup(514, &wsaData)) { if (WSAStartup(514, &wsaData)) {
beammp_error("Can't start Winsock!"); beammp_error("Can't start Winsock! Shutting down");
return; Application::GracefullyShutdown();
} }
#endif // WINDOWS #endif // WINDOWS
TConnection client {}; TConnection client {};
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int optval = 1; if (Listener == BEAMMP_INVALID_SOCKET) {
beammp_error("Failed to create socket: " + GetPlatformAgnosticErrorString()
+ ". This is a fatal error, as a socket is needed for the server to operate. Shutting down.");
Application::GracefullyShutdown();
}
#if defined(BEAMMP_WINDOWS) #if defined(BEAMMP_WINDOWS)
const char* optval_ptr = reinterpret_cast<const char*>(&optval); const char optval = 0;
int ret = ::setsockopt(Listener, SOL_SOCKET, SO_DONTLINGER, &optval, sizeof(optval));
#elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE) #elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
void* optval_ptr = reinterpret_cast<void*>(&optval); int optval = true;
int ret = ::setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void*>(&optval), sizeof(optval));
#endif #endif
setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, optval_ptr, sizeof(optval)); // not a fatal error
// TODO: check optval or return value idk if (ret < 0) {
beammp_error("Failed to set up listening socket to not linger / reuse address. "
"This may cause the socket to refuse to bind(). Error: "
+ GetPlatformAgnosticErrorString());
}
sockaddr_in addr {}; sockaddr_in addr {};
addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons(uint16_t(Application::Settings.Port)); addr.sin_port = htons(uint16_t(Application::Settings.Port));
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) { if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) < 0) {
beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); beammp_error("bind() failed, the server cannot operate and will shut down now. "
std::this_thread::sleep_for(std::chrono::seconds(5)); "Error: "
exit(-1); // TODO: Wtf. + GetPlatformAgnosticErrorString());
Application::GracefullyShutdown();
} }
if (Listener == -1) { if (listen(Listener, SOMAXCONN) < 0) {
beammp_error("Invalid listening socket"); beammp_error("listen() failed, which is needed for the server to operate. "
return; "Shutting down. Error: "
} + GetPlatformAgnosticErrorString());
if (listen(Listener, SOMAXCONN)) { Application::GracefullyShutdown();
beammp_error("listen() failed: " + GetPlatformAgnosticErrorString());
// FIXME leak Listener
return;
} }
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good); Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
beammp_info(("Vehicle event network online")); beammp_info("Vehicle event network online");
do { do {
try { try {
if (mShutdown) { if (mShutdown) {
@ -157,9 +165,9 @@ void TNetwork::TCPServerMain() {
std::thread ID(&TNetwork::Identify, this, client); std::thread ID(&TNetwork::Identify, this, client);
ID.detach(); // TODO: Add to a queue and attempt to join periodically ID.detach(); // TODO: Add to a queue and attempt to join periodically
} catch (const std::exception& e) { } catch (const std::exception& e) {
beammp_error(("fatal: ") + std::string(e.what())); beammp_error("fatal: " + std::string(e.what()));
} }
} while (client.Socket); } while (client.Socket != BEAMMP_INVALID_SOCKET);
beammp_debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__)); beammp_debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));