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)
{