From 0111b2ca2b4b2cff953a91a055cb992ff7a05ef2 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:19:24 +0700 Subject: [PATCH] [FEATURE] Add ControlFile and proxy command handlers --- .../pro/gravit/launchserver/LaunchServer.java | 10 +- .../socket/SocketCommandServer.java | 97 +++++++++++++++++++ .../gravit/utils/command/CommandHandler.java | 14 ++- .../utils/command/StdCommandHandler.java | 39 ++++++++ 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/socket/SocketCommandServer.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index a117dd48..f0f7c790 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -21,6 +21,7 @@ import pro.gravit.launchserver.modules.events.*; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.socket.Client; +import pro.gravit.launchserver.socket.SocketCommandServer; import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler; import pro.gravit.launchserver.socket.response.auth.ProfilesResponse; import pro.gravit.launchserver.socket.response.auth.RestoreResponse; @@ -85,6 +86,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab public final Path modulesDir; public final Path launcherModulesDir; public final Path librariesDir; + public final Path controlFile; /** * This object contains runtime configuration */ @@ -113,6 +115,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab // Server public final CommandHandler commandHandler; public final NettyServerSocketHandler nettyServerSocketHandler; + public final SocketCommandServer socketCommandServer; public final ScheduledExecutorService service; public final AtomicBoolean started = new AtomicBoolean(false); public final LauncherModuleLoader launcherModuleLoader; @@ -139,6 +142,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La modulesDir = directories.modules; launcherModulesDir = directories.launcherModules; librariesDir = directories.librariesDir; + controlFile = directories.controlFile; this.shardId = shardId; if(!Files.isDirectory(launcherPack)) { Files.createDirectories(launcherPack); @@ -190,6 +194,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La } launcherModuleLoader.init(); nettyServerSocketHandler = new NettyServerSocketHandler(this); + socketCommandServer = new SocketCommandServer(commandHandler, controlFile); if(config.sign.checkCertificateExpired) { checkCertificateExpired(); service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS); @@ -353,6 +358,7 @@ public void run() { } })); CommonHelper.newThread("Command Thread", true, commandHandler).start(); + CommonHelper.newThread("Socket Command Thread", true, socketCommandServer).start(); // Sync updates dir CommonHelper.newThread("Profiles and updates sync", true, () -> { try { @@ -461,7 +467,7 @@ public interface LaunchServerConfigManager { public static class LaunchServerDirectories { public static final String UPDATES_NAME = "updates", TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries", - LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries"; + LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries", CONTROL_FILE = "control-file"; public Path updatesDir; public Path librariesDir; public Path launcherLibrariesDir; @@ -473,6 +479,7 @@ public static class LaunchServerDirectories { public Path tmpDir; public Path modules; public Path launcherModules; + public Path controlFile; public void collect() { if (updatesDir == null) updatesDir = getPath(UPDATES_NAME); @@ -486,6 +493,7 @@ public void collect() { if (modules == null) modules = getPath(MODULES); if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES); if (librariesDir == null) librariesDir = getPath(LIBRARIES); + if (controlFile == null) controlFile = getPath(CONTROL_FILE); if (tmpDir == null) tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken())); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/SocketCommandServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/SocketCommandServer.java new file mode 100644 index 00000000..c2b06896 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/SocketCommandServer.java @@ -0,0 +1,97 @@ +package pro.gravit.launchserver.socket; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launchserver.config.log4j.LogAppender; +import pro.gravit.utils.command.CommandHandler; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class SocketCommandServer implements Runnable { + private final Logger logger = LogManager.getLogger(SocketCommandServer.class); + private ServerSocketChannel channel; + private Path path; + private UnixDomainSocketAddress address; + private ServerSocketChannel serverChannel; + private CommandHandler commandHandler; + private transient SocketChannel clientChannel; + + public SocketCommandServer(CommandHandler commandHandler, Path path) { + this.commandHandler = commandHandler; + this.path = path; + } + + private void runCommand(SocketChannel channel, String command) { + logger.info("Command '{}' from socket", command); + clientChannel = channel; + try { + commandHandler.evalNative(command, false); + } catch (Throwable e) { + logger.error("Error when execute command", e); + } finally { + clientChannel = null; + + } + } + + @Override + public void run() { + try { + Files.deleteIfExists(path); + this.address = UnixDomainSocketAddress.of(path); + serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + serverChannel.configureBlocking(true); + serverChannel.bind(address); + LogAppender.getInstance().addListener((logEvent -> { + if(clientChannel != null && clientChannel.isOpen()) { + try { + String s = logEvent.getMessage().getFormattedMessage()+"\n"; + byte[] bytes = s.getBytes(StandardCharsets.UTF_8); + ByteBuffer buffer = ByteBuffer.wrap(bytes); + clientChannel.write(buffer); + } catch (Throwable ignored) { + } + } + })); + ByteBuffer buffer = ByteBuffer.allocate(1024); + while (true) { + SocketChannel channel = serverChannel.accept(); + channel.configureBlocking(true); + String command = null; + try { + mark: + while (true) { + int bytesRead = channel.read(buffer); + if (bytesRead < 0) { + break; + } + logger.info("DEBUG: readed {}", new String(buffer.array(), 0, buffer.limit())); + for (var i=0;i categories = new ArrayList<>(); - private final CommandCategory baseCategory = new BaseCommandCategory(); + protected final List categories; + protected final CommandCategory baseCategory; + + public CommandHandler() { + this.categories = new ArrayList<>(); + this.baseCategory = new BaseCommandCategory(); + } + + protected CommandHandler(List categories, CommandCategory baseCategory) { + this.categories = categories; + this.baseCategory = baseCategory; + } public void eval(String line, boolean bell) { LogHelper.info("Command '%s'", line); diff --git a/LauncherCore/src/main/java/pro/gravit/utils/command/StdCommandHandler.java b/LauncherCore/src/main/java/pro/gravit/utils/command/StdCommandHandler.java index 5bb297aa..257ceecb 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/command/StdCommandHandler.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/command/StdCommandHandler.java @@ -5,6 +5,8 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.util.List; public class StdCommandHandler extends CommandHandler { private final BufferedReader reader; @@ -14,6 +16,31 @@ public StdCommandHandler(boolean readCommands) { reader = readCommands ? IOHelper.newReader(System.in) : null; } + public StdCommandHandler(InputStream stream) { + super(); + this.reader = IOHelper.newReader(stream); + } + + public StdCommandHandler(BufferedReader reader) { + super(); + this.reader = reader; + } + + protected StdCommandHandler(List categories, CommandCategory baseCategory, boolean readCommands) { + super(categories, baseCategory); + this.reader = readCommands ? IOHelper.newReader(System.in) : null; + } + + protected StdCommandHandler(List categories, CommandCategory baseCategory, InputStream stream) { + super(categories, baseCategory); + this.reader = IOHelper.newReader(stream); + } + + protected StdCommandHandler(List categories, CommandCategory baseCategory, BufferedReader reader) { + super(categories, baseCategory); + this.reader = reader; + } + @Override public void bell() { } @@ -37,4 +64,16 @@ public void clear() throws IOException { public String readLine() throws IOException { return reader == null ? null : reader.readLine(); } + + public StdCommandHandler ofHandler(CommandHandler commandHandler, boolean readCommands) { + return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, readCommands); + } + + public StdCommandHandler ofHandler(CommandHandler commandHandler, InputStream stream) { + return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, stream); + } + + public StdCommandHandler ofHandler(CommandHandler commandHandler, BufferedReader reader) { + return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, reader); + } }