diff --git a/src/common/Utilities/Systemd.cpp b/src/common/Utilities/Systemd.cpp new file mode 100644 index 000000000..f7c39cfb1 --- /dev/null +++ b/src/common/Utilities/Systemd.cpp @@ -0,0 +1,59 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information. + * + * Portions of this file are derived from systemd, licensed under: + * - GPL-2.0-or-later (if the original systemd file was GPL-2.0+) + * OR + * - LGPL-2.1-or-later, relicensed under GPL-2.0-or-later as permitted by LGPL-2.1+ + * + * Original systemd copyright: + * Copyright (c) the systemd contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#if defined(__linux__) +#include "Log.h" +#include "StringConvert.h" +#include +#include +#include + +int get_listen_fd() +{ + char* const listen_pid = std::getenv("LISTEN_PID"); + char* const listen_fds = std::getenv("LISTEN_FDS"); + if (!listen_pid || !listen_fds) + return 0; + + pid_t pid = Acore::StringTo(listen_pid).value_or(0); + if (pid != getpid()) + return 0; + + int fds = Acore::StringTo(listen_fds).value_or(0); + if (fds <= 0) + return 0; + + if (fds > 1) + LOG_WARN("network", "Multiple file descriptors received from systemd socket activation, only the first will be used"); + + return 3; +} +#else +// On non-Linux systems, just return 0 (no socket activation) +int get_listen_fd() +{ + return 0; +} +#endif diff --git a/src/common/Utilities/Systemd.h b/src/common/Utilities/Systemd.h new file mode 100644 index 000000000..312345cea --- /dev/null +++ b/src/common/Utilities/Systemd.h @@ -0,0 +1,31 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information. + * + * Portions of this file are derived from systemd, licensed under: + * - GPL-2.0-or-later (if the original systemd file was GPL-2.0+) + * OR + * - LGPL-2.1-or-later, relicensed under GPL-2.0-or-later as permitted by LGPL-2.1+ + * + * Original systemd copyright: + * Copyright (c) the systemd contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _SYSTEMD_H_ +#define _SYSTEMD_H_ + +int get_listen_fd(); + +#endif diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index d1c10131b..511c17e36 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -48,6 +48,7 @@ #include "SecretMgr.h" #include "SharedDefines.h" #include "SteadyTimer.h" +#include "Systemd.h" #include "World.h" #include "WorldSessionMgr.h" #include "WorldSocket.h" @@ -406,7 +407,8 @@ int main(int argc, char** argv) sScriptMgr->OnShutdown(); // set server offline - LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, realm.Id.Realm); + if (!sConfigMgr->GetOption("Network.UseSocketActivation", false)) + LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, realm.Id.Realm); LOG_INFO("server.worldserver", "Halting process..."); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 07945f07f..089c1911a 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -397,6 +397,20 @@ Network.TcpNodelay = 1 Network.EnableProxyProtocol = 0 +# +# Network.UseSocketActivation +# Description: Enable systemd socket activation support for the worldserver. +# When enabled and the process is started by systemd socket activation, +# the server will use the socket passed by systemd instead of +# creating and binding its own listening socket. Disabled by default. +# +# When enabled the realm is not automatically set as offline on shutdown. +# +# Example: 1 - (Enabled) +# Default: 0 - (Disabled) + +Network.UseSocketActivation = 0 + # ################################################################################################### diff --git a/src/server/shared/Network/AsyncAcceptor.h b/src/server/shared/Network/AsyncAcceptor.h index f91c2ca37..71c58ed93 100644 --- a/src/server/shared/Network/AsyncAcceptor.h +++ b/src/server/shared/Network/AsyncAcceptor.h @@ -20,6 +20,7 @@ #include "IpAddress.h" #include "Log.h" +#include "Systemd.h" #include #include #include @@ -33,10 +34,20 @@ class AsyncAcceptor public: typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); - AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port) : + AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, bool supportSocketActivation = false) : _acceptor(ioContext), _endpoint(Acore::Net::make_address(bindIp), port), - _socket(ioContext), _closed(false), _socketFactory([this](){ return DefaultSocketFactory(); }) + _socket(ioContext), _closed(false), _socketFactory([this](){ return DefaultSocketFactory(); }), + _supportSocketActivation(supportSocketActivation) { + int const listen_fd = get_listen_fd(); + if (_supportSocketActivation && listen_fd > 0) + { + LOG_DEBUG("network", "Using socket from systemd socket activation"); + boost::system::error_code errorCode; + _acceptor.assign(boost::asio::ip::tcp::v4(), listen_fd, errorCode); + if (errorCode) + LOG_WARN("network", "Failed to assign socket {}", errorCode.message()); + } } template @@ -72,27 +83,31 @@ public: bool Bind() { boost::system::error_code errorCode; - _acceptor.open(_endpoint.protocol(), errorCode); - if (errorCode) + // with socket activation the acceptor is already open and bound + if (!_acceptor.is_open()) { - LOG_INFO("network", "Failed to open acceptor {}", errorCode.message()); - return false; - } + _acceptor.open(_endpoint.protocol(), errorCode); + if (errorCode) + { + LOG_INFO("network", "Failed to open acceptor {}", errorCode.message()); + return false; + } #if AC_PLATFORM != AC_PLATFORM_WINDOWS - _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode); - if (errorCode) - { - LOG_INFO("network", "Failed to set reuse_address option on acceptor {}", errorCode.message()); - return false; - } + _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode); + if (errorCode) + { + LOG_INFO("network", "Failed to set reuse_address option on acceptor {}", errorCode.message()); + return false; + } #endif - _acceptor.bind(_endpoint, errorCode); - if (errorCode) - { - LOG_INFO("network", "Could not bind to {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message()); - return false; + _acceptor.bind(_endpoint, errorCode); + if (errorCode) + { + LOG_INFO("network", "Could not bind to {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message()); + return false; + } } _acceptor.listen(ACORE_MAX_LISTEN_CONNECTIONS, errorCode); @@ -124,6 +139,7 @@ private: tcp::socket _socket; std::atomic _closed; std::function()> _socketFactory; + bool _supportSocketActivation; }; template diff --git a/src/server/shared/Network/SocketMgr.h b/src/server/shared/Network/SocketMgr.h index dc0c5e6f5..085e4e238 100644 --- a/src/server/shared/Network/SocketMgr.h +++ b/src/server/shared/Network/SocketMgr.h @@ -19,6 +19,7 @@ #define SocketMgr_h__ #include "AsyncAcceptor.h" +#include "Config.h" #include "Errors.h" #include "NetworkThread.h" #include @@ -42,7 +43,8 @@ public: std::unique_ptr acceptor; try { - acceptor = std::make_unique(ioContext, bindIp, port); + bool supportSocketActivation = sConfigMgr->GetOption("Network.UseSocketActivation", false); + acceptor = std::make_unique(ioContext, bindIp, port, supportSocketActivation); } catch (boost::system::system_error const& err) {