mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-02-08 23:39:38 +03:00
[FEATURE] Add ControlFile and proxy command handlers
This commit is contained in:
parent
4671dfe49d
commit
0111b2ca2b
4 changed files with 157 additions and 3 deletions
|
@ -21,6 +21,7 @@
|
||||||
import pro.gravit.launchserver.modules.events.*;
|
import pro.gravit.launchserver.modules.events.*;
|
||||||
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
|
import pro.gravit.launchserver.socket.SocketCommandServer;
|
||||||
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
|
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
|
||||||
import pro.gravit.launchserver.socket.response.auth.ProfilesResponse;
|
import pro.gravit.launchserver.socket.response.auth.ProfilesResponse;
|
||||||
import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
|
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 modulesDir;
|
||||||
public final Path launcherModulesDir;
|
public final Path launcherModulesDir;
|
||||||
public final Path librariesDir;
|
public final Path librariesDir;
|
||||||
|
public final Path controlFile;
|
||||||
/**
|
/**
|
||||||
* This object contains runtime configuration
|
* This object contains runtime configuration
|
||||||
*/
|
*/
|
||||||
|
@ -113,6 +115,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
||||||
// Server
|
// Server
|
||||||
public final CommandHandler commandHandler;
|
public final CommandHandler commandHandler;
|
||||||
public final NettyServerSocketHandler nettyServerSocketHandler;
|
public final NettyServerSocketHandler nettyServerSocketHandler;
|
||||||
|
public final SocketCommandServer socketCommandServer;
|
||||||
public final ScheduledExecutorService service;
|
public final ScheduledExecutorService service;
|
||||||
public final AtomicBoolean started = new AtomicBoolean(false);
|
public final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
public final LauncherModuleLoader launcherModuleLoader;
|
public final LauncherModuleLoader launcherModuleLoader;
|
||||||
|
@ -139,6 +142,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
||||||
modulesDir = directories.modules;
|
modulesDir = directories.modules;
|
||||||
launcherModulesDir = directories.launcherModules;
|
launcherModulesDir = directories.launcherModules;
|
||||||
librariesDir = directories.librariesDir;
|
librariesDir = directories.librariesDir;
|
||||||
|
controlFile = directories.controlFile;
|
||||||
this.shardId = shardId;
|
this.shardId = shardId;
|
||||||
if(!Files.isDirectory(launcherPack)) {
|
if(!Files.isDirectory(launcherPack)) {
|
||||||
Files.createDirectories(launcherPack);
|
Files.createDirectories(launcherPack);
|
||||||
|
@ -190,6 +194,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
||||||
}
|
}
|
||||||
launcherModuleLoader.init();
|
launcherModuleLoader.init();
|
||||||
nettyServerSocketHandler = new NettyServerSocketHandler(this);
|
nettyServerSocketHandler = new NettyServerSocketHandler(this);
|
||||||
|
socketCommandServer = new SocketCommandServer(commandHandler, controlFile);
|
||||||
if(config.sign.checkCertificateExpired) {
|
if(config.sign.checkCertificateExpired) {
|
||||||
checkCertificateExpired();
|
checkCertificateExpired();
|
||||||
service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS);
|
service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS);
|
||||||
|
@ -353,6 +358,7 @@ public void run() {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
CommonHelper.newThread("Command Thread", true, commandHandler).start();
|
CommonHelper.newThread("Command Thread", true, commandHandler).start();
|
||||||
|
CommonHelper.newThread("Socket Command Thread", true, socketCommandServer).start();
|
||||||
// Sync updates dir
|
// Sync updates dir
|
||||||
CommonHelper.newThread("Profiles and updates sync", true, () -> {
|
CommonHelper.newThread("Profiles and updates sync", true, () -> {
|
||||||
try {
|
try {
|
||||||
|
@ -461,7 +467,7 @@ public interface LaunchServerConfigManager {
|
||||||
public static class LaunchServerDirectories {
|
public static class LaunchServerDirectories {
|
||||||
public static final String UPDATES_NAME = "updates",
|
public static final String UPDATES_NAME = "updates",
|
||||||
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
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 updatesDir;
|
||||||
public Path librariesDir;
|
public Path librariesDir;
|
||||||
public Path launcherLibrariesDir;
|
public Path launcherLibrariesDir;
|
||||||
|
@ -473,6 +479,7 @@ public static class LaunchServerDirectories {
|
||||||
public Path tmpDir;
|
public Path tmpDir;
|
||||||
public Path modules;
|
public Path modules;
|
||||||
public Path launcherModules;
|
public Path launcherModules;
|
||||||
|
public Path controlFile;
|
||||||
|
|
||||||
public void collect() {
|
public void collect() {
|
||||||
if (updatesDir == null) updatesDir = getPath(UPDATES_NAME);
|
if (updatesDir == null) updatesDir = getPath(UPDATES_NAME);
|
||||||
|
@ -486,6 +493,7 @@ public void collect() {
|
||||||
if (modules == null) modules = getPath(MODULES);
|
if (modules == null) modules = getPath(MODULES);
|
||||||
if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES);
|
if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES);
|
||||||
if (librariesDir == null) librariesDir = getPath(LIBRARIES);
|
if (librariesDir == null) librariesDir = getPath(LIBRARIES);
|
||||||
|
if (controlFile == null) controlFile = getPath(CONTROL_FILE);
|
||||||
if (tmpDir == null)
|
if (tmpDir == null)
|
||||||
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
|
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,18 @@
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class CommandHandler implements Runnable {
|
public abstract class CommandHandler implements Runnable {
|
||||||
private final List<Category> categories = new ArrayList<>();
|
protected final List<Category> categories;
|
||||||
private final CommandCategory baseCategory = new BaseCommandCategory();
|
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) {
|
public void eval(String line, boolean bell) {
|
||||||
LogHelper.info("Command '%s'", line);
|
LogHelper.info("Command '%s'", line);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class StdCommandHandler extends CommandHandler {
|
public class StdCommandHandler extends CommandHandler {
|
||||||
private final BufferedReader reader;
|
private final BufferedReader reader;
|
||||||
|
@ -14,6 +16,31 @@ public StdCommandHandler(boolean readCommands) {
|
||||||
reader = readCommands ? IOHelper.newReader(System.in) : null;
|
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
|
@Override
|
||||||
public void bell() {
|
public void bell() {
|
||||||
}
|
}
|
||||||
|
@ -37,4 +64,16 @@ public void clear() throws IOException {
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
return reader == null ? null : reader.readLine();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue