mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Implement more features for shells
This commit is contained in:
parent
85389d26f9
commit
07be7eb7d6
17 changed files with 327 additions and 280 deletions
|
@ -130,7 +130,7 @@ public abstract class Charsetter {
|
|||
public NewLine inferNewLine(byte[] content) {
|
||||
Map<NewLine, Integer> count = new HashMap<>();
|
||||
for (var nl : NewLine.values()) {
|
||||
var nlBytes = nl.getNewLine().getBytes(StandardCharsets.UTF_8);
|
||||
var nlBytes = nl.getNewLineString().getBytes(StandardCharsets.UTF_8);
|
||||
count.put(nl, count(content, nlBytes));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public enum NewLine {
|
|||
|
||||
public static NewLine platform() {
|
||||
return Arrays.stream(values())
|
||||
.filter(n -> n.getNewLine().equals(System.getProperty("line.separator")))
|
||||
.filter(n -> n.getNewLineString().equals(System.getProperty("line.separator")))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public enum NewLine {
|
|||
.orElseThrow();
|
||||
}
|
||||
|
||||
public String getNewLine() {
|
||||
public String getNewLineString() {
|
||||
return newLine;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@ public class TextWriteConnection extends StreamWriteConnection implements io.xpi
|
|||
@Override
|
||||
public void writeLine(String line) throws Exception {
|
||||
writer.write(line);
|
||||
writer.write(source.getNewLine().getNewLine());
|
||||
writer.write(source.getNewLine().getNewLineString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ package io.xpipe.core.store;
|
|||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public interface CommandProcessControl extends ProcessControl {
|
||||
|
||||
|
@ -45,7 +43,7 @@ public interface CommandProcessControl extends ProcessControl {
|
|||
CommandProcessControl start() throws Exception;
|
||||
|
||||
@Override
|
||||
CommandProcessControl exitTimeout(int timeout);
|
||||
CommandProcessControl exitTimeout(Integer timeout);
|
||||
|
||||
String readOnlyStdout() throws Exception;
|
||||
|
||||
|
@ -53,6 +51,8 @@ public interface CommandProcessControl extends ProcessControl {
|
|||
readOrThrow();
|
||||
}
|
||||
|
||||
public String readOrThrow() throws Exception;
|
||||
|
||||
public default boolean startAndCheckExit() {
|
||||
try (var pc = start()) {
|
||||
return pc.discardAndCheckExit();
|
||||
|
@ -70,52 +70,6 @@ public interface CommandProcessControl extends ProcessControl {
|
|||
}
|
||||
}
|
||||
|
||||
public default Optional<String> readStderrIfPresent() throws Exception {
|
||||
discardOut();
|
||||
var bytes = getStderr().readAllBytes();
|
||||
var string = new String(bytes, getCharset());
|
||||
var ec = waitFor();
|
||||
return ec ? Optional.of(string) : Optional.empty();
|
||||
}
|
||||
|
||||
public default String readOrThrow() throws Exception {
|
||||
AtomicReference<String> readError = new AtomicReference<>("");
|
||||
var errorThread = new Thread(() -> {
|
||||
try {
|
||||
|
||||
readError.set(new String(getStderr().readAllBytes(), getCharset()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
errorThread.setDaemon(true);
|
||||
errorThread.start();
|
||||
|
||||
AtomicReference<String> read = new AtomicReference<>("");
|
||||
var t = new Thread(() -> {
|
||||
try {
|
||||
read.set(readLine());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
|
||||
var ec = waitFor();
|
||||
if (!ec) {
|
||||
throw new ProcessOutputException("Command timed out");
|
||||
}
|
||||
|
||||
var exitCode = getExitCode();
|
||||
if (exitCode == 0 && !(read.get().isEmpty() && !readError.get().isEmpty())) {
|
||||
return read.get().trim();
|
||||
} else {
|
||||
throw new ProcessOutputException(
|
||||
"Command returned with " + ec + ": " + readError.get().trim());
|
||||
}
|
||||
}
|
||||
|
||||
Thread discardOut();
|
||||
|
||||
Thread discardErr();
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import io.xpipe.core.util.SupportedOs;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface MachineStore extends FileSystemStore, ShellStore {
|
||||
|
||||
@Override
|
||||
default void validate() throws Exception {
|
||||
try (ShellProcessControl pc = create().start()) {
|
||||
}
|
||||
}
|
||||
|
||||
public default boolean isLocal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public default String queryMachineName() throws Exception {
|
||||
try (CommandProcessControl pc = create().commandListFunction(shellProcessControl ->
|
||||
shellProcessControl.getShellType().getOperatingSystemNameCommand())
|
||||
.start()) {
|
||||
return pc.readOrThrow().trim();
|
||||
try (var pc = create().start()) {
|
||||
var operatingSystem = SupportedOs.determine(pc);
|
||||
return operatingSystem.determineOperatingSystemName(pc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ProcessControl extends AutoCloseable {
|
||||
|
||||
|
@ -12,41 +12,15 @@ public interface ProcessControl extends AutoCloseable {
|
|||
|
||||
ShellType getShellType();
|
||||
|
||||
String readResultLine(String input, boolean captureOutput) throws IOException;
|
||||
|
||||
void writeLine(String line) throws IOException;
|
||||
|
||||
void writeLine(String line, boolean captureOutput) throws IOException;
|
||||
|
||||
void typeLine(String line);
|
||||
|
||||
public default String readOutput() throws IOException {
|
||||
var id = UUID.randomUUID();
|
||||
writeLine("echo " + id, false);
|
||||
String lines = "";
|
||||
while (true) {
|
||||
var newLine = readLine();
|
||||
if (newLine.contains(id.toString())) {
|
||||
if (getShellType().echoesInput()) {
|
||||
readLine();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lines = lines + newLine + "\n";
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() throws IOException;
|
||||
void kill() throws Exception;
|
||||
|
||||
String readLine() throws IOException;
|
||||
|
||||
void kill() throws IOException;
|
||||
|
||||
ProcessControl exitTimeout(int timeout);
|
||||
ProcessControl exitTimeout(Integer timeout);
|
||||
|
||||
ProcessControl start() throws Exception;
|
||||
|
||||
|
@ -59,4 +33,7 @@ public interface ProcessControl extends AutoCloseable {
|
|||
InputStream getStderr();
|
||||
|
||||
Charset getCharset();
|
||||
|
||||
BufferedReader getStdoutReader();
|
||||
BufferedReader getStderrReader();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PropertiesFormatsParser {
|
||||
|
||||
@SneakyThrows
|
||||
public static Map<String, String> parse(String text, String split) {
|
||||
var map = new LinkedHashMap<String, String>();
|
||||
|
||||
var reader = new BufferedReader(new StringReader(text));
|
||||
String line;
|
||||
|
||||
String currentKey = null;
|
||||
String currentValue = "";
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("\s") || line.startsWith("\t")) {
|
||||
currentValue += line;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line.contains(split)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyName = line.substring(0, line.indexOf(split)).strip();
|
||||
var value = line.substring(line.indexOf(split) + 1).strip();
|
||||
if (value.startsWith("\"") && value.endsWith("\"")) {
|
||||
value = value.substring(1, value.length() - 1);
|
||||
}
|
||||
|
||||
if (currentKey != null) {
|
||||
map.put(currentKey, currentValue);
|
||||
}
|
||||
|
||||
currentKey = keyName;
|
||||
currentValue = value;
|
||||
}
|
||||
|
||||
if (currentKey != null) {
|
||||
map.put(currentKey, currentValue);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -6,15 +6,20 @@ import lombok.NonNull;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface ShellProcessControl extends ProcessControl {
|
||||
|
||||
ShellProcessControl elevated(Predicate<ShellProcessControl> elevationFunction);
|
||||
|
||||
ShellProcessControl elevation(SecretValue value);
|
||||
|
||||
ShellProcessControl startTimeout(Integer timeout);
|
||||
|
||||
SecretValue getElevationPassword();
|
||||
|
||||
default ShellProcessControl shell(@NonNull ShellType type){
|
||||
default ShellProcessControl shell(@NonNull ShellType type) {
|
||||
return shell(type.openCommand());
|
||||
}
|
||||
|
||||
|
@ -23,15 +28,15 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
command.stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
|
||||
}
|
||||
|
||||
default ShellProcessControl shell(@NonNull String command){
|
||||
default ShellProcessControl shell(@NonNull String command) {
|
||||
return shell(processControl -> command);
|
||||
}
|
||||
|
||||
ShellProcessControl shell(@NonNull Function<ProcessControl, String> command);
|
||||
ShellProcessControl shell(@NonNull Function<ShellProcessControl, String> command);
|
||||
|
||||
void executeCommand(String command) throws IOException;
|
||||
void executeCommand(String command) throws Exception;
|
||||
|
||||
default void executeCommand(List<String> command) throws IOException {
|
||||
default void executeCommand(List<String> command) throws Exception {
|
||||
executeCommand(
|
||||
command.stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
|
||||
}
|
||||
|
@ -40,8 +45,9 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
ShellProcessControl start() throws Exception;
|
||||
|
||||
default CommandProcessControl commandListFunction(Function<ShellProcessControl, List<String>> command) {
|
||||
return commandFunction(shellProcessControl ->
|
||||
command.apply(shellProcessControl).stream().map(s -> s.contains(" ") ? "\"" + s + "\"" : s).collect(Collectors.joining(" ")));
|
||||
return commandFunction(shellProcessControl -> command.apply(shellProcessControl).stream()
|
||||
.map(s -> s.contains(" ") ? "\"" + s + "\"" : s)
|
||||
.collect(Collectors.joining(" ")));
|
||||
}
|
||||
|
||||
CommandProcessControl commandFunction(Function<ShellProcessControl, String> command);
|
||||
|
@ -54,5 +60,4 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
}
|
||||
|
||||
void exit() throws IOException;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@ public interface ShellStore extends DataStore {
|
|||
return new LocalStore();
|
||||
}
|
||||
|
||||
static boolean isLocal(ShellStore s) {
|
||||
return s instanceof LocalStore;
|
||||
}
|
||||
|
||||
ShellProcessControl create();
|
||||
|
||||
public default ShellType determineType() throws Exception {
|
||||
|
|
|
@ -3,20 +3,13 @@ package io.xpipe.core.store;
|
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.core.charsetter.NewLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
property = "type"
|
||||
)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public interface ShellType {
|
||||
|
||||
void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException;
|
||||
|
||||
default void init(ProcessControl proc) throws IOException {
|
||||
}
|
||||
void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception;
|
||||
|
||||
default String getExitCommand() {
|
||||
return "exit";
|
||||
|
@ -28,9 +21,14 @@ public interface ShellType {
|
|||
return ";";
|
||||
}
|
||||
|
||||
String getEchoCommand(String s, boolean newLine);
|
||||
default String getAndConcatenationOperator() {
|
||||
return "&&";
|
||||
}
|
||||
|
||||
String getEchoCommand(String s, boolean toErrorStream);
|
||||
|
||||
List<String> openCommand();
|
||||
|
||||
String switchTo(String cmd);
|
||||
|
||||
List<String> createMkdirsCommand(String dirs);
|
||||
|
@ -41,7 +39,7 @@ public interface ShellType {
|
|||
|
||||
List<String> createFileExistsCommand(String file);
|
||||
|
||||
Charset determineCharset(ProcessControl control) throws Exception;
|
||||
Charset determineCharset(ShellProcessControl control) throws Exception;
|
||||
|
||||
NewLine getNewLine();
|
||||
|
||||
|
@ -49,7 +47,5 @@ public interface ShellType {
|
|||
|
||||
String getDisplayName();
|
||||
|
||||
List<String> getOperatingSystemNameCommand();
|
||||
|
||||
boolean echoesInput();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
|
|||
import io.xpipe.core.charsetter.NewLine;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
@ -16,42 +17,6 @@ public class ShellTypes {
|
|||
public static final ShellType CMD = new Cmd();
|
||||
public static final ShellType SH = new Sh();
|
||||
|
||||
public static ShellType determine(ProcessControl proc) throws Exception {
|
||||
proc.writeLine("echo -NoEnumerate \"a\"", false);
|
||||
String line;
|
||||
while (true) {
|
||||
line = proc.readLine();
|
||||
if (line.equals("-NoEnumerate a")) {
|
||||
return SH;
|
||||
}
|
||||
|
||||
if (line.contains("echo -NoEnumerate \"a\"")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var o = proc.readLine();
|
||||
|
||||
if (o.equals("a")) {
|
||||
return POWERSHELL;
|
||||
} else if (o.equals("-NoEnumerate \"a\"")) {
|
||||
return CMD;
|
||||
} else {
|
||||
return SH;
|
||||
}
|
||||
}
|
||||
|
||||
public static ShellType[] getAvailable(ShellStore store) throws Exception {
|
||||
try (ProcessControl proc = store.create().start()) {
|
||||
var type = determine(proc);
|
||||
if (type == SH) {
|
||||
return getLinuxShells();
|
||||
} else {
|
||||
return getWindowsShells();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ShellType getRecommendedDefault() {
|
||||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
return POWERSHELL;
|
||||
|
@ -81,8 +46,8 @@ public class ShellTypes {
|
|||
public static class Cmd implements ShellType {
|
||||
|
||||
@Override
|
||||
public String getEchoCommand(String s, boolean newLine) {
|
||||
return newLine ? "echo " + s : "echo | set /p dummyName=" + s;
|
||||
public String getEchoCommand(String s, boolean toErrorStream) {
|
||||
return toErrorStream ? "(echo " + s + ")1>&2" : "echo " + s;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,27 +56,20 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
|
||||
control.executeCommand("net session >NUL 2>NUL");
|
||||
control.executeCommand("echo %errorLevel%");
|
||||
var exitCode = Integer.parseInt(control.readLine());
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
|
||||
try (CommandProcessControl c = control.command("net session >NUL 2>NUL")) {
|
||||
var exitCode = c.getExitCode();
|
||||
if (exitCode != 0) {
|
||||
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
|
||||
}
|
||||
}
|
||||
|
||||
control.executeCommand(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ProcessControl proc) throws IOException {
|
||||
proc.readLine();
|
||||
proc.readLine();
|
||||
proc.readLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExitCodeVariable() {
|
||||
return "%errorlevel%";
|
||||
return "!errorlevel!";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,12 +79,12 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public List<String> openCommand() {
|
||||
return List.of("cmd");
|
||||
return List.of("cmd", "/V:on");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String switchTo(String cmd) {
|
||||
return "cmd.exe /c " + cmd;
|
||||
return "cmd.exe /V:on /c " + cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,9 +108,18 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Charset determineCharset(ProcessControl control) throws Exception {
|
||||
var output = control.readResultLine("chcp", true);
|
||||
var matcher = Pattern.compile("\\d+").matcher(output);
|
||||
public Charset determineCharset(ShellProcessControl control) throws Exception {
|
||||
control.writeLine("chcp");
|
||||
|
||||
var r = new BufferedReader(new InputStreamReader(control.getStdout(), StandardCharsets.US_ASCII));
|
||||
// Read echo of command
|
||||
r.readLine();
|
||||
// Read actual output
|
||||
var line = r.readLine();
|
||||
// Read additional empty line
|
||||
r.readLine();
|
||||
|
||||
var matcher = Pattern.compile("\\d+").matcher(line);
|
||||
matcher.find();
|
||||
return Charset.forName("ibm" + matcher.group());
|
||||
}
|
||||
|
@ -167,11 +134,6 @@ public class ShellTypes {
|
|||
return "cmd";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOperatingSystemNameCommand() {
|
||||
return List.of("Get-ComputerInfo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean echoesInput() {
|
||||
return true;
|
||||
|
@ -188,12 +150,14 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
|
||||
control.executeCommand("([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)");
|
||||
var exitCode = Integer.parseInt(control.readLine());
|
||||
if (exitCode != 0) {
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
|
||||
try (CommandProcessControl c = control.command(
|
||||
"([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)")
|
||||
.start()) {
|
||||
if (c.startAndCheckExit()) {
|
||||
throw new IllegalStateException("The command \"" + displayCommand + "\" requires elevation.");
|
||||
}
|
||||
}
|
||||
|
||||
control.executeCommand(command);
|
||||
}
|
||||
|
@ -204,8 +168,8 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getEchoCommand(String s, boolean newLine) {
|
||||
return newLine ? "echo " + s : String.format("Write-Host \"%s\" -NoNewLine", s);
|
||||
public String getEchoCommand(String s, boolean toErrorStream) {
|
||||
return String.format("%s \"%s\"", toErrorStream ? "Write-Error" : "Write-Output", s);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -220,7 +184,7 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public List<String> createMkdirsCommand(String dirs) {
|
||||
return List.of("New-Item", "-Path", "D:\\temp\\Test Folder", "-ItemType", "Directory");
|
||||
return List.of("New-Item", "-Path", dirs, "-ItemType", "Directory");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -239,12 +203,14 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Charset determineCharset(ProcessControl control) throws Exception {
|
||||
var output = control.readResultLine("chcp", true);
|
||||
public Charset determineCharset(ShellProcessControl control) throws Exception {
|
||||
try (CommandProcessControl c = control.command("chcp").start()) {
|
||||
var output = c.readOrThrow().strip();
|
||||
var matcher = Pattern.compile("\\d+").matcher(output);
|
||||
matcher.find();
|
||||
return Charset.forName("ibm" + matcher.group());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewLine getNewLine() {
|
||||
|
@ -260,11 +226,6 @@ public class ShellTypes {
|
|||
public String getDisplayName() {
|
||||
return "PowerShell";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOperatingSystemNameCommand() {
|
||||
return List.of("systeminfo", "|", "findstr", "/B", "/C:\"OS Name\"");
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("sh")
|
||||
|
@ -272,12 +233,24 @@ public class ShellTypes {
|
|||
public static class Sh implements ShellType {
|
||||
|
||||
@Override
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws IOException {
|
||||
if (control.getElevationPassword().getSecretValue() == null) {
|
||||
throw new IllegalStateException("No password for sudo has been set");
|
||||
public String getExitCommand() {
|
||||
return "exit 0";
|
||||
}
|
||||
|
||||
control.executeCommand("sudo -S " + switchTo(command));
|
||||
@Override
|
||||
public String getConcatenationOperator() {
|
||||
return ";";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elevate(ShellProcessControl control, String command, String displayCommand) throws Exception {
|
||||
if (control.getElevationPassword() == null) {
|
||||
control.executeCommand("SUDO_ASKPASS=/bin/false; sudo -p \"\" -S " + command);
|
||||
return;
|
||||
}
|
||||
|
||||
control.executeCommand("sudo -p \"\" -S " + command);
|
||||
// Thread.sleep(200);
|
||||
control.writeLine(control.getElevationPassword().getSecretValue());
|
||||
}
|
||||
|
||||
|
@ -287,13 +260,13 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getEchoCommand(String s, boolean newLine) {
|
||||
return newLine ? "echo " + s : "echo -n " + s;
|
||||
public String getEchoCommand(String s, boolean toErrorStream) {
|
||||
return "echo " + s + (toErrorStream ? " 1>&2" : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> openCommand() {
|
||||
return List.of("sh");
|
||||
return List.of("sh", "-i", "-l");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -322,7 +295,7 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Charset determineCharset(ProcessControl st) throws Exception {
|
||||
public Charset determineCharset(ShellProcessControl st) throws Exception {
|
||||
return StandardCharsets.UTF_8;
|
||||
}
|
||||
|
||||
|
@ -341,11 +314,6 @@ public class ShellTypes {
|
|||
return "/bin/sh";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOperatingSystemNameCommand() {
|
||||
return List.of("uname", "-o");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean echoesInput() {
|
||||
return false;
|
||||
|
|
153
core/src/main/java/io/xpipe/core/util/SupportedOs.java
Normal file
153
core/src/main/java/io/xpipe/core/util/SupportedOs.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import io.xpipe.core.store.*;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SupportedOs {
|
||||
|
||||
Windows WINDOWS = new Windows();
|
||||
Linux LINUX = new Linux();
|
||||
Mac MAC = new Mac();
|
||||
|
||||
static SupportedOs determine(ShellProcessControl pc) throws Exception {
|
||||
try (CommandProcessControl c = pc.command(pc.getShellType().createFileExistsCommand("C:\\pagefile.sys")).start()) {
|
||||
if (c.discardAndCheckExit()) {
|
||||
return WINDOWS;
|
||||
}
|
||||
}
|
||||
|
||||
return LINUX;
|
||||
}
|
||||
|
||||
Map<String, String> getProperties(ShellProcessControl pc) throws Exception;
|
||||
|
||||
String determineOperatingSystemName(ShellProcessControl pc) throws Exception;
|
||||
|
||||
@SneakyThrows
|
||||
public static SupportedOs getLocal() {
|
||||
try (ShellProcessControl pc = ShellStore.local().create().start()) {
|
||||
return determine(pc);
|
||||
}
|
||||
}
|
||||
|
||||
Path getBaseInstallPath();
|
||||
|
||||
UUID getSystemUUID(ShellProcessControl pc) throws Exception;
|
||||
|
||||
static class Windows implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
||||
try (CommandProcessControl c =
|
||||
pc.shell(ShellTypes.CMD).command("systeminfo").start()) {
|
||||
var text = c.readOrThrow();
|
||||
return PropertiesFormatsParser.parse(text, ":");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
||||
var properties = getProperties(pc);
|
||||
return properties.get("OS Name") + " "
|
||||
+ properties.get("OS Version").split(" ")[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
|
||||
try (CommandProcessControl c = pc.command(
|
||||
"reg query \"Computer\\\\HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Microsoft\\\\Cryptography\" /v MachineGuid")) {
|
||||
var output = c.readOnlyStdout();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Linux implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
||||
try (CommandProcessControl c =
|
||||
pc.shell(ShellTypes.SH).command("lsb_release -a").start()) {
|
||||
var text = c.readOnlyStdout();
|
||||
if (c.getExitCode() == 0) {
|
||||
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null);
|
||||
}
|
||||
}
|
||||
|
||||
try (CommandProcessControl c =
|
||||
pc.shell(ShellTypes.SH).command("cat /etc/*release").start()) {
|
||||
var text = c.readOnlyStdout();
|
||||
if (c.getExitCode() == 0) {
|
||||
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null);
|
||||
}
|
||||
}
|
||||
|
||||
String type = "Unknown";
|
||||
try (CommandProcessControl c =
|
||||
pc.shell(ShellTypes.SH).command("uname -o").start()) {
|
||||
var text = c.readOnlyStdout();
|
||||
if (c.getExitCode() == 0) {
|
||||
type = text.strip();
|
||||
}
|
||||
}
|
||||
|
||||
String version = "?";
|
||||
try (CommandProcessControl c =
|
||||
pc.shell(ShellTypes.SH).command("uname -r").start()) {
|
||||
var text = c.readOnlyStdout();
|
||||
if (c.getExitCode() == 0) {
|
||||
version = text.strip();
|
||||
}
|
||||
}
|
||||
|
||||
return type + " " + version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of("/opt/xpipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Mac implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of(System.getProperty("user.home"), "Application Support", "X-Pipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID(ShellProcessControl pc) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import io.xpipe.fxcomps.Comp;
|
|||
import io.xpipe.fxcomps.CompStructure;
|
||||
import io.xpipe.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.event.EventHandler;
|
||||
|
@ -26,7 +27,9 @@ public class TextFieldComp extends Comp<CompStructure<TextField>> {
|
|||
this.currentValue = new SimpleStringProperty(value.getValue());
|
||||
this.lazy = lazy;
|
||||
if (!lazy) {
|
||||
value.bind(currentValue);
|
||||
SimpleChangeListener.apply(currentValue, val -> {
|
||||
value.setValue(val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import java.util.ServiceLoader;
|
|||
|
||||
public abstract class EventHandler {
|
||||
|
||||
private static final EventHandler DEFAULT = new EventHandler() {
|
||||
public static final EventHandler DEFAULT = new EventHandler() {
|
||||
@Override
|
||||
public List<TrackEvent> snapshotEvents() {
|
||||
return List.of();
|
||||
|
|
|
@ -35,6 +35,10 @@ public class TrackEvent {
|
|||
return builder().type("info").message(message);
|
||||
}
|
||||
|
||||
public static TrackEventBuilder withWarn(String category, String message) {
|
||||
return builder().category(category).type("warn").message(message);
|
||||
}
|
||||
|
||||
public static TrackEventBuilder withWarn(String message) {
|
||||
return builder().type("warn").message(message);
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ public class DynamicOptionsBuilder {
|
|||
|
||||
public DynamicOptionsBuilder addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
props.add(prop);
|
||||
if (prop != null) props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
package io.xpipe.extension.util;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SupportedOs {
|
||||
|
||||
Windows WINDOWS = new Windows();
|
||||
Linux LINUX = new Linux();
|
||||
Mac MAC = new Mac();
|
||||
|
||||
public static SupportedOs get() {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
return WINDOWS;
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
return LINUX;
|
||||
} else if (SystemUtils.IS_OS_MAC) {
|
||||
return MAC;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported operating system");
|
||||
}
|
||||
}
|
||||
|
||||
Path getBaseInstallPath();
|
||||
|
||||
UUID getSystemUUID();
|
||||
|
||||
static class Windows implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID() {
|
||||
var s = WindowsRegistry.readRegistry(
|
||||
"Computer\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "MachineGuid")
|
||||
.orElse(null);
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return UUID.fromString(s);
|
||||
}
|
||||
}
|
||||
|
||||
static class Linux implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of("/opt/xpipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Mac implements SupportedOs {
|
||||
|
||||
@Override
|
||||
public Path getBaseInstallPath() {
|
||||
return Path.of(System.getProperty("user.home"), "Application Support", "X-Pipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getSystemUUID() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue