Search code examples
phpiisexecwindows-subsystem-for-linux

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


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


Solution

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