WernerK: PHP exec(), system() unter IIS?

Hallo,

ich versuche in einem lokalen IIS mit PHP (als fastcgi) in einem Script exec() oder system() auszuführen. Leider passiert überhaupt nichts. Es kommt auch keine Fehlermedung. Was muss man tun damit diese Funktionen im IIS laufen? Ist das ein Berechtigungsproblem?

Gruss Werner

  1. Tach!

    ich versuche in einem lokalen IIS mit PHP (als fastcgi) in einem Script exec() oder system() auszuführen. Was muss man tun damit diese Funktionen im IIS laufen?

    Im PHP-Handbuch steht nichts dazu, also gehe ich davon aus, dass seitens PHP nichts weiter notwendig ist.

    Leider passiert überhaupt nichts. Es kommt auch keine Fehlermedung.

    Kannst du mit Sicherheit bestätigen, dass bei anderen Fehlern Meldungen zu sehen sind? Hast du die Funktionen so wie im Handbuch beschrieben verwendet, inklusive Auswertung der Rückgabewerte (via Funktionsergebnis und via als Referenz zu übergebenen Variablen)?

    Der EventViewer schweigt auch?

    Ist das ein Berechtigungsproblem?

    Das wirst du dann wissen, wenn du es herausgefunden hast. Zur Not kann man ja mal mit einem für alle und alles freigegebenem Verzeichnis experimentieren.

    dedlfix.

  2. Hello,

    unter welchem User läuft der IIS?
    Darf der cmd benutzen?
    Anderenfalls bekommst Du ja keine Shell.

    Den Exitcode musst Du beim Aufruf der Funktion als Referenz-Parameter mit angeben, sonst geht der ins Nirwana.

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Hallo Tom,

      leider bin ich im IIS noch nicht so ganz fit. Meinst du unter DefaultAppPool-- erweiterte Einstellungen-- Prozessmodell--Identität? Da steht bei mir:

      ApplicationPoolIdentity

      Gruss

      Werner

      1. Hallo WernerK,

        Application Pool Identity ist eine Identität, deren Name vom Namen des Application Pool abgeleitet ist. Wenn Der Application Pool „Werner“ heißt, dann kannst Du dem User IIS APPPOOL\Werner Rechte vergeben.

        Der Aufruf von cmd.exe sollte standardmäßig an Users (Benutzer bei deutschem Windows GUI) gestattet sein, ob da die Apppool-Identitäten zugehören, weiß ich nicht. Wenn Du die Fehlerausgabe von PHP auf maximal stellst, sollte aber eigentlich bei Problemen etwas im Browser aufscheinen.

        Rolf

        --
        sumpsi - posui - clusi
        1. Hallo Rolf,

          Also ich habe unter dem wwwroot ein Verzeichnis test angelegt mit einer PHP test.php. Darin habe ich error_reporting auf E_ALL gesetzt. Ich versuche ja gerade mit exec() oder system() 7zip.exe auszuführen.

          Ausser dem echo Befehl "test" erscheint aber nichts. Auch keine Fehlermeldung. Von daher vermute ich dass das exec gar nicht ausgeführt werden darf?

          <?php
          echo "Test<br>";
          
          error_reporting(E_ALL);
          ini_set('display_errors', 1);
          ini_set('display_startup_errors', 1);
          ini_set('error_reporting', E_ALL);
          
          $zipfolder = "C:\inetpub\wwwroot\test";
          
          exec('"'. $zipdownloadfolder ."/7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"');
          

          Gruss

          Werner

          1. Tach!

            error_reporting(E_ALL);
            ini_set('error_reporting', E_ALL);
            

            Eins von beiden reicht. Du darfst ruhig Vertrauen haben, dass das was im Handbuch geschrieben steht, auch ordnungsgemäß ausgeführt wird.

            Apropos Handbuch. Schau dir die Handbuchseite zu exec() an. Da sind weitere Parameter, in die Antworten geschrieben werden. Zudem gibt es den Rückgabeparameter, der auch ausgewertet werden kann. Ignorier diese Möglichkeiten nicht, wenn du Antworten haben möchtest. Wie sieht es außerdem mit dem EventViewer aus? Da schreibt das Windows rein, wenn es was zu jammern hat.

            dedlfix.

            1. Hallo,

              also ein wenig weiter bin ich gekommen 😀

              ein echo exec('dir .'); bringt mir die Ausgabe der Dateien und Verzeichnisse im aktuellen Ordner. Der exec Befehl für 7z.exe stimmt soweit. Zumindest kann ich mit dem geichen Aufruf im CDM Fenster eine Zip erstellen. Allerdings wird über Web Aufruf bzw. PHP keine Zip erstellt. Es kommt immer "Zip not created"

              exec('"'. $zipdownloadfolder ."\\7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"', $output, $return);
              
              if (!$return) {
                  echo "ZIP Successfully";
              } else {
                  echo "ZIP not created";
              }
              
              

              In der Windows Ereignisanzeige finde ich keine Meldungen.

              Gruss Werner

              1. Tach!

                exec('"'. $zipdownloadfolder ."\\7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"', $output, $return);
                
                if (!$return) {
                    echo "ZIP Successfully";
                } else {
                    echo "ZIP not created";
                }
                

                Was ist der konkrete Inhalt von $output und $return? Nimm var_dump() zur Anzeige (und davor ein <pre> für eine lesbare Ausgabe). Zudem ist es besser für die Fehlersuche $result = exec(...) zu notieren und auch noch das $result ausgeben.

                dedlfix.

                1. Hallo,

                  habe es nun so geändert:

                  $result = exec('"'. $zipdownloadfolder ."\\7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"', $output, $return);
                  
                  echo "<pre>";
                  var_dump($output);
                  echo "<hr>";
                  
                  var_dump($return);
                  echo "<hr>";
                  var_dump($result);
                  
                  echo "</pre>";
                  

                  Die Ausgabe ist:

                  array(0) { }


                  int(1)


                  string(0) ""

                  Gruss Werner

                  1. Tach!

                    Zumindest ist nun der Return-Code 1 zu sehen. Eine Suche nach "7z.exe return codes" ergab, die 1 steht für:

                    Warning (Non fatal error(s)). For example, one or more files were locked by some other application, so they were not compressed.

                    Zum einen ist es besser, statt der 7z.exe die 7za.exe zu verwenden. Die muss gegebenenfalls extra von der 7zip-Webseite besorgt werden, dafür enthält sich alle Kompressionsroutinen, ohne irgendwelche DLLs nachladen zu müssen. Das wiederum verringert die Chance, dass aufgrund nicht ladbarer DLL was schiefgeht.

                    Zum anderen ist nun zu sehen, dass das 7z ein Problem hatte, die Dateien komprimieren zu können. Vielleicht liegt es an einem Lock, vielleicht auch daran, dass sie anderswo liegen als im aktuellen Verzeichnis. Jedenfalls misslang der Zugriff.

                    Und außerdem ist es auch gut, selbst wenn alles auf Anhieb funktioniert hätte, mit Fehlern zu rechnen und die Resultate der Funktionsaufrufe auszuwerten.

                    dedlfix.

                    1. Hallo,

                      jetzt habe ich es auch mal mit der 7za.exe versucht mit gleichem Ergebnis. Die beiden PDFs 1.pdf und 2.pdf liegen im gleichen Verzeichnis. Das Verzeichnis hat Vollzugriff für "Jeder"

                      Ich bin mir auch nicht so sicher mit dem Return-Code 1 Ich habe nämlich mal zu Test "X7za.exe" geschrieben, also dass die Exe nicht gefunden wird und es kommt trotzdem der Return Code 1.

                      Gruss

                      Werner

                      1. Hallo WernerK,

                        genau so ist es - RC=1 kommt auch wenn das Programm nicht gefunden wird. Dann startet der Exec erst gar nicht. Ich habe das jetzt mit meinem IIS und PHP als Fast-CGI mal durchexerziert.

                        Wo liegt dein 7z.exe oder 7za.exe? Im $zipdownloadfolder? Oder anderswo, und der PATH ist so gesetzt, dass es von der Kommandozeile aus gefunden wird? Als userspezifischer Path? Was gibt

                        echo "Path: " . getenv("PATH") . "<br>";

                        aus? ZEIG UNS DAS NICHT! Guck nur, ob der Installationspfad von 7-zip drinsteht - sowohl beim Aufruf über Kommandozeile als auch über Web.

                        Das ist das erste Problem. In $output musst Du mal zumindest diese oder eine ähnliche Zeile finden:

                        7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04

                        Wenn 7z nicht im Path ist, dann rufe es mit vollem Pfad zu seinem Installationsordner auf. Bei mir sieht die EXEC-Zeile z.B. so aus:

                        $a = exec('"C:\Program Files\7-ZIP\7z.exe" a dings *', $result, $ret);

                        Achte auf die Wahl der Anführungszeichen: Außen die ', innen die ". Die " sind nötig weil der Pfad Leerstellen enthält. Die ' führen dazu, dass Backslashes nicht escaped werden müssen, aber auch dazu, dass PHP-Variablen nicht mehr aufgelöst werden. Wenn Du in den Parametern Variablen brauchst, mach es z.B. so:

                        $a = exec('"C:\Program Files\7-ZIP\7z.exe"' . "a $target *", $result, $ret);

                        Das aktuelle Verzeichnis, in dem der exec läuft, ist der Ordner in dem das Script liegt. Dorthin willst Du das ZIP nicht schreiben. Denn dann müsste der PHP-User Schreibrecht auf sein Web haben. Das ist unsicher. Mach den Ordner mit den PDFs vorher mit chdir() zum aktuellen Ordner, und wenn das Zip an noch anderer Stelle liegen soll, gibt seinen Namen inclusive Path an 7zip mit (Anführungszeichen nicht vergessen wenn Leerstellen in Pfad sind und Backslashes nach Bedarf escapen).

                        Das zweite Problem ist das Schreibrecht auf den Zielordner. Es ist leichtsinnig, auf einem Server Schreibrechte mit der Gießkanne zu verteilen. SYSTEM darf alles, Administratoren auch, alle anderen erstmal GAR NICHTS. Nun wird es spannend - das weitere Vorgehen hängt von der gewählten Authentication ab. Mit PHP wirst Du vermutlich "Anonym" wählen, damit Du PHP-seitig ein Login programmieren kannst. Standardauthentication ist der Browser-Login analog zu .htaccess, und Windows-Authentication ist ein Feature, dass ein Active Directory voraussetzt.

                        Klicke im IIS Manager auf die Website für dein PHP Script. Doppelklicke rechts im IIS Bereich "Authentifizierung", dann auf "Anonym" und ganz rechts auf Bearbeiten. Dort legst Du fest, mit welcher Windows-Identität der Web-Request durchgeführt wird. Das ist per Default der User "IUSR" - bei allen Webs - und eine ganz schlechte Idee. Wähle Application Pool Identität oder vergib einen eigenen technischen User (den musst Du vorher in der Systemsteuerung anlegen, das ist aber etwas komplizierter. Dieser User soll ja nur für das Web da sein. AppPool Identität ist am einfachsten, und der folgende Absatz setzt das voraus).

                        Nun kannst Du dem Ordner, in dem das Web liegt, Rechte erteilen. Füge Lese/Ausführen Recht für den User "IIS AppPool\xyz" hinzu. Bei dem Ordner, in dem Gezippt werden soll, machst Du das genauso, aber hier vergibst Du auch "Schreiben" und "Ändern". NICHT "Vollzugriff".

                        Hoffe, das hilft weiter.
                        Rolf

                        --
                        sumpsi - posui - clusi
                        1. Hallo Rolf,

                          zuerst einmal ganz vielen Dank für deine tolle Hilfe und für die ausführliche Erklärung. Das hat mir sehr geholfen. Jetzt kann ich zumindest ein Zipfile erstellen 😀

                          Die Ausgabe mit PATH ergabe, dass 7zip nicht aufgelistet ist. Ich hatte zuerst unter ..inetpup/wwwroot/test eine 7z.exe und die PDFs hinkopiert. Hier ist auf die zip.php Datei.

                          Wenn ich es jetzt so mache wie du schreibst mit dem "echten" 7Zip Insta Pfad, dann klappt alles.

                          $result = exec('"C:\Program Files\7-Zip\7z.exe" a C:\temp\seven.zip 1.pdf 2.pdf', $output, $return);
                          

                          Jetzt muss ich mir das noch mit den Rechten und dem ApplicationPool anschauen.

                          vielen Dank nochmals

                          viele Grüße

                          Werner

          2. Hallo Werner,

            $zipfolder = "C:\inetpub\wwwroot\test";    // (1)
            
            exec('"'. $zipdownloadfolder ."/7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"');    // (2)
            

            Du definierst in (1) eine Variable $zipfolderohne Backslashes zu escapen, in (2) ist es korrekt – und verwendest in (2) eine Variable $zipdownloadfolder. Sollen das zwei verschiedene Variablen sein?

            Mir fällt in (2) weiterhin auf, dass ein / als Pfadtrenner verwendet wird. Soll das so sein? Der Slash hat unter Windows u.U. eine andere Bedeutung.

            Viele Grüße
            Robert

            1. Tach!

              Mir fällt in (2) weiterhin auf, dass ein / als Pfadtrenner verwendet wird. Soll das so sein? Der Slash hat unter Windows u.U. eine andere Bedeutung.

              Welche? Als Pfadtrenner eignet er sich jedenfalls sehr gut.

              dedlfix.

              1. Moin @dedlfix,

                Mir fällt in (2) weiterhin auf, dass ein / als Pfadtrenner verwendet wird. Soll das so sein? Der Slash hat unter Windows u.U. eine andere Bedeutung.

                Welche? Als Pfadtrenner eignet er sich jedenfalls sehr gut.

                Frag mal cmd.exe/?😉

                Viele Grüße
                Robert

                1. Tach!

                  Mir fällt in (2) weiterhin auf, dass ein / als Pfadtrenner verwendet wird. Soll das so sein? Der Slash hat unter Windows u.U. eine andere Bedeutung.

                  Welche? Als Pfadtrenner eignet er sich jedenfalls sehr gut.

                  Frag mal cmd.exe/?😉

                  Viel zu viel Text. Kannst du bitte die Stelle zitieren, die sich auf Probleme mit / in Pfaden bezieht?

                  dedlfix.

                  1. Moin @dedlfix,

                    Viel zu viel Text. Kannst du bitte die Stelle zitieren, die sich auf Probleme mit / in Pfaden bezieht?

                    Ich habe gerade keine Quelle zur Hand, aber es funktioniert auch mit anderen Programmen, z.B.

                    > java/Hallo
                    Error: Could not find or load main class .Hallo
                    

                    Der Slash ist AFAIK historisch bedingt zur Übergabe von Parametern, weshalb DOS/Windows auch den Backslash als Pfadtrenner verwenden.

                    Viele Grüße
                    Robert

                    1. Tach!

                      Kannst du bitte die Stelle zitieren, die sich auf Probleme mit / in Pfaden bezieht?

                      Ich habe gerade keine Quelle zur Hand, aber es funktioniert auch mit anderen Programmen, z.B.

                      > java/Hallo
                      Error: Could not find or load main class .Hallo
                      

                      Gut, bei relativen Pfadangaben, die genauso anfangen wie eine ausführbare Datei im Pfad. Passiert aber nicht bei absoluten Pfadangaben, wie im vorliegenden Beispiel.

                      dedlfix.

                2. Hello,

                  Frag mal cmd.exe/?😉

                  Da würde dann zumindest ein Leerzeichen fehlen!

                  cmd.exe /?

                  Und ob die Exec-Shell einen gültigen Pfad zu cmd.exe kennt, bleibt auch noch dahingestellt.

                  Es wollen sowohl die Programme, als auch ihre Konfigurationsparameter gefunden werden! Manchmal muss man zu beiden die Pfade und Systemvariablen dediziert angeben!

                  Liebe Grüße
                  Tom S.

                  --
                  Es gibt nichts Gutes, außer man tut es!
                  Das Leben selbst ist der Sinn.
                  1. Hallo @TS,

                    Frag mal cmd.exe/?😉

                    Da würde dann zumindest ein Leerzeichen fehlen!

                    Nein, das funktioniert auch ohne Leerzeichen.

                    Viele Grüße
                    Robert

                    1. Hello,

                      Frag mal cmd.exe/?😉

                      Da würde dann zumindest ein Leerzeichen fehlen!

                      Nein, das funktioniert auch ohne Leerzeichen.

                      OK!?

                      Man lernt da ja nie aus. Ist tatsächlich so sogar schon in WinXP Pro.

                      Liebe Grüße
                      Tom S.

                      --
                      Es gibt nichts Gutes, außer man tut es!
                      Das Leben selbst ist der Sinn.
                      1. Hi @TS,

                        Nein, das funktioniert auch ohne Leerzeichen.

                        Ist tatsächlich so sogar schon in WinXP Pro.

                        Das kommt AFAIK von DOS 😂

                        Viele Grüße
                        Robert

            $zipfolder = "C:\inetpub\wwwroot\test";
            
            echo $zipfolder;
            

            Dann steht in $zipfolder

            C:\inetpub\wwwroot      est
            

            weil Du das so programmiert hast. Vermutlich wolltest Du andere Quotas:

            $zipfolder = 'C:\inetpub\wwwroot\test';
            

            oder die Maskierung durch Maskierung abschalten:

            $zipfolder = "C:\\inetpub\\wwwroot\\test';
            

            $zipdownloadfolder ist leer.

            Fürs Debuggen:

            Anstelle:

            exec('"'. $zipdownloadfolder ."/7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"');
            

            mach stets etwas wie:

            $sys = '"'. $zipdownloadfolder ."/7z.exe a C:\\inetpub\\wwwroot\\test\\seven.zip 1.pdf 2.pdf"  . '"');
            echo ( $sys , PHP_EOL );
            #exec $sys;
            

            Dann siehst Du, was Du übergeben willst.