Various shell connection optimizations

This commit is contained in:
crschnick 2023-02-01 10:15:51 +00:00
parent e57a952a62
commit f35ff6fcd4
14 changed files with 83 additions and 94 deletions

View file

@ -8,7 +8,6 @@ import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.extension.I18n; import io.xpipe.extension.I18n;
import io.xpipe.extension.event.ErrorEvent;
import io.xpipe.extension.fxcomps.Comp; import io.xpipe.extension.fxcomps.Comp;
import io.xpipe.extension.fxcomps.SimpleComp; import io.xpipe.extension.fxcomps.SimpleComp;
import io.xpipe.extension.fxcomps.SimpleCompStructure; import io.xpipe.extension.fxcomps.SimpleCompStructure;
@ -21,6 +20,7 @@ import io.xpipe.extension.fxcomps.impl.PrettyImageComp;
import io.xpipe.extension.fxcomps.util.PlatformThread; import io.xpipe.extension.fxcomps.util.PlatformThread;
import io.xpipe.extension.fxcomps.util.SimpleChangeListener; import io.xpipe.extension.fxcomps.util.SimpleChangeListener;
import io.xpipe.extension.util.OsHelper; import io.xpipe.extension.util.OsHelper;
import io.xpipe.extension.util.ThreadHelper;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
@ -103,7 +103,9 @@ public class StoreEntryComp extends SimpleComp {
var storeIcon = imageComp.createRegion(); var storeIcon = imageComp.createRegion();
storeIcon.getStyleClass().add("icon"); storeIcon.getStyleClass().add("icon");
if (entry.getState().getValue().isUsable()) { if (entry.getState().getValue().isUsable()) {
new FancyTooltipAugment<>(new SimpleStringProperty(entry.getEntry().getProvider().getDisplayName())).augment(storeIcon); new FancyTooltipAugment<>(new SimpleStringProperty(
entry.getEntry().getProvider().getDisplayName()))
.augment(storeIcon);
} }
return storeIcon; return storeIcon;
} }
@ -122,7 +124,6 @@ public class StoreEntryComp extends SimpleComp {
var storeIcon = createIcon(); var storeIcon = createIcon();
grid.getColumnConstraints() grid.getColumnConstraints()
.addAll( .addAll(
createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH), createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH),
@ -167,11 +168,9 @@ public class StoreEntryComp extends SimpleComp {
var button = new IconButtonComp( var button = new IconButtonComp(
actionProvider.getIcon(entry.getEntry().getStore().asNeeded()), () -> { actionProvider.getIcon(entry.getEntry().getStore().asNeeded()), () -> {
try { ThreadHelper.runFailableAsync(() -> {
actionProvider.execute(entry.getEntry().getStore().asNeeded()); actionProvider.execute(entry.getEntry().getStore().asNeeded());
} catch (Exception e) { });
ErrorEvent.fromThrowable(e).handle();
}
}); });
button.apply(new FancyTooltipAugment<>( button.apply(new FancyTooltipAugment<>(
actionProvider.getName(entry.getEntry().getStore().asNeeded()))); actionProvider.getName(entry.getEntry().getStore().asNeeded())));
@ -224,11 +223,9 @@ public class StoreEntryComp extends SimpleComp {
var icon = actionProvider.getIcon(entry.getEntry().getStore().asNeeded()); var icon = actionProvider.getIcon(entry.getEntry().getStore().asNeeded());
var item = new MenuItem(null, new FontIcon(icon)); var item = new MenuItem(null, new FontIcon(icon));
item.setOnAction(event -> { item.setOnAction(event -> {
try { ThreadHelper.runFailableAsync(() -> {
actionProvider.execute(entry.getEntry().getStore().asNeeded()); actionProvider.execute(entry.getEntry().getStore().asNeeded());
} catch (Exception e) { });
ErrorEvent.fromThrowable(e).handle();
}
}); });
item.textProperty().bind(name); item.textProperty().bind(name);
item.disableProperty().bind(Bindings.not(p.getValue())); item.disableProperty().bind(Bindings.not(p.getValue()));

View file

@ -11,7 +11,6 @@ import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.process.ShellTypes; import io.xpipe.core.process.ShellTypes;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.XPipeInstallation; import io.xpipe.core.util.XPipeInstallation;
import io.xpipe.core.util.XPipeTempDirectory;
import io.xpipe.extension.util.ScriptHelper; import io.xpipe.extension.util.ScriptHelper;
import lombok.Getter; import lombok.Getter;
@ -38,8 +37,7 @@ public class AppInstaller {
if (s.isLocal()) { if (s.isLocal()) {
targetFile = localFile.toString(); targetFile = localFile.toString();
} else { } else {
targetFile = FileNames.join( targetFile = FileNames.join(s.getTemporaryDirectory(), localFile.getFileName().toString());
XPipeTempDirectory.get(s), localFile.getFileName().toString());
try (CommandProcessControl c = s.command(s.getShellType().getStreamFileWriteCommand(targetFile)) try (CommandProcessControl c = s.command(s.getShellType().getStreamFileWriteCommand(targetFile))
.start()) { .start()) {
c.discardOut(); c.discardOut();

View file

@ -19,8 +19,6 @@ def getArchName() {
return arch return arch
} }
println(System.getenv('RELEASE'))
project.ext { project.ext {
ci = System.getenv('CI') != null ci = System.getenv('CI') != null
os = org.gradle.internal.os.OperatingSystem.current() os = org.gradle.internal.os.OperatingSystem.current()

View file

@ -15,6 +15,11 @@ public interface ShellProcessControl extends ProcessControl {
String prepareIntermediateTerminalOpen(String content) throws Exception; String prepareIntermediateTerminalOpen(String content) throws Exception;
String getTemporaryDirectory() throws Exception;
public void checkRunning() throws Exception;
default String executeStringSimpleCommand(String command) throws Exception { default String executeStringSimpleCommand(String command) throws Exception {
try (CommandProcessControl c = command(command).start()) { try (CommandProcessControl c = command(command).start()) {
return c.readOrThrow(); return c.readOrThrow();

View file

@ -162,10 +162,10 @@ public class ShellTypes {
var command = new ArrayList<String>(); var command = new ArrayList<String>();
for (String line : content.split("(\n|\r\n)")) { for (String line : content.split("(\n|\r\n)")) {
command.add("echo " + escapeStringValue(line) + ">> \"" + file + "\""); var echoCommand = line.isEmpty() ? "echo." : "echo " + escapeStringValue(line);
command.add("echo." + ">> \"" + file + "\""); command.add(echoCommand + ">> \"" + file + "\"");
} }
return String.join("&", command); return String.join("&", command).replaceFirst(">>", ">");
} }
@Override @Override
@ -437,7 +437,7 @@ public class ShellTypes {
} }
public String getScriptEchoCommand(String s) { public String getScriptEchoCommand(String s) {
return "#!" + getExecutable() + "\n" + getEchoCommand(s, false) + "\nrm -- \"$0\""; return getEchoCommand(s, false) + "\nrm -- \"$0\"";
} }
@Override @Override

View file

@ -51,6 +51,7 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
private final Function<ShellProcessControl, String> terminalCommand; private final Function<ShellProcessControl, String> terminalCommand;
private CommandProcessControlInputStream stdout; private CommandProcessControlInputStream stdout;
private CommandProcessControlInputStream stderr; private CommandProcessControlInputStream stderr;
private String displayCommand;
private boolean elevated; private boolean elevated;
private boolean manageParent; private boolean manageParent;
private int exitCode = -1; private int exitCode = -1;
@ -177,8 +178,9 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
TrackEvent.withTrace("proc", "Starting command execution ...") TrackEvent.withTrace("proc", "Starting command execution ...")
.tag("baseCommand", ShellHelper.censor(baseCommand, sensitive)) .tag("baseCommand", ShellHelper.censor(baseCommand, sensitive))
.tag("shellType", parent.getShellType().getName()) .tag("shellType", parent.getShellType().getName())
.tag("elevated", elevated)
.handle(); .handle();
displayCommand = baseCommand;
if (elevated) { if (elevated) {
string = ElevationHelper.elevateNormalCommand(string, parent, baseCommand); string = ElevationHelper.elevateNormalCommand(string, parent, baseCommand);
} }
@ -225,7 +227,8 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
} }
} }
TrackEvent.withTrace("proc", "Stdout finished") TrackEvent.withDebug("proc", "Command finished")
.tag("command", displayCommand)
.tag("finishReason", r) .tag("finishReason", r)
.tag("exitCode", exitCode) .tag("exitCode", exitCode)
.handle(); .handle();

View file

@ -59,11 +59,9 @@ public class DockerStore extends JacksonizedValue implements MachineStore {
"docker exec -i " + containerName + " " + ShellTypes.BASH.getNormalOpenCommand(), "docker exec -i " + containerName + " " + ShellTypes.BASH.getNormalOpenCommand(),
(shellProcessControl, s) -> { (shellProcessControl, s) -> {
if (s != null) { if (s != null) {
return "docker exec -it " + containerName + " " return "docker exec -it " + containerName + " " + s;
+ ShellTypes.BASH.executeCommandWithShell(s);
} else { } else {
return "docker exec -it " + containerName + " " return "docker exec -it " + containerName;
+ ShellTypes.BASH.getNormalOpenCommand();
} }
}) })
.elevated(shellProcessControl -> true); .elevated(shellProcessControl -> true);

View file

@ -72,10 +72,13 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
getStdin().close(); getStdin().close();
stdinClosed = true; stdinClosed = true;
uuid = null;
if (!PrefsProvider.get(ProcPrefs.class).enableCaching().get()) {
shellType = null; shellType = null;
charset = null; charset = null;
uuid = null;
command = null; command = null;
tempDirectory = null;
}
try { try {
process.waitFor(EXIT_TIMEOUT, TimeUnit.MILLISECONDS); process.waitFor(EXIT_TIMEOUT, TimeUnit.MILLISECONDS);
@ -106,13 +109,12 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
@Override @Override
public String prepareIntermediateTerminalOpen(String content) throws Exception { public String prepareIntermediateTerminalOpen(String content) throws Exception {
try (var pc = start()) { try (var pc = start()) {
var file = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, content); var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, content);
TrackEvent.withDebug("proc", "Writing open init script") TrackEvent.withDebug("proc", "Writing open init script")
.tag("file", file) .tag("initCommand", initCommand)
.tag("content", content) .tag("content", content)
.handle(); .handle();
return initCommand;
return file;
} }
} }
@ -146,8 +148,12 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
process = Runtime.getRuntime().exec(args.toArray(String[]::new)); process = Runtime.getRuntime().exec(args.toArray(String[]::new));
stdinClosed = false; stdinClosed = false;
running = true; running = true;
if (shellType == null) {
shellType = ShellHelper.determineType(this, Charset.defaultCharset(), command, null, startTimeout); shellType = ShellHelper.determineType(this, Charset.defaultCharset(), command, null, startTimeout);
}
if (charset == null) {
charset = shellType.determineCharset(this); charset = shellType.determineCharset(this);
}
osType = OsType.getLocal(); osType = OsType.getLocal();
for (String s : initCommands) { for (String s : initCommands) {

View file

@ -5,6 +5,7 @@ import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellProcessControl; import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.process.ShellType; import io.xpipe.core.process.ShellType;
import io.xpipe.core.util.SecretValue; import io.xpipe.core.util.SecretValue;
import io.xpipe.core.util.XPipeTempDirectory;
import io.xpipe.ext.proc.util.ShellReader; import io.xpipe.ext.proc.util.ShellReader;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
@ -22,6 +23,7 @@ public abstract class ShellProcessControlImpl extends ProcessControlImpl impleme
protected UUID uuid; protected UUID uuid;
protected String command; protected String command;
protected List<String> initCommands = new ArrayList<>(); protected List<String> initCommands = new ArrayList<>();
protected String tempDirectory;
@Getter @Getter
protected ShellType shellType; protected ShellType shellType;
@ -32,6 +34,23 @@ public abstract class ShellProcessControlImpl extends ProcessControlImpl impleme
@Getter @Getter
protected SecretValue elevationPassword; protected SecretValue elevationPassword;
@Override
public String getTemporaryDirectory() throws Exception {
if (tempDirectory == null) {
checkRunning();
tempDirectory = XPipeTempDirectory.get(this);
}
return tempDirectory;
}
@Override
public void checkRunning() throws Exception {
if (!isRunning()) {
throw new IllegalStateException("Shell process control is not running");
}
}
@Override @Override
public ShellProcessControl sensitive() { public ShellProcessControl sensitive() {
this.sensitive = true; this.sensitive = true;

View file

@ -196,6 +196,7 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
shellType = null; shellType = null;
charset = null; charset = null;
command = null; command = null;
tempDirectory = null;
} }
getStderr().close(); getStderr().close();

View file

@ -47,54 +47,26 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
@Override @Override
public String prepareTerminalOpen() throws Exception { public String prepareTerminalOpen() throws Exception {
if (isRunning()) { return prepareIntermediateTerminalOpen(null);
exitAndWait();
}
try (var parentPc = parent.start()) {
var operator = parent.getShellType().getOrConcatenationOperator();
var consoleCommand = this.terminalCommand.apply(parent, null);
var elevated = elevationFunction.test(parent);
if (elevated) {
consoleCommand = ElevationHelper.elevateTerminalCommand(consoleCommand, parent);
}
var openCommand = "";
try (var pc = start()) {
var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, null);
openCommand = consoleCommand + " " + initCommand + operator
+ parent.getShellType().getPauseCommand();
TrackEvent.withDebug("proc", "Preparing for terminal open")
.tag("initCommand", initCommand)
.tag("openCommand", openCommand)
.handle();
}
return parent.prepareIntermediateTerminalOpen(openCommand);
}
} }
@Override @Override
public String prepareIntermediateTerminalOpen(String content) throws Exception { public String prepareIntermediateTerminalOpen(String toExecuteInThis) throws Exception {
if (this.terminalCommand == null) { if (this.terminalCommand == null) {
throw new UnsupportedOperationException("Terminal open not supported"); throw new UnsupportedOperationException("Terminal open not supported");
} }
try (var pc = start()) { try (var pc = start()) {
var operator = parent.getShellType().getOrConcatenationOperator(); var operator = parent.getShellType().getOrConcatenationOperator();
var file = ScriptHelper.createExecScript(this, content, false); var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, toExecuteInThis);
var terminalCommand = this.terminalCommand.apply(parent, initCommand);
var terminalCommand = this.terminalCommand.apply(parent, file);
var elevated = elevationFunction.test(parent); var elevated = elevationFunction.test(parent);
if (elevated) { if (elevated) {
terminalCommand = ElevationHelper.elevateTerminalCommand(terminalCommand, parent); terminalCommand = ElevationHelper.elevateTerminalCommand(terminalCommand, parent);
} }
var openCommand = terminalCommand + operator + parent.getShellType().getPauseCommand();
var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, terminalCommand);
var openCommand = initCommand + operator + parent.getShellType().getPauseCommand();
TrackEvent.withDebug("proc", "Preparing for terminal open") TrackEvent.withDebug("proc", "Preparing for terminal open")
.tag("file", file) .tag("toExecuteInThis", ShellHelper.censor(toExecuteInThis, sensitive))
.tag("content", ShellHelper.censor(content, sensitive))
.tag("openCommand", openCommand)
.tag("initCommand", initCommand) .tag("initCommand", initCommand)
.handle(); .handle();
@ -277,6 +249,7 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
shellType = null; shellType = null;
charset = null; charset = null;
command = null; command = null;
tempDirectory = null;
} }
uuid = null; uuid = null;
} }

View file

@ -21,6 +21,10 @@ public class ShellHelper {
private static final String DETECTOR_COMMAND = "echo " + DETECTOR_STRING; private static final String DETECTOR_COMMAND = "echo " + DETECTOR_STRING;
public static String censor(String command, boolean sensitive ) { public static String censor(String command, boolean sensitive ) {
if (command == null) {
return command;
}
if (!sensitive) { if (!sensitive) {
return command; return command;
} }

View file

@ -6,7 +6,6 @@ import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.process.ShellType; import io.xpipe.core.process.ShellType;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.SecretValue; import io.xpipe.core.util.SecretValue;
import io.xpipe.core.util.XPipeTempDirectory;
import io.xpipe.extension.event.TrackEvent; import io.xpipe.extension.event.TrackEvent;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -15,10 +14,10 @@ import java.util.Random;
public class ScriptHelper { public class ScriptHelper {
public static int getConnectionHash(String command) { public static int getScriptId() {
// This deterministic approach can cause permission problems when two different users execute the same command on a system // A deterministic approach can cause permission problems when two different users execute the same command on a system
// Therefore, use a random approach
return new Random().nextInt(Integer.MAX_VALUE); return new Random().nextInt(Integer.MAX_VALUE);
//return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
} }
@SneakyThrows @SneakyThrows
@ -44,7 +43,8 @@ public class ScriptHelper {
} }
if (toExecuteInShell != null) { if (toExecuteInShell != null) {
content += toExecuteInShell + nl; // Normalize line endings
content += String.join(nl, toExecuteInShell.lines().toList()) + nl;
content += t.getExitCommand() + nl; content += t.getExitCommand() + nl;
} }
@ -52,19 +52,11 @@ public class ScriptHelper {
return t.getInitFileOpenCommand(initFile); return t.getInitFileOpenCommand(initFile);
} }
public static String prepend(ShellProcessControl processControl, List<String> init, String commands) {
var prefix = init != null && init.size() > 0
? String.join(processControl.getShellType().getNewLine().getNewLineString(), init)
+ processControl.getShellType().getNewLine().getNewLineString()
: "";
return prefix + commands;
}
@SneakyThrows @SneakyThrows
public static String createExecScript(ShellProcessControl processControl, String content, boolean restart) { public static String createExecScript(ShellProcessControl processControl, String content, boolean restart) {
var fileName = "exec-" + getConnectionHash(content); var fileName = "exec-" + getScriptId();
ShellType type = processControl.getShellType(); ShellType type = processControl.getShellType();
var temp = XPipeTempDirectory.get(processControl); var temp = processControl.getTemporaryDirectory();
var file = FileNames.join(temp, fileName + "." + type.getScriptFileEnding()); var file = FileNames.join(temp, fileName + "." + type.getScriptFileEnding());
return createExecScript(processControl, file, content, restart); return createExecScript(processControl, file, content, restart);
} }
@ -75,20 +67,15 @@ public class ScriptHelper {
ShellType type = processControl.getShellType(); ShellType type = processControl.getShellType();
content = type.prepareScriptContent(content); content = type.prepareScriptContent(content);
if (processControl.executeBooleanSimpleCommand(type.getFileExistsCommand(file))) {
return file;
}
TrackEvent.withTrace("proc", "Writing exec script") TrackEvent.withTrace("proc", "Writing exec script")
.tag("file", file) .tag("file", file)
.tag("content", content) .tag("content", content)
.handle(); .handle();
processControl.executeSimpleCommand(type.getFileTouchCommand(file), "Failed to create script " + file); // processControl.executeSimpleCommand(type.getFileTouchCommand(file), "Failed to create script " + file);
processControl.executeSimpleCommand(type.getTextFileWriteCommand(content, file));
processControl.executeSimpleCommand( processControl.executeSimpleCommand(
type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable"); type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
processControl.executeSimpleCommand(type.getTextFileWriteCommand(content, file));
return file; return file;
} }
@ -96,8 +83,8 @@ public class ScriptHelper {
public static String createAskPassScript( public static String createAskPassScript(
SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) { SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
var content = type.getScriptEchoCommand(pass.getSecretValue()); var content = type.getScriptEchoCommand(pass.getSecretValue());
var temp = XPipeTempDirectory.get(parent); var temp = parent.getTemporaryDirectory();
var file = FileNames.join(temp, "askpass-" + getConnectionHash(content) + "." + type.getScriptFileEnding()); var file = FileNames.join(temp, "askpass-" + getScriptId() + "." + type.getScriptFileEnding());
return createExecScript(parent, file, content, restart); return createExecScript(parent, file, content, restart);
} }
} }

View file

@ -1 +1 @@
0.4.22 0.4.23