1

Configuration

  • Windows 11
  • IIS 10.0.22000.1
  • PHP 5.6.40 (I know, it's old, but I don't know where my script will be executed, so I went broad)
  • WSL installed

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).

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Master DJon
  • 1,899
  • 2
  • 19
  • 30
  • IIS/FastCGI does not support cross the boundary setup. You have to either fully host on Windows, or fully host inside WSL. That's exactly why you cannot search out anything useful. – Lex Li May 29 '22 at 04:24
  • @LexLi OK, that seems a good reason why it fails, but I have an interrogation. Why then I saw people using WAMP and it works (PHP+Apache+exec('wsl ...')? So it is hosting outside WSL and executing `exec` command inside WSL. – Master DJon May 29 '22 at 10:58
  • If what you meant was https://stackoverflow.com/questions/69751902/how-can-i-start-a-wsl-based-linux-executable-from-within-php-on-windows-and-capt Then you might run application pools a normal user account with WSL access and give it a try. However, 1. web apps on IIS are usually under a system account/service account, which does not have WSL access. 2. PHP on Windows as a whole is no longer covered by Microsoft support, so production usage is risky (not only due to PHP 5.6). 3. Cross OS boundaries via WSL has significant performance overhead, so it's more a game to play in your spare time. – Lex Li May 29 '22 at 16:55
  • That's about the purpose. I'll verify what you suggested tomorrow. – Master DJon May 29 '22 at 18:34
  • @LexLi As stated in the question description, I already changed the user to be the Windows logged in user (my account). I'll try other work around. – Master DJon May 30 '22 at 01:09
  • Is your problem solved? – samwu May 30 '22 at 07:49
  • @samwu No. Not yet. – Master DJon May 30 '22 at 11:46
  • @LexLi I edited the question to include all my current attempts. The last one gave me a more specific error message: WSL file is not found even if giving the full path. – Master DJon May 30 '22 at 21:50
  • @LexLi I found a solution. I built a C# software that takes as argument the Linux command to run and calls WSL with that argument. – Master DJon May 31 '22 at 01:28
  • @LexLi I could another try using PHP 64bits. Because I discovered building this C# app that targeting 32bits doesn't work, but 64 bits yes. – Master DJon May 31 '22 at 21:06
  • @LexLi Yep! That what is. Installed PHP 5.6.40 x64 and now it works. Though, my C# software is not useless if someone is running the x86 version of PHP. – Master DJon May 31 '22 at 21:42
  • Did you happen to install the WSL Preview from the Microsoft Store? Or did you go through a normal `wsl --install` to enable it? – NotTheDr01ds Jun 01 '22 at 00:21
  • @NotTheDr01ds I don't recall exactly. I presume I did it through the Windows features. – Master DJon Jun 01 '22 at 04:23
  • @NotTheDr01ds I added my own answer. – Master DJon Jun 01 '22 at 16:08
  • @LexLi I added my own answer – Master DJon Jun 01 '22 at 16:09
  • @samwu Fixed. Look at my own answer. – Master DJon Jun 01 '22 at 16:09

2 Answers2

1

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.

Master DJon
  • 1,899
  • 2
  • 19
  • 30
1

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.

NotTheDr01ds
  • 15,620
  • 5
  • 44
  • 70