{"id":515,"date":"2011-02-21T23:24:48","date_gmt":"2011-02-21T22:24:48","guid":{"rendered":"https:\/\/d-mueller.de\/blog\/?p=515"},"modified":"2016-01-11T23:16:20","modified_gmt":"2016-01-11T22:16:20","slug":"systemaufrufe-linux-windows-ruckgabewerte-und-der-errorstream","status":"publish","type":"post","link":"https:\/\/d-mueller.de\/blog\/systemaufrufe-linux-windows-ruckgabewerte-und-der-errorstream\/","title":{"rendered":"Systemaufrufe, Linux, Windows, R\u00fcckgabewerte und der Errorstream"},"content":{"rendered":"<p>Heute folgendes Problem gehabt: Es soll ein Systemaufruf an <a href=\"http:\/\/www.openssl.org\/docs\/apps\/ts.html\">openssl ts<\/a> zur Signierung von Timestamps (<a href=\"http:\/\/en.wikipedia.org\/wiki\/Trusted_timestamping\">siehe hier<\/a>) erfolgen. Das an sich ist ja erst mal noch kein Problem. Allerdings soll die geschriebene Klasse gleicherma\u00dfen unter Linux und Windows mit m\u00f6glichst aussagekr\u00e4ftigen Fehlermeldungen zum Einsatz kommen. Jetzt ist die pikante Sache daran, dass der ts-Parameter von openssl <a href=\"http:\/\/stackoverflow.com\/questions\/5043393\/openssl-ts-command-not-working-trusted-timestamps\">erst ab Version 0.99<\/a> mit dabei ist, die standardm\u00e4\u00dfig unter Debian Lenny nicht mit installiert ist. Jetzt erzeugt netterweise ein Aufruf von <b>openssl ts<\/b> einen Returnwert von 0 (= alles okay), obwohl openssl den ts-Befehl in der installierten Version garnicht kennt &#8211; wir haben also keine M\u00f6glichkeit \u00fcber eine Pr\u00fcfung des Returnwertes alleine \u00fcber Erfolg oder Misserfolg der Funktion zu entscheiden:<\/p>\n<div id=\"attachment_517\" style=\"width: 608px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/02\/openssl-ts.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-517\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/02\/openssl-ts.png\" alt=\"OpenSSL ts\" title=\"OpenSSL ts\" width=\"598\" height=\"460\" class=\"size-full wp-image-517\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/02\/openssl-ts.png 598w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2011\/02\/openssl-ts-300x230.png 300w\" sizes=\"auto, (max-width: 598px) 100vw, 598px\" \/><\/a><p id=\"caption-attachment-517\" class=\"wp-caption-text\">OpenSSL ts<\/p><\/div>\n<p>Als Waffe der Wahl haben wir uns nun also f\u00fcr <a href=\"http:\/\/php.net\/manual\/de\/function.exec.php\">exec<\/a> entschieden, da <a href=\"http:\/\/de2.php.net\/manual\/de\/function.shell-exec.php\">shell_exec<\/a> keinen Returnwert zur\u00fcckliefert und <a href=\"http:\/\/www.php.net\/manual\/de\/function.system.php\">system<\/a> \/ <a href=\"http:\/\/www.php.net\/manual\/de\/function.passthru.php\">passthru<\/a> den Programmoutput einfach rausfeueren und nicht returnen. Zur Erinnerung, exec sieht so aus:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nstring exec ( string $befehl [, array $ausgabe [, int $return_var ]] )\r\n<\/pre>\n<p>Den returnten String von exec kann man sich dabei schenken, der beinhaltet nur die letzte Zeile des Arrays aus dem zweiten Parameter. Erster naiver Ansatz also (wohlgemerkt in einer openssl-Version, die den <i>ts<\/i>-Befehl nicht kennt):<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$retarray = array();\r\nexec(&quot;openssl ts&quot;, $retarray, $retcode);\r\n\r\nprint_r($retarray); \/\/Array ( )\r\nvar_dump($retcode); \/\/int(0)\r\n<\/pre>\n<p>Wie oben schon erw\u00e4hnt: Der Returncode 0 hilft uns nichts, das Return-Array ist leer &#8211; und das obwohl es aus openssl nur so heraussprudelt, wenn man <i>openssl ts<\/i> auf der Konsole direkt ausf\u00fchrt. Warum? Ins Return-Array werden nur Programmausgaben auf den <b>Standard-Input<\/b> gepackt. Die &#8222;gescreenshottete&#8220; Ausgabe oben erfolgt jedoch auf <b>STDERR<\/b>. Bl\u00f6d. Die L\u00f6sung? <\/p>\n<pre data-enlighter-language=\"enlighter\" class=\"EnlighterJSRAW\">\r\n2&gt;&amp;1\r\n<\/pre>\n<p>Jaja, richtig gelesen &#8211; Umleitung von stderr auf stdout. Damit:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$retarray = array();\r\nexec(&quot;openssl ts 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\r\nprint_r($retarray); \r\n\/*\r\nArray\r\n(\r\n    [0] =&gt; openssl:Error: &#039;ts&#039; is an invalid command.\r\n    [1] =&gt; \r\n    [2] =&gt; Standard commands\r\n    [3] =&gt; asn1parse      ca             ciphers        crl            crl2pkcs7\r\n    [4] =&gt; dgst           dh             dhparam        dsa            dsaparam\r\n    [5] =&gt; enc            engine         errstr         gendh          gendsa\r\n    [6] =&gt; genrsa         nseq           ocsp           passwd         pkcs12\r\n    [7] =&gt; pkcs7          pkcs8          prime          rand           req\r\n    [8] =&gt; rsa            rsautl         s_client       s_server       s_time\r\n    [9] =&gt; sess_id        smime          speed          spkac          verify\r\n    [10] =&gt; version        x509\r\n    [11] =&gt; \r\n    [12] =&gt; Message Digest commands (see the `dgst&#039; command for more details)\r\n    [13] =&gt; md2            md4            md5            mdc2           rmd160\r\n    [14] =&gt; sha            sha1\r\n    [15] =&gt; \r\n    [16] =&gt; Cipher commands (see the `enc&#039; command for more details)\r\n    [17] =&gt; aes-128-cbc    aes-128-ecb    aes-192-cbc    aes-192-ecb    aes-256-cbc\r\n    [18] =&gt; aes-256-ecb    base64         bf             bf-cbc         bf-cfb\r\n    [19] =&gt; bf-ecb         bf-ofb         cast           cast-cbc       cast5-cbc\r\n    [20] =&gt; cast5-cfb      cast5-ecb      cast5-ofb      des            des-cbc\r\n    [21] =&gt; des-cfb        des-ecb        des-ede        des-ede-cbc    des-ede-cfb\r\n    [22] =&gt; des-ede-ofb    des-ede3       des-ede3-cbc   des-ede3-cfb   des-ede3-ofb\r\n    [23] =&gt; des-ofb        des3           desx           idea           idea-cbc\r\n    [24] =&gt; idea-cfb       idea-ecb       idea-ofb       rc2            rc2-40-cbc\r\n    [25] =&gt; rc2-64-cbc     rc2-cbc        rc2-cfb        rc2-ecb        rc2-ofb\r\n    [26] =&gt; rc4            rc4-40         rc5            rc5-cbc        rc5-cfb\r\n    [27] =&gt; rc5-ecb        rc5-ofb\r\n    [28] =&gt; \r\n)\r\n*\/\r\n\r\nvar_dump($retcode); \/\/int(0)\r\n<\/pre>\n<p>Schon besser. Damit l\u00e4sst sich dann mit einer Konstruktion dieser Art drangehen:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nif ($retcode === 0 &amp;&amp; stripos($retarray[0], &quot;openssl:Error&quot;) === false) {\r\n    \/\/alles okay, &quot;wirklicher&quot; Erfolg\r\n} else {\r\n    \/\/Fehler\r\n}\r\n<\/pre>\n<p>Damit w\u00e4re die Geschichte schonmal abgekl\u00e4rt. Aber es lauert noch eine weitere Gemeinheit: <b>Unter Linux<\/b> ergibt ein Aufruf an ein nicht existierendes Programm den <b>Returncode 127<\/b>:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$retarray = array();\r\nexec(&quot;foobar_this_aint_a_programm 2&gt;&amp;1&quot;, $retarray, $retcode);\r\nprint_r($retarray); \r\n\/*\r\nArray\r\n(\r\n    [0] =&gt; sh: foobar_this_aint_a_programm: not found\r\n)\r\n*\/\r\n\r\nvar_dump($retcode); \/\/int(127)\r\n<\/pre>\n<p>Nicht so jedoch unter <b>Windows<\/b> &#8211; Hier gibts den <b>Returncode 1<\/b> &#8211; was sich wieder \u00e4u\u00dferst toll von einem existenten, aber fehlerhaften Aufruf unterscheiden l\u00e4sst:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$retarray = array();\r\nexec(&quot;foobar_this_aint_a_programm 2&gt;&amp;1&quot;, $retarray, $retcode);\r\nprint_r($retarray); \r\n\/*\r\nArray\r\n(\r\n    [0] =&gt; Der Befehl &quot;foobar_this_aint_a_programm&quot; ist entweder falsch geschrieben oder\r\n    [1] =&gt; konnte nicht gefunden werden.\r\n)\r\n*\/\r\n\r\nvar_dump($retcode); \/\/int(1)\r\n<\/pre>\n<p>Es muss doch was sch\u00f6neres geben als das parsen des umgeleiteten stderr-Streams, oder? Tipps erbeten ;) !<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Heute folgendes Problem gehabt: Es soll ein Systemaufruf an openssl ts zur Signierung von Timestamps (siehe hier) erfolgen. Das an sich ist ja erst mal noch kein Problem. Allerdings soll die geschriebene Klasse gleicherma\u00dfen unter Linux und Windows mit m\u00f6glichst &hellip; <a href=\"https:\/\/d-mueller.de\/blog\/systemaufrufe-linux-windows-ruckgabewerte-und-der-errorstream\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,8,3,7],"tags":[],"class_list":["post-515","post","type-post","status-publish","format-standard","hentry","category-php","category-quicktips","category-webdev","category-linux"],"_links":{"self":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/comments?post=515"}],"version-history":[{"count":0,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/515\/revisions"}],"wp:attachment":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/media?parent=515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/categories?post=515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/tags?post=515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}