Andreas Korthaus: Datenübertragung auf anderen Server

Hallo!

Ich bin dabei ein Script zu schreiben, welches mir einen MySQl-Dump, den ich auf Server1 erzeugt habe, auf Server2 überträgt. Problem an der Sache, das ganze soll über eine gesicherte Verbinduing passieren, also SSL oder SSH. SSH mußte leider ausscheiden, da ich kein SCP verwenden kann. Daher bleibt noch SSL, da ich das Script bereits in PHP geschrieben habe, und PHP selbst das (noch) nicht kann, verwende ich curl. Curl kann per SSL/HTTP Daten übertragen. Bei kleinen Strings als Parameter kein Problem, bei irgendeiner Größe, ich schätze so um 2 KB funktioniert das ganze nicht mehr, es passiert einfach nichts, kein Fehler, nichts. So habe ich es versucht:

<?
// ein bisschen Quatsch zum testen:
$dump = "aaa\n as98a \n\ns sdfs sd sdf sdfsdfsdfds dsfsd fsd sdf df sdfsd fsd\n";

echo system("curl --data client_dump=".urlencode($dump)." https://www.server.de/sync_master.php");

?>

Wie gesagt, der obigen String($dump) funktioniert prima, aber wenn ich den String kopiere und hinten anhänge, geht das nur noch bis zur ca. 50. Kopie gut, danach ist der String wohl zu lang und es ist Ende. Was könnte ich da machen? Wie kann ich längere Strings übertragen,  und wo genau könnte der Engpaß liegen?

Viele Grüße
Andreas

  1. Moin,

    Wie kann ich längere Strings übertragen,  und wo genau könnte der Engpaß liegen?

    Es gibt eine fest einkompilierte Längenbeschränkung für Kommandozeilen und zu versuchen viele Daten darüber zu übertragen ist imho so ziemlich die schlechteste Idee die man haben kann. (Unter anderem auch weil andere Prozesse auf dem selben System unter Umständen die Kommandozeile sehen könnten, daher gibt man dort _nie_ vertrauliche Informationen an.) Da könntest du ja gleich versuchen das mit GET zu schicken.

    Die man-page weiss wie immer Rat:

    If you start the data  with  the  letter  @,  the  rest
              should  be  a  file name to read the data from, or - if
              you want curl to read the data from  stdin.

    Will heissen: entweder schreibst du die Daten in eine Datei und gibst --data @dateiname an (halbwegs schlechte Idee, da die Daten dann auf der Platte liegen und von dort wiederhergestellt werden können; kann auch zu einem Haufen anderer Probleme führen) oder (besser) du benutzt popen() statt system(), gibst --data @- an und schreibst die Daten in den Filedeskriptor den dir popen() gibt (pclose() nicht vergessen).

    --
    Henryk Plötz
    Grüße von der Ostsee

    1. Hi!

      Es gibt eine fest einkompilierte Längenbeschränkung für Kommandozeilen und zu versuchen viele Daten darüber zu übertragen ist imho so ziemlich die schlechteste Idee die man haben kann. (Unter anderem auch weil andere Prozesse auf dem selben System unter Umständen die Kommandozeile sehen könnten, daher gibt man dort _nie_ vertrauliche Informationen an.)

      Oh, wußte ich nicht! Aber auf Linux schicke ich "Tonnenweise" Daten über die Kommandozeile(vornehmlich mysqldump), muß ich mir da auch Sorgen machen? Die Daten sind jedenfalls erheblich länger und ich hatte noch nie Probleme! Oder liegt es daran das ich aus PHP heraus die Daten auf die Kommandozeile schreibe, udn es sich nicht um STDOUT... handelt?

      Da könntest du ja gleich versuchen das mit GET zu schicken.

      ???? Das ist was anderes, der Request - String ist meiens Wissens stark begrenzt! Ich dachte gerade POST wäre dafür da, so z.B. Textfelder wie hier im Forum...

      Will heissen: entweder schreibst du die Daten in eine Datei und gibst --data @dateiname an (halbwegs schlechte Idee, da die Daten dann auf der Platte liegen und von dort wiederhergestellt werden können; kann auch zu einem Haufen anderer Probleme führen)

      Genau das hatte ich auch überlegt, wollte das aber unbedingt vermeiden!

      oder (besser) du benutzt popen() statt system(), gibst --data @- an und schreibst die Daten in den Filedeskriptor den dir popen() gibt (pclose() nicht vergessen).

      OK, Danke! Das probier ich gleich mal. Wie ist das denn auf ddem Server, ich schicke die Datei ja an ein PHP-Script, wie steht mir das dann zu Verfügung, in $_FILES wie bei einem HTML-Formular-Upload?

      Grüße
      Andreas

      1. Hi,

        Es gibt eine fest einkompilierte Längenbeschränkung für Kommandozeilen und zu versuchen viele Daten darüber zu übertragen ist imho so ziemlich die schlechteste Idee die man haben kann. (Unter anderem auch weil andere Prozesse auf dem selben System unter Umständen die Kommandozeile sehen könnten, daher gibt man dort _nie_ vertrauliche Informationen an.)
        Oh, wußte ich nicht! Aber auf Linux schicke ich "Tonnenweise" Daten über die Kommandozeile(vornehmlich mysqldump), muß ich mir da auch Sorgen machen? Die Daten sind jedenfalls erheblich länger und ich hatte noch nie Probleme! Oder liegt es daran das ich aus PHP heraus die Daten auf die Kommandozeile schreibe, udn es sich nicht um STDOUT... handelt?

        Exakt. Das "|" verbindet nur STDIN und STDOUT. (und die Kommandozeile ist nur so lang wie Du sie eintippst)

        ???? Das ist was anderes, der Request - String ist meiens Wissens stark begrenzt! Ich dachte gerade POST wäre dafür da, so z.B. Textfelder wie hier im Forum...

        Aber der Request-String ist so eine Art Kommandozeile ...

        OK, Danke! Das probier ich gleich mal. Wie ist das denn auf ddem Server, ich schicke die Datei ja an ein PHP-Script, wie steht mir das dann zu Verfügung, in $_FILES wie bei einem HTML-Formular-Upload?

        Nein. Die Daten stehen genau _so_ zur Verfügung, wie vorher. CURL holt sich ja nur den Datei-_Inhalt_ (bzw. den Inhalt von STDIN bei @-)

        Grüße,

        Christian

        1. Hallo!
          Danke Euch beiden, aber ein paar Probleme hab ich mit der Sache - wie bekomme ich die Ausgabe dieses Scriptes zurück??? Wie komme ich wieder an die Ausgabe von CURL? Nicht nur das ich keienn Schimmer habe ob es denn jetzt funktioniert, ich brauche die Ausgabe unbedningt! Daher mache ich das überhaupt so, wegen der Möglichkeit der verschlüsselten Kommunikation in beide Richtungen!

          So hab ichs jetzt versucht:

          $fp = popen("curl --data@- http://www.server.de/sync_master.php", "w");
          fputs($fp, "client_dump=hallo");
          pclose($fp);
          Nun, es kommt keine Fehlermeldung, das Script auf dem server gibt mir nur
          print_r($_POST) aus(theoretisch ;-)). Aber ich sehe gar nichts, wie auch, ich weiß ja nicht wie ich an die Ausgabe von CURL kommen soll. OK, vermutlich könnte ich die in eine Datei umleiten und die öffnen, aber geht das nicht anders? Kann ich die ausgabe nicht irgebwie in eine Variable schreiben?

          Problem ist ja, das popen() nur schreiben _oder_ lesen kann!

          Viele Grüße
          Andreas

          1. Hallo,

            Problem ist ja, das popen() nur schreiben _oder_ lesen kann!

            Zitat von http://www.php.net/manual/en/function.popen.php:

            PHP4 appears to allow w+ for reading and writing..
            ie:

            $fh=popen("/usr/local/sbin/stunnel -c -r secure.host.com:443","w+");
            fwrite($fh,"GET /cgi-bin/test.sh HTTP/1.0\n\n");
            $foo=fread($fh,8096);

            echo "Got <pre>\n$foo\n</pre>";
            pclose($fh);

            Grüße,

            Christian

            1. Hi!

              Problem ist ja, das popen() nur schreiben _oder_ lesen kann!

              Zitat von http://www.php.net/manual/en/function.popen.php:

              PHP4 appears to allow w+ for reading and writing..
              ie:

              $fh=popen("/usr/local/sbin/stunnel -c -r secure.host.com:443","w+");
              fwrite($fh,"GET /cgi-bin/test.sh HTTP/1.0\n\n");
              $foo=fread($fh,8096);

              echo "Got <pre>\n$foo\n</pre>";
              pclose($fh);

              Grüße,

              Naja, sollte man sich also doch an die englische Doku halten, ist das 2. mal heute!

              Auf alle Fällte werden Daten übertragen, nur ich bekomme sowas angezeigt:

              ŕjäŕkäŕläŕmäŕnäŕqäŕräŕuäŕväŕwäŕyäŕ{äŕ�äŕ�äŕNćŕYćŕfćŕgćŕhćŕićŕjćŕkćŕlćŕmćŕnćŕ{ćŕ}ćŕ1

              und noch viel mehr davon?! Hat das was mit binären Daten zu tun? Ich  habe die Variable vor dem Schicken mit urlencode() bearbeitet. Der Umfang kommt in etwa hin.

              ABER:

              Der Code hierfür ist der folgende:

              $dump = urlencode(get_table_data("s_objekte"));

              error_reporting(E_ALL);

              $fp = popen("curl --data @- http://www.server.de/sync_master.php", "w+");
              fputs($fp, "dump_client=Hallo");
              $result=fread($fp,1024);
              pclose($fp);

              echo $result;

              Was mir erst hinterher auffällt, theoretisch sollte er ja so wie es da stet nur "dump_client=Hallo" übertragen - sonst nichts, wieso überträgt er aber den ganzen Rest?

              Und wenn ich das denn ändere auf

              fputs($fp, $dump); überträgt er nur noch einen Bruchteil davon. Und auch Kauderwelsch! Hier ist irgendwo noch ein ganz dicker Denkfehler von mir ;-)

              Das Script "sync_master.php" enthält nur:

              <pre>
              <?
              print_r($_POST);
              ?>
              </pre>

              Noch 2 Fragen:

              $result=fread($fp,1024): die Zahl(1024), wie sollte man die setzen?
              fputs($fp, $dump): sollte man hier die Länge angeben, ist ja optional!

              Viele Grüße
              Andreas

              1. Hallo nochmal.

                Also das ganze habe ich mir ehrlich leichter vorgestellt.

                Ich habe das Problem ein wenig eingekreist:

                • hat es evtl was damit zu tun das es sich um Windows handelt?

                jedenfalls sollten bidirektionale Pipes nicht funktionieren! Das ist ein Bug in PHP, der auch nur in wenigen OS funtioniert, WIN anscheinend nicht(Zitat siehe unten: [1]).

                Somit bin ich wieder am Anfang, ich bekomme die Daten jetzt vermutlich auf den Server, wie komme ich jetzt an die Ausgabe?

                Vermutlich bleibt nir bei PHP  nur folgende Variante:

                $dump = urlencode(get_table_data("s_objekte"));

                $fp = popen("curl --data @- http://www.server.de/sync_master.php > curlausgabe.txt", "w");
                fputs($fp, "test=".$dump);
                pclose($fp);

                Das funktioniert jetzt jedenfalls, in der Datei curlausgabe.txt stehen die ersehnten Daten ;-) Wenn Ihr noch ne Idee habet wie ich das ophne Umweg über eine externe Datei machen kann(und ohne auf eine andere Programmiersprachen umzusteigen;-)), dann her damit!

                Ein kleiner Wehrmutstropfen:

                Ich weil ja den Dump übertragen, wo wir vorher mit Mühe und Not die \n´s reinbekommen haben - die sind wieder weg ;-) Aber nicht nur das, der kpomplette Dump ist irgendwo durch eine Art addslashes() gegangen, vor jedem '"\ usw. steht jetzt ein , daher sind vermutlioch jetzt 3 Slashes vor dem n, also \\n und schon ergibt das für die Ausgabe einen \ und einen Umbruch ;-) OK, das bekomme  ich mit stripslashes wieder weg, würde mich nur mal interesieren, wo die Dinger bitte herkommen!

                [1] Zitat von php.net:

                Unfortunately many of these posting are wrong. First, you cannot use 'r+' or 'w+' in popen as it is defined to accept only reading or writing (unidirectional.) There does appear to be some kind of bug that returns the stdout into the page on pclose but this behavior can not be counted on. If you want a bi-directional popen, go to the bug database and lookup bug#9824. You can vote for a bi-directional popen there.

                The post describing the two 'named' pipes approach is currently the only way I can simulate the bi-popen(). THIS HAS A PROBLEM. Writers and Readers will BLOCK on pipes so you could have many webprocess waiting on blocked pipes. Also, notice that there could be race conditions as well since you can't gaurantee that the correct reader and writer get on the pipe at the same time. One web process maybe reading the writings of the WRONG mate process.

                I've tried every conceivable combo with popen, I even tried using popen in combo with only one named pipe for output. This hung though due to blocking behavior. Maybe if you compile in pcntl stuff you can get it to work, but that is not an option for me.

                An dieser Stelle schonmal vielen Dank, Ihr habt mir sehr geholfen!

                Viele Grüße
                Andereas

                PS: Bei meinen Versuchen ist mir der Apache mehrmals abgeschmiert ;-)

                1. Moin,

                  Das funktioniert jetzt jedenfalls, in der Datei curlausgabe.txt stehen die ersehnten Daten ;-)

                  Ja, das wäre die einfachste Idee die ich hätte. Eine weitere wären named pipes (mit mkfifo) die in dem Doku-Ausschnitt erwähnt wurden.
                  Bei einer richtigen[tm] Programmiersprache würde man an dieser Stelle zwei verbundene Pipes (pipe()-Bibliotheksfunktion iirc) erzeugen, einen fork machen (mit fork()), im Kindprozess je ein Ende der beiden Pipes mit Stdin bzw. Stdout verbinden (das andere Ende schliessen; im Elternprozess ebenfalls das andere Ende schliessen) und dann mit exec() das gewünschte Kommando aufrufen. Mit exec() wird dein Programm (im Kindprozess) aus dem Speicher entfernt und das neue Programm an dessen Stelle geladen, aber die umgeleiteten Stdin/out bleiben erhalten. (Perl hat für diesen Zweck übrigens extra open2 und open3-Funktionen.)

                  Wenn Ihr noch ne Idee habet wie ich das ophne Umweg über eine externe Datei machen kann(und ohne auf eine andere Programmiersprachen umzusteigen;-)), dann her damit!

                  Also, wenn du die Möglichkeit hast, das PHP upzudaten: http://www.php.net/manual/en/function.proc-open.php. Ansonsten: PHP ist fast eine richtige Programmiersprache und die erwähnten fork() und exec()-Funktionen, allerdings nicht unter Windows und nicht defaultmäßig aktiviert: http://www.php.net/manual/en/ref.pcntl.php. Solltest du diesen Weg wählen (das ist übrigens der richtige[tm]), solltest du dir aber wirklich was Gutes zum Lesen über Prozesskontrolle und Interprozesskommunikation besorgen.

                  Ich weil ja den Dump übertragen, wo wir vorher mit Mühe und Not die \n´s reinbekommen haben - die sind wieder weg ;-) Aber nicht nur das, der kpomplette Dump ist irgendwo durch eine Art addslashes() gegangen, vor jedem '"\ usw. steht jetzt ein , daher sind vermutlioch jetzt 3 Slashes vor dem n, also \\n und schon ergibt das für die Ausgabe einen \ und einen Umbruch ;-) OK, das bekomme  ich mit stripslashes wieder weg, würde mich nur mal interesieren, wo die Dinger bitte herkommen!

                  Naja, für das PHP-Skript sieht es ja so aus als wären die Daten über POST reingekommen (sind sie ja auch). Wenn du die Rückrichtung meinst, schau mal nach magic_quotes_runtime.

                  Ansonsten kann ich sagen, dass die Dateilösung halbwegs befriedigend ist, wenn darin keine vertraulichen Daten kommen und du dich vor evt. Problemen mit dem Überschreiben vorhandener Dateien hütest. Siehe http://www.php.net/manual/en/function.tmpfile.php.

                  --
                  Henryk Plötz
                  Grüße von der Ostsee

                  1. Hi!

                    Ja, das wäre die einfachste Idee die ich hätte. Eine weitere wären named pipes (mit mkfifo) die in dem Doku-Ausschnitt erwähnt wurden.
                    Bei einer richtigen[tm] Programmiersprache würde man an dieser Stelle zwei verbundene Pipes (pipe()-Bibliotheksfunktion iirc) erzeugen, einen fork machen (mit fork()), im Kindprozess je ein Ende der beiden Pipes mit Stdin bzw. Stdout verbinden (das andere Ende schliessen; im Elternprozess ebenfalls das andere Ende schliessen) und dann mit exec() das gewünschte Kommando aufrufen. Mit exec() wird dein Programm (im Kindprozess) aus dem Speicher entfernt und das neue Programm an dessen Stelle geladen, aber die umgeleiteten Stdin/out bleiben erhalten. (Perl hat für diesen Zweck übrigens extra open2 und open3-Funktionen.)

                    Hm, sowas mit fork hat auch in der Apache Error-Log gestanden, aber das muß ich mir mal in Ruhe angucken, das mit den Prozessen ist doch  nicht so einfach wie es sich anhört ;-)

                    Also, wenn du die Möglichkeit hast, das PHP upzudaten: http://www.php.net/manual/en/function.proc-open.php.

                    Hätte ich wohl, aber wie komme ich da ran? Habe im CVS geguckt, da nur alle möglichen "wirren" Dateien gefunden, wie soll das funktionieren?

                    Ansonsten: PHP ist fast eine richtige Programmiersprache und die erwähnten fork() und exec()-Funktionen, allerdings nicht unter Windows und nicht defaultmäßig aktiviert: http://www.php.net/manual/en/ref.pcntl.php. Solltest du diesen Weg wählen (das ist übrigens der richtige[tm]), solltest du dir aber wirklich was Gutes zum Lesen über Prozesskontrolle und Interprozesskommunikation besorgen.

                    Oh weh, das mit Prozessen muß ich auf alle Fälle mal machen, aber jetzt nicht ;-)

                    Ich weil ja den Dump übertragen, wo wir vorher mit Mühe und Not die \n´s reinbekommen haben - die sind wieder weg ;-) Aber nicht nur das, der kpomplette Dump ist irgendwo durch eine Art addslashes() gegangen, vor jedem '"\ usw. steht jetzt ein , daher sind vermutlioch jetzt 3 Slashes vor dem n, also \\n und schon ergibt das für die Ausgabe einen \ und einen Umbruch ;-) OK, das bekomme  ich mit stripslashes wieder weg, würde mich nur mal interesieren, wo die Dinger bitte herkommen!

                    Naja, für das PHP-Skript sieht es ja so aus als wären die Daten über POST reingekommen (sind sie ja auch). Wenn du die Rückrichtung meinst, schau mal nach magic_quotes_runtime.

                    Das komische, wenn ich das ganze durch stripslashes schicke, ist alles wieder normal, nur das die Absätze sich von \n in echte Absätze geändert haben ;-) also nochmal die regExpr drüber ;-)

                    Ansonsten kann ich sagen, dass die Dateilösung halbwegs befriedigend ist, wenn darin keine vertraulichen Daten kommen und du dich vor evt. Problemen mit dem Überschreiben vorhandener Dateien hütest. Siehe http://www.php.net/manual/en/function.tmpfile.php.

                    Ich verstehe nicht, was da der Unterschied ist, die Daten in einer Variable zwischenzuspeichern? Ob ich das temporär zur Laufzeit in einer Datei oder einer Variable speichere, da ist die Variable bestimmt erheblich schneller!
                    Außerdem bringt das hier nicht viel, denn mein Problem ist ja, das ich die Ausgabe nicht lesen kann, ich schreibe die Datei ja direkt aus der Shell, daher bringt das hier eh nichts!

                    Vielen Dank jedenfalls!

                    Grüße
                    Andreas

                    1. Moin,

                      Also, wenn du die Möglichkeit hast, das PHP upzudaten: http://www.php.net/manual/en/function.proc-open.php.
                      Hätte ich wohl, aber wie komme ich da ran? Habe im CVS geguckt, da nur alle möglichen "wirren" Dateien gefunden, wie soll das funktionieren?

                      Da liegt bestimmt irgendwo eine Anleitung rum wie man sich PHP aus dem CVS holt und kompiliert. Na sag ich doch: http://www.php.net/anoncvs.php. Hier werden ausserdem daily snapshots erwähnt, mit der ausdrücklichen Anmerkung das das nichts für Produktionssysteme ist: http://www.php.net/downloads.php. Du solltest da also vielleicht doch bis zum Release mit warten.

                      Ansonsten kann ich sagen, dass die Dateilösung halbwegs befriedigend ist, wenn darin keine vertraulichen Daten kommen und du dich vor evt. Problemen mit dem Überschreiben vorhandener Dateien hütest. Siehe http://www.php.net/manual/en/function.tmpfile.php.

                      Ich verstehe nicht, was da der Unterschied ist, die Daten in einer Variable zwischenzuspeichern? Ob ich das temporär zur Laufzeit in einer Datei oder einer Variable speichere, da ist die Variable bestimmt erheblich schneller!

                      Biddewas?

                      Außerdem bringt das hier nicht viel, denn mein Problem ist ja, das ich die Ausgabe nicht lesen kann, ich schreibe die Datei ja direkt aus der Shell, daher bringt das hier eh nichts!

                      Genau: Das Problem ist, dass du Daten durch ein externes Programm schicken willst, es zur Zeit aber keine vernünftige Möglichkeit gibt, ein externes Programm zum Lesen _und_ Schreiben zu öffnen. Die vorgeschlagene Lösung war, das Programm nur zum Schreiben zu öffnen und die zu übertragenen Daten dorthin zu schreiben. Die Ausgabe des Programms kannst du dann in eine Datei umleiten (bitte die Datei wie erwähnt erzeugen, damit sich mehrere Instanzen nicht in die Quere kommen und auch einige andere Probleme wegfallen) und diese später einlesen. Dabei gehe ich davon aus, dass die Ausgabe nur eine Art "OK, alles glatt gegangen" sein wird. Du kannst übrigens auch den Statuscode von pclose() auswerten, der sagt dir, ob curl irgendwelche Probleme hatte.

                      PS: Wirklich verrückte Idee: Du könntest auch zwei Verbindungen mit aufmachen und eine zum Schreiben und die andere zum Lesen nehmen. Auf der anderen Seite unterhalten sich die beiden dadurch aufgerufenen PHP-Skripte dann über http://www.php.net/manual/sv/ref.sem.php.

                      --
                      Henryk Plötz
                      Grüße von der Ostsee