php proc_open file descriptor in bash command - php

When we use proc_open in PHP:
<?php
$descriptors = array(
array('pipe', 'r'),
array('pipe', 'w'),
array('pipe', 'w'),
);
$cmd = 'cat <(ls)';
//$cmd = 'ls';
echo $cmd . PHP_EOL;
$ph = proc_open($cmd, $descriptors, $pipes);
echo stream_get_contents($pipes[1]);
echo stream_get_contents($pipes[2]);
proc_close($ph);
When run this script, an error occurred:
$ php test.php
cat <(ls)
sh: 1: Syntax error: "(" unexpected
but run the raw cmd:
$ cat <(ls)
clusterRegions.php
composer.json
composer.lock
cvCells.php
kent
prepareForTF.php
README.txt
scoreRegions.php
test.php
tests
vendor
Seems that the <() thing cannot be recognized by proc_open.
So how to pass a bash command with a file descriptor to proc_open ?

#Etan Reisner, you are right, when I change to bash, it runs well.
$cmd = 'bash -c "cat <(ls)"';

Related

How to read binary from pipe in PHP

I'm trying to create a PNG from an SVG using Inkscape. I'm using Linux. the command,
cat in.svg | inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr 2> out.png
works fine, but I would rather not write the output files on the server.
My code is,
<?php
$svg_data = file_get_contents('in.svg');
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$pipes = array();
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr", $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $svg_data);
fclose($pipes[0]);
$fil_data = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
file_put_contents("out.png", $fil_data);
}
?>
If I change the line,
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr", $descriptorspec, $pipes);
to
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e out.png", $descriptorspec, $pipes);
The correct "out.png" is printed.
The current code writes a file, but the file is corrupted. It seems that it is corrupted from beginning to end, but it is about the right size.
I want the data (out.png) in $fil_data, but I don't want to read it from the disk drive.
Why is out.png corrupted, and how can I make the correct conversion without resorting to writing the disk.
Inkscape was putting some messages to stderr before writing the file. The same thing happened on stdout. The problem was fixed by skipping past the messages. So the line,
$fil_data = stream_get_contents($pipes[2]);
was changed to,
$fil_data = stream_get_contents($pipes[2], -1, 150);
If there is a better way, I would like to see it.

How to use exec function in PHP to run commands in backend?

I am using below curl command. exec() function is taking around 60 ms to execute which caused high response time.
curl -X POST -H 'Content-Type: application/json' --connect-timeout 1 -m 1 -d '<payload>' '<url>' > /dev/null 2>&1 &
You can use proc_open function instead of exec function. Also you can check what cause a problem in term of performance by printing in log file.
$descriptorspec = array(
array('pipe', 'r'), // stdin
array('file', 'response.log', 'a'), // stdout
array('pipe', 'w'), // stderr
);
$env = array(
'PATH' => '/usr/local/bin' //Path to node bin dir
);
$proc = proc_open('wget -qO- --post-data "{YOUR POST DATA}" "{URL}" &', $descriptorspec, $pipes, __DIR__, $env);
if (is_resource($proc))
{
proc_close($proc);
}
**Example:**
$proc = proc_open('wget -qO- --post-data "id=' . $_POST["id"] . '" "http://www.example.com" &', $descriptorspec, $pipes, __DIR__, $env);

Python ImportError while using youtube-dl by php

Traceback (most recent call last): File "/usr/bin/youtube-dl", line
3, in import youtube_dl File
"/usr/lib/python2.7/dist-packages/youtube_dl/init.py", line 65, in
from .utils import ( File
"/usr/lib/python2.7/dist-packages/youtube_dl/utils.py", line 18, in
import ssl File "/usr/lib/python2.7/ssl.py", line 61, in import _ssl
#if we can't import it, let the error propagate ImportError: /usr/lib/python2.7/lib-dynload/_ssl.x86_64-linux-gnu.so: symbol
GENERAL_NAME_free, version OPENSSL_1.0.0 not defined in file
libcrypto.so.1.0.0 with link time reference
The Command i used
youtube-dl --max-quality 2180 --write-thumbnail -x --audio-format mp3
-c -o "/home/bahaa/%(id)s.%(ext)s" https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems
The php code i am using
<?php
$url = 'https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems';
//escapeshellarg
$string = ('youtube-dl --max-quality 2180 --write-thumbnail -x --audio-format mp3 -c -o "/home/bahaa/%(id)s.%(ext)s" https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems');
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
$process = proc_open($string, $descriptorspec, $pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$ret = proc_close($process);
echo json_encode(
array(
'status' => $ret,
'errors' => str_replace("\n", "<br />", $stderr."<hr />"),
'output' => $stdout,
)
);
?>
Hi Finally I got the solution!
Firstly, would you please let me know your server configuration? I'm using XAMPP, in my case, the "Python" and its libs used by Apache are not the same as used by my ubuntu, so I build and install Python3 into /opt/lampp/bin and build openssl by myself. You can download openssl1.0.1 from its website and configure it by "./config shared" to build a .so file.
Next copy the libssl.so* and libcrypto.so* (totally 4 files) into /opt/lampp/lib (this is where LD_LIBRARY_PATH points to) and restart apache now
It works for me, Hope it will help you

How can I force GPG to accept input from STDIN instead of trying to open a file?

I am trying to incorporate GPG clear-signing of text in a string in a PHP script. I can cause GPG to encrypt text in a string like this:
$encrypted = shell_exec("echo '$text' | gpg -e -a -r foo#bar.com --trust-model always");
and that works perfectly, with the encrypted text being sent to the $encrypted variable. This proves GNUPGHOME and GNUPG are set up correctly.
However, when I try to produce a clear-signed message in the same way with this:
$text = "googar";
$signature = exec("echo $passphrase | gpg -v --clearsign --no-tty --passphrase-fd 0 '$text' 2>&1 1> /dev/null", $output);
I am returned this error:
... string(51) "gpg: can't open `googar': No such file or directory"
[3]=>
string(46) "gpg: googar: clearsign failed: file open error"
}
This error is returned with or without the single quotes around the $text variable.
How can I force GPG or shell_exec to treat $text as a pipe instead of it looking for a file?
I need to echo the passphrase in this way (I know, its 'horribly insecure' because GPG has no way to pass in a passphrase as a variable on the command line.
You could use proc_open and create a separate file descriptor for your password:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w"),
3 => array("pipe", "r"),
);
$pipes = false;
$process = proc_open("gpg -v --clearsign --no-tty --passphrase-fd 3", $descriptorspec, $pipes);
if(is_resource($process)) {
fwrite($pipes[3], $passphrase);
fclose($pipes[3]);
fwrite($pipes[0], $text);
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$retval = proc_close($process);
echo "retval = $retval\n";
echo "output= $output\n";
echo "err= $stderr\n";
}
You could use process substitution:
echo $passphrase | gpg -v --clearsign --no-tty --passphrase-fd 0 <(printf '$text') 2>&1 1> /dev/null
^^ ^
This will make gpg thinks it is reading data from a file, but the file will be a temporary named pipe which input will be printf '$text'.

PHP StdErr after Exec()

In PHP I am executing a command with exec(), and it returns if successful an URL;
$url = exec('report');
However, I want to check stderr, if something went wrong. How would I read the stream?
I want to use php://stderr, but I am not sure how to use it.
If you want to execute a command, and get both stderr and stdout, not "merged", a solution would probably to use proc_open, which provides a great level of control over the command that's being executed -- including a way to pipe stdin/stdout/stderr.
And here is an example : let's consider we have this shell-script, in test.sh, which writes to both stderr and stdout :
#!/bin/bash
echo 'this is on stdout';
echo 'this is on stdout too';
echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;
Now, let's code some PHP, in temp.php -- first, we initialize the i/o descriptors :
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
And, then, execute the test.sh command, using those descriptors, in the current directory, and saying the i/o should be from/to $pipes :
$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);
We can now read from the two output pipes :
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
And, if we output the content of those two variables :
echo "stdout : \n";
var_dump($stdout);
echo "stderr :\n";
var_dump($stderr);
We get the following output when executing the temp.php script :
$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"
A little function that might be helpful:
function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {
$proc = proc_open($cmd,[
1 => ['pipe','w'],
2 => ['pipe','w'],
],$pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
return proc_close($proc);
}
The exit code is returned and STDOUT and STDERR are reference params if you need them.
The short way to do such a things with exec is to return the exit code ( status of the command )
Note that I am trying to list a non-exists directory /non-dir/
exec('ls /non-dir/', $out, $retval);
var_dump($retval);
Output
ls: cannot access '/non-dir/': No such file or directory
int(2)
Normally in unix-based system most of successful statuses codes is ( 0 ) so you can check your $retval to know the status of the command.
to dismiss the error from listing an invalid path ls: cannot access '/non-dir/': No such file or directory you can redirect your stderr to null
exec('ls /non-dir/ 2>/dev/null', $out, $retval);
var_dump($retval);
this will output :
int(2)
also if you need the error string to use it in any scenario you may redirect your stderr to the stdout.
exec('ls /non-dir/ 2>&1', $out, $retval);
print_r($out);
var_dump($retval);
this will output the following:
Array
(
[0] => ls: cannot access '/non-dir/': No such file or directory
)
int(2)
Another way to get unmerged stdout/stderr.
$pp_name = "/tmp/pp_test";
#unlink($pp_name);
posix_mkfifo($pp_name, 0777);
$pp = fopen($pp_name, "r+");
stream_set_blocking($pp, FALSE);
exec("wget -O - http://www.youtube.com 2>$pp_name", $r_stdout);
$r_stderr = stream_get_contents($pp);
var_dump($r_stderr);
fclose($pp);
unlink($pp_name);
If you want to ignore stdout and get only stderr, you can try this:
exec("wget -O - http://www.youtube.com 2>&1 >/dev/null", $r_stderr);
exec("{$command} 2>&1"
,$output
,$exitCode
);
2>&1 redirects stderr to stdout for consistent success / fail behaviour.
$exitCode determines $command completion status.
$output contains all output associated with $exitCode.
Slightly ugly but good enough. Put the stderr into a temp file and read it back.
$tmp = tempnam("/tmp", "ERR_");
exec('report 2> ' . escapeshellarg($tmp), $stdout, $retcode);
$stderr = file_get_contents($tmp);
unlink($tmp);
if ($retcode == 0)
{
// good
$url = $stdout[0];
} else {
// bad
error_log("FAIL: $stderr");
}

Categories