xpipe-io / xpipe

Your entire server infrastructure at your fingertips
https://xpipe.io
Apache License 2.0
3.39k stars 88 forks source link

java.io.IOException: Cannot run program "which": error=13, Permission denied #315

Closed MirLach closed 3 months ago

MirLach commented 3 months ago

Related to the FreeBSD port created by MartinFx in issue #314 I tried to install it on my FreeBSD 13.3 desktop, but when I tried to run it, it failed. The logfile contains many errors of permission denied on commands which and bash. Both command are installed, are in the system path and can be executed manually under the same user which I use to run xpipe. Whan can cause this error in Java? (I am not a Java developer, just a user and FreeBSD sysadmin)

14:09:37:689 - info: Loading extension providers ...
14:09:37:832 - info: Loading translations ...
14:09:37:903 - info: Finished initial setup
14:09:37:993 - info: Attempting to switch mode to background
14:09:37:994 - info: Initializing base mode components ...
14:09:37:994 - info: Loading translations ...
14:09:38:037 - error: java.io.IOException: Cannot run program "which": error=13, Permission denied
java.io.IOException: Cannot run program "which": error=13, Permission denied
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1170)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1089)
    at io.xpipe.app.core.AppSid.init(AppSid.java:26)
    at io.xpipe.app.core.mode.BaseMode.onSwitchTo(BaseMode.java:52)
    at io.xpipe.app.core.mode.OperationMode.set(OperationMode.java:330)
    at io.xpipe.app.core.mode.OperationMode.switchToSyncOrThrow(OperationMode.java:157)
    at io.xpipe.app.launcher.LauncherCommand.call(LauncherCommand.java:169)
    at io.xpipe.app.launcher.LauncherCommand.call(LauncherCommand.java:30)
    at picocli.CommandLine.executeUserObject(CommandLine.java:2045)
    at picocli.CommandLine.access$1500(CommandLine.java:148)
    at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2465)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2457)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2419)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
    at picocli.CommandLine$RunLast.execute(CommandLine.java:2421)
    at picocli.CommandLine.execute(CommandLine.java:2174)
    at io.xpipe.app.launcher.LauncherCommand.runLauncher(LauncherCommand.java:74)
    at io.xpipe.app.core.mode.OperationMode.init(OperationMode.java:136)
    at io.xpipe.app.Main.main(Main.java:27)
Caused by: java.io.IOException: error=13, Permission denied
    at java.lang.ProcessImpl.forkAndExec(Native Method)
    at java.lang.ProcessImpl.<init>(ProcessImpl.java:295)
    at java.lang.ProcessImpl.start(ProcessImpl.java:225)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1126)
    ... 18 more

14:09:38:060 - info: Successfully set process sid
14:09:38:066 - error: io.xpipe.ext.proc.m: Unable to launch required system shell: Cannot run program "bash" (in directory "/usr/home/user1/.xpipe/shell"): error=13, Permission denied
io.xpipe.ext.proc.m: Unable to launch required system shell: Cannot run program "bash" (in directory "/usr/home/user1/.xpipe/shell"): error=13, Permission denied
    at io.xpipe.ext.proc.d.r(SourceFile:556)
    at io.xpipe.ext.proc.d.n(SourceFile:437)
    at io.xpipe.ext.proc.d.start(SourceFile:265)
    at io.xpipe.app.util.LocalShell.init(LocalShell.java:18)
    at io.xpipe.app.core.mode.BaseMode.onSwitchTo(BaseMode.java:53)
    at io.xpipe.app.core.mode.OperationMode.set(OperationMode.java:330)
    at io.xpipe.app.core.mode.OperationMode.switchToSyncOrThrow(OperationMode.java:157)
    at io.xpipe.app.launcher.LauncherCommand.call(LauncherCommand.java:169)
    at io.xpipe.app.launcher.LauncherCommand.call(LauncherCommand.java:30)
    at picocli.CommandLine.executeUserObject(CommandLine.java:2045)
    at picocli.CommandLine.access$1500(CommandLine.java:148)
    at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2465)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2457)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2419)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
    at picocli.CommandLine$RunLast.execute(CommandLine.java:2421)
    at picocli.CommandLine.execute(CommandLine.java:2174)
    at io.xpipe.app.launcher.LauncherCommand.runLauncher(LauncherCommand.java:74)
    at io.xpipe.app.core.mode.OperationMode.init(OperationMode.java:136)
    at io.xpipe.app.Main.main(Main.java:27)
Caused by: java.io.IOException: Cannot run program "bash" (in directory "/usr/home/user1/.xpipe/shell"): error=13, Permission denied
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1170)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1089)
    at io.xpipe.ext.proc.d.r(SourceFile:553)
    ... 19 more
Caused by: java.io.IOException: error=13, Permission denied
    at java.lang.ProcessImpl.forkAndExec(Native Method)
    at java.lang.ProcessImpl.<init>(ProcessImpl.java:295)
    at java.lang.ProcessImpl.start(ProcessImpl.java:225)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1126)
    ... 21 more
crschnick commented 3 months ago

It runs these programs normally via the ProcessBuilder. I'm not sure why this is not working. The process implementation that fails is this one: https://github.com/openjdk/jdk21u/blob/2c9f741d9ce27cd81e4ad9395a88af1b34a2ba77/src/java.base/unix/classes/java/lang/ProcessImpl.java#L295

crschnick commented 3 months ago

It doesn't do anything special, this issue should be reproducible with any other program running with Java 21. If other applications do not have that issue, then it's really weird.

MirLach commented 3 months ago

I tried to build and run the simple example with ProcessBuilder and it works for me:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class bash_runner_with_ProcessBuilder {
    public static void main(String[] args) {
        ProcessBuilder processBuilder = new ProcessBuilder();
        Process process = null; // Declare the Process variable here

        // Run a single command
        //processBuilder.command("bash", "-c", "echo 'Hello from bash'");
        processBuilder.command("which", "bash");

        // Merge stdout and stderr
        processBuilder.redirectErrorStream(true);

        try {
            process = processBuilder.start();

            // Capture the combined output of the command
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            int exitCode = process.waitFor();
            System.out.println("Exit code: " + exitCode);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Destroy the process if it's still running
            if (process != null) {
                process.destroy();
            }
        }
    }
}
# javac bash_runner_with_ProcessBuilder.java
# java bash_runner_with_ProcessBuilder
/usr/local/bin/bash
Exit code: 0
MirLach commented 3 months ago

Now I replicate the code style of ProcessBuilder from the AppSid.java and it also works:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import java.util.concurrent.TimeUnit;

public class which_setsid_with_ProcessBuilder {

    private static boolean hasSetsid;

    public static void main(String[] args) {

        //var checkProcess = new ProcessBuilder("which", "bash")
        var checkProcess = new ProcessBuilder("which", "setsid")
                .redirectErrorStream(true)
                .redirectOutput(ProcessBuilder.Redirect.DISCARD);
        try {
            var p = checkProcess.start();
            if (p.waitFor(1000, TimeUnit.MILLISECONDS)) {
                hasSetsid = p.exitValue() == 0;
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

        if (hasSetsid) {
            System.out.println("Found setsid command");
            return;
        } else {
            System.out.println("setsid command not found");
            return;
        }

    }
}
# java which_setsid_with_ProcessBuilder
setsid command not found
crschnick commented 3 months ago

So you are running the release build of xpipe with the bsd port, right? Does the Linux compatibility mode introduce anything here.

Or are you running the dev build with gradle?

crschnick commented 3 months ago

Or is the main issue the working director at /usr/home/user1/.xpipe/shell

crschnick commented 3 months ago

Don't know whether BSD has things like strace to test that

MirLach commented 3 months ago

I am trying release build with Linux compatibility mode.

# ls -ld /usr/home/user1/.xpipe/shell
drwxr-xr-x user1 user1 2 B Sat Aug  3 12:23:55 2024  /usr/home/user1/.xpipe/shell

Commands can be executed from this directory

# cp /usr/local/bin/bash /usr/home/user1/.xpipe/shell/
# /usr/home/user1/.xpipe/shell/bash -c 'echo "Hello from bash in .xpipe/shell"'
Hello from bash in .xpipe/shell

Or execute bash in system PATH when I am in this directory

# cd /usr/home/user1/.xpipe/shell
# rm bash
# bash -c 'echo "Another test"'
Another test

I can start xpipe under struss or ktrace. (I already did, but I don't understand the output / didn't find anything useful). Should I send it to you?

crschnick commented 3 months ago

I don't think I needed trace unless it's telling us exactly why it fails. I'm no expert at that either.

Can you run this with something like root?

crschnick commented 3 months ago

Also, is the PATH correct in this Linux compatibility mode with the correct executables?

MirLach commented 3 months ago

Is there a way to make xpipe print or write to log file the env. variables? I explicitly set /compat/linux/usr/bin/ as the first component of PATH in my user's environment, but xpipe failed with permission denied again.

crschnick commented 3 months ago

Hmm in the release built not really. What if you remove the compat from the PATH? I think in terms of executables it doesn't need any compatibility tools.

MirLach commented 3 months ago

In /compat/linux/usr/bin/ there are installed bin utils from Rocky Linux package (https://www.freshports.org/emulators/linux_base-rl9/) They are used when some Linux program expects some different CLI flags passed to utils or specific output from the utils. I also think they are not needed to run bash or which from Java. My first test runs of xpiped where executed without /compat/linux/usr/bin/ in PATH. It is always failing the same way. I also tried to run it with OpenJDK 20 and 21.

crschnick commented 3 months ago

As a note, the release build does not depend on the installed Java package. It has its own environment bundled.

Maybe that one is missing bsd specific fixes?

crschnick commented 3 months ago

The app/lib/runtime/bin/xpiped script will run the bundled version. I guess you can swap out the Java path to the package path that is installed on your system.

MirLach commented 3 months ago

I modified xpiped script to use system java in /usr/local/openjdk21/bin/java

#!/bin/sh
SCRIPT_NAME=$(basename "$0")
APP_NAME=${SCRIPT_NAME%.sh}

DIR="${0%/*}"

/usr/local/openjdk21/bin/java $CDS_JVM_OPTS --add-opens java.base/java.lang=io.xpipe.app --add-opens java.base/java.lang=io.xpipe.core --add-opens java.desktop/java.awt=io.xpipe.app --add-opens net.synedra.validatorfx/net.synedra.validatorfx=io.xpipe.app --add-opens java.base/java.nio.file=io.xpipe.app --add-exports javafx.graphics/com.sun.javafx.tk=io.xpipe.app --add-opens javafx.graphics/com.sun.glass.ui=io.xpipe.app --add-opens javafx.graphics/javafx.stage=io.xpipe.app --add-opens javafx.graphics/com.sun.javafx.tk.quantum=io.xpipe.app -Xmx8g -Dio.xpipe.app.arch=x86_64 -Dio.xpipe.app.languages=en,nl,es,fr,de,it,pt,ru,ja,zh,tr,da -Dfile.encoding=UTF-8 -Dvisualvm.display.name=XPipe -Djavafx.preloader=io.xpipe.app.core.AppPreloader --add-opens java.desktop/sun.awt.X11=io.xpipe.app -Dio.xpipe.app.version=10.2.2 -Dio.xpipe.app.build=10.2.2-202407261701 -Dio.xpipe.app.buildId=bb046980-ec03-39a2-ae74-970a7d26c41c -Dio.xpipe.app.fullVersion=true -Dio.xpipe.app.staging=false "-Dio.xpipe.app.sentryUrl=https://fd5f67ff10764b7e8a704bec9558c8fe@o1084459.ingest.sentry.io/6094279" -Djna.nosys=false -Djna.nounpack=true -p "$DIR/../app" -m io.xpipe.app/io.xpipe.app.Main  "$@"

But then execution failed:

Error occurred during initialization of boot layer
java.lang.module.FindException: Module io.xpipe.app not found

I also tried to modify -p "$DIR/../app" without any luck. "$DIR/../app" resolves to nonexistent path even with the original xpiped script.

crschnick commented 3 months ago

I see, maybe it does not work when changing the executable to something else than the bundled one.

I don't know how many bsd specific patches there are to openjdk, but I suspect this might be the cause of the issues. Sadly I don't think this is fixable as the Java executable can't be switched out in this case.

MirLach commented 3 months ago

Just in case you're interested, the FreeBSD specific patches can be seen here https://github.com/freebsd/freebsd-ports/tree/main/java/openjdk21/files

I used to think that Java applications should be easily portable between systems. Is there any way to point the xpipe with native Java to the correct path with module io.xpipe.app?

crschnick commented 3 months ago

Nowadays in the Java desktop application space, the focus lies on providing adjusted runtime images with an application. This has many advantages, but I guess in this one specific case it makes it not portable.

In this case the bundled Java runtime isn't even OpenJDK, it's GraalVM. That shouldn't make a big difference though.

I don't think it's possible to point it to the correct path because the runtime image format is different.

crschnick commented 3 months ago

I think this can be closed as won't fix since it is not possible to use the bsd openjdk package to run the release build. It is not possible to just switch out the JDK for bundled runtime images.

Since the development build is also not going to be possible with the latest JavaFX versions not being available on BSD, I don't think this is going to work out. Even if we would somehow be able to fix this process spawn issue, there will probably be more issues down the road with JavaFX.

MirLach commented 3 months ago

Yes, I understand and agree. It can't be fixed for now.