mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Rework
This commit is contained in:
parent
323ca02a43
commit
4b15af2f17
25 changed files with 133 additions and 37 deletions
|
@ -4,7 +4,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.api.ConnectionTerminalExchange;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import io.xpipe.app.beacon.BeaconShellSession;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.api.ShellStartExchange;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import lombok.SneakyThrows;
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.xpipe.app.browser.session.BrowserAbstractSessionModel;
|
|||
import io.xpipe.app.browser.session.BrowserSessionTab;
|
||||
import io.xpipe.app.comp.base.ModalOverlayComp;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
|
|
@ -19,7 +19,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
|
|||
import io.xpipe.app.util.FileReference;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
|
|
@ -15,7 +15,7 @@ import io.xpipe.app.fxcomps.impl.VerticalComp;
|
|||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
|
|
@ -4,12 +4,13 @@ import io.xpipe.core.process.ShellControl;
|
|||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.NetworkTunnelStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.store.StatefulDataStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@JsonTypeName("local")
|
||||
public class LocalStore extends JacksonizedValue
|
||||
implements NetworkTunnelStore, ShellStore, StatefulDataStore<ShellStoreState> {
|
||||
|
|
66
app/src/main/java/io/xpipe/app/ext/ShellSession.java
Normal file
66
app/src/main/java/io/xpipe/app/ext/ShellSession.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
package io.xpipe.app.ext;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.Session;
|
||||
import io.xpipe.core.store.SessionListener;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Getter
|
||||
public class ShellSession extends Session {
|
||||
|
||||
private final Supplier<ShellControl> supplier;
|
||||
private final ShellControl shellControl;
|
||||
|
||||
public ShellSession(SessionListener listener, Supplier<ShellControl> supplier) {
|
||||
super(listener);
|
||||
this.supplier = supplier;
|
||||
this.shellControl = createControl();
|
||||
}
|
||||
|
||||
public void start() throws Exception {
|
||||
if (shellControl.isRunning()) {
|
||||
return;
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
|
||||
try {
|
||||
shellControl.start();
|
||||
} catch (Exception ex) {
|
||||
stop();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private ShellControl createControl() {
|
||||
var pc = supplier.get();
|
||||
pc.onStartupFail(shellControl -> {
|
||||
listener.onStateChange(false);
|
||||
});
|
||||
pc.onInit(shellControl -> {
|
||||
listener.onStateChange(true);
|
||||
});
|
||||
pc.onKill(() -> {
|
||||
listener.onStateChange(false);
|
||||
});
|
||||
// Listen for parent exit as onExit is called before exit is completed
|
||||
// In case it is stuck, we would not get the right status otherwise
|
||||
pc.getParentControl().ifPresent(p -> {
|
||||
p.onExit(shellControl -> {
|
||||
listener.onStateChange(false);
|
||||
});
|
||||
});
|
||||
return pc;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return shellControl.isRunning();
|
||||
}
|
||||
|
||||
public void stop() throws Exception {
|
||||
shellControl.close();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,34 @@
|
|||
package io.xpipe.core.store;
|
||||
package io.xpipe.app.ext;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.*;
|
||||
|
||||
public interface ShellStore extends DataStore, FileSystemStore, ValidatableStore<ShellValidationContext> {
|
||||
public interface ShellStore extends DataStore, FileSystemStore, ValidatableStore<ShellValidationContext>, SingletonSessionStore<ShellSession> {
|
||||
|
||||
default ShellControl getOrStartSession() throws Exception {
|
||||
var session = getSession();
|
||||
if (session != null) {
|
||||
try {
|
||||
session.getShellControl().command("echo hi").execute();
|
||||
return session.getShellControl();
|
||||
} catch (Exception e) {
|
||||
stopSessionIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
startSessionIfNeeded();
|
||||
return getSession().getShellControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
default ShellSession newSession() {
|
||||
return new ShellSession(this, () -> control());
|
||||
}
|
||||
|
||||
@Override
|
||||
default Class<?> getSessionClass() {
|
||||
return ShellSession.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
default FileSystem createFileSystem() {
|
|
@ -12,7 +12,7 @@ import io.xpipe.app.storage.DataStoreEntry;
|
|||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.DataStoreCategoryChoiceComp;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
|
|
|
@ -14,9 +14,11 @@ public class HostHelper {
|
|||
return p;
|
||||
}
|
||||
|
||||
public static int findRandomOpenPortOnAllLocalInterfaces() throws IOException {
|
||||
public static int findRandomOpenPortOnAllLocalInterfaces() {
|
||||
try (ServerSocket socket = new ServerSocket(0)) {
|
||||
return socket.getLocalPort();
|
||||
} catch (IOException e) {
|
||||
return randomPort();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.storage.DataStoreEntry;
|
|||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.process.ShellTtyState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.core.store.ShellValidationContext;
|
||||
import io.xpipe.core.store.ValidationContext;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.core.store.ShellValidationContext;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.core.process;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.store.StatefulDataStore;
|
||||
import io.xpipe.core.util.FailableConsumer;
|
||||
import io.xpipe.core.util.FailableFunction;
|
||||
|
@ -18,6 +18,8 @@ import java.util.function.Function;
|
|||
|
||||
public interface ShellControl extends ProcessControl {
|
||||
|
||||
Optional<ShellControl> getParentControl();
|
||||
|
||||
ShellTtyState getTtyState();
|
||||
|
||||
void setNonInteractive();
|
||||
|
@ -32,9 +34,9 @@ public interface ShellControl extends ProcessControl {
|
|||
|
||||
void setWorkingDirectory(WorkingDirectoryFunction workingDirectory);
|
||||
|
||||
Optional<ShellStore> getSourceStore();
|
||||
Optional<DataStore> getSourceStore();
|
||||
|
||||
ShellControl withSourceStore(ShellStore store);
|
||||
ShellControl withSourceStore(DataStore store);
|
||||
|
||||
List<ShellInitCommand> getInitCommands();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
|
||||
interface TunnelFunction {
|
||||
|
||||
NetworkTunnelSession create(int localPort, int remotePort);
|
||||
NetworkTunnelSession create(int localPort, int remotePort, String address);
|
||||
}
|
||||
|
||||
DataStore getNetworkParent();
|
||||
|
@ -57,7 +57,7 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
}
|
||||
}
|
||||
|
||||
default NetworkTunnelSession sessionChain(int local, int remotePort) throws Exception {
|
||||
default NetworkTunnelSession sessionChain(int local, int remotePort, String address) throws Exception {
|
||||
if (!isLocallyTunneable()) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to create tunnel chain as one intermediate system does not support tunneling");
|
||||
|
@ -75,7 +75,7 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
var currentLocalPort = isLast(current) ? local : randomPort();
|
||||
var currentRemotePort =
|
||||
sessions.isEmpty() ? remotePort : sessions.getLast().getLocalPort();
|
||||
var t = func.create(currentLocalPort, currentRemotePort);
|
||||
var t = func.create(currentLocalPort, currentRemotePort, current == this ? address : "localhost");
|
||||
t.start();
|
||||
sessions.add(t);
|
||||
counter.incrementAndGet();
|
||||
|
|
|
@ -38,7 +38,6 @@ public interface SingletonSessionStore<T extends Session>
|
|||
default void startSessionIfNeeded() throws Exception {
|
||||
synchronized (this) {
|
||||
var s = getSession();
|
||||
setSessionEnabled(true);
|
||||
if (s != null) {
|
||||
if (s.isRunning()) {
|
||||
return;
|
||||
|
@ -50,9 +49,14 @@ public interface SingletonSessionStore<T extends Session>
|
|||
|
||||
try {
|
||||
s = newSession();
|
||||
if (s != null) {
|
||||
setSessionEnabled(true);
|
||||
s.start();
|
||||
setCache("session", s);
|
||||
onStateChange(true);
|
||||
} else {
|
||||
setSessionEnabled(false);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
onStateChange(false);
|
||||
throw ex;
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
public interface Proxyable {
|
||||
|
||||
ShellStore getProxy();
|
||||
}
|
|
@ -8,7 +8,7 @@ import io.xpipe.app.ext.ProcessControlProvider;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
|
|
@ -9,7 +9,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
|
|||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.process.ShellTtyState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.ext.base.script.ScriptHierarchy;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
|
|
@ -9,7 +9,7 @@ import io.xpipe.core.process.CommandControl;
|
|||
import io.xpipe.core.process.ElevationFunction;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellDialects;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
|
|||
import io.xpipe.app.util.ScanAlert;
|
||||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.process.ShellTtyState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public class ChgrpAction implements BranchAction {
|
|||
.filter(e -> !e.getValue().equals("nohome")
|
||||
&& !e.getValue().equals("nogroup")
|
||||
&& !e.getValue().equals("nobody")
|
||||
&& (e.getKey().equals(0) || e.getKey() >= 1000))
|
||||
&& (e.getKey().equals(0) || e.getKey() >= 900))
|
||||
.map(e -> e.getValue())
|
||||
.map(s -> (LeafAction) new Chgrp(s))
|
||||
.toList();
|
||||
|
|
|
@ -44,7 +44,7 @@ public class ChownAction implements BranchAction {
|
|||
return model.getCache().getUsers().entrySet().stream()
|
||||
.filter(e -> !e.getValue().equals("nohome")
|
||||
&& !e.getValue().equals("nobody")
|
||||
&& (e.getKey().equals(0) || e.getKey() >= 1000))
|
||||
&& (e.getKey().equals(0) || e.getKey() >= 900))
|
||||
.map(e -> e.getValue())
|
||||
.map(s -> (LeafAction) new Chown(s))
|
||||
.toList();
|
||||
|
|
|
@ -35,7 +35,7 @@ public abstract class AbstractServiceStore extends JacksonizedValue
|
|||
public NetworkTunnelSession newSession() throws Exception {
|
||||
LicenseProvider.get().getFeature("services").throwIfUnsupported();
|
||||
var l = localPort != null ? localPort : HostHelper.findRandomOpenPortOnAllLocalInterfaces();
|
||||
return getHost().getStore().sessionChain(l, remotePort);
|
||||
return getHost().getStore().sessionChain(l, remotePort, "localhost");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,7 @@ import io.xpipe.app.storage.DataStoreEntry;
|
|||
import io.xpipe.app.util.DataStoreFormatter;
|
||||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.core.process.ShellStoreState;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.ext.base.script.ScriptStore;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
|
|
|
@ -389,3 +389,5 @@ vmIdentity=Guest identity
|
|||
vmIdentityDescription=The SSH identity authentication method to use for connecting if needed
|
||||
vmPort=Port
|
||||
vmPortDescription=The port to connect to via SSH
|
||||
forwardAgent=Forward agent
|
||||
forwardAgentDescription=Make SSH agent identities available on the remote system
|
||||
|
|
Loading…
Reference in a new issue