aboutsummaryrefslogtreecommitdiff
path: root/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote
diff options
context:
space:
mode:
Diffstat (limited to 'spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote')
-rw-r--r--spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/AbstractRemoteInterface.java98
-rw-r--r--spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/RemoteInterface.java28
-rw-r--r--spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/SshRemoteInterface.java78
3 files changed, 204 insertions, 0 deletions
diff --git a/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/AbstractRemoteInterface.java b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/AbstractRemoteInterface.java
new file mode 100644
index 0000000..1c03aa8
--- /dev/null
+++ b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/AbstractRemoteInterface.java
@@ -0,0 +1,98 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) 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 3 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 <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.standalone.remote;
+
+import me.lucko.spark.common.command.CommandResponseHandler;
+import me.lucko.spark.standalone.StandaloneCommandSender;
+import me.lucko.spark.standalone.StandaloneSparkPlugin;
+import net.kyori.adventure.text.Component;
+import org.jline.reader.Candidate;
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.UserInterruptException;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.impl.AbstractTerminal;
+
+public abstract class AbstractRemoteInterface implements RemoteInterface {
+
+ protected final StandaloneSparkPlugin spark;
+
+ public AbstractRemoteInterface(StandaloneSparkPlugin spark) {
+ this.spark = spark;
+ }
+
+ private static String stripSlashSpark(String command) {
+ if (command.startsWith("/")) {
+ command = command.substring(1);
+ }
+ if (command.startsWith("spark ")) {
+ command = command.substring(6);
+ }
+ return command;
+ }
+
+ public void processSession(Terminal terminal, Runnable closer) {
+ LineReader reader = LineReaderBuilder.builder()
+ .terminal(terminal)
+ .completer((lineReader, parsedLine, list) -> {
+ String command = stripSlashSpark(parsedLine.line());
+ String[] args = command.split(" ", -1);
+ for (String suggestion : this.spark.suggest(args, StandaloneCommandSender.NO_OP)) {
+ list.add(new Candidate(suggestion));
+ }
+ })
+ .build();
+
+ StandaloneCommandSender sender = new StandaloneCommandSender(reader::printAbove);
+
+ this.spark.addSender(sender);
+ ((AbstractTerminal) terminal).setOnClose(() -> this.spark.removeSender(sender));
+
+ CommandResponseHandler resp = this.spark.createResponseHandler(sender);
+ resp.replyPrefixed(Component.text("spark remote interface - " + this.spark.getVersion()));
+ resp.replyPrefixed(Component.text("Use '/spark' commands as usual, or run 'exit' to exit."));
+
+ while (true) {
+ try {
+ String line = reader.readLine("> ");
+ if (line.trim().isEmpty()) {
+ continue;
+ }
+
+ String command = stripSlashSpark(line);
+ if (command.equals("exit")) {
+ closer.run();
+ return;
+ }
+
+ this.spark.execute(command.split(" ", 0), sender);
+
+ } catch (UserInterruptException e) {
+ // ignore
+ } catch (EndOfFileException e) {
+ this.spark.removeSender(sender);
+ return;
+ }
+ }
+ }
+
+}
diff --git a/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/RemoteInterface.java b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/RemoteInterface.java
new file mode 100644
index 0000000..ce6a8dc
--- /dev/null
+++ b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/RemoteInterface.java
@@ -0,0 +1,28 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) 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 3 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 <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.standalone.remote;
+
+public interface RemoteInterface extends AutoCloseable {
+
+ @Override
+ void close();
+
+}
diff --git a/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/SshRemoteInterface.java b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/SshRemoteInterface.java
new file mode 100644
index 0000000..025dadb
--- /dev/null
+++ b/spark-standalone-agent/src/main/java/me/lucko/spark/standalone/remote/SshRemoteInterface.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) 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 3 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 <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.standalone.remote;
+
+import me.lucko.spark.standalone.StandaloneSparkPlugin;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.jline.builtins.ssh.ShellFactoryImpl;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.logging.Level;
+
+public class SshRemoteInterface extends AbstractRemoteInterface {
+ private final String password;
+ private final SshServer sshd;
+
+ public SshRemoteInterface(StandaloneSparkPlugin spark, int port) {
+ super(spark);
+ this.password = new SecureRandom().ints(48, 122)
+ .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97))
+ .limit(32)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+
+ this.sshd = SshServer.setUpDefaultServer();
+ if (port > 0) {
+ this.sshd.setPort(port);
+ }
+ this.sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+ this.sshd.setPasswordAuthenticator((username, password, session) -> "spark".equals(username) && MessageDigest.isEqual(this.password.getBytes(), password.getBytes()));
+ this.sshd.setShellFactory(new ShellFactoryImpl(shellParams -> this.processSession(shellParams.getTerminal(), shellParams.getCloser())));
+
+ new Thread(() -> {
+ try {
+ this.start();
+ } catch (IOException e) {
+ this.spark.log(Level.SEVERE, "Error whilst starting SSH server", e);
+ }
+ }, "spark-ssh-server").start();
+ }
+
+ private void start() throws IOException {
+ this.sshd.start();
+ this.spark.log(Level.INFO, "SSH Server started on port " + this.sshd.getPort());
+ this.spark.log(Level.INFO, "Connect using: ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p " + this.sshd.getPort() + " spark@localhost");
+ this.spark.log(Level.INFO, "When prompted, enter the password: " + this.password);
+ }
+
+ @Override
+ public void close() {
+ try {
+ this.sshd.stop();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+} \ No newline at end of file