Master DJon
Master DJon

Reputation: 1965

IIS + PHP exec to run Linux commands with WSL not working

Configuration

Context

I am trying to write a runLinuxCommand that would run either on Windows (with Windows Subsystem for Linux) or on Linux (and perhaps OSX as it is Unix like).

I had some issues with exec("dir", $result, $code) returning $code ==> 1. I then changed the user running PHP in IIS to be my current logged in user (which is an administrator). I know, this is not ideal, but for now, I want to reach my goal. I'll try to reduce rights afterward. And... it worked!

Issue

So, I tried to execute exec("wsl date", $result, $code), but no luck, still an exit code of 1. Yes, opening a command prompt and executing wsl date does work. Either with the logged in user or as administrator.

The only thing I can see for now is it takes too much time to initialize WSL, but quite not sure at all.

Attempts

Attempt #1

The one in the issue section

Attempt #2

exec('C:\\Windows\\System32\\wsl.exe date', $result, $code);

Same result.

Attempt #3

exec('start /B /WAIT C:\Windows\System32\wsl.exe date', $result, $code);

Hanging... never getting through this line.

Attempt #4

exec('start /B /WAIT cmd /C C:\\Windows\\System32\\wsl.exe date', $result, $code);

No error. No result / output.

Attempt #5

$WshShell = new \com("WScript.Shell");
$oExec = $WshShell->Exec("C:\\Windows\\System32\\wsl.exe date");
$out = $oExec->StdOut->ReadLine();

Now I have a specific error message when running the second line: File not found. I can assure you that C:\Windows\System32\wsl.exe exists.

I already confirmed using $oExec = $WshShell->Run("whoami", 0 , true); as second line that the user is the one I am logged in (an administrator account from which I can test using a terminal).

Aside

I searched a lot about it, and I found nothing (well, everything I found was not specific).

Upvotes: 1

Views: 474

Answers (2)

Master DJon
Master DJon

Reputation: 1965

So, after some tribulations, I figured out the issue was coming from the PHP 32 bits version. Using the 64 bits version calling exec('wsl date', $result, $code); works perfectly.

So, for those interested in the class I built:

class Host {
    private function __construct() {}
    
    /**
     * Get if the host is Windows
     * @return boolean
     */
    public static function isWindows() {
        $uname = strtolower(php_uname('s'));
        return strpos($uname, "win") === 0;
    }
    
    /**
     * Get if PHP is 64 bits or not (32 bits)
     * @ref https://code-boxx.com/check-php-version/#:~:text=To%20check%20if%20you%20are,and%208%20for%2064%2Dbit.
     * @return boolean
     */
    public static function isPhp64bits() {
        if (isset($_SERVER["PROCESSOR_ARCHITECTURE"])) {
            // Windows IIS validation
            return $_SERVER["PROCESSOR_ARCHITECTURE"] != "x86";
        }
        return PHP_INT_SIZE == 8;
    }
    
    public static function runLinuxCommand($command) {
        if (self::isWindows()) {
            if (self::isPhp64bits()) {
                $wslExecutor = "wsl";
            } else {
                $wslExecutor = "C:\\Windows\\Sysnative\\wsl.exe";
            }
            $command = "$wslExecutor $command";
        } else {
            putenv('LANG=en_US.UTF-8'); // Ensure the output is UTF-8
        }
        
        $result = [];
        $exitCode = -1;
        exec($command, $result, $exitCode);
        
        return new ExecReturn($result, $exitCode);
    }
}

This is no more needed : For the WSLExecutor software, quite simple, but anyhow: https://github.com/djon2003/WSLExecutor

I know, the framework calls aren't included, but I won't post the whole framework. Moreover, they are methods that can be easily implemented.

Edit #1

With the help of NotTheDr01ds, I no more need the C# software WSLExecutor.

Upvotes: 1

NotTheDr01ds
NotTheDr01ds

Reputation: 20877

Good find on 32-bit PHP being the core issue.

Since that's the case, I believe you can work around it quite simply by using the Windows Sysnative interface, which is designed to allow you to call 64-bit applications from within 32-bit applications.

From the 32-bit PHP, try launching C:\Windows\Sysnative\wsl.exe <arguments>. It's in a virtual folder, so you won't be able to see it from a normal 64-bit Explorer or similar. See this answer (and other sysnative related answers) for more details.

Upvotes: 1

Related Questions