[FEATURE] Add ControlFile and proxy command handlers

This commit is contained in:
Gravita 2024-12-31 14:19:24 +07:00
parent 4671dfe49d
commit 0111b2ca2b
No known key found for this signature in database
GPG key ID: 543A8F335C9CD633
4 changed files with 157 additions and 3 deletions

View file

@ -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()));
}

View file

@ -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<buffer.limit();i++) {
if(buffer.get(i) == '\n') {
command = new String(buffer.array(), 0, i);
break mark;
}
}
}
if(command != null) {
runCommand(channel, command);
}
} finally {
buffer.clear();
channel.close();
}
}
} catch (Throwable e) {
logger.error("Unix command socket server error", e);
}
}
}

View file

@ -10,8 +10,18 @@
import java.util.Map;
public abstract class CommandHandler implements Runnable {
private final List<Category> categories = new ArrayList<>();
private final CommandCategory baseCategory = new BaseCommandCategory();
protected final List<Category> categories;
protected final CommandCategory baseCategory;
public CommandHandler() {
this.categories = new ArrayList<>();
this.baseCategory = new BaseCommandCategory();
}
protected CommandHandler(List<Category> categories, CommandCategory baseCategory) {
this.categories = categories;
this.baseCategory = baseCategory;
}
public void eval(String line, boolean bell) {
LogHelper.info("Command '%s'", line);

View file

@ -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<Category> categories, CommandCategory baseCategory, boolean readCommands) {
super(categories, baseCategory);
this.reader = readCommands ? IOHelper.newReader(System.in) : null;
}
protected StdCommandHandler(List<Category> categories, CommandCategory baseCategory, InputStream stream) {
super(categories, baseCategory);
this.reader = IOHelper.newReader(stream);
}
protected StdCommandHandler(List<Category> 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);
}
}